mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bef190ddd7 | ||
|
|
cf320b3fa2 | ||
|
|
34c8745fa9 | ||
|
|
8096ff7f98 | ||
|
|
dad3006f84 | ||
|
|
0e35f15a86 | ||
|
|
d423740273 | ||
|
|
49f489c4dc | ||
|
|
ac71bf2111 | ||
|
|
6d40dd12fa | ||
|
|
a732abcdd6 | ||
|
|
c2f90f9daf | ||
|
|
898f56c32a | ||
|
|
2ebc4384f5 | ||
|
|
687b8d5e29 | ||
|
|
d09d95e069 | ||
|
|
4dffc1ffe9 | ||
|
|
f3b2cfbb87 | ||
|
|
4af57308d3 | ||
|
|
28ecf8666c | ||
|
|
1f3d1beb8f | ||
|
|
96d21bc544 | ||
|
|
f5cb2b5a1d | ||
|
|
d1d6e73bbc | ||
|
|
2321ae1c0c | ||
|
|
982ee5b6bf | ||
|
|
dbe699843b | ||
|
|
fda19760bd | ||
|
|
97d42152cd | ||
|
|
5981607783 | ||
|
|
7b7927f328 | ||
|
|
a8e2730f2f | ||
|
|
7ae18187bc | ||
|
|
02634c531f | ||
|
|
5e8ff89c43 | ||
|
|
2f307c492f | ||
|
|
e4b3e17c14 | ||
|
|
66d14a0754 | ||
|
|
cb47dcbe12 | ||
|
|
ea15b40e05 | ||
|
|
93cfd74fc0 | ||
|
|
79eb6a9078 | ||
|
|
2e9e5042e1 | ||
|
|
bb520edd53 | ||
|
|
2beb9563bb | ||
|
|
87c15f7611 | ||
|
|
655bc176fa | ||
|
|
66cb30d235 | ||
|
|
cbe0b25b5f | ||
|
|
a8ccf4e1c9 | ||
|
|
cce29c51e3 | ||
|
|
242015b0cc | ||
|
|
f209e1b0f9 | ||
|
|
8a1ab06585 | ||
|
|
50e817a86d | ||
|
|
dbd596f079 | ||
|
|
f8e3a07e6f | ||
|
|
ccd4ac3271 | ||
|
|
fce1925448 | ||
|
|
335eb5ef4d | ||
|
|
654c40b892 | ||
|
|
a937243cdf | ||
|
|
665d6baf1c | ||
|
|
b4d326b52c | ||
|
|
3aacec826b | ||
|
|
3261856385 | ||
|
|
8cf479bf3b | ||
|
|
3c3ef636eb | ||
|
|
3bd0b13740 | ||
|
|
92349227a2 | ||
|
|
4dfc2a638a | ||
|
|
d7c73a8072 | ||
|
|
297acc0322 | ||
|
|
1126b3b471 | ||
|
|
ac59973283 | ||
|
|
4b130ed71d | ||
|
|
ce90a33ce9 | ||
|
|
50fee32218 | ||
|
|
39334a4cd9 | ||
|
|
fec2131ba8 | ||
|
|
7c63436da4 | ||
|
|
de8fe63bd0 | ||
|
|
1c22f2387b | ||
|
|
e1b277582a | ||
|
|
94acc4f356 | ||
|
|
9afb62a6a4 | ||
|
|
95ed957d54 | ||
|
|
58814bc2da | ||
|
|
9e7556c3c1 | ||
|
|
abaf723fb7 | ||
|
|
f52e99c86e | ||
|
|
d6e70c35d8 | ||
|
|
4c91e73f0e | ||
|
|
d083fc6e43 | ||
|
|
ff2df25e3b | ||
|
|
86c2342097 | ||
|
|
6dbd176683 | ||
|
|
141aab1c22 | ||
|
|
126ea4f456 | ||
|
|
a0e5a97594 | ||
|
|
0420c11937 | ||
|
|
27472fd387 | ||
|
|
104bdc646f | ||
|
|
cbc4c275d1 | ||
|
|
372d51f873 | ||
|
|
0dfd404215 | ||
|
|
21b7393274 | ||
|
|
dcd5f5a3e6 | ||
|
|
bf313b3ba1 | ||
|
|
5b4e3c479c | ||
|
|
6dedb05c29 | ||
|
|
5f78e01342 | ||
|
|
881506a3d5 | ||
|
|
e26fc661b2 | ||
|
|
f921758ec0 | ||
|
|
01d65ba780 | ||
|
|
d77e2d6f45 | ||
|
|
7c30f55f31 | ||
|
|
c2d63865d5 | ||
|
|
f406b518c0 | ||
|
|
78f8c99f11 | ||
|
|
f97b9a419f | ||
|
|
f2b88b8cd6 | ||
|
|
cd06700c82 | ||
|
|
fa69040e00 | ||
|
|
fd88c1c0bb | ||
|
|
cae7d8d061 | ||
|
|
2e1e24c225 | ||
|
|
da0307b14f | ||
|
|
082c94ac07 | ||
|
|
2ed4b1b6fc | ||
|
|
8c7c8042e5 | ||
|
|
cca15a2b04 | ||
|
|
5f40411be0 | ||
|
|
191273a728 | ||
|
|
9eb9e9589f | ||
|
|
fdd45952c7 | ||
|
|
98378b222d | ||
|
|
15a0446d70 | ||
|
|
7bd1b7c670 | ||
|
|
46aaa47fdd | ||
|
|
2027e9f58e | ||
|
|
162e4690e9 | ||
|
|
c360575dec | ||
|
|
5b0354c2c0 | ||
|
|
52b0f21bd9 | ||
|
|
1e3cbde3b3 | ||
|
|
267fb727c7 | ||
|
|
68904f58ec | ||
|
|
5cbb1bb5dd | ||
|
|
038d221206 | ||
|
|
22d6ebd0d4 | ||
|
|
e8dd46c701 | ||
|
|
1150d07dbe | ||
|
|
5c294f4ad3 | ||
|
|
1b5bf0aab7 | ||
|
|
8d8a03602e | ||
|
|
df7bdb7a0c | ||
|
|
00a5eb2588 | ||
|
|
90631c66e0 | ||
|
|
c1ba692a79 | ||
|
|
8f3324e0d8 | ||
|
|
5a09a9ce78 | ||
|
|
33fcee7641 | ||
|
|
12b91940c7 | ||
|
|
0e6246f044 | ||
|
|
7f7cf97408 | ||
|
|
090fe9ccb6 | ||
|
|
ffae161282 | ||
|
|
1443850344 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# About
|
||||
This folder contains the plugin's description as advertised on various marketplaces where each one uses a different syntax.
|
||||
(bbcode, markdown).
|
||||
(BBCode, Markdown).
|
||||
|
||||
While this is not an accurate folder to have this stored in (`.github`),
|
||||
it is the closest if we don't want to create a folder for this in the root.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="156" height="20" role="img" aria-label="Minecraft: 1.7.x - 1.21.10"><title>Minecraft: 1.7.x - 1.21.10</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="156" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="95" height="20" fill="#007ec6"/><rect width="156" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">Minecraft</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">Minecraft</text><text aria-hidden="true" x="1075" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="850">1.7.x - 1.21.10</text><text x="1075" y="140" transform="scale(.1)" fill="#fff" textLength="850">1.7.x - 1.21.10</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 5.3.2"><title>Release: 5.3.2</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">Release</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">Release</text><text aria-hidden="true" x="715" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">5.3.2</text><text x="715" y="140" transform="scale(.1)" fill="#fff" textLength="290">5.3.2</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.0.2"><title>Release: 6.0.2</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">Release</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">Release</text><text aria-hidden="true" x="715" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">6.0.2</text><text x="715" y="140" transform="scale(.1)" fill="#fff" textLength="290">6.0.2</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -274,12 +274,9 @@ if __name__ == "__main__":
|
||||
]
|
||||
)
|
||||
|
||||
release = "5.3.2"
|
||||
release = "6.0.2"
|
||||
download_image(f"https://img.shields.io/badge/Release-{release}-blue.svg", "Badge_Release.svg")
|
||||
|
||||
minecraft = "1.7.x - 1.21.10".replace(" ", "%20").replace("-", "--")
|
||||
download_image(f"https://img.shields.io/badge/Minecraft-{minecraft}-blue.svg", "Badge_Minecraft.svg")
|
||||
|
||||
java = "8+"
|
||||
download_image(f"https://img.shields.io/badge/Java-{java}-blue.svg", "Badge_Java.svg")
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
|
||||
## Functions
|
||||
* [Animations](https://github.com/NEZNAMY/TAB/wiki/Animations)
|
||||
* [Component usage (RGB, fonts, sprites)](https://github.com/NEZNAMY/TAB/wiki/How-to-use-Minecraft-components)
|
||||
* [Conditional placeholders](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders)
|
||||
* [Error logging](https://github.com/NEZNAMY/TAB/wiki/Error-logging)
|
||||
* [MySQL](https://github.com/NEZNAMY/TAB/wiki/MySQL)
|
||||
* [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements)
|
||||
* [Placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders)
|
||||
* [RGB/font usage](https://github.com/NEZNAMY/TAB/wiki/How-to-use-RGB-color-codes)
|
||||
|
||||
## Other
|
||||
* [Additional information](https://github.com/NEZNAMY/TAB/wiki/Additional-information)
|
||||
|
||||
@@ -59,7 +59,7 @@ TAB's objective and team naming:
|
||||
# Per-version experience
|
||||
Minecraft has changed over the years.
|
||||
It had some limits removed, some added, some bugs fixed, but also some bugs added.
|
||||
TAB adapts code based on player's version for the best possible experience on each minecraft version.
|
||||
TAB adapts code based on player's version for the best possible experience on each Minecraft version.
|
||||
|
||||
## Detection
|
||||
When TAB is installed on a proxy, the proxy's API is used to get player's version.
|
||||
|
||||
@@ -44,6 +44,7 @@ That includes header/footer, prefix/suffix, belowname, playerlist objective, bos
|
||||
# Tips & Tricks
|
||||
* Animations don't need to be fast. You can use them as a changing text in general.
|
||||
* Animations can also be used as a short alias to a long text. You can do this by creating an animation with one frame.
|
||||
* Adding to the previous point, animations can be used as a way to wrap a placeholder, having two different identifiers return the same value, allowing to use both original and replaced output from [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements).
|
||||
|
||||
# External tools
|
||||
If you want an animation too complicated to be done manually, you can check out these websites:
|
||||
@@ -56,5 +57,5 @@ If you want an animation too complicated to be done manually, you can check out
|
||||
All animations start at the same time, which is time of plugin being (re)loaded.
|
||||
Thanks to this, all animations with the same number of frames and the same change interval will be synchronized.
|
||||
If you want to synchronize multiple animations with a different change interval and different number of frames,
|
||||
you need to make sure `(frame count)*(change interval)` is same for both animations,
|
||||
for example one animation with 10 frames and 1000ms and second animation with 2 frames and 5000ms.
|
||||
you need to make sure `(frame count)*(change interval)` is same for both animations.
|
||||
For example, one animation with 10 frames and 1000ms and second animation with 2 frames and 5000ms.
|
||||
@@ -5,7 +5,7 @@
|
||||
* [Tablist entry widths](#tablist-entry-widths)
|
||||
|
||||
# About
|
||||
This is a collection of mechanics related to the plugin explaining how does the minecraft client process information from the server and what you can and what you cannot impact.
|
||||
This is a collection of mechanics related to the plugin explaining how does the Minecraft client process information from the server and what you can and what you cannot impact.
|
||||
|
||||
# Nametag format in tablist
|
||||
Tablist names can be formatted in 2 ways. The main one, designed explicitly for tablist names was added into Minecraft in version 1.8 and can be controlled by TAB's [Tablist name formatting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting) feature.
|
||||
|
||||
@@ -10,30 +10,30 @@ Commands have required permissions associated with them.
|
||||
|
||||
# Content
|
||||
* [Configuration](#configuration-commands)
|
||||
* [/tab \<player/group/playeruuid\> \<name\> \<property\> \[value\] \[options\]](#tab-playergroupplayeruuid-name-property-value-options)
|
||||
* [/tab \<player/group/playeruuid\> \<name\> remove](#tab-playergroupplayeruuid-name-remove)
|
||||
* [/tab reload](#tab-reload)
|
||||
* [/tab debug \[player\]](#tab-debug-player)
|
||||
* [/tab group \<group\>](#tab-group-group)
|
||||
* [/tab groups](#tab-groups)
|
||||
* [/tab \<player/group/playeruuid\> \<name\> \<property\> \[value\] \[options\]](#tab-playergroupplayeruuid-name-property-value-options)
|
||||
* [/tab \<player/group/playeruuid\> \<name\> remove](#tab-playergroupplayeruuid-name-remove)
|
||||
* [/tab reload](#tab-reload)
|
||||
* [/tab dump \[player\]](#tab-dump-player)
|
||||
* [/tab group \<group\>](#tab-group-group)
|
||||
* [/tab groups](#tab-groups)
|
||||
* [Bossbar](#bossbar)
|
||||
* [/tab bossbar send \<name\> \[player\]](#tab-bossbar-show-name-player)
|
||||
* [/tab bossbar \[on/off/toggle\] \[player\] \[options\]](#tab-bossbar-onofftoggle-player-options)
|
||||
* [/tab bossbar announce \<name\> \<length\>](#tab-bossbar-announce-name-duration)
|
||||
* [/tab bossbar send \<name\> \[player\]](#tab-bossbar-show-name-player)
|
||||
* [/tab bossbar \[on/off/toggle\] \[player\] \[options\]](#tab-bossbar-onofftoggle-player-options)
|
||||
* [/tab bossbar announce \<name\> \<length\>](#tab-bossbar-announce-name-duration)
|
||||
* [Scoreboard](#scoreboard)
|
||||
* [/tab scoreboard show \<name\> \[player\]](#tab-scoreboard-show-name-player)
|
||||
* [/tab scoreboard \[on/off/toggle\] \[player\] \[options\]](#tab-scoreboard-onofftoggle-player-options)
|
||||
* [/tab scoreboard announce \<name\> \<length\>](#tab-scoreboard-announce-name-duration)
|
||||
* [/tab scoreboard show \<name\> \[player\]](#tab-scoreboard-show-name-player)
|
||||
* [/tab scoreboard \[on/off/toggle\] \[player\] \[options\]](#tab-scoreboard-onofftoggle-player-options)
|
||||
* [/tab scoreboard announce \<name\> \<length\>](#tab-scoreboard-announce-name-duration)
|
||||
* [Nametags / Teams](#nametags--teams)
|
||||
* [/tab nametag <show/hide/toggle> \[player\] \[viewer\] \[options\]](#tab-nametag-showhidetoggle-player-viewer-options)
|
||||
* [/tab nametag <showview/hideview/toggleview> \[viewer\] \[options\]](#tab-nametag-showviewhideviewtoggleview-viewer-options)
|
||||
* [/tab setcollision <player> <true/false>](#tab-setcollision-player-truefalse)
|
||||
* [/tab nametag <show/hide/toggle> \[player\] \[viewer\] \[options\]](#tab-nametag-showhidetoggle-player-viewer-options)
|
||||
* [/tab nametag <showview/hideview/toggleview> \[viewer\] \[options\]](#tab-nametag-showviewhideviewtoggleview-viewer-options)
|
||||
* [/tab setcollision <player> <true/false>](#tab-setcollision-player-truefalse)
|
||||
* [MySQL](#mysql)
|
||||
* [/tab mysql upload](#tab-mysql-upload)
|
||||
* [/tab mysql download](#tab-mysql-download)
|
||||
* [/tab mysql upload](#tab-mysql-upload)
|
||||
* [/tab mysql download](#tab-mysql-download)
|
||||
* [Other](#other)
|
||||
* [/tab cpu](#tab-cpu)
|
||||
* [/tab parse \<player\> \<placeholder\>](#tab-parse-player-text)
|
||||
* [/tab cpu](#tab-cpu)
|
||||
* [/tab parse \<player\> \<placeholder\>](#tab-parse-player-text)
|
||||
* [Additional permissions](#additional-permissions)
|
||||
|
||||
# Configuration commands
|
||||
@@ -65,13 +65,9 @@ Commands have required permissions associated with them.
|
||||
* **Permission:** `tab.reload`
|
||||
* **Description:** Unloads the plugin, loads configuration files including changes and loads the plugin.
|
||||
|
||||
## /tab debug [player]
|
||||
* **Permission:** `tab.debug`
|
||||
* **Description:** Shows the server version, plugin version, permission group choice logic, and sorting type. If player argument is filled, shows info about that player:
|
||||
* On proxy, shows whether player is connected to the backend server with [Bridge](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) plugin or not.
|
||||
* Sorting value & explanation, useful to see what went wrong if players are not sorted correctly.
|
||||
* Primary group set using [How to assign players into groups](https://github.com/NEZNAMY/TAB/wiki/How-to-assign-players-into-groups)
|
||||
* List of all configured properties, their values and source.
|
||||
## /tab dump \<player\>
|
||||
* **Permission:** `tab.dump`
|
||||
* **Description:** Creates a very detailed dump about the specified player to help with troubleshooting, uploads it to a paste site and shows the generated URL.
|
||||
|
||||
## /tab group \<group\>
|
||||
* **Permission:** `tab.groupinfo`
|
||||
@@ -90,7 +86,7 @@ Commands have required permissions associated with them.
|
||||
* **Permission:** `tab.scoreboard.toggle` for toggling for yourself, `tab.scoreboard.toggle.other` for toggling for others.
|
||||
* **Description:** Shows / hides / toggles scoreboard of specified player. If no player was given, command affects the sender.
|
||||
* **Options:**
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
|
||||
## /tab scoreboard announce \<name\> \<duration\>
|
||||
* **Permission:** `tab.announce.scoreboard`
|
||||
@@ -105,7 +101,7 @@ Commands have required permissions associated with them.
|
||||
* **Permission:** `tab.bossbar.toggle` for toggling for yourself, `tab.bossbar.toggle.other` for toggling for others.
|
||||
* **Description:** Shows / hides / toggles bossbar of specified player. If no player was given, command affects the sender.
|
||||
* **Options:**
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
|
||||
## /tab bossbar announce \<name\> \<duration\>
|
||||
* **Permission:** `tab.announce.bar`
|
||||
@@ -116,13 +112,13 @@ Commands have required permissions associated with them.
|
||||
* **Permission:** `tab.nametag.visibility` (`tab.nametag.visibility.other` for toggling for other players)
|
||||
* **Description:** Shows / hides / toggles nametag of a specified player. If viewer is specified, view is only affected for the viewer.
|
||||
* **Options:**
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
|
||||
## /tab nametag <showview/hideview/toggleview> \[viewer\] \[options\]
|
||||
* **Permission:** `tab.nametag.view` (`tab.nametag.view.other` for toggling for other players)
|
||||
* **Description:** Shows / hides / toggles nametag VIEW a specified player.
|
||||
* **Options:**
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
* `-s` for silent toggling (no chat message for affected player)
|
||||
|
||||
## /tab setcollision \<player\> \<true|false\>
|
||||
* **Permission:** `tab.setcollision`
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
# Content
|
||||
* [Requirements](#requirements)
|
||||
* [Java](#java)
|
||||
* [Java](#java)
|
||||
* [Supported server software and versions](#supported-server-software-and-versions)
|
||||
* [Supported features per platform](#supported-features-per-platform)
|
||||
* [Plugin hooks](#plugin-hooks)
|
||||
* [All platforms](#all-platforms)
|
||||
* [Bukkit](#bukkit)
|
||||
* [BungeeCord](#bungeecord)
|
||||
* [Fabric](#fabric)
|
||||
* [Forge / NeoForge](#forge--neoforge)
|
||||
* [Sponge](#sponge)
|
||||
* [Velocity](#velocity)
|
||||
* [Plugin / mod hooks](#plugin--mod-hooks)
|
||||
* [Compatibility issues](#compatibility-issues)
|
||||
|
||||
# Requirements
|
||||
@@ -18,7 +11,7 @@ TAB does not depend on any other plugins. The experience can however be enhanced
|
||||
|
||||
## Java
|
||||
TAB's shared module is compiled with Java 8. For the platforms themselves, TAB does have a higher requirement, however, it does not require a Java version higher than the server software itself does. Specifically:
|
||||
* Sponge, Fabric, Forge and NeoForge require Java 21
|
||||
* Fabric, Forge and NeoForge require Java 21
|
||||
* Velocity requires Java 17
|
||||
* Bukkit and BungeeCord are ok with Java 8
|
||||
|
||||
@@ -35,26 +28,26 @@ TAB's shared module is compiled with Java 8. For the platforms themselves, TAB d
|
||||
<tr>
|
||||
<td rowspan=2>Vanilla</td>
|
||||
<td rowspan=1><a href="https://getbukkit.org/">Bukkit</a> (+forge hybrids)</td>
|
||||
<td>✔ (1.7.x - 1.21.10)</td>
|
||||
<td>✔ (1.7.10 ~ 26.1.1) (not all versions included)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://www.spongepowered.org/">Sponge</a></td>
|
||||
<td>✔ (1.20.6 - 1.21.10)</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan=3>Modded</td>
|
||||
<td rowspan=1><a href="https://fabricmc.net">Fabric</a></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
<td>✔ (1.21.11)<sup>1</sup></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://minecraftforge.net">Forge</a></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
<td>✔ (1.21.11)<sup>1</sup></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://neoforged.net/">NeoForge</a></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
<td>✔ (1.21.11)<sup>1</sup></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
@@ -70,7 +63,7 @@ TAB's shared module is compiled with Java 8. For the platforms themselves, TAB d
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<sup>1</sup> Modded platforms only support the latest MC version(s). For older MC versions try [modrinth](https://modrinth.com/plugin/tab-was-taken)'s version download filter. If TAB is not available for your desired Minecraft version or is very outdated, you may [request a backport](https://github.com/NEZNAMY/TAB/issues/new?template=backport.yml).
|
||||
<sup>1</sup> Modded platforms only support the latest MC version(s). For older MC versions try [Modrinth](https://modrinth.com/plugin/tab-was-taken)'s version download filter. If TAB is not available for your desired Minecraft version or is very outdated, you may [request a backport](https://github.com/NEZNAMY/TAB/issues/new?template=backport.yml).
|
||||
|
||||
<sup>2</sup> Latest only doesn't mean only the latest build will work,
|
||||
it means the plugin was made to be compatible with the latest version/build.
|
||||
@@ -81,76 +74,52 @@ Since proxies support all client versions, there is never a reason to stay outda
|
||||
so you can always safely update to new version/build of your proxy software if the plugin requires it.
|
||||
|
||||
# Supported features per platform
|
||||
| Feature \ Platform | Bukkit / Hybrid | Sponge | Fabric / Forge / NeoForge | BungeeCord | Velocity |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|--------|---------------------------|------------|----------------------------------------------------------------------------|
|
||||
| [Belowname](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname) | ✔ | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [BossBar](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Global Playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Header/Footer](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Nametags](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags) & [Sorting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist) | ✔ | ❗ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Per world playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist) | ✔ | ❌ | ❌ | ❌ | ❌ |
|
||||
| [Playerlist objective](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Playerlist-Objective) | ✔ | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Scoreboard](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard) | ✔ | ❗ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Spectator fix](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Tablist names](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting) | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| Feature <sup>Platform</sup> | Bukkit / Hybrid | Fabric / Forge / NeoForge | BungeeCord | Velocity |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|---------------------------|------------|----------------------------------------------------------------------------|
|
||||
| [Belowname](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname) | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [BossBar](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar) | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Global Playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist) | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Header/Footer](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer) | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Nametags](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags) & [Sorting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist) | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Per world playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist) | ✔ | ❌ | ❌ | ❌ |
|
||||
| [Playerlist objective](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Playerlist-Objective) | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Scoreboard](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard) | ✔ | ✔ | ✔ | ✔ (via [VSAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)) |
|
||||
| [Spectator fix](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix) | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Tablist names](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting) | ✔ | ✔ | ✔ | ✔ |
|
||||
|
||||
✔ = Fully functional
|
||||
❗ = NameTags: Anti-override missing, Scoreboard: [compatibility with other plugins](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard#compatibility-with-other-plugins) missing.
|
||||
❌ = Completely missing
|
||||
|
||||
# Plugin hooks
|
||||
To enhance user experience, TAB hooks into other plugins for better experience.
|
||||
This set of plugins is different for each platform based on their availability.
|
||||
# Plugin / mod hooks
|
||||
To enhance user experience, TAB hooks into other plugins / mods for better experience.
|
||||
This set of plugins / mods is different for each platform based on their availability.
|
||||
Some are available on all platforms, some only in a few.
|
||||
## All platforms
|
||||
[**Floodgate**](https://github.com/GeyserMC/Floodgate) - For properly detecting bedrock players to adapt features for the best possible user experience.
|
||||
[**LuckPerms**](https://github.com/LuckPerms/LuckPerms) - Detecting permission groups of players for per-group settings.
|
||||
[**ViaVersion**](https://github.com/ViaVersion/ViaVersion) - For properly detecting player's version to adapt features for the best possible user experience.
|
||||
|
||||
## Bukkit
|
||||
[**LibsDisguises**](https://github.com/libraryaddict/LibsDisguises) - Detecting disguised players to disable collision to avoid endless push by colliding with own copy created by LibsDisguises.
|
||||
[**PlaceholderAPI**](https://github.com/PlaceholderAPI/PlaceholderAPI) - Allows users to use its placeholders inside TAB.
|
||||
[**PremiumVanish**](https://www.spigotmc.org/resources/14404) - Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system.
|
||||
[**Vault**](https://github.com/milkbowl/Vault) - Detecting permission groups of players for per-group settings.
|
||||
|
||||
## BungeeCord
|
||||
[**PremiumVanish**](https://www.spigotmc.org/resources/14404) -
|
||||
Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system.
|
||||
[**RedisBungee**](https://github.com/ProxioDev/RedisBungee) -
|
||||
Communicating with other proxies to properly display visuals on players on another proxy.
|
||||
|
||||
## Fabric
|
||||
[**fabric-permissions-api**](https://github.com/lucko/fabric-permissions-api) - Supporting permission nodes instead of OP levels.
|
||||
[**placeholder-api**](https://modrinth.com/mod/placeholder-api) - Displaying placeholders from that mod and offering TAB's placeholders into it.
|
||||
|
||||
## Forge / NeoForge
|
||||
[**LuckPerms**](https://github.com/LuckPerms/LuckPerms) - Permission checks with a string instead of using vanilla OP levels.
|
||||
|
||||
## Sponge
|
||||
*None*
|
||||
|
||||
## Velocity
|
||||
[**PremiumVanish**](https://www.spigotmc.org/resources/14404) -
|
||||
Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system.
|
||||
[**RedisBungee**](https://github.com/ProxioDev/RedisBungee) -
|
||||
Communicating with other proxies to properly display visuals on players on another proxy.
|
||||
[**VelocityScoreboardAPI**](https://github.com/NEZNAMY/VelocityScoreboardAPI) -
|
||||
Sending scoreboard packets (scoreboard-teams, belowname-objective, playerlist-objective, scoreboard)
|
||||
| Platform | Plugins / mods |
|
||||
|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| All platforms | [**Floodgate**](https://github.com/GeyserMC/Floodgate) - For properly detecting bedrock players to adapt features for the best possible user experience. <br /> [**LuckPerms**](https://github.com/LuckPerms/LuckPerms) - Detecting permission groups of players for per-group settings. <br /> [**ViaVersion**](https://github.com/ViaVersion/ViaVersion) - For properly detecting player's version to adapt features for the best possible user experience. |
|
||||
| Bukkit | [**LibsDisguises**](https://github.com/libraryaddict/LibsDisguises) - Detecting disguised players to disable collision to avoid endless push by colliding with own copy created by LibsDisguises. <br />[**PlaceholderAPI**](https://github.com/PlaceholderAPI/PlaceholderAPI) - Allows users to use its placeholders inside TAB. <br />[**PremiumVanish**](https://www.spigotmc.org/resources/14404) - Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system. <br />[**Vault**](https://github.com/milkbowl/Vault) - Detecting permission groups of players for per-group settings. |
|
||||
| BungeeCord | [**PremiumVanish**](https://www.spigotmc.org/resources/14404) - Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system. <br />[**RedisBungee**](https://github.com/ProxioDev/RedisBungee) - Communicating with other proxies to properly display visuals on players on another proxy. |
|
||||
| Fabric | [**fabric-permissions-api**](https://github.com/lucko/fabric-permissions-api) - Supporting permission nodes instead of OP levels. <br />[**placeholder-api**](https://modrinth.com/mod/placeholder-api) - Displaying placeholders from that mod and offering TAB's placeholders into it. |
|
||||
| Forge | [**LuckPerms**](https://github.com/LuckPerms/LuckPerms) - Permission checks with a string instead of using vanilla OP levels. <br /> [**ForgePlaceholderAPI**](https://github.com/EnvyWare/ForgePlaceholderAPI) - Allows users to use its placeholders inside TAB. <br /> [**Vanishmod**](https://modrinth.com/mod/vanishmod) - Getting player's vanish status. |
|
||||
| NeoForge | [**LuckPerms**](https://github.com/LuckPerms/LuckPerms) - Permission checks with a string instead of using vanilla OP levels. |
|
||||
| Velocity | [**PremiumVanish**](https://www.spigotmc.org/resources/14404) - Supporting PremiumVanish's vanishing levels instead of using a basic compatibility system. <br />[**RedisBungee**](https://github.com/ProxioDev/RedisBungee) - Communicating with other proxies to properly display visuals on players on another proxy. <br />[**VelocityScoreboardAPI**](https://github.com/NEZNAMY/VelocityScoreboardAPI) - Sending scoreboard packets (scoreboard-teams, belowname-objective, playerlist-objective, scoreboard) |
|
||||
|
||||
# Compatibility issues
|
||||
* **Glow plugins** will fail to apply glow color correctly. Check [How to make the plugin compatible with glow plugins](https://github.com/NEZNAMY/TAB/wiki/How-to-make-TAB-compatible-with-glow-plugins) for more information.
|
||||
* Some Skript addons may cause features to not work properly, such as sorting and scoreboard.
|
||||
* **Waterfall**'s `disable_tab_list_rewrite: true` **may** cause tablist to use offline UUIDs while TAB expects online uuids, causing various problems (most notably tablist formatting not working). Checking for this option is not an option either, because tablist rewrite might still be enabled despite being disabled (don't ask how, I have no idea). Set the option to `false` if you are experiencing issues.
|
||||
* **Waterfall**'s `disable_tab_list_rewrite: true` **may** cause tablist to use offline UUIDs while TAB expects online UUIDs, causing various problems (most notably tablist formatting not working). Checking for this option is not an option either, because tablist rewrite might still be enabled despite being disabled (don't ask how, I have no idea). Set the option to `false` if you are experiencing issues.
|
||||
* **ViaVersion on proxy and TAB on backend** acts like a client-sided protocol hack, making it impossible for TAB to know player's real version and causing issues related to it, see [Per-version experience](https://github.com/NEZNAMY/TAB/wiki/Additional-information#per-version-experience) for more info. Avoid this combination. Either install ViaVersion on all backend servers instead or install TAB on the proxy as well.
|
||||
* **Nexo**'s `hide_scoreboard_numbers` config option (may apply to ItemsAdder and Oraxen as well) makes [Belowname](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname)'s and [Playerlist objective](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Playerlist-Objective)'s `value` and `fancy-value` not visible. When using these features, you'll need to keep the option disabled.
|
||||
* **LimboAuth** may prevent TAB from applying tablist formatting in a way that is not detectable using Velocity API, resulting in the feature not working properly on join.
|
||||
* **Custom clients / resource packs** - Unofficially modified minecraft clients often tend to break things. Just Lunar client has tons of bugs that can be reproduced with TAB. Resource packs may also contain modifications you are not aware of, making things not look the way you want them to. If you are experiencing any visual issue and are using a custom client or resource pack, try it with a clean vanilla client. If it works there, it's an issue with the client / resource pack and TAB cannot do anything about it.
|
||||
* **Custom clients / resource packs** - Unofficially modified Minecraft clients often tend to break things. Just Lunar client has tons of bugs that can be reproduced with TAB. Resource packs may also contain modifications you are not aware of, making things not look the way you want them to. If you are experiencing any visual issue and are using a custom client or resource pack, try it with a clean vanilla client. If it works there, it's an issue with the client / resource pack and TAB cannot do anything about it.
|
||||
For example, here are a few bugs in LunarClient / FeatherClient that you may run into when using TAB:
|
||||
* They add their icon to players in tablist, but don't widen the entries. This results in player names overlapping with latency bar. You can avoid this by configuring some spaces in tabsuffix.
|
||||
* They don't support color field in chat components, which means they don't support RGB codes and will display bossbar without colors as well.
|
||||
* Rendering belowname 3 times.
|
||||
* They don't respect nametag visibility rule, showing their own nametag using F5 even if set to invisible by the plugin.
|
||||
* When the scoreboard is set to use all 0s, lines are rendered in opposite order on 1.20.3+.
|
||||
* They don't display 1.20.3+ NumberFormat feature in scoreboard objectives.
|
||||
* **Random Spigot/BungeeCord forks** - All safe patches for improving security & performance are present in well-known public opensource projects, such as Paper, Purpur or Waterfall. Using a random overpriced closed-source (and probably obfuscated) fork from BuiltByBit may not be safe, since they likely include unsafe patches that may break compatibility with plugins in an attempt to fix things that are not broken. Before spending your entire budget on such a fork, reconsider it first. Paper (and its forks) is a performance-oriented fork used by 2/3 of all MC servers worldwide, while the rest is still stuck on Spigot. It is highly unlikely your needs are so specific you need every single "improvement" anyone can come up with. If you need a feature, Purpur is a feature-oriented fork. Together with plugins you should achieve what you are looking for.
|
||||
* They add their icon to players in tablist, but don't widen the entries. This results in player names overlapping with latency bar. You can avoid this by configuring some spaces in tabsuffix.
|
||||
* They don't support color field in chat components, which means they don't support RGB codes and will display bossbar without colors as well.
|
||||
* Rendering belowname 3 times.
|
||||
* They don't respect nametag visibility rule, showing their own nametag using F5 even if set to invisible by the plugin.
|
||||
* When the scoreboard is set to use all 0s, lines are rendered in opposite order on 1.20.3+.
|
||||
* They don't display 1.20.3+ NumberFormat feature in scoreboard objectives.
|
||||
* **XCord** - Has additional amazing optimizations that prevent TAB from being able to read outgoing packets and modify them if needed, resulting in some features not working properly, such as [tablist name formatting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting).
|
||||
* **Random Spigot/BungeeCord forks** - All safe patches for improving security & performance are present in well-known public open-source projects, such as Paper, Purpur or Waterfall. Using a random overpriced closed-source (and probably obfuscated) fork from BuiltByBit may not be safe, since they likely include unsafe patches that may break compatibility with plugins in an attempt to fix things that are not broken. Before spending your entire budget on such a fork, reconsider it first. Paper (and its forks) is a performance-oriented fork used by 2/3 of all MC servers worldwide, while the rest is still stuck on Spigot. It is highly unlikely your needs are so specific you need every single "improvement" anyone can come up with. If you need a feature, Purpur is a feature-oriented fork. Together with plugins you should achieve what you are looking for.
|
||||
@@ -35,7 +35,7 @@ For Maven users, you can add the API dependency like this:
|
||||
<dependency>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>TAB-API</artifactId>
|
||||
<version>5.3.2</version>
|
||||
<version>5.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -50,7 +50,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.github.NEZNAMY:TAB-API:5.3.2'
|
||||
compileOnly 'com.github.NEZNAMY:TAB-API:5.5.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -61,7 +61,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.github.NEZNAMY", "TAB-API", "5.3.2")
|
||||
compileOnly("com.github.NEZNAMY", "TAB-API", "5.5.0")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Copying nametag visibility rule](#additional-note-1---copying-nametag-visibility-rule)
|
||||
* [Additional note 2 - Hidden on sneak on 1.8](#additional-note-2---hidden-on-sneak-on-18)
|
||||
* [Additional note 3 - Visible on NPCs](#additional-note-3---visible-on-npcs)
|
||||
* [Additional note 4 - Compatibility with modified clients](#additional-note-4---compatibility-with-modified-clients)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Limitations](#limitations)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Copying nametag visibility rule](#additional-note-1---copying-nametag-visibility-rule)
|
||||
* [Additional note 2 - Hidden on sneak on 1.8](#additional-note-2---hidden-on-sneak-on-18)
|
||||
* [Additional note 3 - Visible on NPCs](#additional-note-3---visible-on-npcs)
|
||||
* [[26.1] Visible on all entities](#261-visible-on-all-entities)
|
||||
* [Additional note 4 - Compatibility with modified clients](#additional-note-4---compatibility-with-modified-clients)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-world values](#example-1---per-world-values)
|
||||
* [Example 2 - Hiding `title` for 1.20.3+ players](#example-2---hiding-title-for-1203-players)
|
||||
* [Example 3 - Displaying health as 0-10 or in %](#example-3---displaying-health-as-0-10-or-in-)
|
||||
* [Example 4 - Health bar using hearts](#example-4---health-bar-using-hearts)
|
||||
* [Example 1 - Per-world values](#example-1---per-world-values)
|
||||
* [Example 2 - Hiding `title` for 1.20.3+ players](#example-2---hiding-title-for-1203-players)
|
||||
* [Example 3 - Displaying health as 0-10 or in %](#example-3---displaying-health-as-0-10-or-in-)
|
||||
* [Example 4 - Health bar using hearts](#example-4---health-bar-using-hearts)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Tip 1 - Heart symbol](#tip-1---heart-symbol)
|
||||
* [Tip 1 - Heart symbol](#tip-1---heart-symbol)
|
||||
|
||||
# About
|
||||
This features gives you control over Minecraft's scoreboard objective feature with BELOW_NAME slot.
|
||||
This line is displayed below the nametags of all player entities in game. It is not possible to explicitly disable this feature for NPCs, however, this goal can be achieved (see below for more info).
|
||||
This line is displayed below the nametags of all player entities in game. It is not possible to explicitly disable this feature for NPCs; however, this goal can be achieved (see below for more info).
|
||||
It is only visible when within an 8-block range of the player (the range is hardcoded in the client and cannot be changed with a plugin).
|
||||
|
||||
Example visual effect:
|
||||

|
||||
|
||||
The displayed line is not one continuous configurable text. It consists of 2 parts (score and title) joined with a space.
|
||||
The exact format is `[score]` + space + `title` (no, the space cannot be removed), where `[score]` is:
|
||||
* `value` for 1.20.2- (will show `0` on NPCs), which only supports numbers (integers)
|
||||
* `fancy-value` for 1.20.3+ (will show `fancy-value-default` on NPCs), which supports any text
|
||||
|
||||
As you can see, the feature still has limits, even on 1.20.3+. If you are below this version or still don't like the forced space before title, you are out of luck and cannot use this feature the way you would like to. If you have seen this feature seemingly limitless, it could be a feature that was a part of TAB, but removed later. This feature hid the original nametag and placed invisible armor stands instead. If you are interested in this kind of solution, you'll need to look into other plugins.
|
||||
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **belowname-objective** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
@@ -35,27 +44,38 @@ belowname-objective:
|
||||
fancy-value-default: "NPC"
|
||||
disable-condition: '%world%=disabledworld'
|
||||
```
|
||||
All the options are explained in the following table.
|
||||
|
||||
The displayed line is not one continuous configurable text. It consists of 2 parts (score and title) joined with a space.
|
||||
The exact format is `[score]` + space + `title` (no, the space cannot be removed), where `[score]` is:
|
||||
* `value` for 1.20.2- (will show `0` on NPCs), which only supports numbers
|
||||
* `fancy-value` for 1.20.3+ (will show `fancy-value-default` on NPCs), which supports any text
|
||||
| Option name | Default value | Description |
|
||||
|---------------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| value | %health% | [1.20.2-] An integer from -2147483648 to 2147483647, doesn't support decimal values. The number is always white. Supports placeholders with player-specific output, such as player health. Only visible on 1.20.2 and lower. <br/> **Note**: Even if you only support 1.20.3+, you still need to configure this value to properly evaluate to a number, because the value is still sent to the client (just not displayed). You can set it to `0` for simplicity. |
|
||||
| fancy-value | &c%health% | [1.20.3+] Any text, supports placeholders with per-player output. Only visible on 1.20.3+, where it completely replaces `value`. |
|
||||
| fancy-value-default | NPC | [1.20.3+] Default number format (`fancy-value`) for all player entities. `fancy-value` is displayed on every real player, therefore this default value will only appear on player entities which are not actual players, a.k.a. NPCs. Only visible on 1.20.3+. |
|
||||
| title | Health | Shared label shown after the score for every player entity. Player sees the same text on everyone (= placeholders are parsed for the viewer). Use the `value`/`fancy-value` fields for per‑player data; use `title` only for static labels like `Health` or placeholders which are supposed to be parsed for the viewing player. |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) (either name of a condition or a conditional expression) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. <br/> **Note**: Disabling the feature for a player means sending objective unregister packet to them, which results in player not seeing belowname on anyone anymore. It doesn't work the other way around - you cannot disable this feature on target players, only for viewers. |
|
||||
|
||||
As you can see, the feature still has limits, even on 1.20.3+. If you are below this version or still don't like the forced space, you are out of luck and cannot use this feature the way you would like to. If you have seen this feature seemingly limitless, it could be a feature that was a part of TAB, but removed later. This feature hid the original nametag and placed invisible armor stands instead. If you are interested in this kind of solution, you'll need to look into other plugins.
|
||||
# Limitations
|
||||
* [1.12.2-] Title length is limited to 32 characters (including color codes).
|
||||
* [1.20.2-] `value` is limited to a white number.
|
||||
* [Bedrock] Doesn't support 1.20.3+ features (`fancy-value`), `value` will be displayed instead, just like on <1.20.3.
|
||||
* The format is a **[score] + space + shared title**. No, the space cannot be removed.
|
||||
* The title is the same on all players, therefore, it cannot be personalized (such as player's faction). Only [score] can be per-player.
|
||||
* It appears on all entities of player type. **This includes player NPCs.**
|
||||
|
||||
# Configuration
|
||||
| Option name | Default value | Description |
|
||||
|---------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| value | %health% | [1.20.2-] An integer from -2147483648 to 2147483647, doesn't support decimal values. The number is always white. Supports placeholders with player-specific output, such as player health. Only visible on 1.20.2 and lower. <br/> **Note**: Even if you only support 1.20.3+, you still need to configure this value to properly evaluate to a number, because the value is still sent to the client (just not displayed). You can set it to `0` for simplicity. |
|
||||
| fancy-value | &c%health% | [1.20.3+] Any text, supports placeholders with per-player output. Only visible on 1.20.3+, where it completely replaces `value`. |
|
||||
| fancy-value-default | NPC | [1.20.3+] Default display for all player entities. Plugin uses the value above for every real player, therefore this default value will only appear on player entities which are not actual players, a.k.a. NPCs. Only visible on 1.20.3+. |
|
||||
| title | Health | Shared label shown after the score for every player entity. Everyone sees the same text (placeholders are parsed for the viewer). Use the `value`/`fancy-value` fields for per‑player data; use `title` only for static labels like `Health` or placeholders which are supposed to be parsed for the viewing player. |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
# Compatibility with other plugins
|
||||
TAB does not contain any sort of compatibility functionality for this feature.
|
||||
It will not try to prevent other plugins from applying the feature and neither will it detect it to re-add back once the other plugin removes it.
|
||||
Therefore, if another plugin also sends belowname objective, TAB's may not show anymore (depending on who sends it first).
|
||||
Make sure you do not have any other plugin sending it to ensure the feature works properly.
|
||||
|
||||
You can check the list of objectives registered by either commands or plugins using Bukkit API using `/scoreboard objectives list`.
|
||||
If it contains one that could be the cause, you can unregister it by running `/scoreboard objectives remove <name>`. After doing so, reload TAB, so it resends its objectives again.
|
||||
Note that if this was automatically generated by a plugin, it will probably be added back again.
|
||||
|
||||
# Additional Info
|
||||
## Additional note 1 - Copying nametag visibility rule
|
||||
The minecraft feature is programmed to be affected by nametag visibility rule.
|
||||
The Minecraft feature is programmed to be affected by nametag visibility rule.
|
||||
This means that when the nametag is set to invisible, belowname will be invisible as well.
|
||||
|
||||
## Additional note 2 - Hidden on sneak on 1.8
|
||||
@@ -73,33 +93,29 @@ NPC plugin can take advantage of this by using teams to hide the original name a
|
||||
|
||||
This is how you can achieve it using the following popular NPC plugins:
|
||||
* **Citizens**:
|
||||
* 1 - Select the NPC (`/npc select <ID>` or `/npc select` to select the nearest NPC)
|
||||
* 2 - Hide its original nametag (which also hides belowname) using `/npc name`
|
||||
* 3 - Display your desired text using `/npc hologram add <text>` ([more info](https://wiki.citizensnpcs.co/Commands#:~:text=the%20NPC%20hitbox-,/npc%20hologram,-add%20%5Btext%5D%20%7C%20set))
|
||||
* 1 - Select the NPC (`/npc select <ID>` or `/npc select` to select the nearest NPC)
|
||||
* 2 - Hide its original nametag (which also hides belowname) using `/npc name`
|
||||
* 3 - Display your desired text using `/npc hologram add <text>` ([more info](https://wiki.citizensnpcs.co/Commands#:~:text=the%20NPC%20hitbox-,/npc%20hologram,-add%20%5Btext%5D%20%7C%20set))
|
||||
* **FancyNpcs with FancyHolograms**:
|
||||
* 1 - Create a hologram with `/hologram create text <name>`.
|
||||
* 2 - Link your newly created hologram with your NPC: `/hologram edit <hologram name> linkWithNpc <NPC name>`.
|
||||
Its original name will be hidden automatically, so you don't need to manually do it.
|
||||
* 3 - Change the NPC's name by editing your hologram's line(s): `/hologram edit hi setline 1 <text>`.
|
||||
You can also add more lines if you want. See [FancyNpcs's wiki](https://docs.fancyinnovations.com/fancyholograms/commands/hologram/#text-hologram-modification) for more commands & info.
|
||||
* 1 - Create a hologram with `/hologram create text <name>`.
|
||||
* 2 - Link your newly created hologram with your NPC: `/hologram edit <hologram name> linkWithNpc <NPC name>`.
|
||||
Its original name will be hidden automatically, so you don't need to manually do it.
|
||||
* 3 - Change the NPC's name by editing your hologram's line(s): `/hologram edit hi setline 1 <text>`.
|
||||
You can also add more lines if you want. See [FancyNpcs's wiki](https://docs.fancyinnovations.com/fancyholograms/commands/hologram/#text-hologram-modification) for more commands & info.
|
||||
|
||||
# Additional note 4 - Compatibility with modified clients
|
||||
### [26.1] Visible on all entities
|
||||
In Minecraft version 26.1, Mojang "fixed" [MC-99647](https://bugs.mojang.com/browse/MC/issues/MC-99647), which reported that belowname is not visible on non-player entities. As a result, the belowname is now visible on all entities with a custom name (this even includes invisible armor stands, holograms and more). A new bug report was made for this: [MC-307012](https://bugs.mojang.com/browse/MC/issues/MC-307012).
|
||||
|
||||
**This is a client-sided bug. You will experience it on 26.1 regardless of the server version**.
|
||||
If your server is on 26.1 or you support players with this version through ViaVersion, this might be a deal breaker for you. If that's the case, either disable this feature entirely, or set
|
||||
```
|
||||
disable-condition: "%player-version-id%>=775"
|
||||
```
|
||||
which will make 26.1 players not see this feature on anyone (remember: disabling this feature disables it for the viewer, not target players).
|
||||
|
||||
## Additional note 4 - Compatibility with modified clients
|
||||
Sadly, this feature is suffering from bugs introduced by third party clients such as Feather and Lunar. These two completely ignore `fancy-value` as if it was never added into the game, even on 1.20.3+. This is just an example, and it's not limited to these two clients and this one issue. If you experience issues with the feature and believe you configured it correctly, use vanilla client to make sure it's not caused by a broken client.
|
||||
|
||||
# Compatibility with other plugins
|
||||
TAB does not contain any sort of compatibility functionality for this feature.
|
||||
It will not try to prevent other plugins from applying the feature and neither will it detect it to re-add back once the other plugin removes it.
|
||||
Therefore, if another plugin also sends belowname objective, TAB's may not show anymore (depending on who sends it first).
|
||||
Make sure you do not have any other plugin sending it to ensure the feature works properly.
|
||||
|
||||
# Limitations
|
||||
* [1.5 - 1.12.2] Title length is limited to 32 characters (including color codes).
|
||||
* [1.5 - 1.20.2] `value` is limited to a white number.
|
||||
* [Bedrock] Doesn't support 1.20.3+ features (`fancy-value`), `value` will be displayed instead, just like on <1.20.3
|
||||
* The format is a **[score] + space + shared title**. No, the space cannot be removed.
|
||||
* The title is the same on all players, therefore, it cannot be personalized (such as player's faction). Only [score] can be per-player.
|
||||
* It appears on all entities of player type. **This includes player NPCs.**
|
||||
|
||||
# Examples
|
||||
## Example 1 - Per-world values
|
||||
This feature doesn't directly support per-world values or similar.
|
||||
@@ -139,8 +155,7 @@ create two condition chains, one for each value and use them.
|
||||
> [!NOTE]
|
||||
> This is just an example, the plugin is not limited to displaying different values only per world.
|
||||
> If you want per server values on proxy, use %server% with server names.
|
||||
> If you want WorldGuard regions, use %worldguard_region_name% with region names.
|
||||
> This works for any placeholder offered by the plugin or by PlaceholderAPI.
|
||||
> This works for any placeholder offered by TAB or by PlaceholderAPI.
|
||||
|
||||
## Example 2 - Hiding `title` for 1.20.3+ players
|
||||
1.20.3 has replaced `value` field,
|
||||
@@ -181,7 +196,16 @@ You'll either need to live with it, or put some shared text there to avoid havin
|
||||
If you want health to display health as 0-10 instead of 0-20, you can achieve it with PlaceholderAPI:
|
||||
* `%math_0_{player_health}/2%` for 0-10
|
||||
* `%math_0_{player_health}*5%` for 0-100
|
||||
In order to use these placeholders, make sure PlaceholderAPI is installed and its `math` and `player` expansions are downloaded.
|
||||
|
||||
## Example 4 - Health bar using hearts
|
||||
You can achieve this on `1.20.3+` using `healthbar` expansion from PlaceholderAPI:
|
||||
```
|
||||
belowname-objective:
|
||||
fancy-value: "%healthbar_healthbar%"
|
||||
```
|
||||
<img width="130" height="159" alt="image" src="https://github.com/user-attachments/assets/cef217ec-0a39-41ad-8457-f8f67a1e4b02" />
|
||||
|
||||
Sadly, there is no way to remove the forced space between `fancy-value` and `title`. You can only try to play around it by adding some static text after it, to make it look like the space is intended. Or you can just ignore the space and live with it.
|
||||
|
||||
# Tips & Tricks
|
||||
## Tip 1 - Heart symbol
|
||||
@@ -191,14 +215,4 @@ belowname-objective:
|
||||
title: "&4❤"
|
||||
```
|
||||
> [!IMPORTANT]
|
||||
> Make sure your config is [saved in UTF-8 encoding](https://github.com/NEZNAMY/TAB/wiki/How-to-save-the-config-in-UTF8-encoding) to properly read the heart symbol.
|
||||
|
||||
# Example 4 - Health bar using hearts
|
||||
You can achieve this on `1.20.3+` using `healthbar` expansion from PlaceholderAPI:
|
||||
```
|
||||
belowname-objective:
|
||||
fancy-value: "%healthbar_healthbar%"
|
||||
```
|
||||
<img width="130" height="159" alt="image" src="https://github.com/user-attachments/assets/cef217ec-0a39-41ad-8457-f8f67a1e4b02" />
|
||||
|
||||
Sadly, there is no way to remove the forced space between `fancy-value` and `title`. You can only try to play around it by adding some static text after it, to make it look like the space is intended. Or you can just ignore the space and live with it.
|
||||
> Make sure your config is [saved in UTF-8 encoding](https://github.com/NEZNAMY/TAB/wiki/How-to-save-the-config-in-UTF8-encoding) to properly read the heart symbol.
|
||||
@@ -1,137 +1,125 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Defining bossbars](#defining-bossbars)
|
||||
* [style](#style)
|
||||
* [color](#color)
|
||||
* [progress](#progress)
|
||||
* [text](#text)
|
||||
* [announcement-bar](#announcement-bar)
|
||||
* [display-condition](#display-condition)
|
||||
* [Announce command](#announce-command)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Commands](#commands)
|
||||
* [Placeholders](#placeholders)
|
||||
* [Internal placeholders](#internal-placeholders)
|
||||
* [PlaceholderAPI placeholders](#placeholderapi-placeholders)
|
||||
* [Limitations](#limitations)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Hiding bar itself](#additional-note-1---hiding-bar-itself)
|
||||
* [Additional note 2 - Not hiding on server switch](#additional-note-2---not-hiding-on-server-switch)
|
||||
* [Additional note 1 - Hiding bar itself](#additional-note-1---hiding-bar-itself)
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Animated bossbar color](#example-1---animated-bossbar-color)
|
||||
* [Example 2 - Switching between bossbars with condition](#example-2---switching-between-bossbars-with-condition)
|
||||
* [Example 3 - Periodical appearing and disappearing](#example-3---periodical-appearing-and-disappearing)
|
||||
* [Example 1 - Changing text](#example-1---changing-text)
|
||||
* [Example 2 - Switching between bossbars with condition](#example-2---switching-between-bossbars-with-condition)
|
||||
* [Example 3 - Periodical appearing and disappearing](#example-3---periodical-appearing-and-disappearing)
|
||||
* [Example 4 - Dynamic progress tracking a relative placeholder value](#example-4---dynamic-progress-tracking-a-relative-placeholder-value)
|
||||
|
||||
# About
|
||||

|
||||
Bars with text on top of the screen originally designed to display the health of ender dragon & wither,
|
||||
but plugins found another use for it.
|
||||
In 1.9, mojang added a packet
|
||||
dedicated to displaying text without requiring an entity and allowing customizable color and style as well.
|
||||
TAB only supports this new functionality, and the feature does not support 1.8 and lower.
|
||||
|
||||
This feature can be configured in **config.yml** under **bossbar** section.
|
||||
This feature allows you to send modern bossbars to players, which appear on top of the screen.
|
||||
Only works on Minecraft 1.9 and up.
|
||||
Supporting 1.8 and lower would require an entity to be spawned (wither / dragon), which TAB no longer offers.
|
||||

|
||||
|
||||
# Configuration
|
||||
## Defining bossbars
|
||||
The feature can be configured in **config.yml** under **bossbar** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
bossbar:
|
||||
enabled: false
|
||||
toggle-command: /bossbar
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
bars:
|
||||
MyBossbar:
|
||||
style: "PROGRESS"
|
||||
color: "%animation:barcolors%"
|
||||
progress: "100"
|
||||
ServerInfo:
|
||||
style: "PROGRESS" # for 1.9+: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
|
||||
color: "%animation:barcolors%" # for 1.9+: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
|
||||
progress: "100" # in %
|
||||
text: "&fWebsite: &bwww.domain.com"
|
||||
```
|
||||
Every bossbar line has 4 properties:
|
||||
All the options are explained in the following table.
|
||||
|
||||
### style
|
||||
Type of bossbar division.
|
||||
You can choose from 5 styles: **PROGRESS**, **NOTCHED_6**, **NOTCHED_10**, **NOTCHED_12** and **NOTCHED_20**
|
||||

|
||||
These were defined by mojang, no other options are available. Other plugins may be using different names, but it's the same result in the end.
|
||||
| Option name | Default value | Description |
|
||||
|------------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | false | Enables / Disables the feature |
|
||||
| toggle-command | /bossbar | A command to toggle bossbar visibility for player running the command. This command requires `tab.bossbar.toggle` permission. <br />**Note:** Commands are not supposed to be registered / unregistered dynamically at runtime. As such, you may run into small issues when changing the toggle command and reloading. After you modify the command to your liking, reconnect to properly see it in tabcomplete. If you don't use TAB on a proxy, consider restarting the server as well for the old command to actually be unregistered. |
|
||||
| remember-toggle-choice | false | When enabled, disabling bossbar using `toggle-command` is remembered between reloads and restarts. |
|
||||
| hidden-by-default | false | When enabled, bossbar will automatically be hidden until toggle command is used. |
|
||||
| bars | *Map* | Map of defined bossbars, see below for more information. |
|
||||
|
||||
|
||||
### color
|
||||
Color of bossbar.
|
||||
You can choose from 7 colors: **BLUE**, **GREEN**, **PINK**, **PURPLE**, **RED**, **WHITE** and **YELLOW**
|
||||

|
||||
These were defined by mojang, no other options are available.
|
||||
Bossbars can be defined in `bars` section. Bossbar name can be anything, but you cannot use the same name for 2 bossbars.
|
||||
A bossbar consists of up to 6 properties:
|
||||
|
||||
| Option | Example | Description |
|
||||
|-------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| style | PROGRESS | Type of bossbar division. You can choose from 5 styles: **PROGRESS**, **NOTCHED_6**, **NOTCHED_10**, **NOTCHED_12** and **NOTCHED_20**. <br />  <br /> These were defined by mojang, no other options are available. Other plugins may be using different names, but it's the same result in the end. |
|
||||
| color | BLUE | Color of bossbar. You can choose from 7 colors: **BLUE**, **GREEN**, **PINK**, **PURPLE**, **RED**, **WHITE** and **YELLOW**. <br />  <br /> These were defined by mojang, no other options are available. |
|
||||
| progress | 100 | A number between 0 and 100. Accepts decimals. <br />  |
|
||||
| text | "Hi" | Text of the bossbar. Length is not limited, supports RGB codes on 1.16+. Does not support newlines. |
|
||||
| announcement-bar | false | When set to `true`, the bossbar will not appear by default, only when used in announce command. |
|
||||
| display-condition | "%world%=world" | [Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for player to see the bossbar. Even when using announce command on a bossbar with display condition, players must meet it to see the bossbar. When condition is not defined, bossbar has no requirement to be displayed. |
|
||||
|
||||
### progress
|
||||
A number from 0 to 100. Accepts decimals.
|
||||

|
||||
If you want to show the progress of a placeholder's value relative to a maximum one, you can use the [Math Expansion](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Placeholders#math) from PlaceholderAPI and use a placeholder like this one: `%math_{placeholder_current_value}/{placeholder_max_value}*100%` or `%math_{placeholder_current_value}/<max_value>*100%` if your max value is fixed.
|
||||
|
||||
> [!NOTE]
|
||||
> Replace the text in between the `{}` with the actual placeholder you want to use and `<max_value>` with a valid number
|
||||
(i.e., 20).
|
||||
You cannot use the `%placeholder%` format inside the math placeholder.
|
||||
|
||||
Example for showing a player's health: `%math_{player_health}/20*100%` or `%math_{player_health}/{player_max_health}*100%`
|
||||
|
||||
### text
|
||||
Text of the bossbar. Length is not limited, supports RGB codes on 1.16+. Does not support newlines.
|
||||
|
||||
### announcement-bar
|
||||
When set to true, the bossbar will not appear by default, only when used in announce command.
|
||||
Here is an example using all 6 properties:
|
||||
```
|
||||
bossbar:
|
||||
bars:
|
||||
MyBossbar:
|
||||
style: "PROGRESS"
|
||||
color: "BLUE"
|
||||
ServerInfo:
|
||||
style: "PROGRESS"
|
||||
color: "PURPLE"
|
||||
progress: "100"
|
||||
text: "Text"
|
||||
announcement-bar: true #false or undefined means it will be visible all the time
|
||||
```
|
||||
### display-condition
|
||||
[Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for player to see the bossbar. Even when using announce command on a bossbar with display condition, players must meet it to see the bossbar. When condition is not defined, bossbar has no requirement to be displayed.
|
||||
**Example**:
|
||||
```
|
||||
bossbar:
|
||||
bars:
|
||||
MyBossbar:
|
||||
style: "PROGRESS"
|
||||
color: "BLUE"
|
||||
progress: "100"
|
||||
text: "This bossbar is only visible in world 'world'"
|
||||
display-condition: "%world%=world"
|
||||
text: "Welcome to the nether!"
|
||||
announcement-bar: false
|
||||
display-condition: "%world%=world_nether"
|
||||
```
|
||||
|
||||
## Announce command
|
||||
`/tab bossbar announce <name> <time>`
|
||||
`name` is name of bossbar defined in config.yml under **bossbar** section, `time` is length of display time in seconds.
|
||||
# Commands
|
||||
| Command | Permission | Description |
|
||||
|-------------------------------------------------------|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| /tab bossbar show \<name\> \[player\] | `tab.bossbar.show` (for yourself) <br /> `tab.bossbar.show.other` (for others) | Shows the bossbar with the given `name`, either to yourself if no `player` was given, or to the given `player`. |
|
||||
| /tab bossbar \[on/off/toggle\] \[player\] \[options\] | `tab.bossbar.toggle` (for yourself) <br /> `tab.bossbar.toggle.other` (for others) | Shows / hides / toggles bossbar of specified player. If no player was given, command affects the sender. You can use `-s` as option for silent toggling (no chat message for affected player) |
|
||||
| /tab bossbar announce \<name\> \<duration\> | `tab.announce.bar` | Shows the bossbar with the given `name` to every player on the server for the given `duration`, in seconds. |
|
||||
|
||||
When using a bossbar announcement, you have new placeholders available:
|
||||
* `%bossbar_announce_time_left_<bossbar>%` - Returns remaining time of the announcement in seconds.
|
||||
* `%bossbar_announce_time_total_<bossbar>%` - Returns total time of the announcement in seconds (the inputted number).
|
||||
# Placeholders
|
||||
## Internal placeholders
|
||||
Here are TAB's internal placeholders you can use when this feature is enabled:
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| `%bossbar_announce_time_left_<bossbar>%` | Returns remaining time of the announcement of specified bossbar (in seconds). |
|
||||
| `%bossbar_announce_time_total_<bossbar>%` | Returns total time of the announcement of specified bossbar (in seconds) (the inputted number). |
|
||||
|
||||
You can further use these placeholders in progress, for example,
|
||||
using Math expansion from PlaceholderAPI, such as `%math_{tab_placeholder_bossbar_announce_time_left_<bossbar>}/{tab_placeholder_bossbar_announce_time_total_<bossbar>}*100%`.
|
||||
You can further use these placeholders in progress, for example, using Math expansion from PlaceholderAPI, such as `%math_{tab_placeholder_bossbar_announce_time_left_<bossbar>}/{tab_placeholder_bossbar_announce_time_total_<bossbar>}*100%` (remember to replace `<bossbar>` with the actual name of the bossbar).
|
||||
|
||||
## Additional settings
|
||||
| Option name | Default value | Description |
|
||||
|------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | false | Enables / Disables the feature |
|
||||
| toggle-command | /bossbar | A command to toggle bossbar visibility for player running the command. This command requires `tab.bossbar.toggle` permission. <br />**Note:** This command will not appear in command auto-complete, because it's not a real registered command. Registered commands must be defined in plugin jar directly and cannot be dynamic. If you want to solve this, you can try getting a dummy private plugin made which registers that command. |
|
||||
| remember-toggle-choice | false | When enabled, disabling bossbar using `toggle-command` is remembered between reloads and restarts. |
|
||||
| hidden-by-default | false | When enabled, bossbar will automatically be hidden until toggle command is used. |
|
||||
## PlaceholderAPI placeholders
|
||||
Here are TAB's PlaceholderAPI placeholders you can use when this feature is enabled:
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| `%tab_bossbar_visible%` | "Enabled" if visible (not toggled with a command), "Disabled" if not (toggled with a command) |
|
||||
|
||||
# Limitations
|
||||
* Does not support newlines. If you want to display more lines, you'll need to create multiple bossbars.
|
||||
* The bar itself cannot be removed from the server side. However, it can be removed with a resource pack.
|
||||
|
||||
# Compatibility with other plugins
|
||||
Bossbar is a feature that can have multiple instances sent and displayed, so there is no possible conflict like in scoreboard for example. Every plugin manages its own bossbars and doesn't care about others.
|
||||
|
||||
One thing to mention is the order of bossbars. They are ordered by the order they were received. This means the final order depends on which plugin is faster (or slower), which is something you can't really control. As such, there is no feasible way to control the order of bossbars across different plugins. TAB's own bossbars are sent in the order they are defined in the configuration, so that won't be a problem.
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - Hiding bar itself
|
||||
If you want to hide the bossbar itself and only show text, this is possible, but not from the plugin's side.
|
||||
You'll need to create a custom resource pack where the bar is invisible.
|
||||
Then, you need to force your players to get the resource pack.
|
||||
You'll need to create a custom resource pack where the bar is invisible and force your players to get the resource pack.
|
||||
Here is how to do it:
|
||||
**Minecraft 1.20+**:
|
||||
* Navigate to `assets/minecraft/textures/gui/sprites/boss_bar`. You will need to create 2 files. In this example, we will use `WHITE` Bossbar color.
|
||||
* Use GIMP or any other photo editing software and create a **1x1 px** image, that is fully transparent.
|
||||
* Name the file **white_progress.png**. Now copy-paste the same file and name it **white_background.png**.
|
||||
|
||||
## Additional note 2 - Not hiding on server switch
|
||||
When under a proxy network and having TAB installed on backend server and switching to another server,
|
||||
the bossbar will not hide.
|
||||
This is because proxies makes it look like a world switch to the client.
|
||||
To avoid this, the only way is to install TAB on the proxy and disable bossbar on that server.
|
||||
**Minecraft 1.19.4-**:
|
||||
* Navigate to `assets/minecraft/textures/gui/`. You will need to download the default `bars.png` file (https://github.com/Faithful-Pack/Default-Java/blob/1.19.4/assets/minecraft/textures/gui/bars.png).
|
||||
* Use GIMP or any other Photo editing software and remove the White bars in the file. Save the file and move it to `assets/minecraft/textures/gui/`.
|
||||
|
||||
Finally, set your Bossbar color in TAB Config to `WHITE`. Now the line will be invisible!
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
@@ -177,27 +165,29 @@ Methods in `BossBar`:
|
||||
* `BossBar#isAnnouncementBar()` - Returns `true` if this bossbar is defined as announcement bar in config, `false` if visible permanently.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Animated bossbar color
|
||||
You can animate the color of a bossbar using [animations](https://github.com/NEZNAMY/TAB/wiki/Animations).
|
||||
## Example 1 - Changing text
|
||||
You can make the change text periodically using [animations](https://github.com/NEZNAMY/TAB/wiki/Animations).
|
||||
First, create an animation in **animations.yml**, such as
|
||||
```
|
||||
BossColor:
|
||||
BossBarText:
|
||||
change-interval: 1000
|
||||
texts:
|
||||
- "BLUE"
|
||||
- "YELLOW"
|
||||
- "Text1"
|
||||
- "Text2"
|
||||
```
|
||||
Then, use the animation in bossbar configuration
|
||||
```
|
||||
bars:
|
||||
ServerInfo:
|
||||
style: "PROGRESS"
|
||||
color: "%animation:BossColor%"
|
||||
color: "PURPLE"
|
||||
progress: "100"
|
||||
text: "&fWebsite: &bwww.domain.com"
|
||||
text: "%animation:BossBarText%"
|
||||
```
|
||||
This bossbar will change color every second between blue and yellow colors.
|
||||
You can do the same with progress, style and text as well.
|
||||
This bossbar will change text every second between `text1` and `text2`. You can make the change interval higher and add more texts to suit your needs.
|
||||
|
||||
> [!NOTE]
|
||||
> This works for style, color and progress as well, not just text. Just make sure the animation returns a valid value accepted by the field (listed enum constants for style and color, number between 0 and 100 for progress).
|
||||
|
||||
## Example 2 - Switching between bossbars with condition
|
||||
If you want the plugin to switch between bossbars based on a condition, first consider using condition for text itself
|
||||
@@ -240,8 +230,20 @@ Then, create a display condition that checks if the animation is on the first fr
|
||||
ServerInfo:
|
||||
display-condition: "%animation:bossbar-toggle%=ON" # Check for the first frame of the animation
|
||||
style: "PROGRESS"
|
||||
color: "%animation:barcolors%"
|
||||
progress: "100" # in %
|
||||
color: "PURPLE"
|
||||
progress: "100"
|
||||
text: "&fWebsite: &bwww.domain.com"
|
||||
```
|
||||
Now, the bossbar will appear and disappear every 10 seconds.
|
||||
Now, the bossbar will appear and disappear every 10 seconds.
|
||||
|
||||
## Example 4 - Dynamic progress tracking a relative placeholder value
|
||||
If you want to show the progress of a placeholder's value relative to a maximum one, you can use the [Math Expansion](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Placeholders#math) from PlaceholderAPI and use a placeholder like this one: `%math_{placeholder_current_value}/{placeholder_max_value}*100%` or `%math_{placeholder_current_value}/<max_value>*100%` if your max value is fixed.
|
||||
|
||||
> [!NOTE]
|
||||
> Replace the text in between the `{}` with the actual placeholder you want to use and `<max_value>` with a valid number (i.e., 20).
|
||||
|
||||
You cannot use the `%placeholder%` format inside the math placeholder.
|
||||
Example for showing a player's health:
|
||||
`progress: "%math_{player_health}/20*100%"`
|
||||
or
|
||||
`progress: "%math_{player_health}/{player_max_health}*100%"`
|
||||
@@ -1,28 +1,25 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Condition types](#condition-types)
|
||||
* [Number comparisons](#number-comparisons)
|
||||
* [Text operations](#text-operations)
|
||||
* [Permission](#permission)
|
||||
* [Multiple condition requirements](#multiple-condition-requirements)
|
||||
* [Condition output](#condition-output)
|
||||
* [Configuration](#configuration)
|
||||
* [Number comparisons](#number-comparisons)
|
||||
* [Text operations](#text-operations)
|
||||
* [Permission](#permission)
|
||||
* [Usage](#usage)
|
||||
* [Displaying text](#displaying-text)
|
||||
* [Display condition of a feature](#display-condition-of-a-feature)
|
||||
* [Short format](#short-format)
|
||||
* [Conditional text](#conditional-text)
|
||||
* [Using in a field where condition is expected](#using-in-a-field-where-condition-is-expected)
|
||||
* [Using in a field where condition is expected (short format)](#using-in-a-field-where-condition-is-expected-short-format)
|
||||
* [Relational conditions](#relational-conditions)
|
||||
* [Refresh interval](#refresh-interval)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Chaining conditional placeholders](#example-1---chaining-conditional-placeholders)
|
||||
* [Example 2 - Combining AND and OR](#example-2---combining-and-and-or)
|
||||
* [Example 3 - Negating expressions](#example-3---negating-expressions)
|
||||
* [Example 1 - Chaining conditional placeholders](#example-1---chaining-conditional-placeholders)
|
||||
* [Example 2 - Combining AND and OR](#example-2---combining-and-and-or)
|
||||
|
||||
# About
|
||||
Conditions / conditional placeholders allow you
|
||||
to create output which depends on the output of other placeholders or permission requirement.
|
||||
They have 2 main uses in the plugin:
|
||||
* Display condition which must be met to be able to see something (bossbar, scoreboard, layout)
|
||||
* Conditional placeholders which return defined outputs in both cases if the condition passes or fails
|
||||
* `display-condition` which must be met to be able to see something (bossbar, scoreboard, layout) or `disable-condition` for disabling a feature (scoreboard-teams, tablist-name-formatting, ...)
|
||||
* Conditional placeholders which return defined outputs based on if the condition passes or fails
|
||||
|
||||
# Condition types
|
||||
## Number comparisons
|
||||
@@ -49,7 +46,7 @@ They have 2 main uses in the plugin:
|
||||
> For `=` and `!=` you can check for empty output of a placeholder using `%my_placeholder%=` and `%my_placeholder%!=`.
|
||||
|
||||
> [!NOTE]
|
||||
> For string operations, the text must match placeholder's output exactly, including color codes.
|
||||
> For string operations, the text must match placeholder's output exactly, including color codes, as well as lower/UPPER case letters.
|
||||
If you are using [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements),
|
||||
condition must contain the altered output.
|
||||
To see the exact output of a placeholder including color codes, use `/tab parse <player> <placeholder>`.
|
||||
@@ -60,82 +57,65 @@ To see the exact output of a placeholder including color codes, use `/tab parse
|
||||
| `permission:<value>` | Permission requirement | `permission:my.permission` will pass if player has `my.permission` permission |
|
||||
| `!permission:<value>` | Negative permission requirement | `!permission:my.permission` will pass if player **does not have** `my.permission` permission |
|
||||
|
||||
# Multiple condition requirements
|
||||
Each condition has a `conditions` parameter, which a list of conditions. If you define more than 1 condition, you must specify the condition type.
|
||||
This value can be found under `type` field. Types are:
|
||||
* `AND` - all sub-conditions must be met for the final condition to pass
|
||||
* `OR` - at least one sub-condition must be met for the final condition to pass
|
||||
|
||||
If you only defined one subcondition, you don't need to define the type at all, since it's not used for anything.
|
||||
|
||||
# Condition output
|
||||
If using condition as a placeholder, you can specify output in both cases using `true` and `false` values. `true` is used when condition passes, `false` if not. If using condition only as a view requirement, you can leave these values empty / not specify them at all.
|
||||
|
||||
# Configuration
|
||||
Open **config.yml** and find this section:
|
||||
```
|
||||
conditions:
|
||||
health:
|
||||
conditions:
|
||||
- '%health%<21'
|
||||
- '%health%>15'
|
||||
type: AND
|
||||
true: Healthy!
|
||||
false: Damaged!
|
||||
```
|
||||
`health` is name of our condition in this case.
|
||||
`conditions` is a list of subconditions that must be met for this condition to pass.
|
||||
`type` defines whether all subconditions must be met or at least one.
|
||||
`true` & `false` define output in both cases.
|
||||
|
||||
# Usage
|
||||
You have 2 ways to use conditions.
|
||||
There are multiple different ways you can use conditions, all of which are described below.
|
||||
|
||||
## Displaying text
|
||||
The first way is to use conditions to display text.
|
||||
Configure outputs in `true` and `false` values and then use `%condition:<name>%`,
|
||||
which will output text defined in `true` or `false` depending on if condition is met or not.
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
## Conditional text
|
||||
This way allows you to create conditional text that depends on other placeholders.
|
||||
|
||||
First, find the `conditions` section in your config and create a new condition like this one:
|
||||
```
|
||||
conditions:
|
||||
serverName:
|
||||
lobby:
|
||||
conditions:
|
||||
- "%server%=lobby"
|
||||
true: "You are in the lobby"
|
||||
- '%server%=lobby'
|
||||
true: "You are in lobby"
|
||||
false: "You are not in lobby"
|
||||
```
|
||||
This condition checks if player is in server `lobby` and if yes, displays `"You are in lobby"`, otherwise displays `"Your are not in lobby"`.
|
||||
To check for more than just 1 condition, add more to the list. Additionally, you will now need to specify `type`, which is either `AND` or `OR`.
|
||||
|
||||
Use with `%condition:serverName%`
|
||||
</details>
|
||||
|
||||
## Display condition of a feature
|
||||
The second way is to use condition's name in places where a condition is accepted.
|
||||
This includes display conditions for bossbar, scoreboard and layout.
|
||||
In these cases, true/false texts are unused; therefore, they do not need to be defined.
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
For example,
|
||||
```
|
||||
conditions:
|
||||
MyCondition:
|
||||
lobby:
|
||||
conditions:
|
||||
- "permission:tab.admin"
|
||||
- '%server%=lobby'
|
||||
- '%server%=lobby2'
|
||||
type: OR
|
||||
true: "You are in lobby"
|
||||
false: "You are not in lobby"
|
||||
```
|
||||
will return the lobby text if player is in server `lobby` **OR** in server `lobby2`. If you want both of those conditions to be required to display text, use `type: AND`.
|
||||
It is not possible to combine both `AND` and `OR` in a single condition. To do that, create multiple conditions and use one inside another (see Example 2 below).
|
||||
|
||||
Now that the condition is created, we can use it using `%condition:lobby%`. This placeholder will return the texts configured in `true` and `false` values based on whether the condition was met or not.
|
||||
|
||||
## Using in a field where condition is expected
|
||||
A lot of the features support either `display-condition` or `disable-condition`. The value configured is expected to be a condition. Let's say we want to use it as a display condition somewhere, for example when player is in server `lobby`. First, create the condition:
|
||||
```
|
||||
scoreboards:
|
||||
MyScoreboard:
|
||||
display-condition: MyCondition
|
||||
conditions:
|
||||
lobby:
|
||||
conditions:
|
||||
- '%server%=lobby'
|
||||
```
|
||||
As you can see, `true` and `false` were not configured. This is because we are not trying to display any text, we are passing the condition as an object to evaluate.
|
||||
Then, use `display-condition: lobby` in the feature. This will evaluate the condition named `lobby` and either display or not display the visual based on player's server. To check for 2 servers, use
|
||||
```
|
||||
conditions:
|
||||
lobby:
|
||||
conditions:
|
||||
- '%server%=lobby'
|
||||
- '%server%=lobby2'
|
||||
type: OR
|
||||
```
|
||||
Now, the condition `lobby` will pass if player is in server `lobby` **OR** in server `lobby2`. To require both of the conditions to be met instead of at least 1, use `type: AND`.
|
||||
It is not possible to combine both `AND` and `OR` in a single condition. To do that, create multiple conditions and use one inside another (see Example 2 below).
|
||||
|
||||
In this example, scoreboard will only be displayed to players with `tab.admin` permission.
|
||||
</details>
|
||||
## Using in a field where condition is expected (short format)
|
||||
When using condition as a `display-condition` or `disable-condition`, you don't need to specify `true` / `false` texts. In fact, you only need the list of conditions and separator (AND / OR).
|
||||
For this purpose, short format was created. Instead of having to create a condition and using it, you can specify the expression directly.
|
||||
|
||||
### Short format
|
||||
If trying to use a condition on place where it's available (bossbar display condition, scoreboard display condition) where you don't need the true/false values, you can use a short format instead.
|
||||
|
||||
This can be used by simply creating all subconditions and separating them with `;` for `AND` condition type. For `OR` type, use `|`.
|
||||
**Single condition example**:
|
||||
```
|
||||
display-condition: "%server%=lobby"
|
||||
@@ -150,6 +130,24 @@ display-condition: "%server%=lobby;%world%=world"
|
||||
```
|
||||
display-condition: "%server%=lobby|%server%=lobby2"
|
||||
```
|
||||
It is not possible to combine both `AND` (`;`) and `OR` (`|`) in a single condition. To do that, create multiple conditions and use one inside another (see Example 2 below).
|
||||
|
||||
## Relational conditions
|
||||
> [!NOTE]
|
||||
> This section only describes differences from standard conditions. For more information about those, see sections above.
|
||||
|
||||
Relational conditions allow you to check placeholder outputs for the player who is looking instead of only the target player.
|
||||
|
||||
To make placeholder check for viewer instead of the target player, use `%viewer:<placeholder>%` instead of just `%placeholder%`.
|
||||
You can still check for target player using `%placeholder%` or `%target:<placeholder>%` (if you want to make it more clear for the reader).
|
||||
Example: `%viewer:server%=%server%` will pass if viewer is in the same server as the target player.
|
||||
|
||||
There are 2 ways to use it:
|
||||
* display-condition of layout player groups (which is currently the only feature supporting it as it would not make sense elsewhere)
|
||||
* A placeholder using `%rel_condition:<condition>%` syntax
|
||||
|
||||
If trying to use relational condition in `%condition:name%` syntax, it will return a warning text instead of evaluating the condition.
|
||||
Same the other way around, if trying to use standard condition in `%rel_condition:name%` syntax, it will return a warning text.
|
||||
|
||||
# Refresh interval
|
||||
Conditions are just placeholders after all, and, as such, they must be refreshed periodically.
|
||||
@@ -205,32 +203,3 @@ conditions:
|
||||
type: AND
|
||||
```
|
||||
Then, use condition `main` as the display condition (or as a placeholder - `%condition:main%`). Note that `true`/`false` values were not defined, as such, they default to `true` and `false`, respectively. Therefore, we use the placeholder from the condition and check if the result is `true`. Then, check if player is also in the specified server.
|
||||
|
||||
## Example 3 - Negating expressions
|
||||
Most condition types contain their opposites, such as:
|
||||
* `=` -> `!=`
|
||||
* `permission:` -> `!permission:`
|
||||
* `>=` -> `<`
|
||||
* `>` -> `<=`
|
||||
* `|-` -> `!|-`
|
||||
* `-|` -> `!-|`
|
||||
* `<-` -> `!<-`
|
||||
|
||||
You can achieve this by creating a full condition and check if it returned false.
|
||||
**Example:**
|
||||
Original:
|
||||
```
|
||||
display-condition: "%server%|-lobby"
|
||||
```
|
||||
Negated:
|
||||
```
|
||||
conditions:
|
||||
lobby:
|
||||
conditions:
|
||||
- "%server%|-lobby"
|
||||
```
|
||||
...
|
||||
```
|
||||
display-condition: "%condition:lobby%=false"
|
||||
```
|
||||
This way, the display condition will pass if the nested condition returned `false` (player is not in any lobby).
|
||||
@@ -1,25 +1,47 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Grouping players only from certain servers](#grouping-players-only-from-certain-servers)
|
||||
* [Configuring isolated servers](#configuring-isolated-servers)
|
||||
* [Seeing all players on the network from some server](#seeing-all-players-on-the-network-from-some-server)
|
||||
* [Compatibility with vanish plugins](#compatibility-with-vanish-plugins)
|
||||
* [Grouping players only from certain servers](#grouping-players-only-from-certain-servers)
|
||||
* [Pattern Matching](#pattern-matching)
|
||||
* [Configuring isolated servers](#configuring-isolated-servers)
|
||||
* [Seeing all players on the network from some server](#seeing-all-players-on-the-network-from-some-server)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
|
||||
# About
|
||||
This feature allows you to see players from other servers, instead of only seeing players on the same server.
|
||||
If TAB is installed on a backend server, you'll need to set up [Multi server support](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Multi-server-support) for proper linking of servers. On proxy installation, this is not required.
|
||||
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **global-playerlist** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
global-playerlist:
|
||||
enabled: false
|
||||
display-others-as-spectators: false
|
||||
display-vanished-players-as-spectators: true
|
||||
isolate-unlisted-servers: false
|
||||
update-latency: false
|
||||
spy-servers:
|
||||
- spyserver1
|
||||
- spyserver2
|
||||
server-groups:
|
||||
lobbies:
|
||||
- lobby1
|
||||
- lobby2
|
||||
group2:
|
||||
- server1
|
||||
- server2
|
||||
```
|
||||
All of the options are explained in the following table.
|
||||
| Option name | Default value | Description |
|
||||
|----------------------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | false | Enables / Disables the feature |
|
||||
| server-groups | *Map* | See [Grouping players only from certain servers](#grouping-players-only-from-certain-servers) |
|
||||
| spy-servers | *List* | See [Seeing all players on the network from some server](#seeing-all-players-on-the-network-from-some-server) |
|
||||
| display-others-as-spectators | false | When enabled, players on different servers will appear as having spectator gamemode in tablist. |
|
||||
| display-vanished-players-as-spectators | true | When enabled, vanished players will show as in spectator gamemode for those, who have permission to see vanished players (tab.seevanished) (others will obviously not see them at all). Vanished players will still see themselves in the gamemode they are actually in, since changing that would cause problems (client would think it's in spectator gamemode while it isn't). |
|
||||
| isolate-unlisted-servers | false | When enabled, servers not listed in any group will not share playerlist with any other server, instead of sharing it with other unlisted servers. |
|
||||
| update-latency | false | When **enabled**, plugin will send ping update to players on other servers, since backend servers only send ping updates of players on the same server. This is displayed as the green bar in tablist, unless altered by a mod / resource pack (?). <br /> <br />When **disabled**, ping of players on other servers will be set to `0` (). <br /> <br />Disabled by default, because it's not only not very useful, but also has a very high CPU usage to due spamming a large amount of packets (especially on large networks). |
|
||||
| spy-servers | *List* | See [Seeing all players on the network from some server](#seeing-all-players-on-the-network-from-some-server) |
|
||||
| server-groups | *Map* | See [Grouping players only from certain servers](#grouping-players-only-from-certain-servers) |
|
||||
|
||||
## Grouping players only from certain servers
|
||||
In case you want multiple servers to share playerlist,
|
||||
@@ -46,6 +68,36 @@ To make all servers share the playerlist, simply clear server groups and set
|
||||
server-groups: {}
|
||||
```
|
||||
|
||||
### Pattern Matching
|
||||
Server names in groups support multiple pattern types for flexible configuration:
|
||||
|
||||
**Exact Match** - Matches server name exactly:
|
||||
```yaml
|
||||
server-groups:
|
||||
lobbies:
|
||||
- lobby1
|
||||
- lobby2
|
||||
```
|
||||
|
||||
**Wildcard Patterns** - Use `*` for prefix or suffix matching:
|
||||
```yaml
|
||||
server-groups:
|
||||
lobbies:
|
||||
- "Lobby-*" # Matches: Lobby-1, Lobby-2, Lobby-Hub, etc.
|
||||
- "*-lobby" # Matches: eu-lobby, us-lobby, etc.
|
||||
```
|
||||
|
||||
**Regex Patterns** - Use `regex:` prefix for advanced pattern matching:
|
||||
```yaml
|
||||
server-groups:
|
||||
lobbies:
|
||||
- regex:Lobby-[0-9]+ # Matches: Lobby-1, Lobby-23, but not Lobby-Hub
|
||||
smp:
|
||||
- regex:SMP-(EU|US)-[0-9]+ # Matches: SMP-EU-1, SMP-US-2, etc.
|
||||
```
|
||||
|
||||
Note: Wildcard patterns are case-insensitive, while regex patterns are case-sensitive by default (use `(?i)` flag for case-insensitive regex).
|
||||
|
||||
## Configuring isolated servers
|
||||
If you want servers which are isolated (no one will see these players, and they will not see anyone on other servers),
|
||||
create a new group and only put that 1 server there.
|
||||
@@ -71,5 +123,5 @@ global-playerlist:
|
||||
```
|
||||
Now, players connected to "spyserver1" or "spyserver2" will see everyone on the network in tablist, but no one else (unless on another spy server / in the same server group) will see them.
|
||||
|
||||
# Compatibility with vanish plugins
|
||||
For compatibility with vanish plugins, vanish status must be detected correctly. See [Additional information - Vanish detection](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection) for more info.
|
||||
# Compatibility with other plugins
|
||||
For compatibility with vanish plugins, vanish status must be detected correctly. See [Additional information - Vanish detection](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection) for more info.
|
||||
@@ -1,15 +1,16 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Designs](#designs)
|
||||
* [Chaining designs](#chaining-designs)
|
||||
* [Design](#design)
|
||||
* [Chaining designs](#chaining-designs)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - [1.8 - 1.20.1] Not resetting on server switch](#additional-note-1---18---1201-not-resetting-on-server-switch)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Tip 1 - Dynamic line count](#tip-1---dynamic-line-count)
|
||||
* [Additional note 1 - [1.8 - 1.20.1] Not resetting on server switch](#additional-note-1---18---1201-not-resetting-on-server-switch)
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-version header/footer](#example-1---per-version-headerfooter)
|
||||
* [Example 1 - Per-version header/footer](#example-1---per-version-headerfooter)
|
||||
* [Example 2 - Per-world header/footer](#example-2---per-world-headerfooter)
|
||||
* [Example 3 - Dynamic line count](#example-3---dynamic-line-count)
|
||||
|
||||
# About
|
||||
Minecraft feature **introduced in 1.8** showing text above and below playerlist. It cannot be displayed on 1.7 clients in any way.
|
||||
@@ -17,16 +18,48 @@ Minecraft feature **introduced in 1.8** showing text above and below playerlist.
|
||||

|
||||
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **header-footer** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
header-footer:
|
||||
enabled: true
|
||||
designs:
|
||||
world-test:
|
||||
display-condition: "%world%=test"
|
||||
header:
|
||||
- "Test header in world 'test'"
|
||||
footer:
|
||||
- "Test footer in world 'test'"
|
||||
default:
|
||||
header:
|
||||
- "<#FFFFFF>&m </#FFFF00>"
|
||||
- "&3&lServer name"
|
||||
- "&r&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&r&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- ""
|
||||
footer:
|
||||
- "%animation:time%"
|
||||
- "&2Ping: %ping%"
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- ""
|
||||
- "&r&7Visit our webpage %animation:web%"
|
||||
- "<#FFFFFF>&m </#FFFF00>"
|
||||
```
|
||||
All the options are explained in the following table.
|
||||
|
||||
| Option name | Default value | Description |
|
||||
|-------------------|-----------------------|-------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| designs | *Map* | Designs to display based on conditions (see below for more info). |
|
||||
|
||||
# Designs
|
||||
A header/footer design has 3 options:
|
||||
* `display-condition` (optional) - [Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for the design to be displayed. If not defined, design will be displayed without any required condition. If player does not meet condition, another design may be displayed based on chaining (see below for more info).
|
||||
* `header` - List of lines to display in the header.
|
||||
* `footer` - List of lines to display in the footer.
|
||||
## Design
|
||||
A header/footer design consists of the following options:
|
||||
| Option name | Description |
|
||||
|-------------|------------------|
|
||||
| `display-condition` (optional) | [Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) (either condition name or a conditional expression) <br /> that must be met for the design to be displayed. <br />If not defined, design will be displayed without any required condition. <br />If player does not meet condition, another design may be displayed <br /> based on chaining (see below for more info). |
|
||||
| `header` | List of lines to display in the header. |
|
||||
| `footer` | List of lines to display in the footer. |
|
||||
|
||||
**Example**:
|
||||
```
|
||||
@@ -41,13 +74,13 @@ header-footer:
|
||||
- "Footer for world %world%"
|
||||
```
|
||||
|
||||
# Chaining designs
|
||||
## Chaining designs
|
||||
When defining more than 1 design in config, the plugin will display the correct design based on conditions.
|
||||
The plugin goes through all defined designs, starting with the design on top.
|
||||
If the design's condition is met or not set, it is displayed.
|
||||
If not, the next defined design is checked and so on.
|
||||
If no suitable design is found (the last one has a condition requirement which wasn't met), none is displayed.
|
||||
Example:
|
||||
If no suitable design is found (the last one has a condition requirement which wasn't met), none is displayed.
|
||||
**Example**:
|
||||
```
|
||||
header-footer:
|
||||
enabled: true
|
||||
@@ -55,18 +88,23 @@ header-footer:
|
||||
per-world:
|
||||
display-condition: "%world%=world"
|
||||
header:
|
||||
- "Header for world %world%"
|
||||
- "Header for world world"
|
||||
footer:
|
||||
- "Footer for world %world%"
|
||||
- "Footer for world world"
|
||||
default: # No display condition
|
||||
header:
|
||||
- "Header for every world except %world%"
|
||||
- "Header for every world except world"
|
||||
footer:
|
||||
- "Footer for every world except %world%"
|
||||
- "Footer for every world except world"
|
||||
```
|
||||
Design `per-world` is checked first. If condition is not met, `default` is displayed.
|
||||
If a chaining 3 or more and player meets 2 or more conditions for 2 (or more) different designs, the first one defined in the config will be displayed. You can swap order of designs based on your priority needs.
|
||||
|
||||
# Compatibility with other plugins
|
||||
Header/Footer is a feature that cannot be handled by multiple plugins at once.
|
||||
To make sure no other plugin sends their own Header/Footer when not disabled in the plugin's config properly, TAB will block header/footer coming from all other plugins if this feature is enabled (and not disabled for a player with a condition).
|
||||
An exception to this is Velocity installation, where this "anti-override" is not available.
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - [1.8 - 1.20.1] Not resetting on server switch
|
||||
When under a BungeeCord network and having TAB installed on backend server and switching to another server, the header/footer will not reset. This is because BungeeCord makes it look like a world switch to the client. To avoid this, you have 2 options:
|
||||
@@ -75,31 +113,6 @@ When under a BungeeCord network and having TAB installed on backend server and s
|
||||
|
||||
Velocity sends reset packet, so it isn't a problem there.
|
||||
|
||||
# Tips & Tricks
|
||||
## Tip 1 - Dynamic line count
|
||||
The entire header is just a single character sequence that allows newline symbol (`\n`) for new lines. It is only being split into multiple lines in config for convenience to make it readable when configuring and editing. Same for footer.
|
||||
If using a placeholder that returns the newline symbol, it will create a new line. You can use this to your advantage and create a [conditional placeholder](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) or an [animation](https://github.com/NEZNAMY/TAB/wiki/Animations) that includes newline in some frames.
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
**animations.yml**
|
||||
```
|
||||
MyAnimation:
|
||||
change-interval: 1000
|
||||
texts:
|
||||
- "First frame with only 1 line"
|
||||
- "Second frame\nconsisting of 2 lines"
|
||||
```
|
||||
**config.yml**
|
||||
```
|
||||
header-footer:
|
||||
designs:
|
||||
DesignName:
|
||||
header:
|
||||
- "%animation:MyAnimation%"
|
||||
```
|
||||
</details>
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
@@ -130,4 +143,53 @@ header-footer:
|
||||
- "Regular header for old clients"
|
||||
footer:
|
||||
- "Consider updating your game"
|
||||
```
|
||||
```
|
||||
|
||||
## Example 2 - Per-world header/footer
|
||||
For this, we can use design chaining with conditions. There is no limit to how many worlds we can configure, but let's stick to 3 designs - one for `world`, one for `world2` and one for the rest.
|
||||
```
|
||||
header-footer:
|
||||
enabled: true
|
||||
designs:
|
||||
world:
|
||||
display-condition: "%world%=world" # Only show if player is in world "world"
|
||||
header:
|
||||
- "Header for world world"
|
||||
footer:
|
||||
- "Footer for world world"
|
||||
world2:
|
||||
display-condition: "%world%=world2" # Only show if player is in world "world2"
|
||||
header:
|
||||
- "Header for world world2"
|
||||
footer:
|
||||
- "Footer for world world2"
|
||||
default:
|
||||
header:
|
||||
- "Header for all worlds except world and world2"
|
||||
footer:
|
||||
- "Footer for all worlds except world and world2"
|
||||
```
|
||||
|
||||
## Example 3 - Dynamic line count
|
||||
The entire header is just a single character sequence that allows newline symbol (`\n`) for new lines. It is only being split into multiple lines in config for convenience to make it readable when configuring and editing. Same for footer.
|
||||
If using a placeholder that returns the newline symbol, it will create a new line. You can use this to your advantage and create a [conditional placeholder](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) or an [animation](https://github.com/NEZNAMY/TAB/wiki/Animations) that includes newline in some frames.
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
**animations.yml**
|
||||
```
|
||||
MyAnimation:
|
||||
change-interval: 1000
|
||||
texts:
|
||||
- "First frame with only 1 line"
|
||||
- "Second frame\nconsisting of 2 lines"
|
||||
```
|
||||
**config.yml**
|
||||
```
|
||||
header-footer:
|
||||
designs:
|
||||
DesignName:
|
||||
header:
|
||||
- "%animation:MyAnimation%"
|
||||
```
|
||||
</details>
|
||||
@@ -1,82 +1,100 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Creating a layout](#creating-a-layout)
|
||||
* [Display condition](#display-condition)
|
||||
* [default-skin](#default-skin)
|
||||
* [Configuration](#configuration)
|
||||
* [Layout](#layout)
|
||||
* [Fixed slots](#fixed-slots)
|
||||
* [Player groups](#player-groups)
|
||||
* [Managing multiple layouts with conditions](#managing-multiple-layouts-with-conditions)
|
||||
* [Skins](#skins)
|
||||
* [Format](#format)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Compatibility](#compatibility)
|
||||
* [Playerlist objective incompatibility](#playerlist-objective-incompatibility)
|
||||
* [Global playerlist incompatibility](#global-playerlist-incompatibility)
|
||||
* [Per world playerlist incompatibility](#per-world-playerlist-incompatibility)
|
||||
* [Chaining layouts](#chaining-layouts)
|
||||
* [Skins](#skins)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Compatibility with other features](#compatibility-with-other-features)
|
||||
* [Playerlist objective incompatibility](#playerlist-objective-incompatibility)
|
||||
* [Global playerlist incompatibility](#global-playerlist-incompatibility)
|
||||
* [Per world playerlist incompatibility](#per-world-playerlist-incompatibility)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - [1.19.3 - 1.21.1] Entries in chat complete](#additional-note-1---1193---1211-entries-in-chat-complete)
|
||||
* [Additional note 2 - [1.8 - 1.21.3] Second layer of skin missing](#additional-note-2---18---1213-second-layer-of-skin-missing)
|
||||
* [Additional note 3 - Entry overlap](https://github.com/NEZNAMY/TAB/wiki/Feature-guide%3A-Layout/_edit#additional-note-3---entry-overlap)
|
||||
* [Additional note 1 - [1.19.3 - 1.21.1] Entries in chat complete](#additional-note-1---1193---1211-entries-in-chat-complete)
|
||||
* [Additional note 2 - [1.8 - 1.21.3] Second layer of skin missing](#additional-note-2---18---1213-second-layer-of-skin-missing)
|
||||
* [Additional note 3 - Entry overlap](#additional-note-3---entry-overlap)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-server columns](#example-1---per-server-columns)
|
||||
* [Example 1 - Per-server columns](#example-1---per-server-columns)
|
||||
* [Example 2 - Per-world playerlist](#example-2---per-world-playerlist)
|
||||
|
||||
# About
|
||||
This feature allows you to customize all 80 tablist slots. Displaying less than 4 columns is currently not supported ([here's why](https://gist.github.com/NEZNAMY/3dfcbf7d44283735d3c18266a2851651)). This feature can be enabled and configured in **config.yml** file under **layout** section.
|
||||
|
||||
This feature is only available for versions **1.8** and up due to massive tablist changes, which would make 1.7- compatibility require a complete rewrite of the functionality and could still cause all kinds of visual issues, including, but not limited to compatibility with other plugins adding/removing players from the tablist.
|
||||
|
||||
# Creating a layout
|
||||
Layouts can be creating in config under `layout.layouts` section. A layout has 4 settings:
|
||||
* [Display condition](#display-condition) (optional)
|
||||
* [default-skin](#default-skin) (optional)
|
||||
* [Fixed slots](#fixed-slots)
|
||||
* [Player groups](#player-groups)
|
||||
|
||||
## Display condition
|
||||
Condition that must be met for player to see the layout.
|
||||
If player does not meet the condition, the next defined layout's condition is checked.
|
||||
If a player does not meet any defined display condition, they will see the default tablist.
|
||||
Display condition is optional and doesn't need to be set.
|
||||
For list of possible condition types see [Conditions page](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders).
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
staff:
|
||||
condition: "permission:tab.staff"
|
||||
```
|
||||
This layout will only be visible to players with `tab.staff` permission.
|
||||
|
||||
## default-skin
|
||||
When configured, layout will override `default-skin` value and use that one for empty slots and fixed slots without skin definition. When not set, global `default-skin` will be displayed.
|
||||
**Example**:
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **layout** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
layout:
|
||||
enabled: true
|
||||
enabled: false
|
||||
direction: COLUMNS
|
||||
default-skin: mineskin:37e93c8e12cd426cb28fce31969e0674 # Global default skin for all layouts
|
||||
default-skin: mineskin:37e93c8e12cd426cb28fce31969e0674
|
||||
enable-remaining-players-text: true
|
||||
remaining-players-text: '... and %s more'
|
||||
empty-slot-ping-value: 1000
|
||||
layouts:
|
||||
test:
|
||||
default-skin: player:Notch # All empty slots will use skin of player "Notch" in this layout
|
||||
fixed-slots: []
|
||||
default:
|
||||
slot-count: 80
|
||||
fixed-slots:
|
||||
- '1|&3Website&f:'
|
||||
- '2|&bmyserver.net'
|
||||
- '3|&8&m '
|
||||
- '4|&3Name&f:'
|
||||
- '5|&b%player%'
|
||||
- '7|&3Rank&f:'
|
||||
- '8|Rank: %group%'
|
||||
- '10|&3World&f:'
|
||||
- '11|&b%world%'
|
||||
- '13|&3Time&f:'
|
||||
- '14|&b%time%'
|
||||
- '21|&3Teamspeak&f:'
|
||||
- '22|&bts.myserver.net'
|
||||
- '23|&8&m '
|
||||
- '41|&3Store&f:'
|
||||
- '42|&bshop.myserver.net'
|
||||
- '43|&8&m '
|
||||
groups:
|
||||
staff:
|
||||
display-condition: permission:tab.staff
|
||||
slots:
|
||||
- 24-40
|
||||
players:
|
||||
slots:
|
||||
- 1-80
|
||||
- 44-80
|
||||
```
|
||||
All the options are explained in the following table.
|
||||
|
||||
## Fixed slots
|
||||
| Option name | Default value | Description |
|
||||
|-------------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| direction | COLUMNS | Defines direction of slots. Options are COLUMNS (top to bottom, left to right) and ROWS (left to right, top to bottom). This does not only change the slot numbers in configuration, but will also affect the way players are being filled into player groups. |
|
||||
| default-skin | "mineskin:383747683" | Default skin to display for fixed slots that do not define a skin, empty slots and fixed slots with invalid skin. |
|
||||
| enable-remaining-players-text | true | When enabled and there are more players in a player group than available slots, the last slot of player group will show how many more players there are, instead of using the last slot for one more player. |
|
||||
| remaining-players-text | "... and %s more" | Text to show if option above is enabled. Use `%s` variable for defining how many more players there are. |
|
||||
| empty-slot-ping-value | 1000 | Ping value to use for fixed slots and empty slots. The ping intervals for bars are client sided and are as following: <br />- Negative value: ✖ <br />- 0 - 149: 5 bars <br />- 150 - 299: 4 bars <br />- 300 - 599: 3 bars <br />- 600 - 999: 2 bars <br />- 1000+: 1 bar |
|
||||
| layouts | *Map* | Layouts to display based on conditions (see below for more info). |
|
||||
|
||||
## Layout
|
||||
A layout consists of the following options:
|
||||
| Option name | Description |
|
||||
|-------------|------------------|
|
||||
| `display-condition` (optional) | [Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) (either condition name or a conditional expression) that must be met for the layout to be displayed. If not defined, layout will be displayed without any required condition. If player does not meet condition, another layout may be displayed based on chaining (see below for more info). |
|
||||
| `default-skin` (optional) | Overrides global `default-skin` setting for this layout. |
|
||||
| `slot-count` (optional) | Amount of slots to show in this layout. If not specified, defaults to `80`. **Only works on 1.19.3+**. |
|
||||
| `fixed-slots` | List of fixed slots, see below for more info. |
|
||||
| `player-groups` | Map of player groups, see below for more info. |
|
||||
|
||||
### Fixed slots
|
||||
These are slots with fixed position that contain configurable text and skin.
|
||||
They can be configured under `fixed-slots` option of each layout. The definition syntax is `SLOT|TEXT|SKIN`. If you don't want to define custom skin for that slot, use `SLOT|TEXT`.
|
||||
They can be configured under `fixed-slots` option of each layout. The definition syntax is `SLOT|TEXT[|SKIN]` (skin is optional). If you don't want to define custom skin for that slot, use `SLOT|TEXT`.
|
||||
`SLOT` - The position of the fixed slot. It can be from 1 to 80. By default, the direction is set to columns, so the first column is 1-20.
|
||||
`TEXT` - the text. It supports placeholders.
|
||||
`SKIN` - layout's skin, see below for configuration.
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
staff:
|
||||
fixed-slots:
|
||||
- '1|&3RAM&f:'
|
||||
- '2|&b%memory-used%MB / %memory-max%MB'
|
||||
@@ -87,15 +105,15 @@ layouts:
|
||||
If you do not wish to use any fixed slots, make it an empty list (`[]`).
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
staff:
|
||||
fixed-slots: []
|
||||
layouts:
|
||||
staff:
|
||||
fixed-slots: []
|
||||
```
|
||||
|
||||
## Player groups
|
||||
### Player groups
|
||||
These are groups of players that meet the specified condition. They consist of 2 parts - condition and slots.
|
||||
|
||||
**1. Condition**
|
||||
**1. Display Condition**
|
||||
The condition players must meet to be displayed in the group.
|
||||
If a player doesn't meet condition for a group, the next group's condition is checked
|
||||
(groups are checked in the order they are defined in config).
|
||||
@@ -106,30 +124,30 @@ Display condition is not required (can be used to make a "default" group with th
|
||||
Slot intervals dedicated to the group. Interval `1-5` would mean all slots from 1 to 5. In case you want to define multiple intervals to exclude some slots, define another interval below the first one.
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
myLayout:
|
||||
groups:
|
||||
staff:
|
||||
condition: permission:tab.staff
|
||||
slots:
|
||||
- '24-40'
|
||||
players:
|
||||
slots:
|
||||
- '44-80'
|
||||
layouts:
|
||||
myLayout:
|
||||
groups:
|
||||
staff:
|
||||
display-condition: permission:tab.staff
|
||||
slots:
|
||||
- '24-40'
|
||||
players:
|
||||
slots:
|
||||
- '44-80'
|
||||
```
|
||||
In this example, slots `24-40` will be reserved for players with `tab.staff` permission. Players without that permission will be in slots `44-80`.
|
||||
|
||||
# Managing multiple layouts with conditions
|
||||
You can define multiple layouts and display them based on a condition.
|
||||
To do so, define `condition` for layouts.
|
||||
Conditions are checked in the order layouts are defined in config.
|
||||
If layout doesn't specify condition, it is displayed with no requirement.
|
||||
If a player does not meet display condition for any layout, they will see the classic tablist.
|
||||
## Chaining layouts
|
||||
When defining more than 1 layout in config, the plugin will display the correct design based on conditions.
|
||||
The plugin goes through all defined layouts, starting with the design on top.
|
||||
If the layout's condition is met or not set, it is displayed.
|
||||
If not, the next defined layout is checked and so on.
|
||||
If no suitable layout is found (the last one has a condition requirement which wasn't met), no layout is displayed (player will see the normal tablist).
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
staff:
|
||||
condition: "permission:tab.staff"
|
||||
display-condition: "permission:tab.staff"
|
||||
fixed-slots:
|
||||
...
|
||||
groups:
|
||||
@@ -142,31 +160,22 @@ layouts:
|
||||
```
|
||||
In this example, players with `tab.staff` permission will see `staff` layout, others will see `default` layout.
|
||||
|
||||
# Skins
|
||||
*If you're wondering how to enable / disable heads in the tablist, see [FAQ](https://github.com/NEZNAMY/TAB/wiki/Frequently-Asked-Questions#6---how-to-make-player-heads-visible-in-tablist).*
|
||||
In slots which display a connected player, their skin is displayed.
|
||||
For fixed slots, if their skin is defined, it is used, otherwise `default-skin` is used. It is also used if invalid skin is specified.
|
||||
## Skins
|
||||
*If you're wondering how to enable / disable heads in the tablist, see [FAQ](https://github.com/NEZNAMY/TAB/wiki/Frequently-Asked-Questions#6---how-to-make-player-heads-visible-in-tablist).*
|
||||
|
||||
## Format
|
||||
Currently, TAB supports 3 skin formats:
|
||||
* `mineskin:<UUID>` - takes UUID from [MineSkin](https://mineskin.org). You can find some pre-uploaded skins at the bottom of this page.
|
||||
* `player:<name>` - displays skin of player with the `name`
|
||||
* `texture:<texture>` - uses literal skin texture. This lets you use heads from [Minecraft-Heads](https://minecraft-heads.com/custom-heads) by taking the texture in the `Minecraft-URL` field.
|
||||

|
||||
For players, their skin is displayed. For fixed slots and empty slots, you can specify which skin to show using the same skin syntax.
|
||||
Currently, TAB supports these skin formats:
|
||||
| Format | Example | Description |
|
||||
|--------|---------|-------------|
|
||||
| `mineskin:<UUID>` | `mineskin:37e93c8e12cd426cb28fce31969e0674` | Takes UUID from [MineSkin](https://mineskin.org). |
|
||||
| `player:<name>` | `player:Notch` | Displays skin of specified player. |
|
||||
| `texture:<texture>` | `texture:469b3bac406b51ba0e76c2c218aa4d45fde9ea7c101c85fbd8106c92c4aa36dd` | Uses skin texture from `textures.minecraft.net`. Skin browsing websites often show this value (for example `Texture URL` on mineskin.org). |
|
||||
|
||||
# Additional settings
|
||||
| Option name | Default value | Description |
|
||||
|-------------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| direction | COLUMNS | Defines direction of slots. Options are COLUMNS (top to bottom, left to right) and ROWS (left to right, top to bottom). This does not only change the slot numbers in configuration, but will also affect the way players are being filled into player groups. |
|
||||
| enable-remaining-players-text | true | When enabled, the last slot of player group will show how many more players there are, instead of using the last slot for one more player. |
|
||||
| remaining-players-text | "... and %s more" | Text to show if option above is enabled. |
|
||||
| default-skin | "mineskin:383747683" | Default skin to display for fixed slots that do not define a skin, empty slots and fixed slots with invalid skin. |
|
||||
| empty-slot-ping-value | 1000 | Ping value to use for fixed slots and empty slots. The ping intervals for bars are client sided and are as following: <br />- Negative value: ✖ <br />- 0 - 149: 5 bars <br />- 150 - 299: 4 bars <br />- 300 - 599: 3 bars <br />- 600 - 999: 2 bars <br />- 1000+: 1 bar |
|
||||
# Compatibility with other plugins
|
||||
This feature does not have any compatibility issues with other plugins.
|
||||
When it comes to compatibility with vanish plugins, see [Additional information - Vanish detection](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection).
|
||||
|
||||

|
||||

|
||||
|
||||
# Compatibility
|
||||
# Compatibility with other features
|
||||
## Playerlist objective incompatibility
|
||||
To avoid showing fake players in tab-complete, TAB uses an empty string as the fake players' names.
|
||||
Though Minecraft's scoreboard objectives (which help achieve the Playerlist objective feature) only use players' names and not players' UUIDs to define values.
|
||||
@@ -194,19 +203,19 @@ Each layout only displays players in that world.
|
||||
```
|
||||
layouts:
|
||||
layout1:
|
||||
condition: "%world%=world1"
|
||||
display-condition: "%world%=world1"
|
||||
fixed-slots: []
|
||||
groups:
|
||||
players:
|
||||
condition: "%world%=world1"
|
||||
display-condition: "%world%=world1"
|
||||
slots:
|
||||
- 1-80
|
||||
layout2:
|
||||
condition: "%world%=world2"
|
||||
display-condition: "%world%=world2"
|
||||
fixed-slots: []
|
||||
groups:
|
||||
players:
|
||||
condition: "%world%=world2"
|
||||
display-condition: "%world%=world2"
|
||||
slots:
|
||||
- 1-80
|
||||
```
|
||||
@@ -214,11 +223,11 @@ You can also merge multiple worlds into a group.
|
||||
```
|
||||
layouts:
|
||||
layout1:
|
||||
condition: "%world%=world1|%world%=world2"
|
||||
display-condition: "%world%=world1|%world%=world2"
|
||||
fixed-slots: []
|
||||
groups:
|
||||
players:
|
||||
condition: "%world%=world1|%world%=world2"
|
||||
display-condition: "%world%=world1|%world%=world2"
|
||||
slots:
|
||||
- 1-80
|
||||
```
|
||||
@@ -226,7 +235,7 @@ You can also replicate "ignore-effect-in-worlds" by creating a layout with condi
|
||||
```
|
||||
layouts:
|
||||
globalWorld:
|
||||
condition: "%world%=globalWorld"
|
||||
display-condition: "%world%=globalWorld"
|
||||
fixed-slots: []
|
||||
groups:
|
||||
players: # No condition here to show all players
|
||||
@@ -289,21 +298,65 @@ layout:
|
||||
- "61|&2&lSkyblock"
|
||||
groups:
|
||||
lobby:
|
||||
condition: "%server%=lobby"
|
||||
display-condition: "%server%=lobby"
|
||||
slots:
|
||||
- 2-20
|
||||
survival:
|
||||
condition: "%server%=survival"
|
||||
display-condition: "%server%=survival"
|
||||
slots:
|
||||
- 22-40
|
||||
creative:
|
||||
condition: "%server%=creative"
|
||||
display-condition: "%server%=creative"
|
||||
slots:
|
||||
- 42-60
|
||||
skyblock:
|
||||
condition: "%server%=skyblock"
|
||||
display-condition: "%server%=skyblock"
|
||||
slots:
|
||||
- 62-80
|
||||
```
|
||||
You can also remove the last condition (in this case `condition: "%server=skyblock%"`) and the columns will be used for all other servers, instead of just a specified one.
|
||||
You can also remove the last condition (in this case `display-condition: "%server=skyblock%"`) and the columns will be used for all other servers, instead of just a specified one.
|
||||

|
||||
|
||||
|
||||
## Example 2 - Per-world playerlist
|
||||
If you want to reproduce the per-world-playerlist feature but with layouts, you can use relational conditions to check the viewer's world (`%viewer:world%`) with the target player's world (`%world%`) like so:
|
||||
```yml
|
||||
layout:
|
||||
enabled: true
|
||||
direction: COLUMNS
|
||||
default-skin: mineskin:383747683
|
||||
enable-remaining-players-text: true
|
||||
remaining-players-text: '... and %s more'
|
||||
empty-slot-ping-value: 1000
|
||||
layouts:
|
||||
default:
|
||||
slot-count: 80
|
||||
fixed-slots:
|
||||
- '1|&3Website&f:'
|
||||
- '2|&bmyserver.net'
|
||||
- '3|&8&m '
|
||||
- '4|&3Name&f:'
|
||||
- '5|&b%player%'
|
||||
- '7|&3Rank&f:'
|
||||
- '8|Rank: %group%'
|
||||
- '10|&3World&f:'
|
||||
- '11|&b%world%'
|
||||
- '13|&3Time&f:'
|
||||
- '14|&b%time%'
|
||||
- '21|&3Teamspeak&f:'
|
||||
- '22|&bts.myserver.net'
|
||||
- '23|&8&m '
|
||||
- '41|&3Store&f:'
|
||||
- '42|&bshop.myserver.net'
|
||||
- '43|&8&m '
|
||||
groups:
|
||||
players:
|
||||
display-condition: "%viewer:world%=%world%"
|
||||
slots:
|
||||
- 24-40
|
||||
- 44-80
|
||||
```
|
||||
> [!NOTE]
|
||||
> This is just an example, relational conditions are not limited to displaying only players in your world.
|
||||
> If you want players in your server on proxy, use `%viewer:server%=%server%` in `display-condition`.
|
||||
> This works for any placeholder offered by TAB or by PlaceholderAPI.
|
||||
@@ -1,26 +1,32 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [config.yml](#configyml)
|
||||
* [groups.yml and users.yml](#groupsyml-and-usersyml)
|
||||
* [Global settings](#global-settings)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Priority system](#priority-system)
|
||||
* [Name color](#name-color)
|
||||
* [Placeholder support](#placeholder-support)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Priorities](#priorities)
|
||||
* [Name color](#name-color)
|
||||
* [Commands](#commands)
|
||||
* [Placeholders](#placeholders)
|
||||
* [PlaceholderAPI placeholders](#placeholderapi-placeholders)
|
||||
* [Limitations](#limitations)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - NPC (in)compatibility](#additional-note-1---npc-incompatibility)
|
||||
* [Additional note 2 - Prefix/suffix on pets](#additional-note-2---prefixsuffix-on-pets)
|
||||
* [Additional note 3 - Changing name itself](#additional-note-3---changing-name-itself)
|
||||
* [Additional note 4 - F1 view](#additional-note-4---f1-view)
|
||||
* [Additional note 5 - Transparent players](#additional-note-5---transparent-players)
|
||||
* [Additional note 6 - Vanish hook](#additional-note-6---vanish-hook)
|
||||
* [Additional note 1 - NPC (in)compatibility](#additional-note-1---npc-incompatibility)
|
||||
* [Additional note 2 - Prefix/suffix on pets](#additional-note-2---prefixsuffix-on-pets)
|
||||
* [Additional note 3 - Changing name itself](#additional-note-3---changing-name-itself)
|
||||
* [Additional note 4 - F1 view](#additional-note-4---f1-view)
|
||||
* [Additional note 5 - Transparent players](#additional-note-5---transparent-players)
|
||||
* [Additional note 6 - Vanish hook](#additional-note-6---vanish-hook)
|
||||
* [API](#api)
|
||||
* [Changing prefix and suffix](#changing-prefix-and-suffix)
|
||||
* [Collision](#collision)
|
||||
* [Manipulating visibility](#manipulating-visibility)
|
||||
* [Disabling team handling](#disabling-team-handling)
|
||||
* [Changing prefix and suffix](#changing-prefix-and-suffix)
|
||||
* [Collision](#collision)
|
||||
* [Manipulating visibility](#manipulating-visibility)
|
||||
* [Disabling team handling](#disabling-team-handling)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Taking prefix/suffix from permission plugin](#example-1---taking-prefixsuffix-from-permission-plugin)
|
||||
* [Example 2 - Making nametags invisible on a subserver](#example-2---making-nametags-invisible-on-a-subserver)
|
||||
|
||||
# About
|
||||
Nametags are controlled by a feature called scoreboard teams. They offer 6 properties:
|
||||
@@ -30,13 +36,37 @@ Nametags are controlled by a feature called scoreboard teams. They offer 6 prope
|
||||
* Nametag visibility rule
|
||||
* Collision rule
|
||||
* Team color (1.13+) - used to set name and glow color (check out [How to make TAB compatible with glow plugins](https://github.com/NEZNAMY/TAB/wiki/How-to-make-TAB-compatible-with-glow-plugins)) (on 1.12- it uses last color of prefix).
|
||||
* Options - AllowFriendlyFire and CanSeeFriendlyInvisibles
|
||||
|
||||
When enabling this feature, TAB will control all of these. It is not possible to take values from 2 different teams (plugins). Most of the compatibility problems with other plugins can be solved with placeholders. If you want sorting but not nametags, just don't configure any prefix/suffix. If you want another plugin to handle teams, configure sorting in that plugin.
|
||||
|
||||
This feature can be configured in **config.yml** under **scoreboard-teams** section.
|
||||
|
||||
# Configuration
|
||||
## Groups and users
|
||||
This feature is configured in 2 parts based on what you are configuring. Most settings are in the **config.yml**, but prefix/suffix is configured in **groups.yml** and **users.yml**.
|
||||
## config.yml
|
||||
The first part can be configured in **config.yml** under **scoreboard-teams** section. It controls everything except prefix and suffix.
|
||||
|
||||
This is how the default configuration looks (excluding sorting options):
|
||||
```
|
||||
scoreboard-teams:
|
||||
enabled: true
|
||||
enable-collision: true
|
||||
invisible-nametags: false
|
||||
can-see-friendly-invisibles: false
|
||||
disable-condition: '%world%=disabledworld'
|
||||
```
|
||||
All of the options are explained in the following table.
|
||||
| Option name | Default value | Description |
|
||||
|-----------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| enable-collision | true | Controls collision rule. Available values are: <br />- `true` - Collision will be enabled permanently. <br />- `false` - Collision will be disabled permanently. <br /> - *Conditional expression* - Collision will be enabled if player meets the [short format](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders#short-format) conditional expression (for example `%world%=world` will result in collision only being enabled for players in world `world`). <br /> - *Condition name* - Uses name of a [defined condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for a player to have collision enabled. |
|
||||
| invisible-nametags | false | Controls nametags visibility rule. Available values are: <br />- `true` - Nametags will be invisible. <br />- `false` - Nametags will be visible. <br /> - *Conditional expression* - Nametags will be invisible if player meets the [short format](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders#short-format) conditional expression. <br /> - *Condition name* - Uses name of a [defined condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders). <br /> Option to hide nametags was added in Minecraft **1.8**, therefore it will not work on 1.7. |
|
||||
| can-see-friendly-invisibles | false | Controls value of the team flag. It allows you to see invisible players in the same team as transparent instead of completely invisible. Since TAB places each player into an individual team, this option will only take effect in combination with plugins that spawn a dummy clone of the player (sit or disguise plugins). |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
|
||||
## groups.yml and users.yml
|
||||
This part allows you to configure prefix and suffix. They are called `tagprefix` and `tagsuffix`, where "tag" refers to name tag, to allow separation from tablist settings.
|
||||
|
||||
### Global settings
|
||||
Properties can be applied in 2 ways: groups and users. Users can be defined by both username and their UUID. Values applied to users take priority over groups.
|
||||
|
||||
**groups.yml**
|
||||
@@ -44,8 +74,7 @@ Properties can be applied in 2 ways: groups and users. Users can be defined by b
|
||||
admin:
|
||||
tagprefix: "&4&lAdmin &r"
|
||||
```
|
||||
|
||||
This can also be achieved with commands, in this case `/tab group admin tagprefix "&4&lAdmin &r"`.
|
||||
Or with an in-game command `/tab group admin tagprefix "&4&lAdmin &r"`.
|
||||
|
||||
**users.yml**
|
||||
```
|
||||
@@ -56,7 +85,7 @@ _NEZNAMY_:
|
||||
237d8b55-3f97-4749-aa60-e9fe97b45062:
|
||||
tagprefix: "&6&lTAB &r"
|
||||
```
|
||||
This can also be achieved with commands, in this case `/tab player _NEZNAMY_ tagprefix "&4&lAdmin &r"` or `/tab playeruuid _NEZNAMY_ tagprefix "&4&lAdmin &r"`.
|
||||
Or with in-game commands `/tab player _NEZNAMY_ tagprefix "&6&lTAB &r"` and `/tab playeruuid _NEZNAMY_ tagprefix "&6&lTAB &r"` (the player must be online for uuid option to work).
|
||||
|
||||
Properties can also be set as "default" for everyone who does not have them defined.
|
||||
For that purpose, a group keyword `_DEFAULT_` was made.
|
||||
@@ -69,7 +98,7 @@ _DEFAULT_:
|
||||
tagprefix: "&7" # This will be displayed on everyone except admin
|
||||
```
|
||||
|
||||
## Per-world / per-server
|
||||
### Per-world / per-server
|
||||
Values can also be applied per-world (and per-server on a proxy), where they can be defined per group/user. These values take priority over global settings. Example:
|
||||
**groups.yml**
|
||||
```
|
||||
@@ -96,8 +125,8 @@ per-world:
|
||||
tagsuffix: "Suffix in all worlds starting with lobby-"
|
||||
```
|
||||
|
||||
## Priority system
|
||||
The full list of priorities looks like this:
|
||||
### Priorities
|
||||
The full list of priorities to choose correct prefix/suffix for a player looks like this:
|
||||
1. value set using the [API](#api)
|
||||
2. per-world / per-server applied to username
|
||||
3. value applied to username
|
||||
@@ -113,43 +142,42 @@ This list is browsed through until the first match is found. If no match is foun
|
||||
Values are taken independently of each other.
|
||||
This means you can set per-world tagprefix, but only keep one global tagsuffix, for example.
|
||||
|
||||
You can see the source of a value displayed on player by using `/tab debug <player>` and checking "source"
|
||||
part of the value you are looking for.
|
||||
You can see the source of a value displayed on player by using `/tab dump <player>`, opening the link and checking "source"
|
||||
part of the value you are looking for (tagprefix / tagsuffix).
|
||||
|
||||
## Name color
|
||||
On versions 1.12.2 and below, name color follows the last color of prefix. This also includes magic codes. On 1.13+, this is no longer the case. Instead, a new field called "team color" was created. This is an enum constant, where we need to pick from 22 options - 16 colors, 5 magic codes and reset. **As you can see, it doesn't allow for RGB colors**. This also means name can no longer have color and magic codes simultaneously.
|
||||
|
||||
TAB does not have an option for configuring team color to avoid complexity, such as per-version configuration, or having to configure an additional field, while people are used to putting some color code at the end of prefix and expect the name to take it. For that reason, TAB detects the last used color code and uses that for team color.
|
||||
TAB does not have an option for configuring team color to avoid complexity, such as per-version configuration, or having to configure an additional field.
|
||||
For that reason, TAB detects the last used color code and uses that for team color.
|
||||
> [!CAUTION]
|
||||
> An exception is when using MiniMessage syntax / using RGB codes on server which has MiniMessage library. In this scenario, it is not possible to detect reset (`&r`) as last used "color", because in minecraft components it is not a color, rather, it resets formatting by setting all magic codes to false / color to null. If you use `&r` (`<reset>`) and notice it not working, replace it with `&f` (`<white>`) (or add it after the reset to be safe). There seems to be no difference between setting color to `WHITE` versus `RESET`.
|
||||
> An exception is when using MiniMessage syntax / using RGB codes on server which has MiniMessage library. In this scenario, it is not possible to detect reset (`&r`) as last used "color", because in Minecraft components it is not a color, rather, it resets formatting by setting all magic codes to false / color to null. If you use `&r` (`<reset>`) and notice it not working, replace it with `&f` (`<white>`) (or add it after the reset to be safe). There seems to be no difference between setting color to `WHITE` versus `RESET`.
|
||||
|
||||
## Placeholder support
|
||||
All values fully support [TAB's internal placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#internal-placeholders) and [PlaceholderAPI placeholders](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Placeholders) including relational placeholders.
|
||||
The number of placeholders is not limited, and they can be used in combination with static text as well.
|
||||
# Commands
|
||||
| Command | Permission | Description |
|
||||
|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| /tab nametag <show/hide/toggle> \[player\] \[viewer\] \[options\] | `tab.nametag.visibility` (for yourself) <br /> `tab.nametag.visibility.other` (for others) | Shows / hides / toggles nametag of a specified player. If viewer is specified, view is only affected for the viewer. You can use `-s` as option for silent toggling (no chat message for affected player). |
|
||||
| /tab nametag <showview/hideview/toggleview> \[viewer\] \[options\] | `tab.nametag.view` (for yourself) <br /> `tab.nametag.view.other` (for others) | Shows / hides / toggles nametag VIEW a specified player. You can use `-s` as option for silent toggling (no chat message for affected player) |
|
||||
| /tab setcollision \<player\> \<true\|false\> | `tab.setcollision` | Forces collision rule for specified player, overriding configuration. |
|
||||
|
||||
## Additional settings
|
||||
| Option name | Default value | Description |
|
||||
|-----------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| enable-collision | true | Controls collision rule. Available values are: <br />- `true` - Collision will be enabled permanently. <br />- `false` - Collision will be disabled permanently. <br /> - *Conditional expression* - Collision will be enabled if player meets the [short format](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders#short-format) conditional expression (for example `%world%=world` will result in collision only being enabled for players in world `world`). <br /> - *Condition name* - Uses name of a [defined condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for a player to have collision enabled. |
|
||||
| invisible-nametags | false | When evaluates to `true`, makes player's nametag invisible. Available values are: <br />- `true` - Collision will be enabled permanently. <br />- `false` - Collision will be disabled permanently. <br /> - *Conditional expression* - Collision will be enabled if player meets the [short format](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders#short-format) conditional expression (for example `%world%=world` will result in collision only being enabled for players in world `world`). <br /> - *Condition name* - Uses name of a [defined condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders). <br /> Option to hide nametags was added in minecraft **1.8**, therefore it will not work on 1.7 and lower. |
|
||||
| can-see-friendly-invisibles | false | Controlling value of the team flag. It allows you to see invisible players in the same team as transparent instead of completely invisible. Since TAB places each player into an individual team, this option will only take effect in combination with plugins that spawn a dummy clone of the player (sit or disguise plugins). |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
# Placeholders
|
||||
This feature does not offer any internal placeholders, only PlaceholderAPI placeholders.
|
||||
|
||||
# Tips & Tricks
|
||||
If you want TAB to only take prefixes/suffixes from the permission plugin,
|
||||
delete all groups from **groups.yml** and only keep this:
|
||||
```
|
||||
_DEFAULT_:
|
||||
tagprefix: "%luckperms-prefix%"
|
||||
tagsuffix: "%luckperms-suffix%"
|
||||
```
|
||||
## PlaceholderAPI placeholders
|
||||
Here are TAB's PlaceholderAPI placeholders you can use when this feature is enabled:
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| `%tab_tagprefix%` | Player's current tagprefix with placeholders parsed. |
|
||||
| `%tab_tagsuffix%` | Player's current tagprefix with placeholders parsed. |
|
||||
| `%tab_tagprefix_raw%` | Player's current raw tagprefix with placeholder identifiers. |
|
||||
| `%tab_tagsuffix_raw%` | Player's current raw tagsuffix with placeholder identifiers. |
|
||||
| `%tab_nametag_visibility%` | "Enabled" if player can see nametags, "Disabled" if disabled using `/tab nametag toggleview` |
|
||||
|
||||
# Limitations
|
||||
* [1.5 - 1.12.2] Prefix/suffix length is limited to 16 characters (including color codes). Any characters beyond that will be cut to prevent players getting disconnected.
|
||||
* [1.12.2-] Prefix/suffix length is limited to 16 characters (including color codes). Any characters beyond that will be cut to prevent players getting disconnected.
|
||||
* [1.13+] Name color is controlled by a chat format enum constant. This means:
|
||||
* Name can only have one code. That is either color or magic code (such as &4 or &l), but not both.
|
||||
* Name does not support RGB codes. Any used RGB colors will be rounded to the nearest legacy code.
|
||||
* Name can only have one code. That is either color or magic code (such as &4 or &l), but not both.
|
||||
* Name does not support RGB codes. Any used RGB colors will be rounded to the nearest legacy code.
|
||||
* The same value manages name color and glow color, which means they cannot be different.
|
||||
* The name cannot be effectively changed and the plugin doesn't offer it.
|
||||
|
||||
@@ -159,9 +187,13 @@ If you want to avoid these at all costs, the only way is to create entities (arm
|
||||
* [NotJustNamePlates](https://github.com/YouHaveTrouble/NotJustNameplates)
|
||||
* [CustomNameplates](https://polymart.org/product/2543/customnameplates)
|
||||
|
||||
# Compatibility with other plugins
|
||||
Scoreboard teams is a feature that cannot be handled by multiple plugins at once.
|
||||
To make sure no other plugin sends their own teams when not disabled in the plugin's config properly, TAB will block players being added into teams coming from all other plugins (if this feature is enabled and not disabled for a player with a condition).
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - NPC (in)compatibility
|
||||
Teams are bound to player names, not uuids or entity ids.
|
||||
Teams are bound to player names, not UUIDs or entity ids.
|
||||
Because of that, they will affect all player entities with that name.
|
||||
This includes NPCs with the same names as online players, and prefixes/suffixes will be displayed on those as well.
|
||||
To avoid it, make their names not match any online player and use holograms to display them instead
|
||||
@@ -171,9 +203,7 @@ To avoid it, make their names not match any online player and use holograms to d
|
||||
Since Minecraft 1.9, teams affect tamed animals as well, displaying prefix/suffix on them as well and if nametags are set to be invisible, they will be completely invisible as well. If you want to avoid it, install [this plugin](https://www.spigotmc.org/resources/109466/).
|
||||
|
||||
## Additional note 3 - Changing name itself
|
||||
Teams do not allow to change the nametag name itself. The only way to achieve this goal is to send & modify packets to replace the player's actual profile name with a fake one. This is very complicated and can have a lot of side effects. This is out of scope of TAB and is in fact something nick plugins are trying to achieve. As such, **TAB does not offer this**.
|
||||
|
||||
Once you find a nick plugin that supports this, you will need to [make sure TAB properly detects this](https://github.com/NEZNAMY/TAB/wiki/How-to-display-name-from-nickname-plugins#proper-compatibility-with-plugins-that-change-profile-name) to make name-bound features (such as nametags) work properly for nicked players.
|
||||
Teams do not allow to change the nametag name itself and TAB does not offer this functionality in any (other) way. For more information, see [How to display name from nickname plugins - Nametag](https://github.com/NEZNAMY/TAB/wiki/How-to-display-name-from-nickname-plugins#nametag).
|
||||
|
||||
## Additional note 4 - F1 view
|
||||
Using teams causes player nametags to remain visible when using F1 view.
|
||||
@@ -198,7 +228,8 @@ In config.yml. Keep in mind, you'll not be able to use any team features if you
|
||||
## Additional note 6 - Vanish hook
|
||||
When a player is [vanished](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection), their team is unregistered for all other players who do **not** have `tab.seevanished` permission.
|
||||
|
||||
The purpose of this is to avoid players figuring out a staff member is still online but just vanished by "checking" registered teams and their members. The easiest way of "exploiting" this is to have a 3rd party client spawn a player entity with name of a chosen player (staff member) and seeing if their name is formatted (= team is registered = player is online) or not (offline).
|
||||
The purpose of this is to avoid players figuring out a staff member is still online but just vanished by "checking" registered teams and their members.
|
||||
An example of "exploiting" this is to have a 3rd party client spawn a player entity with name of a chosen player (staff member) and seeing if their name is formatted (= team is registered = player is online) or not (offline).
|
||||
|
||||
For this reason, make sure you give all of your staff members who can see vanished players the `tab.seevanished` permission, otherwise the vanished player will appear on top of the tablist (players without team are above players with team) and their nametag will not be formatted.
|
||||
|
||||
@@ -245,4 +276,34 @@ To get the original value set by the plugin based on configuration:
|
||||
## Disabling team handling
|
||||
* `NameTagManager#pauseTeamHandling(TabPlayer)` - Pauses team handling for a specific player. This will unregister the player's team and disable anti-override for teams.
|
||||
* `NameTagManager#resumeTeamHandling(TabPlayer)` - Resumes team handling for a specific player. This will register the player's team and enable anti-override for teams.
|
||||
* `NameTagManager#hasTeamHandlingPaused(TabPlayer)` - Returns `true` if handling is disabled using methods above, `false` if not.
|
||||
* `NameTagManager#hasTeamHandlingPaused(TabPlayer)` - Returns `true` if handling is disabled using methods above, `false` if not.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Taking prefix/suffix from permission plugin
|
||||
If you want TAB to only take prefixes/suffixes from the permission plugin,
|
||||
delete all groups from **groups.yml** and only keep this:
|
||||
```
|
||||
_DEFAULT_:
|
||||
tagprefix: "%luckperms-prefix%"
|
||||
tagsuffix: "%luckperms-suffix%"
|
||||
```
|
||||
Or a PlaceholderAPI placeholder from your permission plugin if you use a different one.
|
||||
|
||||
> [!NOTE]
|
||||
> When doing this, make sure you don't delete something you didn't mean to delete, for example ta**b**prefix and ta**b**suffix from tablist name formatting feature.
|
||||
|
||||
## Example 2 - Making nametags invisible on a subserver
|
||||
The config option `invisible-nametags` takes either a [condition or a conditional expression](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders).
|
||||
We can use short format `%server%=<server name>`. If the server name is `server1`, it will look like this:
|
||||
```
|
||||
invisible-nametags: "%server%=server1"
|
||||
```
|
||||
If we want to disable it in 2 servers, we separate them with `|` (OR). Result:
|
||||
```
|
||||
invisible-nametags: "%server%=server1|%server%=server2"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is just an example, the plugin is not limited to making nametags invisible only based on server.
|
||||
> If you want to disable them in a world, use %world% with world name(s).
|
||||
> This works for any placeholder offered by TAB or by PlaceholderAPI.
|
||||
@@ -1,12 +1,12 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Grouping playerlist from multiple worlds](#grouping-playerlist-from-multiple-worlds)
|
||||
* [Grouping playerlist from multiple worlds](#grouping-playerlist-from-multiple-worlds)
|
||||
|
||||
# About
|
||||
This feature allows you to limit players you see in tablist to only players in your world, or in a group of worlds.
|
||||
This feature can be configured in **config.yml** under **per-world-playerlist** section.
|
||||
To use it, you need TAB installed on bukkit server. This feature is not available on BungeeCord.
|
||||
To use it, you need TAB installed on Bukkit server. This feature is not available on any other platform.
|
||||
|
||||
# Configuration
|
||||
| Option name | Default value | Description |
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Limitations](#limitations)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Spectator gamemode](#additional-note-1---spectator-gamemode)
|
||||
* [Additional note 1 - Spectator gamemode](#additional-note-1---spectator-gamemode)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-world values](#example-1---per-world-values)
|
||||
* [Example 2 - Displaying hearts](#example-2---displaying-hearts)
|
||||
|
||||
# About
|
||||
The scoreboard objective with PLAYER_LIST display slot. On 1.20.2- it supports 2 display types:
|
||||
This features gives you control over Minecraft's scoreboard objective feature with PLAYER_LIST slot.
|
||||
On 1.20.2- it supports 2 display types:
|
||||
* yellow number
|
||||

|
||||
* hearts
|
||||
@@ -15,22 +20,94 @@ The scoreboard objective with PLAYER_LIST display slot. On 1.20.2- it supports 2
|
||||
Since 1.20.3, it can display any text.
|
||||

|
||||
|
||||
This feature can be configured in **config.yml** under **playerlist-objective** section.
|
||||
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **playerlist-objective** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
playerlist-objective:
|
||||
enabled: true
|
||||
value: "%ping%"
|
||||
fancy-value: "&7Ping: %ping%"
|
||||
title: "TAB" # Only visible on Bedrock Edition
|
||||
render-type: INTEGER
|
||||
disable-condition: '%world%=disabledworld'
|
||||
```
|
||||
All of the options are explained in the following table.
|
||||
| Option name | Default value | Description |
|
||||
|-------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| value | "%ping%" | [1.20.2-] Defines value displayed. You can set this to any placeholder that outputs a number. Only visible on 1.20.2 and lower. <br/> **Note**: Even if you only support 1.20.3+, you still need to configure this value to properly evaluate to a number, because the value is still sent to the client (just not displayed). You can set it to `0` for simplicity. |
|
||||
| fancy-value | "&7Ping: %ping%" | [1.20.3+] Value to display for 1.20.3+ clients. Supports any text. |
|
||||
| value | "%ping%" | [1.20.2-] An integer from -2147483648 to 2147483647, doesn't support decimal values. The number is always yellow. Supports placeholders with player-specific output, such as player health. Only visible on 1.20.2 and lower (unless you set `render-type` to `HEARTS`). <br/> **Note**: Even if you only support 1.20.3+, you still need to configure this value to properly evaluate to a number, because the value is still sent to the client (just not displayed). You can set it to `0` for simplicity. |
|
||||
| fancy-value | "&7Ping: %ping%" | [1.20.3+] Any text, supports placeholders with per-player output. Only visible on 1.20.3+, where it completely replaces `value`, or if `render-type` is set to `HEARTS`. |
|
||||
| title | "TAB" | Title to send. Only visible on Bedrock Edition. |
|
||||
| render-type | INTEGER | Render type of the value. Supported values are `INTEGER` and `HEARTS` |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
| render-type | INTEGER | Render type of the value. Supported values are `INTEGER` and `HEARTS`. <br />When set to `HEARTS`, the `value` is used displayed even on 1.20.3+, where `fancy-value` is ignored. |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) (either name of a condition or a conditional expression) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. <br/> **Note**: Disabling the feature for a player means sending objective unregister packet to them, which results in player not seeing the feature on anyone anymore. It doesn't work the other way around - you cannot disable this feature on target players, only for viewers. |
|
||||
|
||||
# Limitations
|
||||
* [1.5 - 1.20.2] Only 2 display types are supported - yellow number and hearts.
|
||||
* [1.5 - 1.20.2] A yellow 0 will appear on all tablist entries that aren't actual players (such as NPCs and [layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) entries) and it cannot be removed.
|
||||
* [1.20.2-] Only 2 display types are supported - yellow number and hearts.
|
||||
* [1.20.2-] A yellow 0 will appear on all tablist entries that aren't actual players (such as NPCs and [layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) entries) and it cannot be removed.
|
||||
|
||||
# Compatibility with other plugins
|
||||
TAB does not contain any sort of compatibility functionality for this feature.
|
||||
It will not try to prevent other plugins from applying the feature and neither will it detect it to re-add back once the other plugin removes it.
|
||||
Therefore, if another plugin also sends playerlist objective, TAB's may not show anymore (depending on who sends it first).
|
||||
Make sure you do not have any other plugin sending it to ensure the feature works properly.
|
||||
|
||||
You can check the list of objectives registered by either commands or plugins using Bukkit API using `/scoreboard objectives list`.
|
||||
If it contains one that could be the cause, you can unregister it by running `/scoreboard objectives remove <name>`. After doing so, reload TAB, so it resends its objectives again.
|
||||
Note that if this was automatically generated by a plugin, it will probably be added back again.
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - Spectator gamemode
|
||||
The feature will not be visible on players with spectator gamemode. To resolve this, check out [Spectator fix](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix) feature.
|
||||
The feature will not be visible on players with spectator gamemode. To resolve this, check out [Spectator fix](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix) feature.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Per-world values
|
||||
This feature doesn't directly support per-world values or similar.
|
||||
However, this can be achieved with conditions.
|
||||
Let's make `fancy-value` conditional based on player's world.
|
||||
Let's make an example with 3 worlds.
|
||||
Check if player is in world `world1`, then show one text.
|
||||
If not, check if player is in world `world2`, then show another text.
|
||||
If not, show the final text.
|
||||
This can be achieved by chaining 2 conditions:
|
||||
```
|
||||
conditions:
|
||||
playerlist1:
|
||||
conditions:
|
||||
- "%world%=world1"
|
||||
true: "Text to display in world world1" # Player is in world world1, display the text
|
||||
false: "%condition:playerlist2%" # Player is not in world world1, check another condition
|
||||
playerlist2:
|
||||
conditions:
|
||||
- "%world%=world2"
|
||||
true: "Text to display in world world2" # Player is in world world2, display the text
|
||||
false: "Text to display in other worlds" # Player is not in any of the 2 worlds
|
||||
```
|
||||
> [!WARNING]
|
||||
> DO NOT JUST RANDOMLY PASTE THIS ENTIRE "CONDITIONS" SECTION INTO YOUR CONFIG!
|
||||
> INSTEAD, EDIT YOUR EXISTING CONDITIONS SECTION TO PREVENT HAVING THE SECTION TWICE,
|
||||
> HAVING SECOND ONE COMPLETELY OVERRIDE THE FIRST ONE!
|
||||
|
||||
Finally, use this condition as value in `fancy-value`:
|
||||
```
|
||||
playerlist-objective:
|
||||
fancy-value: "%condition:playerlist1%"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is just an example, the plugin is not limited to displaying different values only per world.
|
||||
> If you want per server values on proxy, use %server% with server names.
|
||||
> This works for any placeholder offered by TAB or by PlaceholderAPI.
|
||||
|
||||
## Example 2 - Displaying hearts
|
||||
In order to display the value as hearts, set `render-type` to `HEARTS`.
|
||||
Then, set `value` to a placeholder that returns player's health. If you have TAB on a backend server, use `%health%`. If you have TAB on a proxy, use PlaceholderAPI's `%player_health_rounded%`.
|
||||
Example configuration:
|
||||
|
||||
```
|
||||
value: "%health%"
|
||||
render-type: HEARTS
|
||||
```
|
||||
When using `HEARTS` render type, `fancy-value` is not displayed by the client, so it doesn't matter what it is set to.
|
||||
Final result:
|
||||

|
||||
@@ -1,48 +1,128 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Creating a scoreboard](#creating-a-scoreboard)
|
||||
* [title](#title)
|
||||
* [lines](#lines)
|
||||
* [Alignment](#alignment)
|
||||
* [Empty placeholder output](#empty-placeholder-output)
|
||||
* [display-condition](#display-condition)
|
||||
* [Chaining scoreboards](#chaining-scoreboards)
|
||||
* [Announce command](#announce-command)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Configuration](#configuration)
|
||||
* [Scoreboard](#scoreboard)
|
||||
* [Line syntax](#line-syntax)
|
||||
* [Hiding a line](#hiding-a-line)
|
||||
* [Longer lines](#longer-lines)
|
||||
* [Chaining scoreboards](#chaining-scoreboards)
|
||||
* [Commands](#commands)
|
||||
* [Placeholders](#placeholders)
|
||||
* [PlaceholderAPI placeholders](#placeholderapi-placeholders)
|
||||
* [Limitations](#limitations)
|
||||
* [Longer lines](#longer-lines)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Geyser / Bedrock issues](#geyser--bedrock-issues)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Text alignment](#additional-note-1---text-alignment)
|
||||
* [Additional note 2 - Geyser / Bedrock issues](#additional-note-2---geyser--bedrock-issues)
|
||||
* [API](#api)
|
||||
* [Creating custom scoreboards](#creating-custom-scoreboards)
|
||||
* [Showing custom scoreboards](#showing-custom-scoreboards)
|
||||
* [Toggling scoreboard visibility](#toggling-scoreboard-visibility)
|
||||
* [Announcing a scoreboard](#announcing-a-scoreboard)
|
||||
* [Creating custom scoreboards](#creating-custom-scoreboards)
|
||||
* [Showing custom scoreboards](#showing-custom-scoreboards)
|
||||
* [Toggling scoreboard visibility](#toggling-scoreboard-visibility)
|
||||
* [Announcing a scoreboard](#announcing-a-scoreboard)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-world scoreboards](#example-1---per-world-scoreboards)
|
||||
* [Example 2 - Periodical scoreboard switching](#example-2---periodical-scoreboard-switching)
|
||||
* [Example 3 - Per-version scoreboards](#example-3---per-version-scoreboards)
|
||||
* [Example 4 - Conditional lines](#example-4---conditional-lines)
|
||||
* [Example 1 - Per-world scoreboards](#example-1---per-world-scoreboards)
|
||||
* [Example 2 - Periodical scoreboard switching](#example-2---periodical-scoreboard-switching)
|
||||
* [Example 3 - Per-version scoreboards](#example-3---per-version-scoreboards)
|
||||
* [Example 4 - Conditional lines](#example-4---conditional-lines)
|
||||
|
||||
# About
|
||||
Scoreboard objective with SIDEBAR display slot.
|
||||
This features gives you control over Minecraft's scoreboard objective feature with SIDEBAR slot.
|
||||
|
||||
This feature can be configured in **config.yml** under **scoreboard** section.
|
||||
# Configuration
|
||||
The feature can be configured in **config.yml** under **scoreboard** section.
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
scoreboard:
|
||||
enabled: false
|
||||
toggle-command: /sb
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
delay-on-join-milliseconds: 0
|
||||
scoreboards:
|
||||
scoreboard-1.20.3+:
|
||||
title: "<#E0B11E>MyServer</#FF0000>"
|
||||
display-condition: "%player-version-id%>=765;%bedrock%=false" # Only display it to players using 1.20.3+ AND NOT bedrock edition
|
||||
lines:
|
||||
- "&7%date%"
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6Online:"
|
||||
- "* &eOnline&7:||%online%"
|
||||
- "* &eCurrent World&7:||%worldonline%"
|
||||
- "* &eStaff&7:||%staffonline%"
|
||||
- ""
|
||||
- "&6Personal Info:"
|
||||
- "* &bRank&7:||%group%"
|
||||
- "* &bPing&7:||%ping%&8ms"
|
||||
- "* &bWorld&7:||%world%"
|
||||
- "%animation:MyAnimation1%"
|
||||
scoreboard:
|
||||
title: "<#E0B11E>MyServer</#FF0000>"
|
||||
lines:
|
||||
- "&7%date%"
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6Online:"
|
||||
- "* &eOnline&7: &f%online%"
|
||||
- "* &eCurrent World&7: &f%worldonline%"
|
||||
- "* &eStaff&7: &f%staffonline%"
|
||||
- ""
|
||||
- "&6Personal Info:"
|
||||
- "* &bRank&7: &f%group%"
|
||||
- "* &bPing&7: &f%ping%&8ms"
|
||||
- "* &bWorld&7: &f%world%"
|
||||
- "%animation:MyAnimation1%"
|
||||
```
|
||||
All of the options are explained in the following table.
|
||||
| Option name | Default value | Description |
|
||||
|----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | false | Enables / Disables the feature |
|
||||
| toggle-command | /sb | Command that can be used to toggle scoreboard for the player running the command. Players need `tab.scoreboard.toggle` permission to use it. <br />**Note:** Commands are not supposed to be registered / unregistered dynamically at runtime. As such, you may run into small issues when changing the toggle command and reloading. After you modify the command to your liking, reconnect to properly see it in tabcomplete. If you don't use TAB on a proxy, consider restarting the server as well for the old command to actually be unregistered. |
|
||||
| remember-toggle-choice | false | When enabled, toggling decision is saved into a file to remember it even after reloads/restarts/reconnects |
|
||||
| hidden-by-default | false | If enabled, scoreboard will automatically be hidden on join until toggle command is used to show it. |
|
||||
| use-numbers | false | If enabled, numbers 1-15 will be used in the scoreboard. If disabled, `static-number` is shown everywhere. Will not be visible for 1.20.3+ players, instead, you can configure any text to show using `\|\|text` in the lines (scroll up for more info). |
|
||||
| static-number | 0 | If `use-numbers` is disabled, this is number to be in all lines. |
|
||||
| delay-on-join-milliseconds | 0 | Delay in milliseconds to send scoreboard after joining. |
|
||||
| scoreboards | *Map* | Scoreboards to display based on conditions (see below for more info).
|
||||
|
|
||||
|
||||
# Creating a scoreboard
|
||||
Scoreboards can be created in `scoreboards` section. You can define as many scoreboards as you want. Each scoreboard can have a condition requirement, which must be met to display the scoreboard. If it's not met, the next defined scoreboard is checked and so on. If the last defined scoreboard has a condition requirement which isn't met, player won't see anything.
|
||||
## Scoreboard
|
||||
A scoreboard consists of the following options:
|
||||
|
||||
Each scoreboard defines up to 3 parameters:
|
||||
| Option name | Description |
|
||||
|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `display-condition` (optional) | [Condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) (either condition name or a conditional expression) <br /> that must be met for the design to be displayed. <br />If not defined, design will be displayed without any required condition. <br />If player does not meet condition, another design may be displayed <br /> based on chaining (see below for more info). |
|
||||
| `title ` | Title of the scoreboard. Limited to 32 characters (including color codes) on Minecraft versions below 1.13. It is automatically centered by the client. |
|
||||
| `lines` | List of lines to display. The client will only display up to 15 lines. See below for more information about syntax. |
|
||||
|
||||
## title
|
||||
Title of the scoreboard.
|
||||
Limited to 32 characters on MineCraft versions below 1.13 (including color codes).
|
||||
It is automatically centered by the client.
|
||||
**Example**:
|
||||
```
|
||||
scoreboards:
|
||||
MyScoreboard:
|
||||
display-condition: "%world%=world" # Only display it in world "world"
|
||||
title: "Scoreboard for world %world%"
|
||||
lines:
|
||||
- "Line1||Right side 1"
|
||||
- "Line2||Right side 2"
|
||||
- "Line3||Right side 3"
|
||||
```
|
||||
|
||||
## lines
|
||||
Up to 15 lines of text. You can define more, but the client won't display them.
|
||||
### Line syntax
|
||||
The lines are specified as a list of strings.
|
||||
|
||||
Since 1.20.3, you can also configure the value on the right side, which no longer has to be a red number. To set it, use `||` sequence and put right-side text after it. Example:
|
||||
Since 1.20.3, you can also configure the value on the right side, which no longer has to be a red number.
|
||||
On <1.20.3, you can configure red numbers with either a static number, or a placeholder that evaluates to a number.
|
||||
|
||||
The final syntax is `<text>||<number format>||<score>`, where:
|
||||
* `<text>` is the main text displayed on the left side
|
||||
* `<number format>` is the new 1.20.3+ feature that replaces `score` with any text, not just a number
|
||||
* `<score>` is the value (red number), only visible on <1.20.3 and must be a number (or a placeholder evaluating to one)
|
||||
|
||||
The second and third part are optional. Example syntaxes that configure a line are:
|
||||
* `<text>`
|
||||
* `<text>||<number format>`
|
||||
* `<text>||<number format>||<score>`
|
||||
* `<text>||||<score>`
|
||||
|
||||
For example:
|
||||
```
|
||||
lines:
|
||||
- "Left 1---"
|
||||
@@ -74,78 +154,14 @@ Additionally, the following configuration:
|
||||
Will give you
|
||||
<img width="583" height="131" alt="image" src="https://github.com/user-attachments/assets/15e809c6-4ecd-4234-a30e-744c2d413335" />
|
||||
|
||||
The `number format` will not be visible at 1.20.2 and lower, and `score` will be displayed instead.
|
||||
|
||||
The right side text will not be visible at 1.20.2 and lower, and numbers will be displayed instead.
|
||||
|
||||
### Alignment
|
||||
The left side of the text is automatically aligned to the left,
|
||||
while the right side is automatically aligned to the right.
|
||||
This is done by the client and is out of plugin's control.
|
||||
If you want to center a line, you'll need to do it manually.
|
||||
If dynamic placeholder output length is preventing your from doing it,
|
||||
you can try to bypass it by adding spaces before and after it to artificially increase and force the scoreboard width,
|
||||
such as
|
||||
`- " Centered text "` (or more spaces if needed).
|
||||
|
||||
### Empty placeholder output
|
||||
### Hiding a line
|
||||
When a line consists only of a placeholder that returned empty output, the line will be hidden completely. This is intentional to allow dynamic scoreboard size based on placeholder output. This is not the case if empty line is configured (`""`), to allow empty lines in configuration.
|
||||
|
||||
## display-condition
|
||||
A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met to display this scoreboard. If it isn't met for the player, the next scoreboard's condition is checked.
|
||||
### Longer lines
|
||||
*Since 1.13 the line length is unlimited, so you don't need to worry about this if you use 1.13+.*
|
||||
|
||||
# Chaining scoreboards
|
||||
When defining more than 1 scoreboard in config, the plugin will display the correct scoreboard based on conditions.
|
||||
The plugin goes through all defined scoreboards, starting with the scoreboard on top.
|
||||
If the scoreboard's condition is met or not set, it is displayed.
|
||||
If not, the next defined scoreboard is checked and so on.
|
||||
If no suitable scoreboard is found (the last one has a condition requirement which wasn't met), none is displayed.
|
||||
Example:
|
||||
```
|
||||
scoreboards:
|
||||
scoreboard1:
|
||||
display-condition: "permission:tab.scoreboard.admin"
|
||||
title: "Title 1"
|
||||
lines:
|
||||
- "Scoreboard for admins"
|
||||
scoreboard2:
|
||||
display-condition: "%world%=myWorld"
|
||||
title: "Title 2"
|
||||
lines:
|
||||
- "Scoreboard in world myWorld"
|
||||
scoreboard3:
|
||||
title: "Title 3"
|
||||
lines:
|
||||
- "Line 1"
|
||||
```
|
||||
Scoreboard `scoreboard1` is checked first. If condition is not met, `scoreboard2` is checked. If that condition is not met, `scoreboard3` is displayed.
|
||||
If a player meets both conditions (has defined permission and is in defined world), `scoreboard1` will be displayed, because it is defined first and therefore has the highest priority. You can switch scoreboards based on your priority needs.
|
||||
|
||||
# Announce command
|
||||
This takes priority over the current scoreboard.
|
||||
Once the announcement is over, the previous scoreboard is restored.
|
||||
`/tab scoreboard announce <name> <time>`
|
||||
name - name of scoreboard to be displayed
|
||||
time - number of seconds to display the scoreboard for
|
||||
|
||||
# Additional settings
|
||||
| Option name | Default value | Description |
|
||||
|----------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | false | Enables / Disables the feature |
|
||||
| toggle-command | /sb | Command that can be used to toggle scoreboard for the player running the command. Players need `tab.scoreboard.toggle` permission to use it. <br />**Note:** This command will not appear in command auto-complete, because it's not a real registered command. Registered commands must be defined in plugin jar directly and cannot be dynamic. If you want to solve this, you can try getting a dummy private plugin made which registers that command. |
|
||||
| remember-toggle-choice | false | When enabled, toggling decision is saved into a file to remember it even after reloads/restarts/reconnects |
|
||||
| hidden-by-default | false | If enabled, scoreboard will automatically be hidden on join until toggle command is used to show it. |
|
||||
| use-numbers | false | If enabled, numbers 1-15 will be used in the scoreboard. If disabled, `static-number` is shown everywhere. Will not be visible for 1.20.3+ players, instead, you can configure any text to show using `\|\|text` in the lines (scroll up for more info). |
|
||||
| static-number | 0 | If `use-numbers` is disabled, this is number to be in all lines. |
|
||||
| delay-on-join-milliseconds | 0 | Delay in milliseconds to send scoreboard after joining. |
|
||||
|
||||
# Limitations
|
||||
* [1.5 - 1.12.2] The title is limited to 32 characters (including color codes).
|
||||
* [1.5 - 1.12.2] Line length is limited to 28 characters (including color codes) (64 on lines with static text / [Longer lines](#longer-lines)).
|
||||
* [1.5 - 1.20.2] The red numbers on the right really cannot be removed from the plugin side (a client modification / resource pack is necessary).
|
||||
* The client only displays up to 15 lines. If a plugin sends more, only the top 15 scores will be displayed. Changing this would require a client modification (on versions below 1.8 the scoreboard won't appear at all if more than 15 lines are displayed).
|
||||
* [Bedrock] Scoreboard lines may be cut off and show `...`, this can be bypassed by using [GeyserOptionalPack](https://github.com/GeyserMC/GeyserOptionalPack)
|
||||
|
||||
# Longer lines
|
||||
To make sure the scoreboard never flickers, it's only using prefix/suffix components to display text.
|
||||
These can easily be changed without any visual issues.
|
||||
The player name part, however, cannot be changed.
|
||||
@@ -168,52 +184,77 @@ This bypass is automatically enabled for all lines that only use static text
|
||||
(since there's nothing to refresh and cause flickering),
|
||||
therefore doing it for such lines (like in the example above) has no effect.
|
||||
|
||||
## Chaining scoreboards
|
||||
When defining more than 1 scoreboard in config, the plugin will display the correct scoreboard based on conditions.
|
||||
The plugin goes through all defined scoreboards, starting with the scoreboard on top.
|
||||
If the scoreboard's condition is met or not set, it is displayed.
|
||||
If not, the next defined scoreboard is checked and so on.
|
||||
If no suitable scoreboard is found (the last one has a condition requirement which wasn't met), none is displayed.
|
||||
**Example**:
|
||||
```
|
||||
scoreboards:
|
||||
scoreboard1:
|
||||
display-condition: "permission:tab.scoreboard.admin"
|
||||
title: "Title 1"
|
||||
lines:
|
||||
- "Scoreboard for admins"
|
||||
scoreboard2:
|
||||
display-condition: "%world%=myWorld"
|
||||
title: "Title 2"
|
||||
lines:
|
||||
- "Scoreboard in world myWorld"
|
||||
scoreboard3:
|
||||
title: "Title 3"
|
||||
lines:
|
||||
- "Line 1"
|
||||
```
|
||||
Scoreboard `scoreboard1` is checked first. If condition is not met, `scoreboard2` is checked. If that condition is not met, `scoreboard3` is displayed.
|
||||
If a player meets both conditions (has defined permission and is in defined world), `scoreboard1` will be displayed, because it is defined first and therefore has the highest priority. You can switch scoreboards based on your priority needs.
|
||||
|
||||
# Commands
|
||||
| Command | Permission | Description |
|
||||
|------------------------------------------------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `/tab scoreboard show <name> [player]` | `tab.scoreboard.show` (for yourself) <br /> `tab.scoreboard.show.other` (for others) | Shows the scoreboard with the given `name`, either to yourself if no `player` was given, or to the given `player`. |
|
||||
| `/tab scoreboard [on/off/toggle] [player] [options]` | `tab.scoreboard.toggle` (for yourself) <br /> `tab.scoreboard.toggle.other` (for others) | Shows / hides / toggles scoreboard of specified player. If no player was given, command affects the sender. You can use `-s` as option for silent toggling (no chat message for affected player) |
|
||||
| `/tab scoreboard announce <name> <duration>` | `tab.announce.scoreboard` | Shows the scoreboard with the given `name` to every player on the server for the given `duration`, in seconds. |
|
||||
|
||||
# Placeholders
|
||||
This feature does not offer any internal placeholders, only PlaceholderAPI placeholders.
|
||||
|
||||
## PlaceholderAPI placeholders
|
||||
Here are TAB's PlaceholderAPI placeholders you can use when this feature is enabled:
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| `%tab_scoreboard_name%` | Returns name of player's currently displayed scoreboard or empty string if none is displayed due to no display condition being met. |
|
||||
| `%tab_scoreboard_visible%` | "Enabled" if visible, "Disabled" if not (toggled with a command) |
|
||||
|
||||
# Limitations
|
||||
* [1.12.2-] The title is limited to 32 characters (including color codes).
|
||||
* [1.12.2-] Line length is limited to 28 characters (including color codes) (64 on lines with static text / [Longer lines](#longer-lines)).
|
||||
* [1.20.2-] The red numbers on the right really cannot be removed from the plugin side (a client modification / resource pack is necessary).
|
||||
* [Bedrock] Scoreboard lines may be cut off and show `...`, this can be bypassed by using [GeyserOptionalPack](https://github.com/GeyserMC/GeyserOptionalPack)
|
||||
* The client only displays up to 15 lines. If a plugin sends more, only the top 15 scores will be displayed. Changing this would require a client modification (on versions below 1.8 the scoreboard won't appear at all if more than 15 lines are sent).
|
||||
|
||||
# Compatibility with other plugins
|
||||
TAB automatically detects scoreboard coming from other plugins, and when one is sent, TAB hides its own scoreboard.
|
||||
Later, when the other plugin hides its scoreboard, TAB will send its scoreboard back to the player.
|
||||
|
||||
This detection must be available on your platform.
|
||||
For that,
|
||||
see the Scoreboard section on [compatibility page](https://github.com/NEZNAMY/TAB/wiki/Compatibility#supported-features-per-platform).
|
||||
If it says ✔, the detection is available.
|
||||
If it says ❗, it means this detection is not available on your platform.
|
||||
As a result, when the other plugin hides its scoreboard, TAB will not be able to resend its own.
|
||||
# Additional info
|
||||
## Additional note 1 - Text alignment
|
||||
**Title**:
|
||||
The title is always centered by the client. You cannot disable this. You can only pad it with spaces to move it towards your desired direction.
|
||||
|
||||
If you want this detection,
|
||||
and it's not supported by your platform,
|
||||
you can achieve it
|
||||
using an alternate solution with conditions that will make TAB display no scoreboard if the condition is not met.
|
||||
For this, you'll need to have more information about when exactly do plugins send scoreboards and how to detect it.
|
||||
**Lines**:
|
||||
The left side of the text is automatically aligned to the left,
|
||||
while the right side is automatically aligned to the right.
|
||||
This is done by the client and is out of plugin's control.
|
||||
If you want to center a line, you'll need to do it manually.
|
||||
If dynamic placeholder output length is preventing your from doing it,
|
||||
you can try to bypass it by adding spaces before and after it to artificially increase and force the scoreboard width,
|
||||
such as
|
||||
`- " Centered text "`.
|
||||
|
||||
<details>
|
||||
<summary>Example using PremiumVanish</summary>
|
||||
|
||||
When you are vanished using PremiumVanish plugin, placeholder `%premiumvanish_isvanished%` will return `Yes` instead of the usual `No`. You can put this as a display condition to all defined scoreboards.
|
||||
An original
|
||||
```
|
||||
scoreboards:
|
||||
scoreboard1:
|
||||
title: Default
|
||||
lines:
|
||||
- 'Line of text'
|
||||
```
|
||||
would turn into
|
||||
```
|
||||
scoreboards:
|
||||
scoreboard1:
|
||||
display-condition: "%premiumvanish_isvanished%=No"
|
||||
title: Default
|
||||
lines:
|
||||
- 'Line of text'
|
||||
```
|
||||
Remember to add this condition to all of your scoreboards if you have more than 1
|
||||
(for those with an existing condition merge them using `;`).
|
||||
Keep in mind [placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements) apply to placeholders here as well,
|
||||
so if you configured a fancy output for that placeholder, you'll need to use it in the display condition as well.
|
||||
To see what exactly has placeholder returned, use `/tab parse <player> <placeholder>`.
|
||||
</details>
|
||||
|
||||
# Geyser / Bedrock issues
|
||||
## Additional note 2 - Geyser / Bedrock issues
|
||||
Currently, there is a great suspicion that there is a geyser bug causing scoreboard to not appear for Bedrock players ([Geyser #5304](https://github.com/GeyserMC/Geyser/issues/5304) and more). This affects other scoreboard plugins as well, not just TAB. If you are experiencing this issue, make sure it is not caused by TAB configuration (for example using %bedrock%=false as display condition). If that wasn't the case, consider making a high quality bug report on Geyser with steps to reproduce.
|
||||
|
||||
# API
|
||||
@@ -234,10 +275,10 @@ To change whether a player can see scoreboards, use `ScoreboardManager#setScoreb
|
||||
To toggle whether a player can see scoreboards, use `ScoreboardManager#toggleScoreboard(TabPlayer, boolean)`. This will either make scoreboards visible if they are not already, or make them hidden if they are currently visible.
|
||||
|
||||
## Announcing a scoreboard
|
||||
To announce a scoreboard to all players on the server for a given number of ticks,
|
||||
To announce a scoreboard to all players on the server for a given number of milliseconds,
|
||||
use `ScoreboardManager#announceScoreboard(String, int)`,
|
||||
which will look up a scoreboard with the given `String` name,
|
||||
and if one is found, send it to all players for the given `int` ticks.
|
||||
and if one is found, send it to all players for the given `int` milliseconds.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Per-world scoreboards
|
||||
@@ -274,8 +315,7 @@ If none of the conditions are met, display the default scoreboard.
|
||||
> [!NOTE]
|
||||
> This is just an example, the plugin is not limited to displaying scoreboard only per world.
|
||||
> If you want per server scoreboards on proxy, use %server% with server names.
|
||||
> If you want worldguard regions, use %worldguard_region_name% with region names.
|
||||
> This works for any placeholder offered by the plugin or by PlaceholderAPI.
|
||||
> This works for any placeholder offered by TAB or by PlaceholderAPI.
|
||||
|
||||
## Example 2 - Periodical scoreboard switching
|
||||
We can use [animations](https://github.com/NEZNAMY/TAB/wiki/Animations) to make the plugin switch between scoreboards using display condition. Let's say we want to switch between 2 scoreboards. First, we create an animation:
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
# Content
|
||||
* [Enabling](#enabling)
|
||||
* [Methods of sorting](#methods-of-sorting)
|
||||
* [GROUPS](#groups)
|
||||
* [PERMISSIONS](#permissions)
|
||||
* [PLACEHOLDER](#placeholder)
|
||||
* [PLACEHOLDER_A_TO_Z](#placeholder_a_to_z)
|
||||
* [PLACEHOLDER_Z_TO_A](#placeholder_z_to_a)
|
||||
* [PLACEHOLDER_LOW_TO_HIGH](#placeholder_low_to_high)
|
||||
* [PLACEHOLDER_HIGH_TO_LOW](#placeholder_high_to_low)
|
||||
* [GROUPS](#groups)
|
||||
* [PERMISSIONS](#permissions)
|
||||
* [PLACEHOLDER](#placeholder)
|
||||
* [PLACEHOLDER_A_TO_Z](#placeholder_a_to_z)
|
||||
* [PLACEHOLDER_Z_TO_A](#placeholder_z_to_a)
|
||||
* [PLACEHOLDER_LOW_TO_HIGH](#placeholder_low_to_high)
|
||||
* [PLACEHOLDER_HIGH_TO_LOW](#placeholder_high_to_low)
|
||||
* [Multiple elements with the same priority](#multiple-elements-with-the-same-priority)
|
||||
* [Combining multiple sorting types](#combining-multiple-sorting-types)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Limitations](#additional-note-1---limitations)
|
||||
* [Additional note 2 - per-world sorting](#additional-note-2---per-world-sorting)
|
||||
* [Additional note 3 - Compatibility issues with other plugins](#additional-note-3---compatibility-issues-with-other-plugins)
|
||||
* [Additional note 1 - Limitations](#additional-note-1---limitations)
|
||||
* [Additional note 2 - per-world sorting](#additional-note-2---per-world-sorting)
|
||||
* [Additional note 3 - Compatibility issues with other plugins](#additional-note-3---compatibility-issues-with-other-plugins)
|
||||
* [Common mistakes](#common-mistakes)
|
||||
* [API](#api)
|
||||
|
||||
# Enabling
|
||||
To enable sorting, you must have either [Nametags](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags) or [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) (or both) enabled.
|
||||
|
||||
> [!TIP]
|
||||
> To verify you enabled sorting, run `/tab debug`. It will say `Sorting type:` followed by anything except `DISABLED`.
|
||||
|
||||
# Methods of sorting
|
||||
Below are all the different methods that you can use to sort players.
|
||||
|
||||
@@ -33,10 +30,6 @@ This is the default and recommended method. Players will be sorted by their prim
|
||||
First, put your players into groups in your permission plugin.
|
||||
All the ways to do it can be found
|
||||
at [How to assign players into groups](https://github.com/NEZNAMY/TAB/wiki/How-to-assign-players-into-groups).
|
||||
> [!TIP]
|
||||
> Verify player's group using `/tab debug <player>`.
|
||||
It should say `Primary permission group:` followed by group you configured.
|
||||
If not, you did not assign players into groups correctly.
|
||||
|
||||
Second, place all of your groups in to a comma separated list **in order of priority** into `sorting-types`. Example:
|
||||
```yaml
|
||||
@@ -44,15 +37,6 @@ scoreboard-teams:
|
||||
sorting-types:
|
||||
- "GROUPS:owner,admin,mod,default"
|
||||
```
|
||||
> [!TIP]
|
||||
> Verify configured sorting priority of players using `/tab debug <player>`.
|
||||
|
||||
It should show a message like this:
|
||||

|
||||
|
||||
If a group is not in the list, you will get the following:
|
||||

|
||||
If that's the case, you forgot to add that group into the list.
|
||||
|
||||
## PERMISSIONS
|
||||
> [!CAUTION]
|
||||
@@ -71,8 +55,6 @@ scoreboard-teams:
|
||||
sorting-types:
|
||||
- "PERMISSIONS:my.permission.1,my.permission.2,my.permission.3"
|
||||
```
|
||||
> [!TIP]
|
||||
> Verify sorting permissions of a player using `/tab debug <player>`
|
||||
|
||||
## PLACEHOLDER
|
||||
This method sorts players using the output of a placeholder and comparing that to pre-defined values.
|
||||
@@ -228,12 +210,11 @@ The most common mistakes include:
|
||||
* Disabling both teams and layout, not realizing it disables sorting as well.
|
||||
* Mistaking `primary-group-finding-list` for the sorting list, despite that list having nothing to do with sorting and by default even having a comment above it saying it has nothing to do with sorting.
|
||||
* Not configuring primary groups correctly. This can have multiple reasons, such as
|
||||
* Not configuring group weights in LuckPerms.
|
||||
* Accidentally enabling `use-bukkit-permission-manager` option when on a proxy without knowing what it does.
|
||||
* Installing TAB on a proxy without having any permission plugin on the proxy.
|
||||
* Enabling `assign-groups-by-permissions` without giving those permissions (or the opposite - giving away OP to users, resulting in the highest group being taken).
|
||||
* Not configuring group weights in LuckPerms.
|
||||
* Accidentally enabling `use-bukkit-permission-manager` option when on a proxy without knowing what it does.
|
||||
* Installing TAB on a proxy without having any permission plugin on the proxy.
|
||||
* Enabling `assign-groups-by-permissions` without giving those permissions (or the opposite - giving away OP to users, resulting in the highest group being taken).
|
||||
|
||||
[Debug command](https://github.com/NEZNAMY/TAB/wiki/Commands-&-Permissions#tab-debug-player) will help you identify if this is your case.
|
||||
* Using a plugin that causes TAB to fail to apply teams (such as Tablisknu). This includes installing TAB plugin on both backend and proxy, causing the installations to conflict.
|
||||
* Using TAB on Velocity without installing [VelocityScoreboardAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
* [Enabling](#enabling)
|
||||
* [Permissions](#permissions)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Seeing yourself still as spectator](#additional-note-1---seeing-yourself-still-as-spectator)
|
||||
* [Additional note 1 - Seeing yourself still as spectator](#additional-note-1---seeing-yourself-still-as-spectator)
|
||||
|
||||
# About
|
||||
With the introduction of spectator gamemode in 1.8,
|
||||
players having it automatically appear on the bottom of tablist with transparent name and missing [Playerlist Objective](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Playerlist-Objective).
|
||||
This feature cancels gamemode change packets to spectator gamemode, cancelling this minecraft feature.
|
||||
This feature cancels gamemode change packets to spectator gamemode, cancelling this Minecraft feature.
|
||||
|
||||
# Enabling
|
||||
To enable this feature, open **config.yml** and set
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Introduction](#introduction)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [config.yml](#configyml)
|
||||
* [groups.yml and users.yml](#groupsyml-and-usersyml)
|
||||
* [Format structure](#format-structure)
|
||||
* [Global settings](#global-settings)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Priority system](#priority-system)
|
||||
* [Placeholder support](#placeholder-support)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Priorities](#priorities)
|
||||
* [Placeholders](#placeholders)
|
||||
* [PlaceholderAPI placeholders](#placeholderapi-placeholders)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Taking prefix/suffix from permission plugin](#example-1---taking-prefixsuffix-from-permission-plugin)
|
||||
|
||||
# About
|
||||
This feature allows you to configure player name formats in the tablist.
|
||||
It was added to minecraft in version 1.8.
|
||||
It was added to Minecraft in version 1.8.
|
||||
Versions 1.7 and lower only allow up to 16 characters including name,
|
||||
which is too limiting and not supported by this plugin.
|
||||
If you wish to have prefix/suffix for 1.7 and lower,
|
||||
@@ -20,10 +24,25 @@ take advantage of [this client-sided mechanic](https://github.com/NEZNAMY/TAB/wi
|
||||
which will appear in the tablist.
|
||||

|
||||
|
||||
Settings of this feature can be configured in **config.yml** under **tablist-name-formatting** section.
|
||||
|
||||
# Configuration
|
||||
## Introduction
|
||||
This feature is configured in 2 parts based on what you are configuring. Some settings are in the **config.yml**, but prefix/suffix is configured in **groups.yml** and **users.yml**.
|
||||
## config.yml
|
||||
The first part can be configured in **config.yml** under **tablist-name-formatting** section. It controls everything except prefix and suffix.
|
||||
|
||||
This is how the default configuration looks:
|
||||
```
|
||||
tablist-name-formatting:
|
||||
enabled: true
|
||||
disable-condition: '%world%=disabledworld'
|
||||
```
|
||||
All of the options are explained in the following table.
|
||||
| Option name | Default value | Description |
|
||||
|-----------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
|
||||
## groups.yml and users.yml
|
||||
### Format structure
|
||||
The display name is split into 3 parts for convenience:
|
||||
`tabprefix` - first part of the text, this is where prefix is usually configured
|
||||
`customtabname` - second part of the text, it will be used as player name. If customtabname is not defined at all (not just setting empty value!), player's original name will be used instead
|
||||
@@ -33,22 +52,20 @@ These values are then merged and displayed in the tablist.
|
||||

|
||||
|
||||
You are free to use the 3 properties in any way you want since they are only split for convenience.
|
||||
However, it is still a good idea to use them for their designed purpose to not get lost a poor configuration.
|
||||
However, it is still a good idea to use them for their designed purpose to not get lost in a poor configuration.
|
||||
This means that the text which should appear before player's name should be put into `tabprefix`,
|
||||
any placeholder that is supposed to display player's name (such as from a nickname plugin)
|
||||
should go into `customtabname` and any text that should appear after player's name should be put into `tabsuffix`.
|
||||
|
||||
## Groups and users
|
||||
Properties can be applied in 2 ways: groups and users.
|
||||
Users can be defined by both username and their UUID.
|
||||
Values applied to users take priority over groups.
|
||||
Example:
|
||||
### Global settings
|
||||
Properties can be applied in 2 ways: groups and users. Users can be defined by both username and their UUID. Values applied to users take priority over groups.
|
||||
|
||||
**groups.yml**
|
||||
```
|
||||
admin:
|
||||
tabprefix: "&4&lAdmin &r"
|
||||
```
|
||||
This can also be achieved with commands, in this case `/tab group admin tabprefix "&4&lAdmin &r"`.
|
||||
Or with an in-game command `/tab group admin tabprefix "&4&lAdmin &r"`.
|
||||
|
||||
**users.yml**
|
||||
```
|
||||
@@ -59,11 +76,11 @@ _NEZNAMY_:
|
||||
237d8b55-3f97-4749-aa60-e9fe97b45062:
|
||||
tabprefix: "&6&lTAB &r"
|
||||
```
|
||||
This can also be achieved with commands, in this case `/tab player _NEZNAMY_ tabprefix "&6&lTAB &r"` or `/tab playeruuid _NEZNAMY_ tabprefix "&6&lTAB &r"`.
|
||||
Or with in-game commands `/tab player _NEZNAMY_ tabprefix "&6&lTAB &r"` and `/tab playeruuid _NEZNAMY_ tabprefix "&6&lTAB &r"` (the player must be online for uuid option to work).
|
||||
|
||||
Properties can also be set as "default" for everyone who does not have them defined.
|
||||
For that purpose, a group keyword `_DEFAULT_` was made.
|
||||
Example:
|
||||
For that purpose, a group keyword `_DEFAULT_` was made.
|
||||
Example:
|
||||
**groups.yml**
|
||||
```
|
||||
admin:
|
||||
@@ -72,7 +89,7 @@ _DEFAULT_:
|
||||
tabprefix: "&7" # This will be displayed on everyone except admin
|
||||
```
|
||||
|
||||
## Per-world / per-server
|
||||
### Per-world / per-server
|
||||
Values can also be applied per-world (and per-server on a proxy), where they can be defined per group/user. These values take priority over global settings. Example:
|
||||
**groups.yml**
|
||||
```
|
||||
@@ -85,21 +102,22 @@ per-server:
|
||||
_DEFAULT_:
|
||||
tabprefix: "&a" # Everyone in server "server1" will have this prefix
|
||||
```
|
||||
For multiple worlds/servers to share the same settings, separate them with `;`.
|
||||
For worlds/servers starting with a specified text, use `*` after shared part. For ending with a shared part, use `*` at the beginning.
|
||||
|
||||
For multiple worlds/servers to share the same settings, separate them with a`;`.
|
||||
For worlds/server starting with a specified text, use `*` after shared part. For ending with a shared part, use `*` at the beginning.
|
||||
Example:
|
||||
```
|
||||
per-world:
|
||||
world1;world2:
|
||||
_DEFAULT_:
|
||||
tabsuffix: "Shared tabsuffix in worlds world1 and world2"
|
||||
tabsuffix: "Shared tagsuffix in worlds world1 and world2"
|
||||
lobby-*:
|
||||
_DEFAULT_:
|
||||
tabsuffix: "Suffix in all worlds starting with lobby-"
|
||||
```
|
||||
|
||||
## Priority system
|
||||
The full list of priorities looks like this:
|
||||
### Priorities
|
||||
The full list of priorities to choose correct prefix/name/suffix for a player looks like this:
|
||||
1. value set using the [API](#api)
|
||||
2. per-world / per-server applied to username
|
||||
3. value applied to username
|
||||
@@ -110,32 +128,31 @@ The full list of priorities looks like this:
|
||||
8. value applied to player's group
|
||||
9. value applied to group `_DEFAULT_`
|
||||
|
||||
This list is browsed through until the first match is found. If no match is found, empty value is used (except `customtabname`, which defaults to player's name).
|
||||
This list is browsed through until the first match is found. If no match is found, empty value is used.
|
||||
|
||||
Values are taken independently of each other.
|
||||
This means you can set per-world tabprefix, but only keep one global tabsuffix, for example.
|
||||
|
||||
You can see the source of a value displayed on player by using `/tab debug <player>` and checking "source"
|
||||
part of the value you are looking for.
|
||||
You can see the source of a value displayed on player by using `/tab dump <player>`, opening the link and checking "source"
|
||||
part of the value you are looking for (tabprefix / customtabname / tabsuffix).
|
||||
|
||||
## Placeholder support
|
||||
All values fully support [TAB's internal placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#internal-placeholders) and [PlaceholderAPI placeholders](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Placeholders) including relational placeholders.
|
||||
The number of placeholders is not limited, and they can be used in combination with static text as well.
|
||||
# Placeholders
|
||||
This feature does not offer any internal placeholders, only PlaceholderAPI placeholders.
|
||||
|
||||
## Additional settings
|
||||
| Option name | Default value | Description |
|
||||
|-------------------|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| disable-condition | %world%=disabledworld | A [condition](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders) that must be met for disabling the feature for players. Set to empty for not disabling the feature ever. |
|
||||
## PlaceholderAPI placeholders
|
||||
Here are TAB's PlaceholderAPI placeholders you can use when this feature is enabled:
|
||||
| Placeholder | Description |
|
||||
|-------------|-------------|
|
||||
| `%tab_tabprefix%` | Player's current tabprefix with placeholders parsed. |
|
||||
| `%tab_customtabname%` | Player's current customtabname with placeholders parsed. |
|
||||
| `%tab_tabsuffix%` | Player's current tabprefix with placeholders parsed. |
|
||||
| `%tab_tabprefix_raw%` | Player's current raw tabprefix with placeholder identifiers. |
|
||||
| `%tab_customtabname_raw%` | Player's current raw customtabname with placeholder identifiers. |
|
||||
| `%tab_tabsuffix_raw%` | Player's current raw tabsuffix with placeholder identifiers. |
|
||||
|
||||
# Tips & Tricks
|
||||
If you want TAB to only take prefixes/suffixes from your permission plugin,
|
||||
delete all groups from **groups.yml** and only keep this:
|
||||
```
|
||||
_DEFAULT_:
|
||||
tabprefix: "%luckperms-prefix%"
|
||||
tabsuffix: "%luckperms-suffix%"
|
||||
```
|
||||
# Compatibility with other plugins
|
||||
Tablist formatting is a feature that cannot be effectively handled by multiple plugins at once.
|
||||
To make sure no other plugin sets tablist display names when not disabled in the plugin's config properly, TAB will block attempts to modify tablist display names coming from all other plugins (if this feature is enabled and not disabled for a player with a condition).
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
@@ -162,4 +179,18 @@ To get the original value set by the plugin based on configuration:
|
||||
* `TabListFormatManager#getOriginalReplacedName(TabPlayer)` - Name with all placeholders parsed
|
||||
* `TabListFormatManager#getOriginalReplacedSuffix(TabPlayer)` - Suffix with all placeholders parsed
|
||||
|
||||
**Note**: These values are only temporary, meaning they won't get saved anywhere and will get reset on player quit or plugin reload. If you wish to save these values into file, use [commands](https://github.com/NEZNAMY/TAB/wiki/Commands-&-Permissions#tab-playergroupplayeruuid-name-property-value-options).
|
||||
**Note**: These values are only temporary, meaning they won't get saved anywhere and will get reset on player quit or plugin reload. If you wish to save these values into file, use [commands](https://github.com/NEZNAMY/TAB/wiki/Commands-&-Permissions#tab-playergroupplayeruuid-name-property-value-options).
|
||||
|
||||
# Examples
|
||||
## Example 1 - Taking prefix/suffix from permission plugin
|
||||
If you want TAB to only take prefixes/suffixes from the permission plugin,
|
||||
delete all groups from **groups.yml** and only keep this:
|
||||
```
|
||||
_DEFAULT_:
|
||||
tabprefix: "%luckperms-prefix%"
|
||||
tabsuffix: "%luckperms-suffix%"
|
||||
```
|
||||
Or a PlaceholderAPI placeholder from your permission plugin if you use a different one.
|
||||
|
||||
> [!NOTE]
|
||||
> When doing this, make sure you don't delete something you didn't mean to delete, for example ta**g**prefix and ta**g**suffix from nametag feature.
|
||||
@@ -1,8 +1,5 @@
|
||||
# About
|
||||
A collection of things people have requested that I might look into implementing when I get extremely bored sometime next decade.
|
||||
* Less than 4 columns in Layout
|
||||
* Relational placeholder support in sorting
|
||||
* Relational conditions for layout player groups
|
||||
* 1.21.2+ sorting support
|
||||
* disable-condition support in multi-server-support
|
||||
* Custom relational placeholders
|
||||
* disable-condition support in multi-server-support
|
||||
@@ -10,7 +10,7 @@
|
||||
* [#9 - How to use space in prefix/suffix command?](#9---how-to-use-spaces-in-prefixsuffix-command)
|
||||
* [#10 - Can I change/remove the green connection bar in tablist?](#10---can-i-changeremove-the-green-connection-bar-in-tablist)
|
||||
* [#11 - Placeholder is not working](#11---placeholder-is-not-working)
|
||||
* [#12 - How can I display ItemsAdder images?](#12---how-can-i-display-itemsadder-images)
|
||||
* [#12 - How can I display images from resource pack plugin?](#12---how-can-i-display-images-from-resource-pack-plugin)
|
||||
* [#13 - How can I use UTF characters in configuration?](#13---how-can-i-use-utf-characters-in-configuration)
|
||||
* [#14 - Is there a way to remove all players from tablist?](#14---is-there-a-way-to-remove-all-players-from-tablist)
|
||||
* [#15 - How to add images to tablist?](#15---how-to-add-images-to-tablist)
|
||||
@@ -18,6 +18,7 @@
|
||||
* [#17 - Is it possible to show the number of players in a specific group, similar to staffonline placeholder?](#17---is-it-possible-to-show-the-number-of-players-in-a-specific-group-similar-to-staffonline-placeholder)
|
||||
* [#18 - Why is the plugin flagged as a virus by Windows defender?](#18---why-is-the-plugin-flagged-as-a-virus-by-windows-defender)
|
||||
* [#19 - How can I show amount of online players on the entire network?](#19---how-can-i-show-amount-of-online-players-on-the-entire-network)
|
||||
* [#20 - Why is LuckPerms prefix/suffix not updating on a network?](#20---why-is-luckperms-prefixsuffix-not-updating-on-a-network)
|
||||
|
||||
## #1 - Why are NPCs showing up in the tablist?
|
||||
See [Citizens FAQ](https://wiki.citizensnpcs.co/Frequently_Asked_Questions#Why_are_NPCs_showing_up_in_the_tablist.3F).
|
||||
@@ -26,8 +27,8 @@ See [Citizens FAQ](https://wiki.citizensnpcs.co/Frequently_Asked_Questions#Why_a
|
||||
* If you want to take prefixes/suffixes, check [this guide](https://github.com/NEZNAMY/TAB/wiki/Mini-guides-collection#taking-prefixessuffixes-from-permission-plugin) and use `%luckperms-prefix%` & `%luckperms-suffix%`.
|
||||
* Sorting:
|
||||
First, configure weights correctly in LuckPerms. Then, you have 2 options:
|
||||
* Configure [sorting by groups](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist#groups) (recommended).
|
||||
* Sorting by weights directly: `PLACEHOLDER_HIGH_TO_LOW:%luckperms_highest_group_weight%`.
|
||||
* Configure [sorting by groups](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist#groups) (recommended).
|
||||
* Sorting by weights directly: `PLACEHOLDER_HIGH_TO_LOW:%luckperms_highest_group_weight%`.
|
||||
|
||||
## #3 - I enabled MySQL, and my prefix is now gone!
|
||||
When enabling [MySQL](https://github.com/NEZNAMY/TAB/wiki/MySQL), it will be used as a data storage and groups.yml / users.yml files will no longer be used.
|
||||
@@ -71,7 +72,7 @@ Use "", for example `/tab group owner tabprefix "&2&lOwner&r "`
|
||||
|
||||
## #10 - Can I change/remove the green connection bar in tablist?
|
||||
The connection bar is client sided and cannot be re-textured or removed by TAB.
|
||||
You will need a custom minecraft client or a resource pack.
|
||||
You will need a custom Minecraft client or a resource pack.
|
||||
|
||||
## #11 - Placeholder is not working
|
||||
Most common reasons for a placeholder to not work include:
|
||||
@@ -82,14 +83,29 @@ Most common reasons for a placeholder to not work include:
|
||||
|
||||
The full list of reasons can be found on the [Placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#placeholder-is-not-working) page.
|
||||
|
||||
## #12 - How can I display ItemsAdder images?
|
||||
ItemsAdder offers placeholders, which return the respective symbol to which your desired image is bound to. You can get them using ItemsAdder's PlaceholderAPI expansion using their `%img_<name>%` placeholder. ItemsAdder's internal placeholders (`:something:`) will not work in TAB.
|
||||
## #12 - How can I display images from resource pack plugin?
|
||||
Images work by mapping a chosen character to an image in a resource pack.
|
||||
When the game receives this character, it displays the image instead of the character.
|
||||
What you need to do is send this character to the client.
|
||||
Although resource pack plugins are not all the same, they all have things in common.
|
||||
The first main functionality is offering placeholders.
|
||||
You can use their PlaceholderAPI placeholders in TAB, and they will return the symbol, which then the client displays as an image.
|
||||
The second main functionality is creating internal placeholders, which are different per plugin.
|
||||
The plugin then listens to outgoing packets and replaces the internal placeholder with the symbol.
|
||||
This can, however, fail.
|
||||
Here are possible reasons why:
|
||||
* You have TAB on proxy, so backend plugin cannot modify packets sent by the proxy
|
||||
* TAB also listens to packets and modifies them to make sure no other plugin is overriding it. This mostly applies to [Tablist name formatting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting). TAB then ends up having the last shot, overriding the resource pack plugin.
|
||||
|
||||
In order to make sure images display properly, use PlaceholderAPI placeholders instead of internal placeholders of your resource pack plugins.
|
||||
Here is some extra information for each resource pack plugin you might find useful:
|
||||
* ItemsAdder:
|
||||
* The PlaceholderAPI placeholders are formatted as `%img_<name>%`, internal placeholders (`:something:`) may not always work (see above).
|
||||
* If you are unable to change the placeholder references from internal placeholders to PlaceholderAPI placeholders (for example, because they are defined in permission plugin and used elsewhere as well, where PlaceholderAPI isn't supported), you can use [PlaceholderAPI](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide)'s `imgfix` expansion that replaces format of given placeholders into the correct format.
|
||||
Example: `%luckperms_prefix%` -> `%imgfix_{luckperms_prefix}%`
|
||||
* Nexo:
|
||||
* Here is a collection of tips from Nexo discord: <br /><img width="476" height="238" alt="image" src="https://github.com/user-attachments/assets/0eca8e9b-a73f-4e66-9c76-11a230e823b4" />
|
||||
|
||||
If you are unable to change the references
|
||||
(for example, because they are defined in permission plugin and used elsewhere as well),
|
||||
you can use [PlaceholderAPI](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide)'s `imgfix` expansion
|
||||
that replaces format of given placeholders into the correct format.
|
||||
Example: `%luckperms_prefix%` -> `%imgfix_{luckperms_prefix}%`
|
||||
|
||||
## #13 - How can I use UTF characters in configuration?
|
||||
**Option 1**: [Save the file in UTF-8 encoding](https://github.com/NEZNAMY/TAB/wiki/How-to-save-the-config-in-UTF8-encoding) and use your desired symbol directly.
|
||||
@@ -114,7 +130,7 @@ An alternative solution using TAB is putting a lot of empty lines into header,
|
||||
which will push all players out of the screen.
|
||||
|
||||
## #15 - How to add images to tablist?
|
||||
You can check out [this reddit post](https://www.reddit.com/r/admincraft/comments/llrgty/comment/gnswdcz/?utm_source=share&utm_medium=web2x&context=3).
|
||||
You can check out [this Reddit post](https://www.reddit.com/r/admincraft/comments/llrgty/comment/gnswdcz/?utm_source=share&utm_medium=web2x&context=3).
|
||||
When using the symbol in configuration using \u format, remember to use `""` in config instead of `''`.
|
||||
|
||||
## #16 - Is MiniMessage supported?
|
||||
@@ -141,4 +157,11 @@ Windows defender could be flagging it for the same reason.
|
||||
|
||||
## #19 - How can I show amount of online players on the entire network?
|
||||
If you have TAB installed on the proxy, use `%online%` placeholder.
|
||||
If you have TAB installed on the backend servers, use `%bungee_total%` from [PlaceholderAPI](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide). This placeholder has an internal cooldown, which can be configured in `plugins/PlaceholderAPI/config.yml` under `expansions` -> `bungee` -> `check_interval`, default value is 30 (seconds).
|
||||
If you have TAB installed on the backend servers, use `%bungee_total%` from [PlaceholderAPI](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide). This placeholder has an internal cooldown, which can be configured in `plugins/PlaceholderAPI/config.yml` under `expansions` -> `bungee` -> `check_interval`, default value is 30 (seconds).
|
||||
|
||||
## #20 - Why is LuckPerms prefix/suffix not updating on a network?
|
||||
In order for LuckPerms instances to pick up the changes to player data in real time, it needs to communicate the changes between proxy ↔ backend.
|
||||
If you notice changes not taking effect in TAB, try running `/lpb user <player> info` (or `/lpv` on velocity). If running the command updates it in TAB, that's the problem - LuckPerms instances not communicating in real time.
|
||||
|
||||
This is controlled by a LuckPerms setting called [messaging-service](https://luckperms.net/wiki/Syncing-data-between-servers#messaging-services). Set it to `pluginmsg` to make changes update immediately.
|
||||
Additionally, make sure `auto-push-updates` is still `true` (it is by default already).
|
||||
@@ -1,5 +1,5 @@
|
||||
# About the wiki
|
||||
The wiki explains the functionality of the plugin as of version **5.3.2**.
|
||||
The wiki explains the functionality of the plugin as of version **6.0.0**.
|
||||
If your config looks different or is missing some features, you are using an old version of the plugin.
|
||||
To get the latest version, check [releases](https://github.com/NEZNAMY/TAB/releases/).
|
||||
Wiki for older plugin versions is not available.
|
||||
@@ -14,9 +14,9 @@ A lot of examples on the wiki use PlaceholderAPI placeholders without explaining
|
||||
On order to not have to explain something every time it is mentioned, it will be explained here once.
|
||||
| Name | Description |
|
||||
|----------------|-----------------------|
|
||||
| proxy server | Server running BungeeCord or Velocity software, which are used to connect multiple servers, allowing easy transfer between them |
|
||||
| modded server | Fabric, Forge and NeoForge |
|
||||
| backend server | Minecraft server that is actually being played on, not a proxy. This includes Bukkit (and its forks), Sponge and modded servers |
|
||||
| proxy server | Server running BungeeCord or Velocity software, which are used to connect multiple servers, allowing easy transfer between them. |
|
||||
| modded server | Fabric, Forge and NeoForge. |
|
||||
| backend server | Minecraft server that is actually being played on, not a proxy. This includes Bukkit (and its forks) and modded servers |
|
||||
|
||||
# Limitations of plugins
|
||||
TAB is a plugin (even on modded platforms, it is only a server-sided mod).
|
||||
|
||||
@@ -22,7 +22,7 @@ There are 2 simple steps to this:
|
||||
> (for luckperms /lp group \<group\> setweight \<number\>).
|
||||
|
||||
> [!TIP]
|
||||
> Verify player's group using `/tab debug <player>`. It should say `Primary permission group: <your group>`.
|
||||
> Verify player's group using `/tab dump <player>`, opening the link and check "permissionGroup" in player info.
|
||||
|
||||
## Option 2: Permission nodes
|
||||
Instead of taking whole groups, TAB will check for permission nodes and assign groups based on the highest permission.
|
||||
@@ -46,7 +46,7 @@ Instead of taking whole groups, TAB will check for permission nodes and assign g
|
||||
By default, TAB takes groups from the permission plugin installed on the same server instance.
|
||||
In the case of proxy installation, groups are by default taken from the proxy.
|
||||
To see how is your proxy permission plugin configured, use its respective command
|
||||
(for LuckPerms it's `/lpb user <player> info` instead of `/lp`).
|
||||
(for LuckPerms it's `/lpb user <player> info` on BungeeCord and `/lpv user <player> info` on Velocity).
|
||||
|
||||
If you don't have your permission plugin synced using MySQL for sharing groups and don't wish to, and want groups to be taken from backend servers instead, enable the config option:
|
||||
```
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* [Nametag](#nametag)
|
||||
* [Proper compatibility with plugins that change profile name](#proper-compatibility-with-plugins-that-change-profile-name)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Tip 1 - Nickname prefix](#tip-1---nickname-prefix)
|
||||
* [Tip 2 - Sorting nicked players with the lowest priority](#tip-2---sorting-nicked-players-with-the-lowest-priority)
|
||||
* [Tip 1 - Nickname prefix](#tip-1---nickname-prefix)
|
||||
* [Tip 2 - Sorting nicked players with the lowest priority](#tip-2---sorting-nicked-players-with-the-lowest-priority)
|
||||
|
||||
# Tablist
|
||||
This one is straightforward.
|
||||
@@ -29,12 +29,22 @@ This example uses `%essentials_nickname%` placeholder from Essentials. Giving it
|
||||
> Put the nickname placeholder into customtabname instead.
|
||||
|
||||
# Nametag
|
||||
Nametags are more complicated because they consist of player's username, team prefix and team suffix.
|
||||
There is no intended way to do this.
|
||||
The only way is changing it using a nickname plugin.
|
||||
Some nickname plugins actually change name in game profile, which also changes it in nametag.
|
||||
You will need to find one that offers this and then use it.
|
||||
This is a complicated process with lots of side effects, so TAB does not offer it.
|
||||
**TLDR: TAB does not offer this functionality**.
|
||||
|
||||
With that out of the way, let's learn more about this issue.
|
||||
Unlike with tablist name formatting, which is a single component containing everything (and therefore fully customizable), nametags are different.
|
||||
They consist of prefix and suffix components (coming from scoreboard teams) and name itself.
|
||||
This name is contained in player info packet (coincidentally the same one that controls tablist format), along with player's skin and other things.
|
||||
This name is limited to 16 characters (since Minecraft doesn't allow longer names) and since it's not a component, naturally it does not support RGB colors.
|
||||
Strangely enough, legacy codes using `§` work, but they further limit the length of the rest of the name.
|
||||
|
||||
There are really only 2 ways to achieve this solution:
|
||||
* **Change the profile name** - This requires sending player info packet that removes the old player and adds a new one with the same name. Then respawn the player. Additionally, it requires a lot of packets to be sent to the affected player, since you cannot just remove & add self, as well as a lot of other packets to restore previously sent data. These packets have been changing a lot with various MC versions. A lot of them cannot even be sent from a proxy server. As such, TAB does not offer this functionality, since it would be very limited anyway (see above) and require a lot of adapting for proper functionality.
|
||||
If you decide to use a plugin with this functionality, keep in mind it breaks all name-bound features, such as scoreboard teams, belowname or playerlist objective. See below for compatibility information.
|
||||
Example third party plugins that offer this: None?
|
||||
* **Use an armor stand / text display to show any text** - TAB no longer offers this functionality ([here's why](https://gist.github.com/NEZNAMY/f4cabf2fd9251a836b5eb877720dee5c)), so you will need to look into other plugins.
|
||||
If you decide to use a plugin with this functionality, just remember to make the vanilla nametag invisible, so you don't see both vanilla nametag and the entity (`invisible-nametags: true`).
|
||||
Example third party plugins that offer this: [UnlimitedNameTags](https://www.spigotmc.org/resources/unlimitednametags.117526/), [DisplayNameTags](https://github.com/Matt-MX/DisplayNameTags), [NotJustNamePlates](https://github.com/YouHaveTrouble/NotJustNameplates), [CustomNameplates](https://polymart.org/product/2543/customnameplates)
|
||||
|
||||
# Proper compatibility with plugins that change profile name
|
||||
If using a nickname plugin that changes player's profile name to change it in the name tag,
|
||||
@@ -42,14 +52,12 @@ this has lots of side effects.
|
||||
Most notably, it breaks all name-bound features,
|
||||
especially [nametags](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags) and [sorting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist).
|
||||
For that reason, TAB must detect name change.
|
||||
This is done automatically on Bukkit, BungeeCord and modded platforms.
|
||||
**This detection is not available on Sponge and Velocity**.
|
||||
|
||||
> [!WARNING]
|
||||
> If you have TAB installed on backend server and a proxy plugin changes nickname, TAB is not able to detect this change, since the modified packets never actually go through the backend server where TAB is installed.
|
||||
|
||||
> [!TIP]
|
||||
> If automatic detection is not working for you (either you use TAB on Sponge / Velocity, or use proxy plugin for changing names while TAB is on backend), you can use [this API method](https://github.com/NEZNAMY/TAB/blob/master/api/src/main/java/me/neznamy/tab/api/TabPlayer.java#L88) to let TAB know player's name changed.
|
||||
> If automatic detection is not working for you (you use proxy plugin for changing names while TAB is on backend), you can use [this API method](https://github.com/NEZNAMY/TAB/blob/master/api/src/main/java/me/neznamy/tab/api/TabPlayer.java#L88) to let TAB know player's name changed.
|
||||
|
||||
In order for TAB to properly detect name change, player UUID must remain the same,
|
||||
otherwise it's a random entry with a random uuid and random name, which TAB isn't able to match with actual players.
|
||||
@@ -74,7 +82,7 @@ conditions:
|
||||
```
|
||||
Then, use `%condition:nick%` in your `customtabname` instead of the placeholder directly.
|
||||
|
||||
# Tip 2 - Sorting nicked players with the lowest priority
|
||||
## Tip 2 - Sorting nicked players with the lowest priority
|
||||
Let's say your sorting list looks like this:
|
||||
```
|
||||
sorting-types:
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [RGB Colors (1.16+)](#rgb-colors-116)
|
||||
* [Usage](#usage)
|
||||
* [Magic codes](#magic-codes)
|
||||
* [Invalid code handling](#invalid-code-handling)
|
||||
* [Compatibility with <1.16](#compatibility-with-116)
|
||||
* [Usage](#usage)
|
||||
* [Magic codes](#magic-codes)
|
||||
* [Invalid code handling](#invalid-code-handling)
|
||||
* [Compatibility with <1.16](#compatibility-with-116)
|
||||
* [Fonts (1.16+)](#fonts-116)
|
||||
* [Usage](#usage-1)
|
||||
* [Compatibility with RGB and gradients](#compatibility-with-rgb-and-gradients)
|
||||
* [Usage](#usage-1)
|
||||
* [Compatibility with RGB and gradients](#compatibility-with-rgb-and-gradients)
|
||||
* [Shadow color (1.21.4+)](#shadow-color-1214)
|
||||
* [Object components (1.21.9+)](#object-components-1219)
|
||||
* [Atlas sprite](#atlas-sprite)
|
||||
* [Player sprite](#player-sprite)
|
||||
* [Compatibility with < 1.21.9](#compatibility-with--1219)
|
||||
* [Atlas sprite](#atlas-sprite)
|
||||
* [Player sprite](#player-sprite)
|
||||
* [Compatibility with < 1.21.9](#compatibility-with--1219)
|
||||
* [MiniMessage](#minimessage)
|
||||
* [Config options](#config-options)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [Tip 1 - Manually defining legacy color](#tip-1---manually-defining-legacy-color)
|
||||
* [Tip 1 - Manually defining legacy color](#tip-1---manually-defining-legacy-color)
|
||||
|
||||
# About
|
||||
In Minecraft 1.7, new feature called components was introduced. This is an upgrade from using legacy colors (using § symbol) into creating a json object where text, color, and magic codes (boolean values) are separated.
|
||||
In Minecraft 1.7, new feature called components was introduced. This is an upgrade from using legacy colors (using § symbol) into creating a JSON object where text, color, and magic codes (boolean values) are separated.
|
||||
Additionally, it received new features called hover event and click event. The only place this affects is chat, which TAB does not handle, so these functions are not implemented by TAB at all.
|
||||
|
||||
Components have been receiving a lot of new functions since then, which are explained below.
|
||||
@@ -104,12 +104,20 @@ where `ATLAS` is the atlas and `SPRITE` is the sprite. Note that atlas can conta
|
||||
Example:
|
||||
```
|
||||
header:
|
||||
- '&bDiamond helmet: <sprite:"minecraft:blocks":"minecraft:item/diamond_helmet">'
|
||||
- 'Diamond chestplate: <sprite:"minecraft:blocks":"minecraft:item/diamond_chestplate">'
|
||||
- 'Diamond leggings: <sprite:"minecraft:blocks":"minecraft:item/diamond_leggings">'
|
||||
- 'Diamond boots: <sprite:"minecraft:blocks":"minecraft:item/diamond_boots">'
|
||||
- '&bDiamond helmet: <sprite:"minecraft:items":"minecraft:item/diamond_helmet">'
|
||||
- 'Diamond chestplate: <sprite:"minecraft:items":"minecraft:item/diamond_chestplate">'
|
||||
- 'Diamond leggings: <sprite:"minecraft:items":"minecraft:item/diamond_leggings">'
|
||||
- 'Diamond boots: <sprite:"minecraft:items":"minecraft:item/diamond_boots">'
|
||||
```
|
||||
<img width="386" height="101" alt="image" src="https://github.com/user-attachments/assets/e9a66305-2ae4-44f4-9d8e-a227fb7591e4" />
|
||||
<img width="386" height="101" alt="image" src="https://github.com/user-attachments/assets/e9a66305-2ae4-44f4-9d8e-a227fb7591e4" />
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> These values change over MC versions (for example items were in `minecraft:blocks` atlas in 1.21.9 - 1.21.10, but got moved to `minecraft:items` in 1.21.11), so make sure you are using the correct values for your MC version. For proxy installations, you may need to use conditional placeholders and check for player's version. On backend, ViaVersion may or may not automatically translate these.
|
||||
|
||||
> [!TIP]
|
||||
> For full list of available sprites you can check out https://www.gamergeeks.net/apps/minecraft/list-of-atlas-sprites. However, it doesn't contain any information regarding different Minecraft versions.
|
||||
|
||||
|
||||
|
||||
## Player sprite
|
||||
@@ -121,7 +129,7 @@ Example:
|
||||
> ```
|
||||
> in **config.yml**.
|
||||
|
||||
This type allows you to display minecraft heads. There are 3 ways to define a head:
|
||||
This type allows you to display Minecraft heads. There are 3 ways to define a head:
|
||||
| Type | Functionality |
|
||||
|--------------------------|---------------------------------------------------------------------------|
|
||||
| Player name | The client checks recently received profiles (players on current server) and shows skin of player with that name. If no such player was found, connects to Mojang and gets skin of player with specified name. <br />**Note**: This means using a plugin for changing skins will result in the skin being changed here too (the client has a cache, so it won't be visible immediately (restarting the client resets the cache)). If using offline mode and no skin plugin, head won't display correct skin even if such player is registered at Mojang. |
|
||||
@@ -136,7 +144,7 @@ Here are all the ways you can display player skins using TAB and their implement
|
||||
| `<head:signed_texture:VALUE;SIGNATURE>` | Shows head defined by value and signature | value and signature |
|
||||
| `<head:player:NAME` | Connects to Mojang and retrieves value and signature of defined player | value and signature |
|
||||
| `<head:mineskin:ID>` | Connects to Mineskin and retrieves value and signature of specified upload ID | value and signature |
|
||||
| `<head:texture:VALUE>` | Connects to Mojang and retrieves skin value and signature from "texture" | value and signature |
|
||||
| `<head:texture:TEXTURE>` | Gets the texture from `http://textures.minecraft.net/texture/TEXTURE` | value and signature |
|
||||
|
||||
Here is an example for all types:
|
||||
```
|
||||
@@ -155,12 +163,12 @@ Here is an example for all types:
|
||||
You may be wondering what happens when you try to use these and the client is below 1.21.9.
|
||||
See table below for behavior based on your setup.
|
||||
|
||||
| Setup | Legacy text decider | Displayed text for <1.21.9 players |
|
||||
|-----------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Backend <1.21.9 | TAB | `<Object components were added in 1.21.9>` (even for 1.21.9+ players) |
|
||||
| Backend 1.21.9+ | ViaVersion | ViaBackwards 5.5.0 - [Client disconnects with a packet decoding error](https://github.com/ViaVersion/ViaBackwards/issues/1107) <br /> ViaBackwards 5.5.1+ - (empty) |
|
||||
| BungeeCord | TAB | `<Object components were added in 1.21.9>` |
|
||||
| Velocity | Velocity | Client disconnects with a packet decoding error |
|
||||
| Setup | Legacy text decider | Displayed text for <1.21.9 players |
|
||||
|-----------------|---------------------|-----------------------------------------------------------------------|
|
||||
| Backend <1.21.9 | TAB | `<Object components were added in 1.21.9>` (even for 1.21.9+ players) |
|
||||
| Backend 1.21.9+ | ViaVersion | (empty text) |
|
||||
| BungeeCord | TAB | `<Object components were added in 1.21.9>` |
|
||||
| Velocity | (no one) | Client disconnects with a packet decoding error |
|
||||
|
||||
# MiniMessage
|
||||
TAB has [MiniMessage](https://docs.advntr.dev/minimessage/format.html) hook, however, it does not include this library on its own. Including it would cause conflicts, so it has to be included in the server software.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Content
|
||||
* [Backend](#backend)
|
||||
* [Proxy](#proxy)
|
||||
* [Proxy installation](#proxy-installation)
|
||||
* [Backend installation](#backend-installation)
|
||||
* [Mixed installation](#mixed-installation)
|
||||
* [Proxy installation](#proxy-installation)
|
||||
* [Backend installation](#backend-installation)
|
||||
* [Mixed installation](#mixed-installation)
|
||||
* [Updating the plugin](#updating-the-plugin)
|
||||
|
||||
# Backend
|
||||
@@ -35,6 +35,7 @@ Advantages of proxy installation compared to installing on all backend servers i
|
||||
* [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) can work with all players connected to the proxy
|
||||
* Avoid [header/footer](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer#additional-note-1---not-resetting-on-server-switch) and [bossbar](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar#additional-note-2---not-hiding-on-server-switch) not disappearing on server switch
|
||||
* Configuration files in a single place for easier editing / no need to use MySQL for syncing data
|
||||
* Can use new features for new players even if backend is using an older version (RGB, 1.20.3+ NumberFormat and more).
|
||||
|
||||
|
||||
Disadvantages compared to installing on all backend servers instead:
|
||||
@@ -44,11 +45,12 @@ Disadvantages compared to installing on all backend servers instead:
|
||||
Just like when not running a network, install the plugin on all servers where you want the plugin. However, this setup is not as good (see advantages of proxy installation).
|
||||
|
||||
## Mixed installation
|
||||
Mixed installation is absolutely not recommended and should be avoided.
|
||||
It requires you to disable features to make only 1 instance handle a single feature at a time,
|
||||
which most people fail miserably to do,
|
||||
resulting in issues such as header/footer flashing between proxy and backend configuration.
|
||||
With bridge, you are able to achieve almost anything with proxy installation.
|
||||
Mixed installation is absolutely not supported and should be avoided.
|
||||
|
||||
There is no sort of communication between backend and proxy TAB installations (this is only done by TAB-Bridge, which doesn't do anything on its own). Having TAB on both proxy and backend is effectively installing 2 plugins to do the same thing, which will naturally cause issues (although TAB's anti-override for key features should help with this).
|
||||
|
||||
If you really wish to proceed (for example having feature X handled by proxy and feature Y on backend), make sure to properly disable features (`enabled: false`) to avoid problems. However, the only case where this makes any sense is per world playerlist feature, which is not available on proxies. Everything else can be done from proxy side.
|
||||
|
||||
|
||||
# Updating the plugin
|
||||
Updating the plugin is always as simple as replacing old jar with the new one.
|
||||
@@ -59,5 +61,5 @@ TAB will automatically convert your existing configuration files to the latest f
|
||||
(most notably 2.9.2 → 3.0.0, but any other changes as well).
|
||||
|
||||
Downgrading is completely unsupported, as an old plugin version cannot know what the future config format is / will be.
|
||||
Just like Minecraft itself.
|
||||
There is no reason for downgrading anyway, as every version is superior to older versions released before it.
|
||||
Just like Minecraft itself, although TAB won't fail with an error message like Minecraft does, but you will get warnings and / or features not working correctly.
|
||||
There is no reason for downgrading anyway, since in general, every version is typically superior to older versions released before it.
|
||||
@@ -1,5 +1,5 @@
|
||||
# About
|
||||
This is collection if minecraft issues/bugs you may experience when using TAB, which are not TAB bugs and cannot be fixed with a plugin.
|
||||
This is collection if Minecraft issues/bugs you may experience when using TAB, which are not TAB bugs and cannot be fixed with a plugin.
|
||||
|
||||
# 1.5.x - 1.12.x - Unwanted space
|
||||
You may see unwanted space at the end of a line of text even if you didn't configure any.
|
||||
|
||||
@@ -7,8 +7,9 @@ Combine the knowledge gathered by reading the wiki to get interesting results, m
|
||||
* [BedWars1058 compatibility](#bedwars1058-compatibility)
|
||||
* [Displaying AFK status in tablist](#displaying-afk-status-in-tablist)
|
||||
* [Showing combined online player count from multiple servers](#showing-combined-online-player-count-from-multiple-servers)
|
||||
* [Option 1 - Using global playerlist](#option-1---using-global-playerlist)
|
||||
* [Option 2 - Using PlaceholderAPI](#option-2---using-placeholderapi)
|
||||
* [Option 1 - Using global playerlist](#option-1---using-global-playerlist)
|
||||
* [Option 2 - Using PlaceholderAPI](#option-2---using-placeholderapi)
|
||||
* [Showing head components only to 1.21.9+ players](#showing-head-components-only-to-1219-players)
|
||||
|
||||
# Displaying kill / death counter in tablist
|
||||
This example uses PlaceholderAPI placeholders.
|
||||
@@ -203,4 +204,31 @@ Unlike the first option, this one offers much more freedom, but also requires an
|
||||
First, you need placeholders for online count on specific servers. Bungee expansion does exactly that. The format is `%bungee_<server>%`. To merge the online counts, use math expansion. The syntax will be `%math_0_<expression>%`, where `expression` uses online counts from specific servers from bungee expansion. Finally, you'll end up with a placeholder like `%math_0_{bungee_lobby1}+{bungee_lobby2}%`. This placeholder will merge online counts from `lobby1` and `lobby2`.
|
||||
Please note that by default, bungee expansion updates values only every 30 seconds. This can be changed in PlaceholderAPI's config. If the numbers still don't show correct value, check if the placeholder works correctly using `/papi parse me <placeholder>`. If not, this is not a TAB issue.
|
||||
|
||||
Advantage of this is no requirement for a specific TAB feature to be enabled, which also includes no need to have TAB installed on the proxy. The downside is relying on 3rd party software. If TAB is on proxy, you'll also need [bridge plugin](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge).
|
||||
Advantage of this is no requirement for a specific TAB feature to be enabled, which also includes no need to have TAB installed on the proxy. The downside is relying on 3rd party software. If TAB is on proxy, you'll also need [bridge plugin](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge).
|
||||
|
||||
# Showing head components only to 1.21.9+ players
|
||||
Object components were added in 1.21.9, which means they cannot be shown to 1.21.8- players. [If you try to do so, you'll either get an ugly text (BungeeCord / backend), or disconnect with a packet decode error (on Velocity)](https://github.com/NEZNAMY/TAB/wiki/How-to-use-Minecraft-components#compatibility-with--1219).
|
||||
|
||||
To achieve this goal, we will use [relational conditions](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders#relational-conditions) and check if viewer is 1.21.9+:
|
||||
```
|
||||
conditions:
|
||||
head:
|
||||
conditions:
|
||||
- "%viewer:player-version-id%>=773"
|
||||
true: "<head:name:%player%> "
|
||||
false: "" # Show nothing
|
||||
```
|
||||
The `773` is the protocol version of 1.21.9, so we can use a simple math operation instead of comparing strings. You can check [TAB's source code](https://github.com/NEZNAMY/TAB/blob/master/shared/src/main/java/me/neznamy/tab/shared/ProtocolVersion.java) for the full list. This condition will return head component of the **target** player if the **viewer** is 1.21.9+, empty value otherwise.
|
||||
[If you want to use this head syntax, you'll need to disable MiniMessage support](https://github.com/NEZNAMY/TAB/wiki/How-to-use-Minecraft-components#player-sprite), or use a syntax supported by MiniMessage (which is currently extremely limited).
|
||||
Use this condition with `%rel_condition:head%`. The rel is important, so TAB knows the placeholder is a relational placeholder. If you forget to, the condition will return a warning message instead of configured value.
|
||||
To showcase it, I have this in **groups.yml** (to show player versions as well):
|
||||
```
|
||||
_DEFAULT_:
|
||||
tabprefix: "%rel_condition:head%"
|
||||
tabsuffix: " %player-version% (%player-version-id%)"
|
||||
```
|
||||
1.21.11 player's view:
|
||||
<img width="634" height="103" alt="image" src="https://github.com/user-attachments/assets/4c762fae-3166-4247-865f-c87d1916afd7" />
|
||||
|
||||
1.21.8 player's view:
|
||||
<img width="581" height="106" alt="image" src="https://github.com/user-attachments/assets/cb7ab93d-0db5-4968-bcfe-60004125ddf1" />
|
||||
@@ -3,8 +3,8 @@
|
||||
* [Enabling](#enabling)
|
||||
* [Configuration changes](#configuration-changes)
|
||||
* [Data conversion](#data-conversion)
|
||||
* [Downloading from MySQL to files](#downloading-from-mysql-to-files)
|
||||
* [Uploading from files to MySQL](#uploading-from-files-to-mysql)
|
||||
* [Downloading from MySQL to files](#downloading-from-mysql-to-files)
|
||||
* [Uploading from files to MySQL](#uploading-from-files-to-mysql)
|
||||
* [Synchronization frequency](#synchronization-frequency)
|
||||
|
||||
# About
|
||||
@@ -26,8 +26,7 @@ See the compatibility table below for current information about compatibility:
|
||||
|------------|-----------|
|
||||
| Bukkit | ✔ |
|
||||
| BungeeCord | ✔ |
|
||||
| Fabric | ❌ |
|
||||
| Sponge | ❌ |
|
||||
| Modded | ❌ |
|
||||
| Velocity | ❌ |
|
||||
|
||||
# Enabling
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Internal placeholders](#internal-placeholders)
|
||||
* [Universal](#universal)
|
||||
* [Backend only](#backend-only)
|
||||
* [Proxy only](#proxy-only)
|
||||
* [Universal](#universal)
|
||||
* [Backend only](#backend-only)
|
||||
* [Proxy only](#proxy-only)
|
||||
* [PlaceholderAPI](#placeholderapi)
|
||||
* [Relational placeholders](#relational-placeholders)
|
||||
* [About](#about-1)
|
||||
* [Usage](#usage)
|
||||
* [About](#about-1)
|
||||
* [Usage](#usage)
|
||||
* [Refreshing](#refreshing)
|
||||
* [Refresh intervals](#refresh-intervals)
|
||||
* [Sync refreshing](#sync-refreshing)
|
||||
* [Refresh intervals](#refresh-intervals)
|
||||
* [Sync refreshing](#sync-refreshing)
|
||||
* [Placeholder is not working](#placeholder-is-not-working)
|
||||
* [Internal placeholder is not working](#internal-placeholder-is-not-working)
|
||||
* [PlaceholderAPI placeholder is not working](#placeholderapi-placeholder-is-not-working)
|
||||
* [Internal placeholder is not working](#internal-placeholder-is-not-working)
|
||||
* [PlaceholderAPI placeholder is not working](#placeholderapi-placeholder-is-not-working)
|
||||
* [API](#api)
|
||||
* [Server placeholders](#server-placeholders)
|
||||
* [Player placeholders](#player-placeholders)
|
||||
* [Relational placeholders](#relational-placeholders-1)
|
||||
* [Server placeholders](#server-placeholders)
|
||||
* [Player placeholders](#player-placeholders)
|
||||
* [Relational placeholders](#relational-placeholders-1)
|
||||
|
||||
# About
|
||||
TAB offers various placeholders to display the most common information. It also supports [PlaceholderAPI placeholders](https://github.com/PlaceholderAPI/PlaceholderAPI/wiki/Placeholders).
|
||||
@@ -37,43 +37,44 @@ Refresh interval for conditions is set to be equal to the fastest refreshing pla
|
||||
## Universal
|
||||
These placeholders are available on all platforms.
|
||||
|
||||
| Identifier | Refresh interval | Description |
|
||||
|-------------------------------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| %online% | 1000 | Total online player count on server where plugin is installed (excluding [vanished players](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection)). If the plugin is installed on a proxy, it will show total player count on the whole network. |
|
||||
| %world% | -1 | Name of world where player is |
|
||||
| %worldonline% | 1000 | Amount of players in the same world (excluding [vanished players](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection)) as the player |
|
||||
| %ping% | 500 | Player's ping to the server where TAB is installed. <br />**Note**: This value is measured by the server and TAB is only retrieving that value. If it's wrong, it's not a TAB issue. |
|
||||
| %player% | -1 | Player's name |
|
||||
| %uuid% | -1 | Player's UUID |
|
||||
| %time% | 500 | Current real time, output is configurable in **config.yml** in `time-format` option |
|
||||
| %date% | 60000 | Current date, output is configurable in **config.yml** in `date-format` option |
|
||||
| %staffonline% | 2000 | Amount of online players with `tab.staff` permission |
|
||||
| %nonstaffonline% | 2000 | Amount of online players without `tab.staff` permission |
|
||||
| %memory-used% | 200 | Used RAM of server in MB |
|
||||
| %memory-max% | -1 | Total RAM of server in MB |
|
||||
| %memory-used-gb% | 200 | Used RAM of server in GB |
|
||||
| %memory-max-gb% | -1 | Total RAM of server in GB |
|
||||
| %player-version% | -1 | User-friendly version of player, such as `1.14.4`. For versions that share the same protocol version number, the higher one is returned (for example 1.20 & 1.20.1 -> 1.20.1 is returned) |
|
||||
| %player-version-id% | -1 | Network ID of player's version. You can see the full version map on [fan wiki](https://wiki.vg/Protocol_version_numbers) for all versions or [TAB's source code](https://github.com/NEZNAMY/TAB/blob/master/shared/src/main/java/me/neznamy/tab/shared/ProtocolVersion.java) for releases only. Useful for conditions to create per-version results. |
|
||||
| %luckperms-prefix% | 1000 | Prefix from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-suffix% | 1000 | Suffix from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-prefixes% | 1000 | All prefixes combined from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-suffixes% | 1000 | All suffixes combined from LuckPerms from the server where TAB is installed |
|
||||
| %displayname% | 500 | Display name variable set by permission plugin, typically prefix + name + suffix |
|
||||
| %group% | 1000 | Returns player's primary group name. Created for internal functionality, but can be used as a display placeholder as well. |
|
||||
| %vanished% | 1000 | Returns `true`/`false` based on player's vanish status. Created for internal functionality, but can be used as a display placeholder as well. |
|
||||
| %bedrock% | -1 | Returns `true`/`false` based on whether player is using Bedrock edition or not (Java) |
|
||||
| %bossbar_announce_time_total_\<bossbar\>% | -1 | Total time (inputted value) of an announced bossbar. |
|
||||
| %bossbar_announce_time_left_\<bossbar\>% | 100 | Remaining time of an announced bossbar. |
|
||||
| %% | -1 | Returns the `%` symbol. Useful to display `%` symbol without breaking all placeholders after it |
|
||||
| Identifier | Refresh interval | Description |
|
||||
|-------------------------------------------|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| %online% | 1000 | Total online player count on server where plugin is installed (excluding [vanished players](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection)). If the plugin is installed on a proxy, it will show total player count on the whole network. |
|
||||
| %world% | -1 | Name of world where player is |
|
||||
| %worldonline% | 1000 | Amount of players in the same world (excluding [vanished players](https://github.com/NEZNAMY/TAB/wiki/Additional-information#vanish-detection)) as the player |
|
||||
| %ping% | 500 | Player's ping to the server where TAB is installed. <br />**Note**: This value is measured by the server and TAB is only retrieving that value. If it's wrong, it's not a TAB issue. |
|
||||
| %player% | -1 | Player's name |
|
||||
| %uuid% | -1 | Player's UUID |
|
||||
| %time% | 500 | Current real time, output is configurable in **config.yml** in `time-format` option |
|
||||
| %date% | 60000 | Current date, output is configurable in **config.yml** in `date-format` option |
|
||||
| %staffonline% | 2000 | Amount of online players with `tab.staff` permission |
|
||||
| %nonstaffonline% | 2000 | Amount of online players without `tab.staff` permission |
|
||||
| %memory-used% | 200 | Used RAM of server in MB |
|
||||
| %memory-max% | -1 | Total RAM of server in MB |
|
||||
| %memory-used-gb% | 200 | Used RAM of server in GB |
|
||||
| %memory-max-gb% | -1 | Total RAM of server in GB |
|
||||
| %player-version% | -1 | User-friendly version of player, such as `1.14.4`. For versions that share the same protocol version number, the higher one is returned (for example 1.20 & 1.20.1 -> 1.20.1 is returned) |
|
||||
| %player-version-id% | -1 | Network ID of player's version. You can see the full version list in [TAB's source code](https://github.com/NEZNAMY/TAB/blob/master/shared/src/main/java/me/neznamy/tab/shared/ProtocolVersion.java). Useful for conditions to create per-version results using numeric operations since version names cannot be effectively compared. |
|
||||
| %luckperms-prefix% | 1000 | Prefix from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-suffix% | 1000 | Suffix from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-prefixes% | 1000 | All prefixes combined from LuckPerms from the server where TAB is installed |
|
||||
| %luckperms-suffixes% | 1000 | All suffixes combined from LuckPerms from the server where TAB is installed |
|
||||
| %displayname% | 500 | Display name variable set by permission plugin, typically prefix + name + suffix |
|
||||
| %group% | 1000 | Returns player's primary group name. Created for internal functionality, but can be used as a display placeholder as well. |
|
||||
| %vanished% | 1000 | Returns `true`/`false` based on player's vanish status. Created for internal functionality, but can be used as a display placeholder as well. |
|
||||
| %bedrock% | -1 | Returns `true`/`false` based on whether player is using Bedrock edition or not (Java) |
|
||||
| %bossbar_announce_time_total_\<bossbar\>% | -1 | Total time (inputted value) of an announced bossbar. |
|
||||
| %bossbar_announce_time_left_\<bossbar\>% | 100 | Remaining time of an announced bossbar. |
|
||||
| %online_\<server name\>% | 1000 | Amount of online players on specified server (excluding vanished players) |
|
||||
| %% | -1 | Returns the `%` symbol. Useful to display `%` symbol without breaking all placeholders after it |
|
||||
|
||||
## Backend only
|
||||
These placeholders only work when TAB is installed on a backend server, they don't work when TAB is on a proxy. If you have TAB installed on proxy and wish to use these, use their [PlaceholderAPI](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide) alternative with [Bridge plugin](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) installed for PlaceholderAPI support on proxy.
|
||||
| Identifier | Refresh interval | Description | PlaceholderAPI alternative |
|
||||
| ------------- | ------------- | ------------- | ------------- |
|
||||
|---------------|---------------| ------------- | ------------- |
|
||||
| %health% | 100 | Player's health, rounded up to match Minecraft's heart display. | %player_health_rounded% |
|
||||
| %displayname% | 500 | Player's display name value set by permission plugin. | %player_displayname% |
|
||||
| %tps% | 1000 | TPS of server from the last minute. Measured by the server. Not directly available on Fabric, Forge and Neoforge. Primitive approximation using MSPT (TPS = 1000/MSPT, max 20) is used there. | %server_tps_1% |
|
||||
| %tps% | 1000 | TPS of server from the last minute. Measured by the server. Not directly available on modded platforms - primitive approximation using MSPT (TPS = 1000/MSPT, max 20) is used there. | %server_tps_1% |
|
||||
| %mspt% | 1000 | Server's current milliseconds per tick (not available on Spigot) | - |
|
||||
| %deaths% | 1000 | Player's death count | %statistic_deaths% |
|
||||
|
||||
@@ -82,7 +83,6 @@ These placeholders only work when TAB is installed on a backend server, they don
|
||||
|------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| %server% | -1 | Name of server where player is, defined in proxy's config. |
|
||||
| %serveronline% | 1000 | Amount of online players on server where the player is (excluding vanished players) |
|
||||
| %online_\<server name\>% | 1000 | Amount of online players on specified server (excluding vanished players) |
|
||||
| %playerlist-group_\<group\>% | 1000 | Amount of online players in specified [global playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist) group (therefore the feature must be enabled) |
|
||||
|
||||
# PlaceholderAPI
|
||||
@@ -100,25 +100,48 @@ placeholders:
|
||||
register-tab-expansion: true
|
||||
```
|
||||
in **config.yml**.
|
||||
Full list of placeholders:
|
||||
| Identifier | Description |
|
||||
| ------------- | ------------- |
|
||||
| %tab_tabprefix% | Current tabprefix with replaced placeholders |
|
||||
| %tab_tabsuffix% | Current tabsuffix with replaced placeholders |
|
||||
| %tab_tagprefix% | Current tagprefix with replaced placeholders |
|
||||
| %tab_tagsuffix% | Current tagsuffix with replaced placeholders |
|
||||
| %tab_customtabname% | Current customtabname with replaced placeholders |
|
||||
| %tab_tabprefix_raw% | Raw tabprefix containing raw placeholders |
|
||||
| %tab_tabsuffix_raw% | Raw tabsuffix containing raw placeholders |
|
||||
| %tab_tagprefix_raw% | Raw tagprefix containing raw placeholders |
|
||||
| %tab_tagsuffix_raw% | Raw tagsuffix containing raw placeholders |
|
||||
| %tab_customtabname_raw% | Raw customtabname containing raw placeholders |
|
||||
| %tab_scoreboard_name% | Returns name of player's currently displayed scoreboard or empty string if none is displayed due to no display condition being met |
|
||||
| %tab_scoreboard_visible% | "Enabled" if visible, "Disabled" if not |
|
||||
| %tab_bossbar_visible% | "Enabled" if visible, "Disabled" if not |
|
||||
| %tab_nametag_visibility% | "Enabled" if player can see nametags, "Disabled" if disabled using `/tab nametag toggleview` |
|
||||
| %tab_replace_\<placeholder\>% | Applies [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements) to a PlaceholderAPI placeholder (for example %tab_replace_essentials_vanished%) |
|
||||
| %tab_placeholder_\<placeholder\>% | returns value of tab's internal placeholder (such as %tab_placeholder_animation:name% for %animation:name%) |
|
||||
Full list of placeholders (general + feature specific):
|
||||
|
||||
**General**:
|
||||
|
||||
| Identifier | Description |
|
||||
|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `%tab_replace_<placeholder>%` | Applies [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements) to a PlaceholderAPI placeholder (for example `%tab_replace_essentials_vanished%`) |
|
||||
| `%tab_placeholder_<placeholder>%` | returns value of tab's internal placeholder (such as `%tab_placeholder_animation:name%` for `%animation:name%`) (this also applies [Placeholder output replacements](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements) to them if configured) |
|
||||
|
||||
**BossBar**:
|
||||
|
||||
| Identifier | Description |
|
||||
|-------------------------|-----------------------------------------|
|
||||
| `%tab_bossbar_visible%` | "Enabled" if visible, "Disabled" if not |
|
||||
|
||||
**NameTags**:
|
||||
|
||||
| Identifier | Description |
|
||||
|----------------------------|----------------------------------------------------------------------------------------------|
|
||||
| `%tab_tagprefix%` | Player's current tagprefix with placeholders parsed. |
|
||||
| `%tab_tagsuffix%` | Player's current tagprefix with placeholders parsed. |
|
||||
| `%tab_tagprefix_raw%` | Player's current raw tagprefix with placeholder identifiers. |
|
||||
| `%tab_tagsuffix_raw%` | Player's current raw tagsuffix with placeholder identifiers. |
|
||||
| `%tab_nametag_visibility%` | "Enabled" if player can see nametags, "Disabled" if disabled using `/tab nametag toggleview` |
|
||||
|
||||
**Scoreboard**:
|
||||
|
||||
| Identifier | Description |
|
||||
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `%tab_scoreboard_name%` | Returns name of player's currently displayed scoreboard or empty string if none is displayed due to no display condition being met. |
|
||||
| `%tab_scoreboard_visible%` | "Enabled" if visible, "Disabled" if not (toggled with a command) |
|
||||
|
||||
**Tablist name formatting**:
|
||||
|
||||
| Identifier | Description |
|
||||
|---------------------------|------------------------------------------------------------------|
|
||||
| `%tab_tabprefix%` | Player's current tabprefix with placeholders parsed. |
|
||||
| `%tab_customtabname%` | Player's current customtabname with placeholders parsed. |
|
||||
| `%tab_tabsuffix%` | Player's current tabprefix with placeholders parsed. |
|
||||
| `%tab_tabprefix_raw%` | Player's current raw tabprefix with placeholder identifiers. |
|
||||
| `%tab_customtabname_raw%` | Player's current raw customtabname with placeholder identifiers. |
|
||||
| `%tab_tabsuffix_raw%` | Player's current raw tabsuffix with placeholder identifiers. |
|
||||
|
||||
# Relational placeholders
|
||||
## About
|
||||
@@ -167,22 +190,15 @@ When using a placeholder, you should know if it's a [TAB's internal placeholder]
|
||||
> **Example:** `Sale 100% OFF! %some_placeholder%` -> `Sale 100%% OFF! %some_placeholder%`.
|
||||
|
||||
## Internal placeholder is not working
|
||||
All TAB's internal placeholders are listed above.
|
||||
Some of them are backend only (meaning they won't work on proxy) and some are proxy only.
|
||||
> [!IMPORTANT]
|
||||
> Trying to use a backend-only placeholder on proxy will not work.
|
||||
> If you want to use those,
|
||||
> [set up PlaceholderAPI support on proxy](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge)
|
||||
> and find their PlaceholderAPI equivalent.
|
||||
> For example, you can use %vault_prefix% instead of %vault-prefix%,
|
||||
> %player_health_rounded% instead of %health% and so on.
|
||||
Here are few possible reasons:
|
||||
* You attempted to use a proxy-only placeholder on backend, or backend-only placeholder on proxy
|
||||
* You used the `%` symbol before the placeholder, breaking placeholder starts and ends (see above).
|
||||
|
||||
## PlaceholderAPI placeholder is not working
|
||||
If you have TAB installed on a proxy, make sure you [set up PlaceholderAPI support on proxy](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) as there is no other way to retrieve data from technically a different server.
|
||||
|
||||
> [!TIP]
|
||||
> Use `/papi parse me <placeholder>` to check if the placeholder is working.
|
||||
> If it does not work, the issue is not on TAB's end.
|
||||
Here are few possible reasons:
|
||||
* The placeholder is not working in PlaceholderAPI. You can check this using `/papi parse me <placeholder>`. If it isn't working there, it's not a TAB issue. If the placeholder you used is advertised somewhere, make sure you have the adequate expansion downloaded.
|
||||
* If you have TAB installed on a proxy, make sure [TAB-Bridge](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) is installed on backend servers to forward support to the proxy.
|
||||
* You used the `%` symbol before the placeholder, breaking placeholder starts and ends (see above).
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
This is a quick guide for [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) plugin, which is often referred to on the wiki, and we also get many assistance requests with TAB which turn out to be asking for help with PlaceholderAPI. Whether its documentation is not clear enough, missing information or users just don't read it isn't the topic here to judge, instead, a quick guide with everything needed is provided.
|
||||
|
||||
# Installation
|
||||
PlaceholderAPI is a standalone bukkit plugin that can be installed just like any other plugin.
|
||||
PlaceholderAPI is a standalone Bukkit plugin that can be installed just like any other plugin.
|
||||
It does not support proxies.
|
||||
If you have TAB on a proxy server and wish to use PlaceholderAPI placeholders,
|
||||
you'll need to install [Bridge](https://www.spigotmc.org/resources/83966) plugin on backend servers.
|
||||
|
||||
@@ -20,14 +20,9 @@ Bridge itself has no config file, as everything is configured in the main plugin
|
||||
* Forward player's gamemode for spectator fix feature to work
|
||||
|
||||
# Compatibility
|
||||
Just like with Minecraft, when there is a change in the communication protocol between TAB <-> TAB-Bridge,
|
||||
Just like with Minecraft, when there is a change in the communication protocol between TAB ↔ TAB-Bridge,
|
||||
older software will not be compatible with the new one.
|
||||
You should always be running the latest versions of both TAB and Bridge.
|
||||
|
||||
# Confirming it works
|
||||
You have many ways to verify TAB and Bridge are properly connected. Besides checking if the features above work, the easiest one is to run `/btab debug <player>`.
|
||||
If it's properly connected, you'll get a message saying this:
|
||||

|
||||
If it's not properly connected, you'll get this message:
|
||||

|
||||
If this is the case, it means Bridge is either not installed or versions are not compatible. Update both to the latest version.
|
||||
You have many ways to verify TAB and Bridge are properly connected. Besides checking if the features above work, the easiest one is to run `/btab dump <player>` and check if bridge connection says connected. If not, it means Bridge is either not installed or versions are not compatible. Update both to the latest version.
|
||||
@@ -1,147 +0,0 @@
|
||||
# ---------------------------------------
|
||||
# THIS PAGE IS A WORK IN PROGRESS
|
||||
# ---------------------------------------
|
||||
|
||||
# About
|
||||
This page compares TAB to other plugins offering similar features based on various parameters.
|
||||
|
||||
### Open issues
|
||||
Number of open issues on GitHub. Keep in mind this number shouldn't be taken 100% seriously, because:
|
||||
* People often open issues to ask for help instead of reporting an issue.
|
||||
* If nobody is using a plugin, nobody can find bugs. Bug not found doesn't mean it's not there.
|
||||
* Some issues may be feature requests, which may not be present elsewhere as those are not accepted.
|
||||
|
||||
### Price
|
||||
The price to get the jar. Most plugins are free, some are paid.
|
||||
|
||||
### Minecraft
|
||||
Supported Minecraft versions by the plugin.
|
||||
|
||||
### Platforms
|
||||
Supported platforms by the plugin. Full tracked list includes:
|
||||
* Bukkit 
|
||||
* Sponge 
|
||||
* Fabric 
|
||||
* Forge 
|
||||
* BungeeCord 
|
||||
* Velocity 
|
||||
|
||||
### Async
|
||||
Whether the plugin runs in server's main thread (sync) or in a different thread (async). If a plugin is running sync, it may slow down the server's TPS. All of these features can be done async.
|
||||
|
||||
# Belowname
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) |
|
||||
|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |
|
||||
| **Price** |  |
|
||||
| **Minecraft** | 1.5+ |
|
||||
| **Platforms** |      |
|
||||
| **Async** | ✔ |
|
||||
| PlaceholderAPI support | ✔ |
|
||||
| Configurable number | ✔ |
|
||||
| Configurable text | ✔ |
|
||||
| 1.20.3+ NumberFormat support | ✔ |
|
||||
| Conditionally disable the feature | If any specified condition is met |
|
||||
|
||||
# Bossbar
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [UltraBar](https://github.com/ryandw11/UltraBar) | [AnimaBossBar](https://www.spigotmc.org/resources/bossbar-spigot-free-bossbar-plugin-1-9-1-18-1.59135/) |
|
||||
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  | - |
|
||||
| **Price** |  |  |  |
|
||||
| **Minecraft** | 1.5+ | 1.12+ | 1.9 - 1.18.1 |
|
||||
| **Platforms** |      |  |  |
|
||||
| **Async** | ✔ | ❌ | ❌ |
|
||||
| Announcements | ✔ (defined in config) | ✔ (defined at runtime) | ✔ (defined at runtime) |
|
||||
| Toggle command | ✔ (customizable) | ❌ | ❌ |
|
||||
| Display condition | Anything that has a placeholder | On join / death / command / in worldguard region | ❌ |
|
||||
|
||||
# Global Playerlist
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [BungeeTabListPlus](https://github.com/CodeCrafter47/BungeeTabListPlus) | [Velocitab](https://github.com/WiIIiam278/Velocitab) |
|
||||
|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |  |
|
||||
| **Price** |  |  |  |
|
||||
| **Platforms** |   |  |  |
|
||||
|
||||
# Header/Footer
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [TabList](https://github.com/montlikadani/TabList) |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |
|
||||
| **Price** |  |  |
|
||||
| **Platforms** |      |    |
|
||||
|
||||
# Layout
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [BungeeTabListPlus](https://github.com/CodeCrafter47/BungeeTabListPlus) |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |
|
||||
| **Price** |  |  |
|
||||
| **Platforms** |      |   |
|
||||
|
||||
# Nametags
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [NametagEdit](https://github.com/sgtcaze/NametagEdit) |
|
||||
|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |
|
||||
| **Price** |  |  |
|
||||
| **Minecraft** | 1.5+ | 1.7+ |
|
||||
| **Platforms** |      |  |
|
||||
| **Async** | ✔ | ❌ |
|
||||
| Customization | Per-group<br />Per-player<br />Per-world<br />Per-server | Per-group<br />Per-player |
|
||||
| PlaceholderAPI support | ✔ | ✔ |
|
||||
| Configurable collision | ✔ | ✔ |
|
||||
| Configurable nametag visibility | ✔ | ❌ |
|
||||
| Conditionally disable the feature | If any specified condition is met | ❌ |
|
||||
| Bonus | - Option to use armor stands to bypass<br /> all limits and support multiple lines<br />- Anti-override to avoid random plugins breaking it | - |
|
||||
|
||||
# Per world playerlist
|
||||
|
||||
# Playerlist objective
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [TabList](https://github.com/montlikadani/TabList) |
|
||||
|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |
|
||||
| **Price** |  |  |
|
||||
| **Platforms** |      |   |
|
||||
| Display any number | ✔ | ✔ |
|
||||
| Supports hearts display type | ✔ | ✔ |
|
||||
| 1.20.3+ NumberFormat support | ✔ | ❌ |
|
||||
| Conditionally disable the feature | When any specified condition is met | Disable in worlds |
|
||||
|
||||
# Scoreboard
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [RealScoreboard](https://github.com/joserodpt/RealScoreboard) | [AnimatedScoreboard](https://www.spigotmc.org/resources/20848/) | [SternalBoard](https://github.com/ShieldCommunity/SternalBoard) | [KiteBoard](https://github.com/Niall7459/KiteBoard-Documentation) |
|
||||
|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  | - |  |  |
|
||||
| **Price** |  |  |  |  |  |
|
||||
| **Minecraft** | 1.5+ | 1.13+ | 1.8+ | 1.7+ | 1.8 - 1.20.1 |
|
||||
| **Platforms** |      |  |  |  |  |
|
||||
| **Async** | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| Customizable toggle command | ✔ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 1.20.3+ NumberFormat support | ✔ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Max line length limit (<1.13 / 1.13+) |  /  | - /  |  /  |  /  |  /  |
|
||||
|
||||
| Attribute | [FeatherBoard](https://www.spigotmc.org/resources/featherboard.2691/) | [UltraScoreboards](https://www.spigotmc.org/resources/ultra-scoreboards.93726/) | [QuickBoard](https://github.com/tadeas-drab/QuickBoard) | [SimpleScore](https://github.com/r4g3baby/SimpleScore) | [InfiniteScoreboard](https://www.spigotmc.org/resources/infinite-scoreboard.87425/) |
|
||||
|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Open issues** | - | - |  |  | - |
|
||||
| **Price** |  |  |  |  |  |
|
||||
| **Minecraft** | 1.7+ | 1.8+ | 1.8 - 1.16.1 | 1.8+ | 1.8 - 1.19 |
|
||||
| **Platforms** |  |   |  |  |  |
|
||||
| **Async** | ✔ | ❌ | ❌ | ❌ | ✔ |
|
||||
| Customizable toggle command | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 1.20.3+ NumberFormat support | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Max line length limit (<1.13 / 1.13+) | - /  |  /  |  /  |  /  |  /  |
|
||||
|
||||
# Sorting
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [NametagEdit](https://github.com/sgtcaze/NametagEdit) | [TabList](https://github.com/montlikadani/TabList) | [Velocitab](https://github.com/WiIIiam278/Velocitab) |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |  |  |
|
||||
| **Price** |  |  |  |  |
|
||||
| **Minecraft** | 1.8+ | 1.8+ | 1.8+ | 1.8+ |
|
||||
| **Platforms** |      |  |   |  |
|
||||
| **Async** | ✔ | ❌ | ✔ | ✔ |
|
||||
| Sorting options | Groups<br />Permissions<br />Placeholder alphabetically<br />Placeholder numerically<br />Pre-defined placeholder values | Groups | Groups | Placeholder alphabetically<br />Placeholder numerically |
|
||||
|
||||
# Tablist name formatting
|
||||
| Attribute | [TAB](https://github.com/NEZNAMY/TAB) | [TabList](https://github.com/montlikadani/TabList) | [BungeeTabListPlus](https://github.com/CodeCrafter47/BungeeTabListPlus) | [SimpleTabList](https://github.com/TheYIMIR/SimpleTabList) | [Velocitab](https://github.com/WiIIiam278/Velocitab) |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
|
||||
| **Open issues** |  |  |  |  |  |
|
||||
| **Price** |  |  |  |  |  |
|
||||
| **Minecraft** | 1.8+ | 1.8+ | 1.8+ | 1.16+ | 1.8+ |
|
||||
| **Platforms** |      |    |   |  |  |
|
||||
| **Async** | ✔ | ✔ | ✔ | ❌ | ✔ |
|
||||
@@ -1,6 +1,6 @@
|
||||
## Easy to install
|
||||
Support for all possible Minecraft versions containing features the plugin is able to take advantage of.
|
||||
Support for multiple platforms (Bukkit, BungeeCord, Fabric, Forge, NeoForge, Sponge, Velocity),
|
||||
Support for multiple platforms (Bukkit, BungeeCord, Fabric, Forge, NeoForge, Velocity),
|
||||
with everything contained in a single jar which you can download directly.
|
||||
No need to browse version history, ask what plugin version you need for your server version,
|
||||
visit random websites looking for the download button, or anything similar.
|
||||
|
||||
@@ -8,14 +8,14 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # https://github.com/actions/checkout/releases
|
||||
- uses: actions/checkout@v6 # https://github.com/actions/checkout/releases
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4 # https://github.com/actions/setup-java/releases
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v5 # https://github.com/actions/setup-java/releases
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
java-version: 25
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v4.2.0 # https://github.com/actions/cache/releases
|
||||
with:
|
||||
@@ -31,11 +31,15 @@ jobs:
|
||||
run: ./gradlew clean build
|
||||
- name: Rename JAR with Build Number
|
||||
run: |
|
||||
original_jar=$(find jar/build/libs -type f -name "TAB-*.jar")
|
||||
new_name=$(echo "$original_jar" | sed "s/TAB-\(.*\)\.jar/TAB-\1+${{ github.run_number }}.jar/")
|
||||
mv "$original_jar" "$new_name"
|
||||
find jar/build/libs -type f -name "TAB v*.jar" -print0 | while IFS= read -r -d '' original; do
|
||||
dir=$(dirname "$original")
|
||||
base=$(basename "$original")
|
||||
new_base="${base%.jar}+${{ github.run_number }}.jar"
|
||||
new_path="$dir/$new_base"
|
||||
mv "$original" "$new_path"
|
||||
done
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4.3.3 # https://github.com/actions/upload-artifact/releases
|
||||
with:
|
||||
name: Click here to download
|
||||
path: jar/build/libs/TAB-*+${{ github.run_number }}.jar
|
||||
path: "jar/build/libs/TAB v*+${{ github.run_number }}.jar"
|
||||
@@ -15,7 +15,7 @@ If you want to benefit from a recent change in the code before official release,
|
||||
from [GitHub actions](https://github.com/NEZNAMY/TAB/actions) (you must be logged into GitHub to be able to download it).
|
||||
|
||||
# Compiling
|
||||
Compilation requires JDK 21 and up.
|
||||
Compilation requires JDK 25 and up.
|
||||
To compile the plugin, run `./gradlew build` from the terminal.
|
||||
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
|
||||
The universal jar contains all modules for all supported platforms.
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package me.neznamy.tab.api.event.plugin;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.event.TabEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Called when an unknown placeholder is about to be registered.
|
||||
* It defaults to a PlaceholderAPI implementation, but can be overridden.
|
||||
*/
|
||||
public interface PlaceholderRegisterEvent extends TabEvent {
|
||||
|
||||
/**
|
||||
* Returns placeholder identifier starting and ending with {@code %}
|
||||
*
|
||||
* @return placeholder identifier starting and ending with {@code %}
|
||||
*/
|
||||
@NotNull String getIdentifier();
|
||||
|
||||
/**
|
||||
* Sets placeholder to specified server placeholder implementation.
|
||||
*
|
||||
* @param supplier
|
||||
* Placeholder replacer
|
||||
*/
|
||||
void setServerPlaceholder(@NonNull Supplier<String> supplier);
|
||||
|
||||
/**
|
||||
* Sets placeholder to specified player placeholder implementation.
|
||||
*
|
||||
* @param function
|
||||
* Placeholder replacer
|
||||
*/
|
||||
void setPlayerPlaceholder(@NonNull Function<TabPlayer, String> function);
|
||||
|
||||
/**
|
||||
* Sets placeholder to specified relational placeholder implementation.
|
||||
*
|
||||
* @param function
|
||||
* Placeholder replacer
|
||||
*/
|
||||
void setRelationalPlaceholder(@NonNull BiFunction<TabPlayer, TabPlayer, String> function);
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An interface allowing placeholder registration. Instance can be
|
||||
@@ -65,6 +67,14 @@ public interface PlaceholderManager {
|
||||
*/
|
||||
@NotNull RelationalPlaceholder registerRelationalPlaceholder(@NonNull String identifier, int refresh, @NonNull BiFunction<TabPlayer, TabPlayer, String> function);
|
||||
|
||||
void registerServerPlaceholder(@NonNull Pattern identifier, int refresh, @NonNull Function<Matcher, Supplier<String>> function);
|
||||
|
||||
void registerPlayerPlaceholder(@NonNull Pattern identifier, int refresh,
|
||||
@NonNull Function<Matcher, Function<me.neznamy.tab.api.TabPlayer, String>> function);
|
||||
|
||||
void registerRelationalPlaceholder(@NonNull Pattern identifier, int refresh,
|
||||
@NonNull Function<Matcher, BiFunction<me.neznamy.tab.api.TabPlayer, me.neznamy.tab.api.TabPlayer, String>> function);
|
||||
|
||||
/**
|
||||
* Returns placeholder from specified identifier. If it does not exist, it is registered
|
||||
* as a PlaceholderAPI placeholder and returned.
|
||||
|
||||
@@ -84,8 +84,24 @@ public interface Scoreboard {
|
||||
*/
|
||||
void removeLine(int index);
|
||||
|
||||
/**
|
||||
* Replaces all current lines with the provided list.
|
||||
* Supports placeholders, which will automatically be refreshed.
|
||||
* Client can only see up to 15 lines at a time.
|
||||
* If the new list is identical to the current one, nothing happens.
|
||||
*
|
||||
* @param lines
|
||||
* New list of lines to display
|
||||
* @throws NullPointerException
|
||||
* if {@code lines} is {@code null}
|
||||
* @see #getLines()
|
||||
* @see #addLine(String)
|
||||
* @see #removeLine(int)
|
||||
*/
|
||||
void setLines(@NonNull List<String> lines);
|
||||
|
||||
/**
|
||||
* Unregisters this scoreboard from all players who can see it.
|
||||
*/
|
||||
void unregister();
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,20 @@ public interface Layout {
|
||||
*/
|
||||
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping);
|
||||
|
||||
/**
|
||||
* Adds fixed slot with specified parameters
|
||||
*
|
||||
* @param slot
|
||||
* Slot (1-80)
|
||||
* @param text
|
||||
* Text to display
|
||||
* @param skin
|
||||
* Skin definition like in config
|
||||
* @param ping
|
||||
* Slot's ping, accepts placeholders that evaluate to a number
|
||||
*/
|
||||
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, @NonNull String ping);
|
||||
|
||||
/**
|
||||
* Adds a player group.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,18 @@ public interface LayoutManager {
|
||||
@NotNull
|
||||
Layout createNewLayout(@NonNull String name);
|
||||
|
||||
/**
|
||||
* Creates new layout with given name and slot count (1.19.3+ only).
|
||||
*
|
||||
* @param name
|
||||
* Unique layout name
|
||||
* @param slotCount
|
||||
* Amount of slots to use for 1.19.3+ players
|
||||
* @return Created layout
|
||||
*/
|
||||
@NotNull
|
||||
Layout createNewLayout(@NonNull String name, int slotCount);
|
||||
|
||||
/**
|
||||
* Returns defined layout from config by name. If no such layout is defined in config,
|
||||
* {@code null} is returned.
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.2.2")
|
||||
implementation("io.freefair.gradle:lombok-plugin:9.0.0")
|
||||
implementation("dev.architectury.loom:dev.architectury.loom.gradle.plugin:1.13-SNAPSHOT")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.4.1")
|
||||
implementation("io.freefair.gradle:lombok-plugin:9.2.0")
|
||||
implementation("dev.architectury.loom-no-remap:dev.architectury.loom-no-remap.gradle.plugin:1.14-SNAPSHOT")
|
||||
}
|
||||
@@ -13,7 +13,8 @@ tasks {
|
||||
"version" to project.version,
|
||||
"description" to project.description,
|
||||
"website" to rootProject.ext.get("website")!!.toString(),
|
||||
"author" to rootProject.ext.get("author")!!.toString()
|
||||
"author" to rootProject.ext.get("author")!!.toString(),
|
||||
"credits" to rootProject.ext.get("credits")!!.toString()
|
||||
))
|
||||
}
|
||||
javadoc {
|
||||
@@ -31,6 +32,6 @@ tasks {
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "5.4.0"
|
||||
version = "6.0.2"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
@@ -56,11 +56,12 @@ val platformPaths = setOf(
|
||||
":bukkit:v1_21_R5",
|
||||
":bukkit:v1_21_R6",
|
||||
":bukkit:v1_21_R7",
|
||||
":bukkit:v26_1",
|
||||
":bungeecord",
|
||||
":velocity",
|
||||
":fabric",
|
||||
":neoforge",
|
||||
":forge"
|
||||
":neoforge"
|
||||
// ":forge"
|
||||
)
|
||||
|
||||
val specialPaths = setOf(
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_20_5;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
|
||||
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperComponentConverter extends ComponentConverter<Component> {
|
||||
public class NMSComponentConverter extends ComponentConverter<Component> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
+14
-7
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_13_R1;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
@@ -7,12 +7,13 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_13_R1.entity.CraftPlayer;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.13.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for paper 1.20.5 - 1.21.1.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
@@ -34,12 +35,18 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_20_5;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -25,7 +25,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
@@ -39,7 +39,7 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public PaperPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_20_5;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
@@ -23,7 +23,7 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
|
||||
@@ -37,7 +37,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -87,11 +87,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
@@ -119,6 +114,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
boolean listed = nmsData.listed();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
@@ -138,11 +134,17 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
nmsData.profileId(), nmsData.profile(), nmsData.listed(), latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
|
||||
for (UUID id : remove.profileIds()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
|
||||
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
|
||||
tablistEntries.add(nmsData.profileId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper_1_20_5;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for versions 1.20.5 - 1.21.1.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import net.minecraft.network.chat.*;
|
||||
import net.minecraft.network.chat.contents.objects.AtlasSprite;
|
||||
import net.minecraft.network.chat.contents.objects.PlayerSprite;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.component.ResolvableProfile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
public class NMSComponentConverter extends ComponentConverter<Component> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Component newTextComponent(@NotNull String text) {
|
||||
return Component.literal(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Component newTranslatableComponent(@NotNull String key) {
|
||||
return Component.translatable(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Component newKeybindComponent(@NotNull String keybind) {
|
||||
return Component.keybind(keybind);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
|
||||
return Component.object(new AtlasSprite(Identifier.parse(sprite.getAtlas()), Identifier.parse(sprite.getSprite())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
|
||||
ResolvableProfile profile;
|
||||
if (sprite.getId() != null) {
|
||||
profile = ResolvableProfile.createUnresolved(sprite.getId());
|
||||
} else if (sprite.getName() != null) {
|
||||
profile = ResolvableProfile.createUnresolved(sprite.getName());
|
||||
} else if (sprite.getSkin() != null) {
|
||||
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
|
||||
builder.put(TabList.TEXTURES_PROPERTY, new Property(TabList.TEXTURES_PROPERTY, sprite.getSkin().getValue(), sprite.getSkin().getSignature()));
|
||||
profile = ResolvableProfile.createResolved(new GameProfile(NIL_UUID, "", new PropertyMap(builder.build())));
|
||||
} else {
|
||||
throw new IllegalStateException("Player head component does not have id, name or skin set");
|
||||
}
|
||||
return Component.object(new PlayerSprite(profile, sprite.isShowHat()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
|
||||
Style style = Style.EMPTY
|
||||
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
|
||||
.withBold(modifier.getBold())
|
||||
.withItalic(modifier.getItalic())
|
||||
.withUnderlined(modifier.getUnderlined())
|
||||
.withStrikethrough(modifier.getStrikethrough())
|
||||
.withObfuscated(modifier.getObfuscated())
|
||||
.withFont(modifier.getFont() == null ? null : new FontDescription.Resource(Identifier.parse(modifier.getFont())));
|
||||
if (modifier.getShadowColor() != null) style = style.withShadowColor(modifier.getShadowColor()); // withShadowColor takes int instead of Integer, bug?
|
||||
((MutableComponent)nmsComponent).setStyle(style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSibling(@NotNull Component parent, @NotNull Component child) {
|
||||
((MutableComponent)parent).append(child);
|
||||
}
|
||||
}
|
||||
+13
-6
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_18_R1;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
@@ -7,12 +7,13 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.18 - 1.18.1.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.11.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
@@ -34,8 +35,14 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().b.a.k;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.numbers.FixedFormat;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.world.scores.PlayerTeam;
|
||||
import net.minecraft.world.scores.Scoreboard;
|
||||
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
|
||||
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player.
|
||||
*
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
|
||||
dummyScoreboard,
|
||||
objective.getName(),
|
||||
ObjectiveCriteria.DUMMY,
|
||||
objective.getTitle().convert(),
|
||||
RenderType.values()[objective.getHealthDisplay().ordinal()],
|
||||
false,
|
||||
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
|
||||
);
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySlot(@NonNull Objective objective) {
|
||||
sendPacket(new ClientboundSetDisplayObjectivePacket(
|
||||
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
|
||||
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObjective(@NonNull Objective objective) {
|
||||
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
|
||||
obj.setDisplayName(objective.getTitle().convert());
|
||||
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
|
||||
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScore(@NonNull Score score) {
|
||||
sendPacket(new ClientboundSetScorePacket(
|
||||
score.getHolder(),
|
||||
score.getObjective().getName(),
|
||||
score.getValue(),
|
||||
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
|
||||
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NonNull Score score) {
|
||||
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object createTeam(@NonNull String name) {
|
||||
return new PlayerTeam(dummyScoreboard, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
|
||||
t.getPlayers().addAll(team.getPlayers());
|
||||
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(@NonNull Team team) {
|
||||
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam(@NonNull Team team) {
|
||||
updateTeamProperties(team);
|
||||
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
|
||||
}
|
||||
|
||||
private void updateTeamProperties(@NonNull Team team) {
|
||||
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
|
||||
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
|
||||
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
|
||||
t.setColor(formats[team.getColor().ordinal()]);
|
||||
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
|
||||
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
|
||||
t.setPlayerPrefix(team.getPrefix().convert());
|
||||
t.setPlayerSuffix(team.getSuffix().convert());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
|
||||
}
|
||||
if (packet instanceof ClientboundSetObjectivePacket objective) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
|
||||
}
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int action = getMethod(team);
|
||||
if (action != TeamAction.UPDATE) {
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
|
||||
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
|
||||
return 0;
|
||||
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
|
||||
return 1;
|
||||
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
|
||||
return 3;
|
||||
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
|
||||
return 4;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
|
||||
}
|
||||
}
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_11;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
@@ -24,7 +24,7 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
|
||||
@@ -40,7 +40,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -90,11 +90,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
@@ -122,6 +117,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
boolean listed = nmsData.listed();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
@@ -141,11 +137,17 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().name());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
nmsData.profileId(), nmsData.profile(), nmsData.listed(), latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
|
||||
for (UUID id : remove.profileIds()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
|
||||
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
|
||||
tablistEntries.add(nmsData.profileId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_11;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for version 1.21.11.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_2;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
|
||||
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
@@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperComponentConverter extends ComponentConverter<Component> {
|
||||
public class NMSComponentConverter extends ComponentConverter<Component> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
+14
-7
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_16_R1;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
@@ -7,12 +7,13 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.16 - 1.16.1.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.2 - 1.21.3.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
@@ -34,12 +35,18 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_2;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -25,7 +25,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
@@ -39,7 +39,7 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public PaperPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_2;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
@@ -23,7 +23,7 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
|
||||
@@ -38,7 +38,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -88,11 +88,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
@@ -120,6 +115,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
boolean listed = nmsData.listed();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
@@ -139,11 +135,17 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
nmsData.profileId(), nmsData.profile(), nmsData.listed(), latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
|
||||
for (UUID id : remove.profileIds()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
|
||||
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
|
||||
tablistEntries.add(nmsData.profileId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_2;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for versions 1.21.2 - 1.21.3.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_4;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
|
||||
|
||||
import me.neznamy.tab.shared.chat.TabStyle;
|
||||
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperComponentConverter extends ComponentConverter<Component> {
|
||||
public class NMSComponentConverter extends ComponentConverter<Component> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
+14
-7
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_16_R2;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
@@ -7,12 +7,13 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct NMS code for 1.16.2 - 1.16.3.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.4 - 1.21.8.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
@@ -34,12 +35,18 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return ((CraftPlayer)player.getPlayer()).getHandle().ping;
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_11;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -25,7 +25,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
@@ -39,7 +39,7 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public PaperPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_4;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
@@ -23,7 +23,7 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
|
||||
@@ -39,7 +39,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -89,11 +89,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
@@ -121,6 +116,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
boolean listed = nmsData.listed();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
@@ -140,11 +136,17 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
nmsData.profileId(), nmsData.profile(), nmsData.listed(), latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
|
||||
for (UUID id : remove.profileIds()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
|
||||
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
|
||||
tablistEntries.add(nmsData.profileId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_4;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for versions 1.21.4 - 1.21.8.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_9;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
@@ -19,7 +19,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Component converter using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperComponentConverter extends ComponentConverter<Component> {
|
||||
public class NMSComponentConverter extends ComponentConverter<Component> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.9 - 1.21.10.
|
||||
*/
|
||||
@Getter
|
||||
public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new NMSPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_9;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -25,7 +25,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* Scoreboard implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
private static final ChatFormatting[] formats = ChatFormatting.values();
|
||||
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
|
||||
@@ -39,7 +39,7 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this scoreboard will belong to
|
||||
*/
|
||||
public PaperPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_9;
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
@@ -25,7 +25,7 @@ import java.util.*;
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
|
||||
@@ -41,7 +41,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
@@ -123,6 +118,7 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
boolean listed = nmsData.listed();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
@@ -142,11 +138,17 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
|
||||
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
|
||||
listed = false;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().name());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
nmsData.profileId(), nmsData.profile(), nmsData.listed(), latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
|
||||
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
|
||||
for (UUID id : remove.profileIds()) {
|
||||
tablistEntries.remove(id);
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
|
||||
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
|
||||
tablistEntries.add(nmsData.profileId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package me.neznamy.tab.platforms.paper_1_21_9;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for versions 1.21.9 - 1.21.10.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@NotNull
|
||||
private final ComponentConverter<?> componentConverter = new PaperComponentConverter();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketScoreboard(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList newTabList(@NotNull BukkitTabPlayer player) {
|
||||
return new PaperPacketTabList(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().connection.connection.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing(@NotNull BukkitTabPlayer player) {
|
||||
return player.getPlayer().getPing();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.platform.EventListener;
|
||||
@@ -16,8 +17,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* The core for bukkit forwarding events into all enabled features
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitEventListener implements EventListener<Player>, Listener {
|
||||
|
||||
/** Platform reference */
|
||||
private final BukkitPlatform platform;
|
||||
|
||||
/**
|
||||
* Listens to player quit event.
|
||||
*
|
||||
@@ -37,6 +42,10 @@ public class BukkitEventListener implements EventListener<Player>, Listener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
TAB.getInstance().addTablistTracker(
|
||||
e.getPlayer().getUniqueId(),
|
||||
platform.getServerVersionInfo().getImplementationProvider().newTabListEntryTracker(e.getPlayer())
|
||||
);
|
||||
join(e.getPlayer());
|
||||
}
|
||||
|
||||
@@ -54,6 +63,6 @@ public class BukkitEventListener implements EventListener<Player>, Listener {
|
||||
@Override
|
||||
@NotNull
|
||||
public TabPlayer createPlayer(@NotNull Player player) {
|
||||
return new BukkitTabPlayer((BukkitPlatform) TAB.getInstance().getPlatform(), player);
|
||||
return new BukkitTabPlayer(platform, player);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
|
||||
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
@@ -21,8 +20,7 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
protected Channel getChannel(@NotNull TabPlayer player) {
|
||||
return ((BukkitPlatform)player.getPlatform()).getImplementationProvider().getChannelFunction().apply((BukkitTabPlayer) player);
|
||||
return ((BukkitPlatform)player.getPlatform()).getServerVersionInfo().getImplementationProvider().getChannel(((BukkitTabPlayer) player).getPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,13 @@ public class BukkitTAB extends JavaPlugin {
|
||||
boolean folia = ReflectionUtils.classExists("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
try {
|
||||
TAB.create(folia ? new FoliaPlatform(this) : new BukkitPlatform(this));
|
||||
} catch (UnsupportedOperationException e) {
|
||||
} catch (IllegalStateException e) {
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] ================================================================================");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion().split("-")[0] + ") is not supported. " +
|
||||
"This jar only supports 1.7 - 1.21.11. If you just updated to a new Minecraft version, check for TAB updates.");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion() + ") is not supported.");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.9.4 - 1.15.x, 1.16.5, 1.17.1, 1.18.2 - 26.1.2");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you just updated to a new Minecraft version, check for TAB updates.");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you are using an unsupported 1.x version, use an older version of TAB (latest TAB 5.x supports all MC 1.x versions).");
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] Thrown error message: " + e.getMessage());
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] ================================================================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class BukkitTabCommand implements CommandExecutor, TabCompleter {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (TAB.getInstance().isPluginDisabled()) {
|
||||
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(((BukkitPlatform)TAB.getInstance().getPlatform()).toBukkitFormat(TabComponent.fromColoredText(message)));
|
||||
for (TabComponent message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(((BukkitPlatform)TAB.getInstance().getPlatform()).toBukkitFormat(message));
|
||||
}
|
||||
} else {
|
||||
TabPlayer p = null;
|
||||
|
||||
@@ -25,7 +25,7 @@ public class BukkitTabPlayer extends BackendTabPlayer {
|
||||
* bukkit player
|
||||
*/
|
||||
public BukkitTabPlayer(@NotNull BukkitPlatform platform, @NotNull Player p) {
|
||||
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersion().getNetworkId());
|
||||
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersionInfo().getServerVersion().getNetworkId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,7 +35,7 @@ public class BukkitTabPlayer extends BackendTabPlayer {
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
return getPlatform().getImplementationProvider().getPing(this);
|
||||
return getPlatform().getServerVersionInfo().getImplementationProvider().getPing(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class ServerVersionInfo {
|
||||
|
||||
/** Package name of the server implementation, null on Paper 1.20.5+ / Spigot 26+ */
|
||||
@Nullable
|
||||
private final String serverPackage;
|
||||
|
||||
/** Minecraft version as returned by the platform */
|
||||
@NotNull
|
||||
private final String minecraftVersion;
|
||||
|
||||
@NotNull
|
||||
private final String serverName;
|
||||
|
||||
/** Server version */
|
||||
private final ProtocolVersion serverVersion;
|
||||
|
||||
/** Implementation for creating new instances using content available on the server */
|
||||
@NotNull
|
||||
@Setter
|
||||
private ImplementationProvider implementationProvider;
|
||||
|
||||
/**
|
||||
* Constructs new instance and detects server version info.
|
||||
*/
|
||||
public ServerVersionInfo() {
|
||||
// Server package
|
||||
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
|
||||
serverPackage = array.length > 3 ? array[3] : null;
|
||||
|
||||
// Minecraft version
|
||||
if (ReflectionUtils.methodExists(Bukkit.class, "getMinecraftVersion")) {
|
||||
serverName = "Paper";
|
||||
minecraftVersion = Bukkit.getMinecraftVersion();
|
||||
} else {
|
||||
serverName = "Spigot";
|
||||
minecraftVersion = Bukkit.getBukkitVersion().split("-")[0];
|
||||
}
|
||||
|
||||
// Minecraft version to server version
|
||||
serverVersion = ProtocolVersion.fromFriendlyName(minecraftVersion);
|
||||
|
||||
// Find implementation
|
||||
implementationProvider = findImplementationProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds implementation provider for current server software and version.
|
||||
*
|
||||
* @return Implementation provider for current server
|
||||
* @throws IllegalStateException
|
||||
* If no implementation was found
|
||||
*/
|
||||
@NotNull
|
||||
private ImplementationProvider findImplementationProvider() {
|
||||
if (serverPackage != null) {
|
||||
// Paper <1.20.5 or Spigot 1.x
|
||||
try {
|
||||
// Does not actually support flat 1.19, but whatever, no one is using it anyway
|
||||
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + serverPackage + ".NMSImplementationProvider").getConstructor().newInstance();
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Your server version (%s - %s) is no longer supported.",
|
||||
minecraftVersion, serverPackage
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Paper 1.20.5+ or Spigot 26+
|
||||
Map<ProtocolVersion, String> spigotVersions = new LinkedHashMap<>();
|
||||
spigotVersions.put(ProtocolVersion.V26_1, "v26_1");
|
||||
spigotVersions.put(ProtocolVersion.V26_1_1, "v26_1");
|
||||
spigotVersions.put(ProtocolVersion.V26_1_2, "v26_1");
|
||||
|
||||
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
|
||||
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
|
||||
paperVersions.put(ProtocolVersion.V1_20_6, "paper_1_20_5");
|
||||
paperVersions.put(ProtocolVersion.V1_21, "paper_1_20_5");
|
||||
paperVersions.put(ProtocolVersion.V1_21_1, "paper_1_20_5");
|
||||
paperVersions.put(ProtocolVersion.V1_21_2, "paper_1_21_2");
|
||||
paperVersions.put(ProtocolVersion.V1_21_3, "paper_1_21_2");
|
||||
paperVersions.put(ProtocolVersion.V1_21_4, "paper_1_21_4");
|
||||
paperVersions.put(ProtocolVersion.V1_21_5, "paper_1_21_4");
|
||||
paperVersions.put(ProtocolVersion.V1_21_6, "paper_1_21_4");
|
||||
paperVersions.put(ProtocolVersion.V1_21_7, "paper_1_21_4");
|
||||
paperVersions.put(ProtocolVersion.V1_21_8, "paper_1_21_4");
|
||||
paperVersions.put(ProtocolVersion.V1_21_9, "paper_1_21_9");
|
||||
paperVersions.put(ProtocolVersion.V1_21_10, "paper_1_21_9");
|
||||
paperVersions.put(ProtocolVersion.V1_21_11, "paper_1_21_11");
|
||||
paperVersions.put(ProtocolVersion.V26_1, "paper_1_21_11"); // v26_1 works too
|
||||
paperVersions.put(ProtocolVersion.V26_1_1, "paper_1_21_11"); // v26_1 works too
|
||||
paperVersions.put(ProtocolVersion.V26_1_2, "paper_1_21_11"); // v26_1 works too
|
||||
|
||||
if (serverVersion == ProtocolVersion.UNKNOWN) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Unknown server version (%s), can not find implementation.",
|
||||
minecraftVersion
|
||||
));
|
||||
}
|
||||
String implementation = (serverName.equals("Paper") ? paperVersions : spigotVersions).get(serverVersion);
|
||||
if (implementation == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Your server version (%s %s) is no longer supported.",
|
||||
serverName, minecraftVersion
|
||||
));
|
||||
}
|
||||
try {
|
||||
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + implementation + ".NMSImplementationProvider").getConstructor().newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Your server version (%s %s) is marked as compatible, but the implementation does not exist. This is probably a bug.",
|
||||
serverName, minecraftVersion
|
||||
), e);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Failed to initialize implementation for %s %s. This is probably a bug.",
|
||||
serverName, minecraftVersion
|
||||
), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BossBar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* BossBar for 1.9+ servers where Bukkit API is used. If ViaVersion is used
|
||||
* to allow 1.8 players, it will handle the entity and teleporting by itself.
|
||||
@@ -32,7 +34,7 @@ public class BukkitBossBar extends SafeBossBar<BossBar> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public BossBar constructBossBar(@NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
public BossBar constructBossBar(@NotNull UUID id, @NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
BossBar bar = Bukkit.createBossBar(
|
||||
player.getPlatform().toBukkitFormat(title),
|
||||
org.bukkit.boss.BarColor.valueOf(color.name()),
|
||||
@@ -43,7 +45,7 @@ public class BukkitBossBar extends SafeBossBar<BossBar> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
|
||||
public void show(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().addPlayer(player.getPlayer());
|
||||
}
|
||||
|
||||
@@ -68,7 +70,7 @@ public class BukkitBossBar extends SafeBossBar<BossBar> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
|
||||
public void hide(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().removePlayer(player.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.decorators.SafeBossBar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Handler for 1.9+ players on 1.8 server using ViaVersion API.
|
||||
*/
|
||||
@@ -27,7 +29,7 @@ public class ViaBossBar extends SafeBossBar<BossBar> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public BossBar constructBossBar(@NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
public BossBar constructBossBar(@NotNull UUID id, @NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
return Via.getAPI().legacyAPI().createLegacyBossBar(
|
||||
title.toLegacyText(),
|
||||
progress,
|
||||
@@ -37,7 +39,7 @@ public class ViaBossBar extends SafeBossBar<BossBar> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(@NotNull BossBarInfo bar) {
|
||||
public void show(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().addPlayer(player.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@@ -62,7 +64,7 @@ public class ViaBossBar extends SafeBossBar<BossBar> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull BossBarInfo bar) {
|
||||
public void hide(@NotNull BossBarInfo bar) {
|
||||
bar.getBossBar().removePlayer(player.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
+35
-10
@@ -3,10 +3,13 @@ package me.neznamy.tab.platforms.bukkit.features;
|
||||
import lombok.Getter;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.clip.placeholderapi.expansion.Relational;
|
||||
import me.neznamy.tab.api.placeholder.Placeholder;
|
||||
import me.neznamy.tab.shared.ProjectVariables;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
|
||||
import me.neznamy.tab.shared.placeholders.types.RelationalPlaceholderImpl;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -21,7 +24,7 @@ import java.util.List;
|
||||
* TAB's expansion for PlaceholderAPI
|
||||
*/
|
||||
@Getter
|
||||
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
|
||||
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion, Relational {
|
||||
|
||||
/** List of all placeholders offered by the plugin for command suggestions */
|
||||
@NotNull
|
||||
@@ -95,19 +98,41 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
|
||||
String requestedPlaceholder = "%" + identifier.substring("placeholder_".length()) + "%";
|
||||
PlaceholderManagerImpl pm = TAB.getInstance().getPlaceholderManager();
|
||||
pm.addUsedPlaceholder(requestedPlaceholder, pm);
|
||||
return pm.getPlaceholder(requestedPlaceholder).getLastValue(p);
|
||||
return pm.getPlaceholder(requestedPlaceholder).parse(p);
|
||||
}
|
||||
return p.expansionValues.get(identifier);
|
||||
return p.expansionData.getValue(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaceholderValue(@NotNull TabPlayer player, @NotNull String placeholder, @NotNull String value) {
|
||||
// Do not unnecessarily do all placeholders, just retrieve them on request to save resources by not using string builder all the time
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value) {
|
||||
player.expansionValues.put(key, value);
|
||||
public String onPlaceholderRequest(@NotNull Player viewer, @NotNull Player target, @NotNull String identifier) {
|
||||
if (identifier.startsWith("replace_")) {
|
||||
String text = "%" + identifier.substring(8) + "%";
|
||||
String textBefore;
|
||||
do {
|
||||
textBefore = text;
|
||||
for (String placeholder : PlaceholderManagerImpl.detectPlaceholders(text)) {
|
||||
text = text.replace(placeholder, TAB.getInstance().getPlaceholderManager().findReplacement(placeholder,
|
||||
PlaceholderAPI.setRelationalPlaceholders(viewer, target, placeholder)));
|
||||
}
|
||||
} while (!textBefore.equals(text));
|
||||
return text;
|
||||
}
|
||||
if (identifier.startsWith("placeholder_")) {
|
||||
String requestedPlaceholder = "%" + identifier.substring("placeholder_".length()) + "%";
|
||||
PlaceholderManagerImpl pm = TAB.getInstance().getPlaceholderManager();
|
||||
pm.addUsedPlaceholder(requestedPlaceholder, pm);
|
||||
Placeholder placeholder = pm.getPlaceholder(requestedPlaceholder);
|
||||
if (placeholder instanceof RelationalPlaceholderImpl) {
|
||||
RelationalPlaceholderImpl rel = (RelationalPlaceholderImpl) placeholder;
|
||||
TabPlayer v = TAB.getInstance().getPlayer(viewer.getUniqueId());
|
||||
TabPlayer t = TAB.getInstance().getPlayer(target.getUniqueId());
|
||||
if (v == null || t == null) return "<Player is not loaded>";
|
||||
return rel.getLastValue(v, t);
|
||||
} else {
|
||||
return "<Not a relational placeholder: " + requestedPlaceholder + ">";
|
||||
}
|
||||
}
|
||||
return "<Unknown identifier: \"" + identifier + "\">";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+44
-6
@@ -7,6 +7,9 @@ import me.neznamy.tab.shared.features.PerWorldPlayerListConfiguration;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.TabFeature;
|
||||
import me.neznamy.tab.shared.features.types.UnLoadable;
|
||||
import me.neznamy.tab.shared.features.types.VanishListener;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -14,6 +17,8 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -24,7 +29,7 @@ import java.util.Map.Entry;
|
||||
* Per-world-PlayerList feature handler
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PerWorldPlayerList extends TabFeature implements Listener, Loadable, UnLoadable {
|
||||
public class PerWorldPlayerList extends TabFeature implements Listener, Loadable, UnLoadable, VanishListener {
|
||||
|
||||
/** Reference to platform */
|
||||
@NotNull
|
||||
@@ -34,6 +39,9 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
|
||||
@NotNull
|
||||
private final PerWorldPlayerListConfiguration configuration;
|
||||
|
||||
/** Check for presence of modern (1.12.2+) methods that take plugin as argument to avoid conflict */
|
||||
private final boolean modernMethodsAvailable = ReflectionUtils.methodExists(Player.class, "hidePlayer", Plugin.class, Player.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance and registers events.
|
||||
*
|
||||
@@ -61,7 +69,7 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
|
||||
public void unload() {
|
||||
for (Player p : platform.getOnlinePlayers()) {
|
||||
for (Player pl : platform.getOnlinePlayers()) {
|
||||
p.showPlayer(pl);
|
||||
showPlayer(p, pl);
|
||||
}
|
||||
}
|
||||
HandlerList.unregisterAll(this);
|
||||
@@ -104,10 +112,32 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
|
||||
private void checkPlayer(@NotNull Player p) {
|
||||
for (Player all : platform.getOnlinePlayers()) {
|
||||
if (all == p) continue;
|
||||
if (!shouldSee(p, all) && p.canSee(all)) p.hidePlayer(all);
|
||||
if (shouldSee(p, all) && !p.canSee(all)) p.showPlayer(all);
|
||||
if (!shouldSee(all, p) && all.canSee(p)) all.hidePlayer(p);
|
||||
if (shouldSee(all, p) && !all.canSee(p)) all.showPlayer(p);
|
||||
if (shouldSee(p, all)) {
|
||||
showPlayer(p, all);
|
||||
} else {
|
||||
hidePlayer(p, all);
|
||||
}
|
||||
if (shouldSee(all, p)) {
|
||||
showPlayer(all, p);
|
||||
} else {
|
||||
hidePlayer(all, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hidePlayer(@NotNull Player viewer, @NotNull Player target) {
|
||||
if (modernMethodsAvailable) {
|
||||
viewer.hidePlayer(platform.getPlugin(), target);
|
||||
} else {
|
||||
viewer.hidePlayer(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void showPlayer(@NotNull Player viewer, @NotNull Player target) {
|
||||
if (modernMethodsAvailable) {
|
||||
viewer.showPlayer(platform.getPlugin(), target);
|
||||
} else {
|
||||
viewer.showPlayer(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +151,9 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
|
||||
*/
|
||||
private boolean shouldSee(@NotNull Player viewer, @NotNull Player target) {
|
||||
if (target == viewer) return true;
|
||||
for (MetadataValue v : target.getMetadata("vanished")) {
|
||||
if (v.asBoolean() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return false;
|
||||
}
|
||||
if ((configuration.isAllowBypassPermission() && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || configuration.getIgnoredWorlds().contains(viewer.getWorld().getName())) return true;
|
||||
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
|
||||
String targetWorldGroup = target.getWorld().getName() + "-default";
|
||||
@@ -138,4 +171,9 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
|
||||
public String getFeatureName() {
|
||||
return "Per world PlayerList";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVanishStatusChange(@NotNull TabPlayer player) {
|
||||
platform.runSync((Player) player.getPlayer(), () -> checkPlayer((Player) player.getPlayer()));
|
||||
}
|
||||
}
|
||||
+71
-112
@@ -1,23 +1,17 @@
|
||||
package me.neznamy.tab.platforms.bukkit.platform;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitEventListener;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitPipelineInjector;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabCommand;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.*;
|
||||
import me.neznamy.tab.platforms.bukkit.bossbar.BukkitBossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.bossbar.ViaBossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
|
||||
import me.neznamy.tab.platforms.bukkit.hook.BukkitPremiumVanishHook;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.GroupManager;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.*;
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform;
|
||||
import me.neznamy.tab.shared.chat.TabTextColor;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
@@ -46,10 +40,14 @@ import net.milkbowl.vault.permission.Permission;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -57,7 +55,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
@@ -69,8 +67,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@NotNull
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/** Server version */
|
||||
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
|
||||
/** Information about server version */
|
||||
@NotNull
|
||||
private final ServerVersionInfo serverVersionInfo = new ServerVersionInfo();
|
||||
|
||||
/** Variables checking presence of other plugins to hook into */
|
||||
private final boolean placeholderAPI = ReflectionUtils.classExists("me.clip.placeholderapi.PlaceholderAPI");
|
||||
@@ -84,15 +83,6 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
/** Detection for presence of Paper's MSPT getter */
|
||||
private final boolean paperMspt = ReflectionUtils.methodExists(Bukkit.class, "getAverageTickTime");
|
||||
|
||||
/** Package name of the server implementation, null on Paper 1.20.5+ */
|
||||
@Nullable
|
||||
private final String serverPackage;
|
||||
|
||||
/** Implementation for creating new instances using content available on the server */
|
||||
@NotNull
|
||||
@Setter
|
||||
private ImplementationProvider implementationProvider;
|
||||
|
||||
private final boolean modernOnlinePlayers;
|
||||
|
||||
/** Command map for dynamic command registering */
|
||||
@@ -113,10 +103,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
public BukkitPlatform(@NotNull JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
modernOnlinePlayers = Bukkit.class.getMethod("getOnlinePlayers").getReturnType() == Collection.class;
|
||||
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
|
||||
serverPackage = array.length > 3 ? array[3] : null;
|
||||
implementationProvider = findImplementationProvider();
|
||||
logInfo(new TabTextComponent("Found NMS implementation: " + serverVersionInfo.getImplementationProvider().getClass().getName(), TabTextColor.GRAY));
|
||||
try {
|
||||
Object server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
|
||||
recentTps = ((double[]) server.getClass().getField("recentTps").get(server));
|
||||
@@ -130,63 +117,6 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
knownCommands = (Map<String, Command>) ReflectionUtils.getField(SimpleCommandMap.class, "knownCommands").get(commandMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private ImplementationProvider findImplementationProvider() {
|
||||
if (serverPackage == null) {
|
||||
// Paper 1.20.5+, check for available module
|
||||
String paperModule = getPaperModule();
|
||||
if (paperModule != null) {
|
||||
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.paper_" + paperModule + ".PaperImplementationProvider").getConstructor().newInstance();
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
} else {
|
||||
// Paper <1.20.5 or Spigot
|
||||
try {
|
||||
// Does not actually support flat 1.19, but whatever, no one is using it anyway
|
||||
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + serverPackage + ".NMSImplementationProvider").getConstructor().newInstance();
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the paper module that can be used on this server.
|
||||
* If this server is not using paper or no module is available for any other reason,
|
||||
* {@code null} is returned.
|
||||
*
|
||||
* @return Name of the available paper module or {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
private String getPaperModule() {
|
||||
if (!ReflectionUtils.classExists("org.bukkit.craftbukkit.CraftServer")) return null;
|
||||
switch (serverVersion) {
|
||||
case V1_20_5:
|
||||
case V1_20_6:
|
||||
case V1_21:
|
||||
case V1_21_1:
|
||||
return "1_20_5";
|
||||
case V1_21_2:
|
||||
case V1_21_3:
|
||||
return "1_21_2";
|
||||
case V1_21_4:
|
||||
case V1_21_5:
|
||||
case V1_21_6:
|
||||
case V1_21_7:
|
||||
case V1_21_8:
|
||||
return "1_21_4";
|
||||
case V1_21_9:
|
||||
case V1_21_10:
|
||||
return "1_21_9";
|
||||
case V1_21_11:
|
||||
return "1_21_11";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
@@ -197,14 +127,14 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
public void registerPlaceholders() {
|
||||
PlaceholderManagerImpl manager = TAB.getInstance().getPlaceholderManager();
|
||||
manager.registerInternalServerPlaceholder("%vault-prefix%", -1, () -> "");
|
||||
manager.registerInternalServerPlaceholder("%vault-suffix%", -1, () -> "");
|
||||
manager.registerServerPlaceholder("%vault-prefix%", -1, () -> "");
|
||||
manager.registerServerPlaceholder("%vault-suffix%", -1, () -> "");
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
|
||||
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
if (rspChat != null) {
|
||||
Chat chat = rspChat.getProvider();
|
||||
manager.registerInternalPlayerPlaceholder("%vault-prefix%", 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerInternalPlayerPlaceholder("%vault-suffix%", 1000, p -> chat.getPlayerSuffix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder("%vault-prefix%", p -> chat.getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder("%vault-suffix%", p -> chat.getPlayerSuffix((Player) p.getPlayer()));
|
||||
}
|
||||
}
|
||||
BackendPlatform.super.registerPlaceholders();
|
||||
@@ -213,7 +143,8 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
@Nullable
|
||||
public PipelineInjector createPipelineInjector() {
|
||||
return implementationProvider.getChannelFunction() != null ? new BukkitPipelineInjector() : null;
|
||||
return serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_8.getNetworkId()
|
||||
? new BukkitPipelineInjector() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -285,15 +216,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
Bukkit.getConsoleSender().sendMessage("§c[TAB] [WARN] " + toBukkitFormat(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getServerVersionInfo() {
|
||||
return "[Bukkit] " + Bukkit.getName() + " - " + Bukkit.getBukkitVersion().split("-")[0] + " (" + serverPackage + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener() {
|
||||
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(), plugin);
|
||||
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(this), plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -313,8 +238,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
Metrics metrics = new Metrics(plugin, TabConstants.BSTATS_PLUGIN_ID_BUKKIT);
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM,
|
||||
() -> TAB.getInstance().getGroupManager().getPermissionPlugin()));
|
||||
String version = serverVersion == ProtocolVersion.UNKNOWN ? "Unknown" : "1." + serverVersion.getMinorVersion() + ".x";
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION, () -> version));
|
||||
metrics.addCustomChart(new SimplePie("tab_6_0_0_servers", serverVersionInfo.getServerVersion()::getFriendlyName));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -326,13 +250,13 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
@NotNull
|
||||
public Object convertComponent(@NotNull TabComponent component) {
|
||||
return implementationProvider.getComponentConverter().convert(component);
|
||||
return serverVersionInfo.getImplementationProvider().getComponentConverter().convert(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
|
||||
return implementationProvider.newScoreboard((BukkitTabPlayer) player);
|
||||
return serverVersionInfo.getImplementationProvider().newScoreboard((BukkitTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -345,7 +269,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
if (BukkitBossBar.isAvailable()) return new BukkitBossBar((BukkitTabPlayer) player);
|
||||
|
||||
// 1.9+ player on 1.8 server, handle using ViaVersion API
|
||||
if (player.getVersion().getMinorVersion() >= 9) return new ViaBossBar((BukkitTabPlayer) player);
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_9.getNetworkId()) return new ViaBossBar((BukkitTabPlayer) player);
|
||||
|
||||
// 1.8- server and player, no implementation
|
||||
return new DummyBossBar();
|
||||
@@ -354,7 +278,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
@NotNull
|
||||
public TabList createTabList(@NotNull TabPlayer player) {
|
||||
return implementationProvider.newTabList((BukkitTabPlayer) player);
|
||||
return serverVersionInfo.getImplementationProvider().newTabList((BukkitTabPlayer) player);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -362,31 +286,36 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsListed() {
|
||||
return serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsListOrder() {
|
||||
return serverVersion.getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId();
|
||||
return serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSafeFromPacketEventsBug() {
|
||||
return serverVersion.getMinorVersion() >= 13;
|
||||
return serverVersionInfo.getServerVersion().getMinorVersion() >= 13;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCustomCommand(@NotNull String commandName, @NotNull Consumer<TabPlayer> function) {
|
||||
public void registerCustomCommand(@NotNull String commandName, @NotNull BiConsumer<TabPlayer, String[]> function) {
|
||||
Command cmd = new BukkitCommand(commandName) {
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender commandSender, @NotNull String alias, @NotNull String[] args) {
|
||||
if (commandSender instanceof ConsoleCommandSender) {
|
||||
if (commandSender instanceof Player) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(((Player) commandSender).getUniqueId());
|
||||
if (p == null) return false; //player not loaded correctly
|
||||
function.accept(p, args);
|
||||
} else {
|
||||
commandSender.sendMessage(toBukkitFormat(
|
||||
TabComponent.fromColoredText(TAB.getInstance().getConfiguration().getMessages().getCommandOnlyFromGame())
|
||||
));
|
||||
return false;
|
||||
}
|
||||
TabPlayer p = TAB.getInstance().getPlayer(((Player) commandSender).getUniqueId());
|
||||
if (p == null) return false; //player not loaded correctly
|
||||
function.accept(p);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -401,6 +330,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
knownCommands.remove(command.getName() + ":" + command.getName());
|
||||
command.unregister(commandMap);
|
||||
}
|
||||
customCommands.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,7 +389,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
public String toBukkitFormat(@NotNull TabComponent component) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (component.getModifier().getColor() != null) {
|
||||
if (serverVersion.supportsRGB()) {
|
||||
if (serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
|
||||
String hexCode = component.getModifier().getColor().getHexCode();
|
||||
sb.append('§').append("x").append('§').append(hexCode.charAt(0)).append('§').append(hexCode.charAt(1))
|
||||
.append('§').append(hexCode.charAt(2)).append('§').append(hexCode.charAt(3))
|
||||
@@ -501,4 +431,33 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
}
|
||||
return Arrays.asList((Player[]) Bukkit.class.getMethod("getOnlinePlayers").invoke(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object dump() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("server-type", "Bukkit");
|
||||
map.put("server-name", Bukkit.getName());
|
||||
map.put("server-version", serverVersionInfo.getMinecraftVersion());
|
||||
map.put("craftbukkit-package", serverVersionInfo.getServerPackage());
|
||||
map.put("nms-implementation", serverVersionInfo.getImplementationProvider().getClass().getName());
|
||||
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
|
||||
Map<String, Object> plugins = new LinkedHashMap<>();
|
||||
Plugin[] pluginArray = Bukkit.getPluginManager().getPlugins();
|
||||
Arrays.sort(pluginArray, Comparator.comparing(p -> p.getDescription().getName(), String.CASE_INSENSITIVE_ORDER));
|
||||
for (Plugin p : pluginArray) {
|
||||
plugins.put(p.getDescription().getName(), p.getDescription().getVersion());
|
||||
}
|
||||
map.put("plugins", plugins);
|
||||
if (placeholderAPI) {
|
||||
Map<String, String> expansions = new LinkedHashMap<>();
|
||||
PlaceholderExpansion[] expansionArray = PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getExpansions().toArray(new PlaceholderExpansion[0]);
|
||||
Arrays.sort(expansionArray, Comparator.comparing(PlaceholderExpansion::getIdentifier, String.CASE_INSENSITIVE_ORDER));
|
||||
for (PlaceholderExpansion p : expansionArray) {
|
||||
expansions.put(p.getIdentifier(), p.getVersion());
|
||||
}
|
||||
map.put("placeholderapi-expansions", expansions);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,8 @@ public class FoliaPlatform extends BukkitPlatform {
|
||||
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
|
||||
symbols.setDecimalSeparator('.');
|
||||
decimal2 = new DecimalFormat("#.##", symbols);
|
||||
registerInternalSyncPlaceholder(TabConstants.Placeholder.MSPT, 1000, p -> decimal2.format(Bukkit.getAverageTickTime()));
|
||||
registerInternalSyncPlaceholder(TabConstants.Placeholder.TPS, 1000, p -> decimal2.format(Math.min(20, Bukkit.getTPS()[0])));
|
||||
registerInternalSyncPlaceholder(TabConstants.Placeholder.MSPT, p -> decimal2.format(Bukkit.getAverageTickTime()));
|
||||
registerInternalSyncPlaceholder(TabConstants.Placeholder.TPS, p -> decimal2.format(Math.min(20, Bukkit.getTPS()[0])));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,9 +85,9 @@ public class FoliaPlatform extends BukkitPlatform {
|
||||
});
|
||||
}
|
||||
|
||||
private void registerInternalSyncPlaceholder(@NonNull String identifier, int refresh, @NonNull Function<TabPlayer, String> function) {
|
||||
private void registerInternalSyncPlaceholder(@NonNull String identifier, @NonNull Function<TabPlayer, String> function) {
|
||||
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
|
||||
ppl[0] = TAB.getInstance().getPlaceholderManager().registerInternalPlayerPlaceholder(identifier, refresh, p -> {
|
||||
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, p -> {
|
||||
runSync((Entity) p.getPlayer(), () -> {
|
||||
long time = System.nanoTime();
|
||||
String output = function.apply((TabPlayer) p);
|
||||
@@ -112,6 +112,18 @@ public class FoliaPlatform extends BukkitPlatform {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding the method to fix initial error caused by ServerPlaceholder implementation trying to
|
||||
* retrieve the value in constructor, but folia does not support that. Overriding this function to avoid the
|
||||
* TPS function being called in the wrong thread.
|
||||
*
|
||||
* @return -1
|
||||
*/
|
||||
@Override
|
||||
public double getTPS() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs task using player's entity scheduler. It's using reflection, because
|
||||
* Folia uses Java 17 while TAB maintains Java 8 compatibility for compatibility
|
||||
|
||||
+17
-5
@@ -4,7 +4,8 @@ import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -42,13 +43,24 @@ public interface ImplementationProvider {
|
||||
ComponentConverter<?> getComponentConverter();
|
||||
|
||||
/**
|
||||
* Returns a function that extracts Netty channel from the given player.
|
||||
* If not supported in the version, returns {@code null}.
|
||||
* Gets player's channel. If not available, returns {@code null}.
|
||||
*
|
||||
* @return Channel extractor function or {@code null} if not supported
|
||||
* @param player
|
||||
* Player to get channel of
|
||||
* @return Player's channel or {@code null} if not supported
|
||||
*/
|
||||
@Nullable
|
||||
FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction();
|
||||
Channel getChannel(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Creates a new instance of tab list entry tracker.
|
||||
*
|
||||
* @param player
|
||||
* Player to create tab list entry tracker for
|
||||
* @return Newly created tab list entry tracker
|
||||
*/
|
||||
@NotNull
|
||||
TabListEntryTracker newTabListEntryTracker(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Returns ping of the given player.
|
||||
|
||||
@@ -25,8 +25,8 @@ permissions:
|
||||
tab.cpu:
|
||||
description: Allows player to run /tab cpu
|
||||
default: op
|
||||
tab.debug:
|
||||
description: Allows player to run /tab debug [player]
|
||||
tab.dump:
|
||||
description: Allows player to run /tab dump <player>
|
||||
default: op
|
||||
tab.groupinfo:
|
||||
description: Allows player to run /tab group <group>
|
||||
|
||||
+10
-3
@@ -7,8 +7,9 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -34,8 +35,14 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-5
@@ -97,11 +97,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_10_R1;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.server.v1_10_R1.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.server.v1_10_R1.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, EnumPlayerInfoAction.class);
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
private static final Class<?> PlayerInfoData = Arrays.stream(PacketPlayOutPlayerInfo.class.getDeclaredClasses()).filter(c -> !c.isEnum() && c.getConstructors().length > 0).findFirst().get();
|
||||
private static final Field PlayerInfoData_Profile = ReflectionUtils.getOnlyField(PlayerInfoData, GameProfile.class);
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerInfo) {
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
tablistEntries.add(profile.getId());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.REMOVE_PLAYER) {
|
||||
tablistEntries.remove(profile.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -7,8 +7,9 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -34,8 +35,14 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-5
@@ -97,11 +97,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package me.neznamy.tab.platforms.bukkit.v1_11_R1;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.platform.NettyTabListEntryTracker;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.server.v1_11_R1.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of TabListEntryTracker.
|
||||
*/
|
||||
public class NMSTabListEntryTracker extends NettyTabListEntryTracker {
|
||||
|
||||
private static final Field ACTION = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, EnumPlayerInfoAction.class);
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
private static final Class<?> PlayerInfoData = PacketPlayOutPlayerInfo.class.getDeclaredClasses()[0];
|
||||
private static final Field PlayerInfoData_Profile = ReflectionUtils.getOnlyField(PlayerInfoData, GameProfile.class);
|
||||
|
||||
public NMSTabListEntryTracker(@NotNull Channel channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NotNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerInfo) {
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
tablistEntries.add(profile.getId());
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.REMOVE_PLAYER) {
|
||||
tablistEntries.remove(profile.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -7,8 +7,9 @@ import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
|
||||
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
|
||||
import me.neznamy.tab.shared.platform.Scoreboard;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.util.function.FunctionWithException;
|
||||
import me.neznamy.tab.shared.platform.TabListEntryTracker;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -34,8 +35,14 @@ public class NMSImplementationProvider implements ImplementationProvider {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public FunctionWithException<BukkitTabPlayer, Channel> getChannelFunction() {
|
||||
return player -> ((CraftPlayer)player.getPlayer()).getHandle().playerConnection.networkManager.channel;
|
||||
public Channel getChannel(@NotNull Player player) {
|
||||
return ((CraftPlayer)player).getHandle().playerConnection.networkManager.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabListEntryTracker newTabListEntryTracker(@NotNull Player player) {
|
||||
return new NMSTabListEntryTracker(getChannel(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loaded 100 of 436 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user