mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63f9daf334 | ||
|
|
b277d0973a | ||
|
|
33c0f1672a | ||
|
|
4224922ebb | ||
|
|
6eb30d84b4 | ||
|
|
592a75b53f | ||
|
|
8e08ea4e44 | ||
|
|
cff1e831d0 | ||
|
|
83b5a0f88a | ||
|
|
2ba535111e | ||
|
|
4bbf0bb0f1 | ||
|
|
1e80070d2d | ||
|
|
0afe0f28e1 | ||
|
|
fbc04b1183 | ||
|
|
bdd2a139e1 | ||
|
|
e956e59888 | ||
|
|
1debc54db6 | ||
|
|
37786935c3 | ||
|
|
425748af12 | ||
|
|
c0deaa40ed | ||
|
|
70be098f8a | ||
|
|
838ef437d8 | ||
|
|
44d7473c71 | ||
|
|
b8a7dfe543 | ||
|
|
4c4bf79371 | ||
|
|
a4b4bd0146 | ||
|
|
9553e310e4 | ||
|
|
e00caddb1e | ||
|
|
898887225d | ||
|
|
8eef805418 | ||
|
|
8509718d32 | ||
|
|
f2cb7c564f | ||
|
|
9335e5af65 | ||
|
|
5a5eadac1f | ||
|
|
bcd015b6eb | ||
|
|
91c6b8f08e | ||
|
|
c7251295a1 | ||
|
|
db2110b11e | ||
|
|
be1122ce50 | ||
|
|
cf265987db | ||
|
|
cd82e75514 | ||
|
|
74d36f1ab5 | ||
|
|
67da0ff32b | ||
|
|
aabad04e28 | ||
|
|
9236da40dd | ||
|
|
88ff78b7e3 | ||
|
|
21ebd68607 | ||
|
|
9adad40aa9 | ||
|
|
f63b3de63a | ||
|
|
767933e522 | ||
|
|
0fb86ddf6c | ||
|
|
d52969597f | ||
|
|
70e1537bc0 | ||
|
|
605a09b290 | ||
|
|
8e2f8d67f3 | ||
|
|
d00764e548 | ||
|
|
953472a41a | ||
|
|
6be5bb4757 | ||
|
|
b48a9e4f22 | ||
|
|
6d42e252a1 | ||
|
|
ef98b15c86 | ||
|
|
6945e8ee81 |
No files matched your search
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="20" role="img" aria-label="Minecraft: 1.5 - 1.21.8"><title>Minecraft: 1.5 - 1.21.8</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="138" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="77" height="20" fill="#007ec6"/><rect width="138" 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="985" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="670">1.5 - 1.21.8</text><text x="985" y="140" transform="scale(.1)" fill="#fff" textLength="670">1.5 - 1.21.8</text></g></svg>
|
||||
<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 After 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.2.5"><title>Release: 5.2.5</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.2.5</text><text x="715" y="140" transform="scale(.1)" fill="#fff" textLength="290">5.2.5</text></g></svg>
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -274,10 +274,10 @@ if __name__ == "__main__":
|
||||
]
|
||||
)
|
||||
|
||||
release = "5.2.5"
|
||||
release = "5.3.2"
|
||||
download_image(f"https://img.shields.io/badge/Release-{release}-blue.svg", "Badge_Release.svg")
|
||||
|
||||
minecraft = "1.5 - 1.21.8".replace(" ", "%20").replace("-", "--")
|
||||
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+"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
# Detection
|
||||
For TAB to properly detect player as a bedrock player, floodgate and TAB must be installed on the same server instance.
|
||||
If TAB is installed on bukkit and floodgate is on both bungeecord and bukkit,
|
||||
If TAB is installed on backend and floodgate is on both proxy and backend,
|
||||
make sure [they are connected](https://geysermc.org/wiki/floodgate/setup/?platform=proxy-servers).
|
||||
|
||||
To make sure floodgate is configured correctly, you have two options:
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
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
|
||||
When disabling [Tablist name formatting](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting), nametag prefix/suffix will be displayed in tablist instead. If you don't want any prefixes in tablist, keep the feature enabled but don't set any prefixes. Disabling this option is only useful to allow other plugins to use the feature or to intentionally display teams 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.
|
||||
If the feature is disabled or not applied properly (disabled with condition, some deeper compatibility issue ...), nametag prefix/suffix will be displayed in tablist instead. This mechanic is also used for <1.8 players due to 16-character tablist name limit there. You can also do this intentionally, for example for better performance.
|
||||
If you don't want any prefixes in tablist, keep the feature enabled but don't set any prefix / suffix. Disabling this feature is only useful to allow other plugins to use the feature or to intentionally display teams in tablist.
|
||||
|
||||
# Tablist entry overlap
|
||||
When having enough online players for 3–4 columns (or using [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout)) and their name formats are too long, they might overlap. This is caused by attempting to display text wider than the monitor can handle.
|
||||
|
||||
@@ -2,38 +2,38 @@
|
||||
This page lists all the commands and permissions that are available with TAB.
|
||||
Commands have required permissions associated with them.
|
||||
|
||||
**BungeeCord / Velocity users who installed TAB on the proxy:**
|
||||
**Users who installed TAB on a proxy server:**
|
||||
* Use **/btab** instead of /tab. Permissions are the same.
|
||||
* Permission nodes are checked on the server where the plugin is installed (by default). This means that with TAB on BungeeCord, permission nodes are checked on BungeeCord, and therefore you'll need a permission plugin there as well. If you wish to take permission groups and checks from backend servers instead, set `use-bukkit-permissions-manager: true` in config.
|
||||
* Giving yourself OP on the backend server does not give you permissions on the proxy. It's a completely different server. You'll need to give yourself permissions using a permission plugin installed on the proxy.
|
||||
* Permission nodes are checked on the server where the plugin is installed (by default). This means that with TAB on proxy, permission nodes are checked on the proxy, and therefore you'll need a permission plugin there as well. If you wish to take permission groups and checks from backend servers instead, set `use-bukkit-permissions-manager: true` in config.
|
||||
* Giving yourself OP on the backend server does not give you permissions on the proxy. It's a completely different server. You'll need to give yourself permissions using a permission plugin installed on the proxy (unless you enabled the option above).
|
||||
* You are not able to execute proxy commands using a backend plugin (for example some menu plugin). If you want to achieve this, you'll need to find an updated plugin that allows you to send commands to the proxy.
|
||||
|
||||
# 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 debug \[player\]](#tab-debug-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
|
||||
@@ -68,10 +68,10 @@ Commands have required permissions associated with them.
|
||||
## /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 BungeeCord, 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.
|
||||
* 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 group \<group\>
|
||||
* **Permission:** `tab.groupinfo`
|
||||
@@ -90,7 +90,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 +105,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 +116,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,16 @@
|
||||
# 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)
|
||||
* [All platforms](#all-platforms)
|
||||
* [Bukkit](#bukkit)
|
||||
* [BungeeCord](#bungeecord)
|
||||
* [Fabric](#fabric)
|
||||
* [Forge / NeoForge](#forge--neoforge)
|
||||
* [Sponge](#sponge)
|
||||
* [Velocity](#velocity)
|
||||
* [Compatibility issues](#compatibility-issues)
|
||||
|
||||
# Requirements
|
||||
@@ -35,26 +35,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.5 - 1.21.8)</td>
|
||||
<td>✔ (1.7.x - 1.21.10)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://www.spongepowered.org/">Sponge</a></td>
|
||||
<td>✔ (1.20.6 - 1.21.8)</td>
|
||||
<td>✔ (1.20.6 - 1.21.10)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan=3>Modded</td>
|
||||
<td rowspan=1><a href="https://fabricmc.net">Fabric</a></td>
|
||||
<td>✔ (1.21.6 - 1.21.8)<sup>1</sup></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://minecraftforge.net">Forge</a></td>
|
||||
<td>✔ (1.21.6 - 1.21.8)<sup>1</sup></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=1><a href="https://neoforged.net/">NeoForge</a></td>
|
||||
<td>✔ (1.21.6 - 1.21.8)<sup>1</sup></td>
|
||||
<td>✔ (1.21.9 - 1.21.10)<sup>1</sup></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
@@ -140,18 +140,17 @@ Sending scoreboard packets (scoreboard-teams, belowname-objective, playerlist-ob
|
||||
|
||||
# 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.
|
||||
* **[Tablisknu](https://forums.skunity.com/resources/tablisknu.727/)** (skript addon) prevents TAB from assigning teams (sorting & nametags).
|
||||
* **SkBee** (skript addon) sends empty scoreboard, causing TAB's to not show sometimes.
|
||||
* 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.
|
||||
* **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 `value` and [Playerlist objective](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Playerlist-Objective)'s `value` not visible. When using these features, you'll need to keep the option disabled.
|
||||
* **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.
|
||||
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.
|
||||
* 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.
|
||||
@@ -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.2.5</version>
|
||||
<version>5.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -50,7 +50,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.github.NEZNAMY:TAB-API:5.2.5'
|
||||
compileOnly 'com.github.NEZNAMY:TAB-API:5.3.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -61,7 +61,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.github.NEZNAMY", "TAB-API", "5.2.5")
|
||||
compileOnly("com.github.NEZNAMY", "TAB-API", "5.3.2")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
* [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)
|
||||
* [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)
|
||||
* [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.
|
||||
@@ -73,15 +73,15 @@ 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
|
||||
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.
|
||||
@@ -138,7 +138,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 BungeeCord, use %server% with server names.
|
||||
> 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.
|
||||
|
||||
|
||||
@@ -130,8 +130,8 @@ Then, you need to force your players to get the resource pack.
|
||||
## 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 BungeeCord makes it look like a world switch to the client.
|
||||
To avoid this, the only way is to install TAB on BungeeCord and disable bossbar on that server.
|
||||
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.
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Condition types](#condition-types)
|
||||
* [Number comparisons](#number-comparisons)
|
||||
* [Text operations](#text-operations)
|
||||
* [Permission](#permission)
|
||||
* [Number comparisons](#number-comparisons)
|
||||
* [Text operations](#text-operations)
|
||||
* [Permission](#permission)
|
||||
* [Multiple condition requirements](#multiple-condition-requirements)
|
||||
* [Condition output](#condition-output)
|
||||
* [Configuration](#configuration)
|
||||
* [Usage](#usage)
|
||||
* [Displaying text](#displaying-text)
|
||||
* [Display condition of a feature](#display-condition-of-a-feature)
|
||||
* [Short format](#short-format)
|
||||
* [Displaying text](#displaying-text)
|
||||
* [Display condition of a feature](#display-condition-of-a-feature)
|
||||
* [Short format](#short-format)
|
||||
* [Refresh interval](#refresh-interval)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Chaining conditional placeholders](#example-1---chaining-conditional-placeholders)
|
||||
* [Example 2 - Conditions in conditions](#example-2---conditions-in-conditions)
|
||||
* [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)
|
||||
* [Example 3 - Negating expressions](#example-3---negating-expressions)
|
||||
|
||||
# About
|
||||
Conditions / conditional placeholders allow you
|
||||
@@ -34,13 +34,16 @@ They have 2 main uses in the plugin:
|
||||
| `<` | Less than | `%ping%<100` will pass if the player's ping is less than `100` |
|
||||
|
||||
## Text operations
|
||||
| Operation | Description | Example |
|
||||
|-----------|----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
|
||||
| `=` | Equal to | `%world%=world` will pass if player is in world `world` |
|
||||
| `!=` | Not equal to | `%world%!=world` will pass if player is in any world except `world` |
|
||||
| `<-` | Contains (left side for full text, right side text to contain) | `%world%<-lobby-` will pass if player is in any world that contains `lobby-` (such as `lobby-1` etc.) |
|
||||
| `\|-` | Starts with (left side for full text, right side text to start with) | `%world%\|-lobby-` will pass if player is in any world that starts with `lobby-` (such as `lobby-1` etc.) |
|
||||
| `-\|` | Ends with (left side for full text, right side text to end with) | `%world%-\|nether` will pass if player is in any world that ends with `nether` (such as `world_nether` etc.) |
|
||||
| Operation | Description | Example |
|
||||
|-----------|------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
||||
| `=` | Equal to | `%world%=world` will pass if player is in world `world` |
|
||||
| `!=` | Not equal to | `%world%!=world` will pass if player is in any world except `world` |
|
||||
| `<-` | Contains (left side for full text, right side text to contain) | `%world%<-lobby-` will pass if player is in any world that contains `lobby-` (such as `lobby-1` etc.) |
|
||||
| `!<-` | Not Contains (left side for full text, right side text to not contain) | `%world%!<-lobby-` will pass if player is in any world that does not contain `lobby-` (such as `lobby-1` etc.) |
|
||||
| `\|-` | Starts with (left side for full text, right side text to start with) | `%world%\|-lobby-` will pass if player is in any world that starts with `lobby-` (such as `lobby-1` etc.) |
|
||||
| `!\|-` | Not Starts with (left side for full text, right side text to not start with) | `%world%!\|-lobby-` will pass if player is in any world that does not start with `lobby-` (such as `lobby-1` etc.) |
|
||||
| `-\|` | Ends with (left side for full text, right side text to end with) | `%world%-\|nether` will pass if player is in any world that ends with `nether` (such as `world_nether` etc.) |
|
||||
| `!-\|` | Not Ends with (left side for full text, right side text to not end with) | `%world%!-\|nether` will pass if player is in any world that does not end with `nether` (such as `world_nether` etc.) |
|
||||
|
||||
> [!NOTE]
|
||||
> For `=` and `!=` you can check for empty output of a placeholder using `%my_placeholder%=` and `%my_placeholder%!=`.
|
||||
@@ -66,7 +69,7 @@ This value can be found under `type` field. Types are:
|
||||
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 `yes` and `no` values. `yes` is used when condition passes, `no` if not. If using condition only as a view requirement, you can leave these values empty / not specify them at all.
|
||||
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:
|
||||
@@ -77,21 +80,21 @@ conditions:
|
||||
- '%health%<21'
|
||||
- '%health%>15'
|
||||
type: AND
|
||||
yes: Healthy!
|
||||
no: Damaged!
|
||||
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.
|
||||
`yes` & `no` define output in both cases.
|
||||
`true` & `false` define output in both cases.
|
||||
|
||||
# Usage
|
||||
You have 2 ways to use conditions.
|
||||
|
||||
## Displaying text
|
||||
The first way is to use conditions to display text.
|
||||
Configure outputs in `yes` and `no` values and then use `%condition:<name>%`,
|
||||
which will output text defined in `yes` or `no` depending on if condition is met or not.
|
||||
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>
|
||||
|
||||
@@ -100,8 +103,8 @@ conditions:
|
||||
serverName:
|
||||
conditions:
|
||||
- "%server%=lobby"
|
||||
yes: "You are in the lobby"
|
||||
no: "You are not in lobby"
|
||||
true: "You are in the lobby"
|
||||
false: "You are not in lobby"
|
||||
```
|
||||
|
||||
Use with `%condition:serverName%`
|
||||
@@ -110,7 +113,7 @@ Use with `%condition:serverName%`
|
||||
## 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, yes/no texts are unused; therefore, they do not need to be defined.
|
||||
In these cases, true/false texts are unused; therefore, they do not need to be defined.
|
||||
<details>
|
||||
<summary>Example</summary>
|
||||
|
||||
@@ -130,7 +133,7 @@ In this example, scoreboard will only be displayed to players with `tab.admin` p
|
||||
</details>
|
||||
|
||||
### 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 yes/no values, you can use a short format instead.
|
||||
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**:
|
||||
@@ -151,7 +154,7 @@ display-condition: "%server%=lobby|%server%=lobby2"
|
||||
# Refresh interval
|
||||
Conditions are just placeholders after all, and, as such, they must be refreshed periodically.
|
||||
Refresh intervals of conditions are not directly configurable.
|
||||
They are based on placeholders used inside (subconditions, yes/no values).
|
||||
They are based on placeholders used inside (subconditions, true/false values).
|
||||
Permission checks count as 1000ms.
|
||||
To configure refresh intervals of placeholders,
|
||||
check out the [Optimization guide](https://github.com/NEZNAMY/TAB/wiki/Optimizing-the-plugin#2---all-platforms-placeholder-refresh-intervals).
|
||||
@@ -184,9 +187,8 @@ conditions:
|
||||
Finally, we can use this ping placeholder using `%condition:ping%`.
|
||||
This example chained 2 conditions, but more can be used. There is no limit.
|
||||
|
||||
## Example 2 - Conditions in conditions
|
||||
If you want to use a condition in another one,
|
||||
for example, to use both "AND" and "OR" types, create 2 conditions and use one in the other one.
|
||||
## Example 2 - Combining AND and OR
|
||||
If you want to combine both "AND" and "OR" types, create 2 conditions and use first one in the second one.
|
||||
For example, if we want to check that player is in server `lobby` **and** in worlds **either** `world1` or `world2`,
|
||||
it can be achieved in the following way:
|
||||
```
|
||||
@@ -202,7 +204,7 @@ conditions:
|
||||
- "%server%=lobby"
|
||||
type: AND
|
||||
```
|
||||
Then, use condition `main` as the display condition. Note that `yes`/`no` 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.
|
||||
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:
|
||||
@@ -210,11 +212,9 @@ Most condition types contain their opposites, such as:
|
||||
* `permission:` -> `!permission:`
|
||||
* `>=` -> `<`
|
||||
* `>` -> `<=`
|
||||
|
||||
However, some of them don't, as things could get too complicated. This includes:
|
||||
* `|-` starts with
|
||||
* `-|` ends with
|
||||
* `<-` contains
|
||||
* `|-` -> `!|-`
|
||||
* `-|` -> `!-|`
|
||||
* `<-` -> `!<-`
|
||||
|
||||
You can achieve this by creating a full condition and check if it returned false.
|
||||
**Example:**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# 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)
|
||||
* [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)
|
||||
|
||||
# About
|
||||
@@ -37,7 +37,7 @@ global-playerlist:
|
||||
```
|
||||
Contains 2 groups, each of them consisting of 2 servers.
|
||||
Server group name can be anything (in our case, they're called "lobbies" and "group2").
|
||||
Under group name, list the actual server names defined in config.yml of bungeecord.
|
||||
Under group name, list the actual server names defined in config.yml of the proxy server.
|
||||
|
||||
With this setup, "lobby1" and "lobby2" will share playerlist, as well as "server1" with "server2". All unlisted servers are automatically put into a hidden default group and share playerlist. This means that all other servers except these 4 will share playerlist.
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Global settings](#global-settings)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Per-group / per-player](#per-group--per-player)
|
||||
* [Designs](#designs)
|
||||
* [Chaining designs](#chaining-designs)
|
||||
* [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)
|
||||
* [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)
|
||||
* [Tip 1 - Dynamic line count](#tip-1---dynamic-line-count)
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Example 1 - Per-version header/footer](#example-1---per-version-headerfooter)
|
||||
|
||||
# About
|
||||
Minecraft feature **introduced in 1.8** showing text above and below playerlist. It cannot be displayed on 1.7 clients in any way.
|
||||
@@ -16,74 +17,55 @@ Minecraft feature **introduced in 1.8** showing text above and below playerlist.
|
||||

|
||||
|
||||
# Configuration
|
||||
| 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. |
|
||||
| Option name | Default value | Description |
|
||||
|-------------------|-----------------------|-------------------------------------------------------------------|
|
||||
| enabled | true | Enables / Disables the feature |
|
||||
| designs | *Map* | Designs to display based on conditions (see below for more info). |
|
||||
|
||||
## Global settings
|
||||
The example above is using this configuration:
|
||||
# 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.
|
||||
|
||||
**Example**:
|
||||
```
|
||||
header-footer:
|
||||
header:
|
||||
- "This is the first line of header"
|
||||
- "2nd line of header"
|
||||
footer:
|
||||
- "This is the first line of footer"
|
||||
- "2nd line of footer"
|
||||
```
|
||||
It is the default header/footer for everyone, unless overridden in some way.
|
||||
|
||||
## Per-world / per-server
|
||||
```
|
||||
header-footer:
|
||||
per-world:
|
||||
<your world>:
|
||||
enabled: true
|
||||
designs:
|
||||
MyDesign: # Design name
|
||||
display-condition: "%world%=world" # Delete the line for no condition requirement
|
||||
header:
|
||||
- "Header in specified world"
|
||||
- "Header for world %world%"
|
||||
footer:
|
||||
- "Footer in specified world"
|
||||
per-server:
|
||||
<your server>:
|
||||
header:
|
||||
- "Header in specified server"
|
||||
footer:
|
||||
- "Footer in specified server"
|
||||
- "Footer for world %world%"
|
||||
```
|
||||
|
||||
For multiple worlds/servers to share the same settings, separate them with `;`.
|
||||
For worlds/servers starting with a specified text, use `*` after the shared part. For ending with a shared part, use `*` at the beginning.
|
||||
# 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:
|
||||
```
|
||||
header-footer:
|
||||
per-world:
|
||||
world1;world2:
|
||||
enabled: true
|
||||
designs:
|
||||
per-world:
|
||||
display-condition: "%world%=world"
|
||||
header:
|
||||
- "Shared header in worlds world1 and world2"
|
||||
lobby-*:
|
||||
- "Header for world %world%"
|
||||
footer:
|
||||
- "Footer for world %world%"
|
||||
default: # No display condition
|
||||
header:
|
||||
- "Header in all worlds starting with lobby-"
|
||||
- "Header for every world except %world%"
|
||||
footer:
|
||||
- "Footer for every world except %world%"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> To make per-world work on proxy installation,
|
||||
> install the [TAB-Bridge](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) plugin on your backend servers.
|
||||
|
||||
## Per-group / per-player
|
||||
**groups.yml**
|
||||
```
|
||||
MyGroup:
|
||||
header:
|
||||
- "This is a header for MyGroup group"
|
||||
footer:
|
||||
- "This is a footer for MyGroup group"
|
||||
per-world:
|
||||
MyWorld:
|
||||
TestGroup:
|
||||
header:
|
||||
- "Header for group TestGroup in world MyWorld"
|
||||
```
|
||||
Same for users, which can be configured in **users.yml**.
|
||||
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.
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - [1.8 - 1.20.1] Not resetting on server switch
|
||||
@@ -111,8 +93,10 @@ MyAnimation:
|
||||
**config.yml**
|
||||
```
|
||||
header-footer:
|
||||
header:
|
||||
- "%animation:MyAnimation%"
|
||||
designs:
|
||||
DesignName:
|
||||
header:
|
||||
- "%animation:MyAnimation%"
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -126,4 +110,24 @@ To set the header and/or footer for a player, use the following:
|
||||
* `HeaderFooterManager#setFooter(TabPlayer, String)`
|
||||
* `HeaderFooterManager#setHeaderAndFooter(TabPlayer, String, String)`
|
||||
|
||||
To reset the header and/or footer for a player back to their original values, use `null` as argument.
|
||||
To reset the header and/or footer for a player back to their original values, use `null` as argument.
|
||||
|
||||
# Examples
|
||||
## Example 1 - Per-version header/footer
|
||||
Minecraft 1.21.9 has introduced [object components](https://github.com/NEZNAMY/TAB/wiki/How-to-use-Minecraft-components#object-components-1219), but these are not available for older versions. Let's make 2 designs, one for 1.21.9+ and second one for <1.21.9.
|
||||
```
|
||||
header-footer:
|
||||
enabled: true
|
||||
designs:
|
||||
newer-client:
|
||||
display-condition: "%player-version-id%>=773" # 1.21.9 uses 773
|
||||
header:
|
||||
- "Look, it's your head! <head:name:%player%>"
|
||||
footer:
|
||||
- "Cool, isn't it?"
|
||||
older-client:
|
||||
header:
|
||||
- "Regular header for old clients"
|
||||
footer:
|
||||
- "Consider updating your game"
|
||||
```
|
||||
@@ -1,23 +1,24 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Creating a layout](#creating-a-layout)
|
||||
* [Display condition](#display-condition)
|
||||
* [Fixed slots](#fixed-slots)
|
||||
* [Player groups](#player-groups)
|
||||
* [Display condition](#display-condition)
|
||||
* [default-skin](#default-skin)
|
||||
* [Fixed slots](#fixed-slots)
|
||||
* [Player groups](#player-groups)
|
||||
* [Managing multiple layouts with conditions](#managing-multiple-layouts-with-conditions)
|
||||
* [Skins](#skins)
|
||||
* [Format](#format)
|
||||
* [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)
|
||||
* [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](https://github.com/NEZNAMY/TAB/wiki/Feature-guide%3A-Layout/_edit#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)
|
||||
|
||||
# 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.
|
||||
@@ -25,7 +26,11 @@ This feature allows you to customize all 80 tablist slots. Displaying less than
|
||||
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. Each layout contains 3 settings: Fixed slots, player groups and display condition (optional).
|
||||
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.
|
||||
@@ -41,6 +46,27 @@ layouts:
|
||||
```
|
||||
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**:
|
||||
```
|
||||
layout:
|
||||
enabled: true
|
||||
direction: COLUMNS
|
||||
default-skin: mineskin:37e93c8e12cd426cb28fce31969e0674 # Global default skin for all layouts
|
||||
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: []
|
||||
groups:
|
||||
players:
|
||||
slots:
|
||||
- 1-80
|
||||
```
|
||||
|
||||
## 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`.
|
||||
@@ -58,6 +84,13 @@ layouts:
|
||||
- '4|&3TPS&f:'
|
||||
- '5|&b%tps%'
|
||||
```
|
||||
If you do not wish to use any fixed slots, make it an empty list (`[]`).
|
||||
Example:
|
||||
```
|
||||
layouts:
|
||||
staff:
|
||||
fixed-slots: []
|
||||
```
|
||||
|
||||
## Player groups
|
||||
These are groups of players that meet the specified condition. They consist of 2 parts - condition and slots.
|
||||
@@ -142,7 +175,7 @@ As a result, it is impossible for TAB to assign a different value for each fake
|
||||
## Global playerlist incompatibility
|
||||
The way this feature works is by pushing all real players out of the tablist and showing fake players instead.
|
||||
As a result, enabling [Global playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist) feature won't make any difference, since real players aren't visible in the tablist.
|
||||
Layout feature is capable of working with all online players connected to the server where TAB is installed, meaning it can show players from all servers when installed on BungeeCord. Because of this, if you use layout, you can disable global playerlist to reduce resource usage.
|
||||
Layout feature is capable of working with all online players connected to the server where TAB is installed, meaning it can show players from all servers when installed on a proxy. Because of this, if you use layout, you can disable global playerlist to reduce resource usage.
|
||||
|
||||
## Per world playerlist incompatibility
|
||||
Layout works by adding 80 fake players into the tablist, pushing real player entries out of view.
|
||||
|
||||
@@ -23,7 +23,7 @@ Pick your desired way and configure it. I don't know anything about redis or rab
|
||||
> ```
|
||||
> server-name: "server"
|
||||
> ```
|
||||
> Ideally, name it the same as servers in your BungeeCord / Velocity config. This value will be used for getting player's server name. Aside from %server% placeholder, it will be used for internal logic when checking for player's server. One of them is global playerlist not adding/removing players who are on the same server in the tablist, as backend servers already do this (and duplicating actions would cause problems).
|
||||
> Ideally, name it the same as servers in your proxy config. This value will be used for getting player's server name. Aside from %server% placeholder, it will be used for internal logic when checking for player's server. One of them is global playerlist not adding/removing players who are on the same server in the tablist, as backend servers already do this (and duplicating actions would cause problems).
|
||||
> **As a result, not configuring this value will result in everyone's server being "N/A", therefore global playerlist will think everyone is on the same server and not add anyone into the tablist, effectively making the feature not work.**
|
||||
|
||||
Supported plugins:
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Priority system](#priority-system)
|
||||
* [Name color](#name-color)
|
||||
* [Placeholder support](#placeholder-support)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [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)
|
||||
* [Limitations](#limitations)
|
||||
* [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)
|
||||
|
||||
# About
|
||||
Nametags are controlled by a feature called scoreboard teams. They offer 6 properties:
|
||||
@@ -59,8 +59,8 @@ _NEZNAMY_:
|
||||
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"`.
|
||||
|
||||
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:
|
||||
@@ -70,7 +70,7 @@ _DEFAULT_:
|
||||
```
|
||||
|
||||
## Per-world / per-server
|
||||
Values can also be applied per-world (and per-server on bungeecord), where they can be defined per group/user. These values take priority over global settings. Example:
|
||||
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**
|
||||
```
|
||||
per-world:
|
||||
@@ -128,13 +128,13 @@ All values fully support [TAB's internal placeholders](https://github.com/NEZNAM
|
||||
The number of placeholders is not limited, and they can be used in combination with static text as well.
|
||||
|
||||
## 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 enabled, everyone will have invisible nametag all the time. 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. |
|
||||
| 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. |
|
||||
|
||||
# Tips & Tricks
|
||||
If you want TAB to only take prefixes/suffixes from the permission plugin,
|
||||
@@ -147,10 +147,17 @@ _DEFAULT_:
|
||||
|
||||
# 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.13+] The name can only have one code. That is either color or magic code (such as &4 or &l), but not both.
|
||||
* The name cannot be effectively changed and the plugin doesn't offer it.
|
||||
* Name does not support RGB codes. Any used RGB colors will be rounded to the nearest legacy code.
|
||||
* [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.
|
||||
* 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.
|
||||
|
||||
If you want to avoid these at all costs, the only way is to create entities (armor stands / display) and make them ride the player (or teleport). TAB no longer offers this functionality due to a large amount of unfixable issues (full thread [here](https://gist.github.com/NEZNAMY/f4cabf2fd9251a836b5eb877720dee5c/)). You can, however, check out these 3rd party plugins (**not affiliated with me in any way, use at your own risk**):
|
||||
* [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)
|
||||
|
||||
# Additional info
|
||||
## Additional note 1 - NPC (in)compatibility
|
||||
@@ -188,7 +195,7 @@ scoreboard-teams:
|
||||
```
|
||||
In config.yml. Keep in mind, you'll not be able to use any team features if you do so.
|
||||
|
||||
# Additional note 6 - Vanish hook
|
||||
## 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).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* [Configuration](#configuration)
|
||||
* [Limitations](#limitations)
|
||||
* [Additional info](#additional-info)
|
||||
* [Additional note 1 - Spectator gamemode](#additional-note-1---spectator-gamemode)
|
||||
* [Additional note 1 - Spectator gamemode](#additional-note-1---spectator-gamemode)
|
||||
|
||||
# About
|
||||
The scoreboard objective with PLAYER_LIST display slot. On 1.20.2- it supports 2 display types:
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
# 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)
|
||||
* [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)
|
||||
* [Limitations](#limitations)
|
||||
* [Longer lines](#longer-lines)
|
||||
* [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||
* [Geyser / Bedrock issues](#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.
|
||||
@@ -44,12 +45,35 @@ Up to 15 lines of text. You can define more, but the client won't display them.
|
||||
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:
|
||||
```
|
||||
lines:
|
||||
- "Left side text 1||Right side text 1"
|
||||
- "Left side text 2....||Right side text 2...."
|
||||
- "Left side text 3.......||Right side text 3......."
|
||||
- "Left 1---"
|
||||
- "||---Right 1"
|
||||
- "Left 2------"
|
||||
- "||------Right 2"
|
||||
- "Left 3---------"
|
||||
- "||---------Right 3"
|
||||
- "Left 4------------"
|
||||
- "||------------Right 4"
|
||||
- "Left 5---------------"
|
||||
- "||---------------Right 5"
|
||||
- "Left 6------------------"
|
||||
- "||------------------Right 6"
|
||||
```
|
||||
Will give you the following:
|
||||

|
||||
<img width="305" height="239" alt="image" src="https://github.com/user-attachments/assets/a712b5fb-21ea-4ee4-83ec-d962bd431a07" />
|
||||
|
||||
Additionally, the following configuration:
|
||||
```
|
||||
lines:
|
||||
- "Left 1---||---Right 1"
|
||||
- "Left 2------||------Right 2"
|
||||
- "Left 3---------||---------Right 3"
|
||||
- "Left 4------------||------------Right 4"
|
||||
- "Left 5---------------||---------------Right 5"
|
||||
- "Left 6------------------||------------------Right 6"
|
||||
```
|
||||
Will give you
|
||||
<img width="583" height="131" alt="image" src="https://github.com/user-attachments/assets/15e809c6-4ecd-4234-a30e-744c2d413335" />
|
||||
|
||||
|
||||
The right side text will not be visible at 1.20.2 and lower, and numbers will be displayed instead.
|
||||
|
||||
@@ -189,6 +213,9 @@ so if you configured a fancy output for that placeholder, you'll need to use it
|
||||
To see what exactly has placeholder returned, use `/tab parse <player> <placeholder>`.
|
||||
</details>
|
||||
|
||||
# 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
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
@@ -326,8 +353,8 @@ conditions:
|
||||
faction:
|
||||
conditions:
|
||||
- '%factionsuuid_faction_name%='
|
||||
yes: "" # No faction, return empty string to hide the line
|
||||
no: "Faction: %factionsuuid_faction_name%" # Player has a faction, show the static text before it as well
|
||||
true: "" # No faction, return empty string to hide the line
|
||||
false: "Faction: %factionsuuid_faction_name%" # Player has a faction, show the static text before it as well
|
||||
```
|
||||
> [!WARNING]
|
||||
> DO NOT JUST RANDOMLY PASTE THIS ENTIRE "CONDITIONS" SECTION INTO YOUR CONFIG!
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# 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)
|
||||
|
||||
@@ -210,7 +210,7 @@ If your file is empty / does not exist, it means you have no conflicting plugins
|
||||
If the file exists, most of the time you can guess where the teams come from by their name.
|
||||
Here are a few common teams and their sources (`xxxx` means any, usually random character sequence):
|
||||
* `collideRule_xxxx` - this comes from Paper. Not going to explain why as that would be quite long, but the way you can avoid is by setting `enable-player-collisions: true` in paper config and `enable-collision: false` in TAB config (yes, collisions will be disabled).
|
||||
* `CMINPxx` - CMI, set `DisableTeamManagement: true` in `plugins/CMI/config.yml`.
|
||||
* `CMINPxx` - CMI, set `DisableTeamManagement: true` in `plugins/CMI/config.yml` (though CMI won't allow you to change glow color if you do, so keep that in mind).
|
||||
* `CIT-xxxxxxxxxxxx` - Citizens NPC with the same name as some online player. Make NPC names not match real players and use holograms to display your desired text (/npc name or something).
|
||||
* `PVP-xxxxxxxxxxxx` - Team coming from [PvPManager](https://www.spigotmc.org/resources/pvpmanager-lite.845/) plugin.
|
||||
|
||||
@@ -228,13 +228,14 @@ 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 BungeeCord without knowing what it does.
|
||||
* Installing TAB on BungeeCord without having any permission plugin on BungeeCord.
|
||||
* 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 bukkit and bungeecord, causing the installations to conflict.
|
||||
* 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)
|
||||
|
||||
# API
|
||||
*To get started with the API, see [Developer API](https://github.com/NEZNAMY/TAB/wiki/Developer-API) page.*
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* [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,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Configuration](#configuration)
|
||||
* [Introduction](#introduction)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Priority system](#priority-system)
|
||||
* [Placeholder support](#placeholder-support)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Introduction](#introduction)
|
||||
* [Groups and users](#groups-and-users)
|
||||
* [Per-world / per-server](#per-world--per-server)
|
||||
* [Priority system](#priority-system)
|
||||
* [Placeholder support](#placeholder-support)
|
||||
* [Additional settings](#additional-settings)
|
||||
* [Tips & Tricks](#tips--tricks)
|
||||
* [API](#api)
|
||||
|
||||
@@ -73,7 +73,7 @@ _DEFAULT_:
|
||||
```
|
||||
|
||||
## Per-world / per-server
|
||||
Values can also be applied per-world (and per-server on bungeecord), where they can be defined per group/user. These values take priority over global settings. Example:
|
||||
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**
|
||||
```
|
||||
per-world:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* [#16 - Is MiniMessage supported?](#16---is-minimessage-supported)
|
||||
* [#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)
|
||||
|
||||
## #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).
|
||||
@@ -25,8 +26,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.
|
||||
@@ -50,6 +51,8 @@ Most commonly used plugins for this are [FastLogin](https://www.spigotmc.org/res
|
||||
which change connections of premium players who enabled it to online connections.
|
||||
The same goes for disabling heads - you cannot disable them if you have online mode enabled.
|
||||
|
||||
Minecraft 1.21.9 has added [object components](https://github.com/NEZNAMY/TAB/wiki/How-to-use-Minecraft-components#object-components-1219), which allow you to display player heads. You can put for example `<head:name:%player%>` at the beginning of tabprefix to simulate this.
|
||||
|
||||
## #7 - Where can i find current default config files?
|
||||
You can do any of the following:
|
||||
* Check the [source code](https://github.com/NEZNAMY/TAB/tree/master/shared/src/main/resources/config).
|
||||
@@ -74,8 +77,8 @@ You will need a custom minecraft client or a resource pack.
|
||||
Most common reasons for a placeholder to not work include:
|
||||
* Using % symbol by itself, which breaks placeholder starts and ends and therefore breaks all placeholders after it (use %% to display the symbol)
|
||||
* Trying to use a PlaceholderAPI placeholder without downloading its expansion or not having PlaceholderAPI installed at all
|
||||
* Trying to use [TAB's internal bukkit-only placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#bukkit-only) on BungeeCord
|
||||
* Trying to use [PlaceholderAPI placeholders on BungeeCord](https://github.com/NEZNAMY/TAB/wiki/How-to-set-up-PlaceholderAPI-support-on-bungeecord) without installing [bridge plugin](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge)
|
||||
* Trying to use [TAB's internal backend-only placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#backend-only) on a proxy
|
||||
* Trying to use PlaceholderAPI placeholders on proxy without installing [bridge plugin](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge)
|
||||
|
||||
The full list of reasons can be found on the [Placeholders](https://github.com/NEZNAMY/TAB/wiki/Placeholders#placeholder-is-not-working) page.
|
||||
|
||||
@@ -134,4 +137,8 @@ where plugin authors can see how many servers use their plugins (if they don't d
|
||||
It also checks for CPU and OS types, which could be the reason for the false positive.
|
||||
[Here](https://bstats.org/plugin/bukkit/TAB%20Reborn/5304) is one of TAB's pages as an example.
|
||||
|
||||
Windows defender could be flagging it for the same reason.
|
||||
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).
|
||||
@@ -1,5 +1,5 @@
|
||||
# About the wiki
|
||||
The wiki explains the functionality of the plugin as of version **5.2.5**.
|
||||
The wiki explains the functionality of the plugin as of version **5.3.2**.
|
||||
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.
|
||||
@@ -10,6 +10,14 @@ This will give you knowledge to do things you had no idea were possible, giving
|
||||
|
||||
A lot of examples on the wiki use PlaceholderAPI placeholders without explaining its installation step by step. If you are going to use the examples and aren't familiar with the plugin, check out [Quick PlaceholderAPI startup guide](https://github.com/NEZNAMY/TAB/wiki/Quick-PlaceholderAPI-startup-guide).
|
||||
|
||||
# Terminology
|
||||
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 |
|
||||
|
||||
# Limitations of plugins
|
||||
TAB is a plugin (even on modded platforms, it is only a server-sided mod).
|
||||
It only modifies the server behavior to your liking.
|
||||
|
||||
@@ -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.
|
||||
@@ -69,8 +69,8 @@ conditions:
|
||||
nick:
|
||||
conditions:
|
||||
- '%essentials_nickname%=%player%'
|
||||
yes: "%player%" # If nickname = player name -> player is not nicked
|
||||
no: "~%essentials_nickname%" # If nickname != player name -> player is nicked
|
||||
true: "%player%" # If nickname = player name -> player is not nicked
|
||||
false: "~%essentials_nickname%" # If nickname != player name -> player is nicked
|
||||
```
|
||||
Then, use `%condition:nick%` in your `customtabname` instead of the placeholder directly.
|
||||
|
||||
@@ -87,8 +87,8 @@ conditions:
|
||||
group:
|
||||
conditions:
|
||||
- '%haonick_isNicked%=true'
|
||||
yes: "default"
|
||||
no: "%group%"
|
||||
true: "default"
|
||||
false: "%group%"
|
||||
```
|
||||
When the condition is met, meaning player is nicked, it will return the group we want nicked players to be sorted as, in this case `default`. Otherwise, return player's actual group.
|
||||
The condition check may differ with different nickname plugins. You will need to check documentation of your nickname plugin to see what kind of placeholders it offers and what they return (you can also use `/tab parse` to see what exactly did a placeholder return).
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
# 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)
|
||||
* [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.
|
||||
@@ -92,23 +93,8 @@ When trying to use both font and RGB or gradients, font must be outside and RGB/
|
||||
TAB (currently?) does not have any built-in way to convert text input into shadow color field. However, you can use [MiniMessage](#minimessage) for that. TAB will properly read this value from adventure components and convert it further into Minecraft components.
|
||||
|
||||
# Object components (1.21.9+)
|
||||
> [!NOTE]
|
||||
> This is a draft for future versions - MC 1.21.9+ and TAB 5.3.0+.
|
||||
|
||||
A new component of "object" type was added into the game in 1.21.9. It currently has 2 implementations described below.
|
||||
|
||||
> [!WARNING]
|
||||
> In order to use these, you will need to disable MiniMessage support, because object components were not added to MiniMessage yet. Even when they are added, TAB will require and update to read them from adventure. To disable MiniMessage support, set:
|
||||
> ```
|
||||
> components:
|
||||
> minimessage-support: false
|
||||
> ```
|
||||
> in **config.yml**.
|
||||
|
||||
> [!WARNING]
|
||||
> The syntax for object components is just temporary and will change in the future to match MiniMessage (once they add it) to avoid confusing users / adding conversion from TAB syntax to MiniMessage syntax even for these.
|
||||
> Expect the syntax to break in a future TAB update. For now, use the existing syntax for prototyping and testing.
|
||||
|
||||
## Atlas sprite
|
||||
This type allows you to display any item or block. The syntax is
|
||||
```
|
||||
@@ -118,7 +104,7 @@ where `ATLAS` is the atlas and `SPRITE` is the sprite. Note that atlas can conta
|
||||
Example:
|
||||
```
|
||||
header:
|
||||
- 'Diamond helmet: <sprite:"minecraft:blocks":"minecraft:item/diamond_helmet">'
|
||||
- '&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">'
|
||||
@@ -127,28 +113,54 @@ Example:
|
||||
|
||||
|
||||
## Player sprite
|
||||
This type allows you to display minecraft heads. There are 3 ways to define a head along with their TAB syntaxes:
|
||||
* `name`: `<head:name:NAME>` where `NAME` is player name
|
||||
* `uuid`: `<head:id:UUID>` where `UUID` is UUID of a player
|
||||
* `skin`: `<head:signed_texture:VALUE;SIGNATURE>` where `VALUE` and `SIGNATURE` are value and signature of that skin
|
||||
> [!WARNING]
|
||||
> In order to use these, you will need to disable MiniMessage support, because head components were not added to MiniMessage yet. Even when they are added, they will have a different syntax, so TAB will be updated to match the format to avoid complications. For now, use the existing syntax for prototyping and testing. To disable MiniMessage support, set:
|
||||
> ```
|
||||
> components:
|
||||
> minimessage-support: false
|
||||
> ```
|
||||
> in **config.yml**.
|
||||
|
||||
Here is an example configuration
|
||||
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. |
|
||||
| Player UUID | The client checks recently received profiles (players on current server) and shows skin of player with that UUID. If no such player was found, connects to Mojang and gets skin of player with specified UUID. <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)). |
|
||||
| Skin value and signature | The client displays skin from given value and signature |
|
||||
|
||||
Here are all the ways you can display player skins using TAB and their implementation (using `ALL CAPS` for "placeholder" text instead of `<>` to avoid confusion as `<>` is actual part of the syntax):
|
||||
| Syntax | Explanation | Implementation |
|
||||
|-----|-------------|------------|
|
||||
| `<head:name:NAME>` | Shows head defined by player name | name |
|
||||
| `<head:id:UUID>` | Shows head defined by player UUID | uuid |
|
||||
| `<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 |
|
||||
|
||||
Here is an example for all types:
|
||||
```
|
||||
footer:
|
||||
- "Head by your UUID: <head:id:%uuid%>"
|
||||
- "Head by your name: <head:name:%player%>"
|
||||
- "Head by raw texture: <head:signed_texture:ewogICJ0aW1lc3RhbXAiIDogMTc1NjMxNjc0OTk4MywKICAicHJvZmlsZUlkIiA6ICIyMzdkOGI1NTNmOTc0NzQ5YWE2MGU5ZmU5N2I0NTA2MiIsCiAgInByb2ZpbGVOYW1lIiA6ICJfTkVaTkFNWV8iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmY1ZTNhNWRmODkxYzEzMTdhZjUzMmMzNmY4NDgxNDBhNDBmMGZhMGNlZGRmZmEwNWU5NGU1NzU1OGQxY2Q4YyIKICAgIH0KICB9Cn0=;YFVoqA0CqWgnZhme5s/M7xtadURyLkyLWVsh0vN8BqdfwzN32nqlBJb2pRjkxIs0V21EOeKbGFjgdKQKzPRQdf6sTFQ/x0KOjUg8A/neAiwCvDSDrsTQ2Yf7yoVP6M2PpuB7te19N+I139mGY5psxU100x6GiV/uFfpUfG3XBJog43JtXzRJ9fRtoSeIEzqLrkpCmt6o5Mzo6GZFZc4CtI76OU90Mg9ZvTZTYelvtjFtllxTtkNZCcglzvh5R19+qtzLOzEr+N8m4Ed+5yZyezEb3LeeSgmbSLIjuOKuUupE+2F6yVYP3eKhgGLZ2G+cg9TZZjTCsNMzHqewM/1+qStzTQCdNmggXCGcIfC8HcYsBfdv4SicxBq8ff+BDyveFZMFyREpQNqX/fLmlz16cmxHvBQ9qqA+IzIsBJ7i/mrG78jBhkJsvtcHMHvTviXascCSQP1TCn58D6HJ/Agww6HFTJe/B6sX0Utzm0URE4jZK7wnhrx0q2H8OkCXc5ZwuXALqtvk0uWmZo2RMnIGQNi4nb5AUsGV8pNirhS16MfqZeJ4q0HGaIVscRp4jazab6kMVKusbuqQB1cZNbaao7mP1HAUVCd5geWQL4CQQIc6gv0q3KT2E45d0YeYqpy/RppKMWmg1+aQ5wVGQW4/p2mqXos71FKy6vP0ur6txd8=>"
|
||||
|
||||
footer:
|
||||
- 'Head by id: <head:id:%uuid%>' # This will obviously not work in offline mode
|
||||
- 'Head by name: <head:name:%player%>' # May or may not work in offline mode (see above for full explanation)
|
||||
- 'Head by raw texture: <head:signed_texture:ewogICJ0aW1lc3RhbXAiIDogMTc1NjMxNjc0OTk4MywKICAicHJvZmlsZUlkIiA6ICIyMzdkOGI1NTNmOTc0NzQ5YWE2MGU5ZmU5N2I0NTA2MiIsCiAgInByb2ZpbGVOYW1lIiA6ICJfTkVaTkFNWV8iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmY1ZTNhNWRmODkxYzEzMTdhZjUzMmMzNmY4NDgxNDBhNDBmMGZhMGNlZGRmZmEwNWU5NGU1NzU1OGQxY2Q4YyIKICAgIH0KICB9Cn0=;YFVoqA0CqWgnZhme5s/M7xtadURyLkyLWVsh0vN8BqdfwzN32nqlBJb2pRjkxIs0V21EOeKbGFjgdKQKzPRQdf6sTFQ/x0KOjUg8A/neAiwCvDSDrsTQ2Yf7yoVP6M2PpuB7te19N+I139mGY5psxU100x6GiV/uFfpUfG3XBJog43JtXzRJ9fRtoSeIEzqLrkpCmt6o5Mzo6GZFZc4CtI76OU90Mg9ZvTZTYelvtjFtllxTtkNZCcglzvh5R19+qtzLOzEr+N8m4Ed+5yZyezEb3LeeSgmbSLIjuOKuUupE+2F6yVYP3eKhgGLZ2G+cg9TZZjTCsNMzHqewM/1+qStzTQCdNmggXCGcIfC8HcYsBfdv4SicxBq8ff+BDyveFZMFyREpQNqX/fLmlz16cmxHvBQ9qqA+IzIsBJ7i/mrG78jBhkJsvtcHMHvTviXascCSQP1TCn58D6HJ/Agww6HFTJe/B6sX0Utzm0URE4jZK7wnhrx0q2H8OkCXc5ZwuXALqtvk0uWmZo2RMnIGQNi4nb5AUsGV8pNirhS16MfqZeJ4q0HGaIVscRp4jazab6kMVKusbuqQB1cZNbaao7mP1HAUVCd5geWQL4CQQIc6gv0q3KT2E45d0YeYqpy/RppKMWmg1+aQ5wVGQW4/p2mqXos71FKy6vP0ur6txd8=>'
|
||||
- 'Head by player: <head:player:%player%>'
|
||||
- 'Head by mineskin: <head:mineskin:37e93c8e12cd426cb28fce31969e0674>'
|
||||
- 'Head by texture: <head:texture:ff9bb9e56125c8227b94bbda9f6e0f862931c229255ba8f1205d13c44c1bb561>'
|
||||
```
|
||||
With dynamic UUID and name, but hardcoded texture.
|
||||
<img width="261" height="61" alt="image" src="https://github.com/user-attachments/assets/88e61963-9408-42fe-8075-95479c7fadda" />
|
||||
|
||||
<img width="331" height="111" alt="image" src="https://github.com/user-attachments/assets/5912975a-a892-42c3-b9ab-7eab33ee42b6" />
|
||||
|
||||
Additionally, you can use
|
||||
* `<head:mineskin:ID>` to display skin from mineskin
|
||||
* `<head:texture:VALUE>` to display skin from defined texture
|
||||
## Compatibility with < 1.21.9
|
||||
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.
|
||||
|
||||
TAB will retrieve skin value + signature and use those values.
|
||||
| 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 |
|
||||
|
||||
# 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.
|
||||
@@ -158,9 +170,6 @@ TAB has [MiniMessage](https://docs.advntr.dev/minimessage/format.html) hook, how
|
||||
When MiniMessage is detected on your server, it is automatically used. All codes are translated to MiniMessage syntax and then parsed by MiniMessage.
|
||||
|
||||
# Config options
|
||||
> [!NOTE]
|
||||
> This is a draft for future versions - TAB 5.3.0+.
|
||||
|
||||
| Option name | Default value | Description |
|
||||
|--------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| minimessage-support | true | Enables / Disables MiniMessage support (if it's available). |
|
||||
@@ -177,8 +186,8 @@ conditions:
|
||||
rgb:
|
||||
conditions:
|
||||
- '%player-version-id%>=735'
|
||||
yes: "#00FF00 RGB text"
|
||||
no: "&a Legacy text"
|
||||
true: "#00FF00 RGB text"
|
||||
false: "&a Legacy text"
|
||||
```
|
||||
Then, use this condition with `%condition:rgb%`.
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# Content
|
||||
* [Backend](#backend)
|
||||
* [BungeeCord / Velocity](#bungeecord--velocity)
|
||||
* [Proxy installation](#proxy-installation)
|
||||
* [Backend installation](#backend-installation)
|
||||
* [Mixed installation](#mixed-installation)
|
||||
* [Proxy](#proxy)
|
||||
* [Proxy installation](#proxy-installation)
|
||||
* [Backend installation](#backend-installation)
|
||||
* [Mixed installation](#mixed-installation)
|
||||
* [Updating the plugin](#updating-the-plugin)
|
||||
|
||||
# Backend
|
||||
Installation on Bukkit / Fabric / Sponge is as simple as it can be.
|
||||
Put the plugin into the plugins folder and restart the server.
|
||||
Dynamic (re)loads at runtime such as `/plugman load tab` to load the plugin are fully supported.
|
||||
However, plugins hooking into TAB may break when you use such ways to (re)load it.
|
||||
Installation on backend servers is as simple as it can be.
|
||||
Put the plugin into the plugins folder and restart the server.
|
||||
|
||||
# BungeeCord / Velocity
|
||||
# Proxy
|
||||
When running a network, you have 3 options:
|
||||
* Installing TAB only on the proxy (recommended)
|
||||
* Installing TAB on all backend servers instead
|
||||
|
||||
@@ -7,8 +7,8 @@ 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)
|
||||
|
||||
# Displaying kill / death counter in tablist
|
||||
This example uses PlaceholderAPI placeholders.
|
||||
@@ -37,7 +37,7 @@ _DEFAULT_:
|
||||
```
|
||||
This requires Vault and a permission plugin to work.
|
||||
If you have luckperms installed on the same server instance, you can use TAB's internal `%luckperms-prefix%` (and suffix) placeholders instead.
|
||||
**Note:** If TAB is on bungeecord, and you want prefixes to be taken from backend servers, use `%vault_prefix%` (or `%luckperms_prefix%`) with [PlaceholderAPI support on bungeecord](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge).
|
||||
**Note:** If TAB is on a proxy, and you want prefixes to be taken from backend servers, use `%vault_prefix%` (or `%luckperms_prefix%`) with [PlaceholderAPI support on proxy](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge).
|
||||
|
||||
# Showing player's version in tablist
|
||||
Run `/tab group _DEFAULT_ tabsuffix " &8[&3%player-version%&8]"` or any other color combination you want. You can also include other placeholders with it. You can also modify it directly in **groups.yml** file instead. With the provided example command, you'll get the following result:
|
||||
@@ -63,13 +63,13 @@ conditions:
|
||||
tps:
|
||||
conditions:
|
||||
- "%tps%>=19"
|
||||
yes: "<#55FF55>%tps%</#00AA00>"
|
||||
no: "%condition:tps2%"
|
||||
true: "<#55FF55>%tps%</#00AA00>"
|
||||
false: "%condition:tps2%"
|
||||
tps2:
|
||||
conditions:
|
||||
- "%tps%>=15"
|
||||
yes: "<#FFFF55>%tps%</#FFAA00>"
|
||||
no: "<#FF5555>%tps%</#AA0000>"
|
||||
true: "<#FFFF55>%tps%</#FFAA00>"
|
||||
false: "<#FF5555>%tps%</#AA0000>"
|
||||
```
|
||||
To use this, we will use `%condition:tps%`.
|
||||
Now, repeat for mspt and ping. For mspt, purpur's intervals are <40, <50, 50+. For ping, it's <100, <200, 200+.
|
||||
@@ -80,23 +80,23 @@ conditions:
|
||||
mspt:
|
||||
conditions:
|
||||
- "%mspt%<40"
|
||||
yes: "<#55FF55>%mspt%</#00AA00>"
|
||||
no: "%condition:mspt2%"
|
||||
true: "<#55FF55>%mspt%</#00AA00>"
|
||||
false: "%condition:mspt2%"
|
||||
mspt2:
|
||||
conditions:
|
||||
- "%mspt%<50"
|
||||
yes: "<#FFFF55>%mspt%</#FFAA00>"
|
||||
no: "<#FF5555>%mspt%</#AA0000>"
|
||||
true: "<#FFFF55>%mspt%</#FFAA00>"
|
||||
false: "<#FF5555>%mspt%</#AA0000>"
|
||||
ping:
|
||||
conditions:
|
||||
- "%ping%<100"
|
||||
yes: "<#55FF55>%ping%</#00AA00>"
|
||||
no: "%condition:ping2%"
|
||||
true: "<#55FF55>%ping%</#00AA00>"
|
||||
false: "%condition:ping2%"
|
||||
ping2:
|
||||
conditions:
|
||||
- "%ping%<200"
|
||||
yes: "<#FFFF55>%ping%</#FFAA00>"
|
||||
no: "<#FF5555>%ping%</#AA0000>"
|
||||
true: "<#FFFF55>%ping%</#FFAA00>"
|
||||
false: "<#FF5555>%ping%</#AA0000>"
|
||||
```
|
||||
To use them, we will use `%condition:mspt%` and `%condition:ping%`. Purpur's TPSBar is using mspt as bossbar progress with range from 0 to 50. This can be achieved with PlaceholderAPI's math expansion: `%math_0_{tab_placeholder_mspt}*2%`. It always uses `GREEN` color and `NOTCHED_20` style. Let's put all of this together:
|
||||
```
|
||||
@@ -137,8 +137,8 @@ conditions:
|
||||
rankOrTeam:
|
||||
conditions:
|
||||
- "%bw1058_player_team%="
|
||||
yes: "%luckperms-prefix%"
|
||||
no: "%bw1058_player_team%"
|
||||
true: "%luckperms-prefix%"
|
||||
false: "%bw1058_player_team%"
|
||||
```
|
||||
And then do `/tab group _DEFAULT_ tabprefix %condition:rankOrTeam%` and `/tab group _DEFAULT_ tagprefix %condition:rankOrTeam%` so it shows in your nametag and in your tablist name.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,8 +8,8 @@ The usage can be checked at any time using [**/tab cpu**](https://github.com/NEZ
|
||||
* [#4 - [All platforms] Don't use animations](#4---all-platforms-dont-use-animations)
|
||||
* [#5 - [All platforms] Disable tablist name formatting](#5---all-platforms-disable-tablist-name-formatting)
|
||||
* [#6 - [All platforms] Increase permission refresh interval](#6---all-platforms-increase-permission-refresh-interval)
|
||||
* [#7 - [BungeeCord / Velocity] Disable TAB expansion](#7---bungeecord--velocity-disable-tab-expansion)
|
||||
* [#8 - [BungeeCord / Velocity] Disable update-latency option in global playerlist](#8---bungeecord--velocity-disable-update-latency-option-in-global-playerlist)
|
||||
* [#7 - [Proxies] Disable TAB expansion](#7---proxies-disable-tab-expansion)
|
||||
* [#8 - [Proxies] Disable update-latency option in global playerlist](#8---proxies-disable-update-latency-option-in-global-playerlist)
|
||||
|
||||
# #1 - [All platforms] Static text instead of placeholders
|
||||
Static text doesn't need to be refreshed periodically, resulting in better performance.
|
||||
@@ -79,7 +79,7 @@ Refreshes them every second.
|
||||
If you don't need any permission/group changes to take effect within a second,
|
||||
you can increase this value for better performance.
|
||||
|
||||
# #7 - [BungeeCord / Velocity] Disable TAB expansion
|
||||
# #7 - [Proxies] Disable TAB expansion
|
||||
To maximize the performance of TAB's response to PlaceholderAPI request,
|
||||
values are tracked in advance and then quickly returned.
|
||||
This process takes resources and is especially heavy on proxy installation,
|
||||
@@ -90,5 +90,5 @@ Disabling TAB's PlaceholderAPI expansion if you don't use it improves performanc
|
||||
placeholders:
|
||||
register-tab-expansion: false
|
||||
```
|
||||
# #8 - [BungeeCord / Velocity] Disable update-latency option in global playerlist
|
||||
# #8 - [Proxies] Disable update-latency option in global playerlist
|
||||
When [Global playerlist](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist) is enabled, you can enable / disable [update-latency](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Global-playerlist#:~:text=other%20unlisted%20servers.-,update%2Dlatency,-false) option (open the link for detailed description of the option). Disabling this option massively improves performance.
|
||||
@@ -1,23 +1,23 @@
|
||||
# Content
|
||||
* [About](#about)
|
||||
* [Internal placeholders](#internal-placeholders)
|
||||
* [Universal](#universal)
|
||||
* [Backend only](#backend-only)
|
||||
* [BungeeCord only](#bungeecord-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).
|
||||
@@ -44,6 +44,7 @@ These placeholders are available on all platforms.
|
||||
| %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 |
|
||||
@@ -67,15 +68,16 @@ These placeholders are available on all platforms.
|
||||
| %% | -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 (Bukkit / Sponge / Fabric), they don't work when TAB is on BungeeCord. If you have TAB installed on BungeeCord 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 BungeeCord.
|
||||
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% |
|
||||
| %mspt% | 1000 | Server's current milliseconds per tick (not available on Spigot) | - |
|
||||
| %deaths% | 1000 | Player's death count | %statistic_deaths% |
|
||||
|
||||
## BungeeCord only
|
||||
## Proxy only
|
||||
| Identifier | Refresh interval | Description |
|
||||
|------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| %server% | -1 | Name of server where player is, defined in proxy's config. |
|
||||
@@ -166,17 +168,17 @@ When using a placeholder, you should know if it's a [TAB's internal placeholder]
|
||||
|
||||
## Internal placeholder is not working
|
||||
All TAB's internal placeholders are listed above.
|
||||
Some of them are bukkit only (meaning they won't work on bungeecord) and some are bungee only.
|
||||
Some of them are backend only (meaning they won't work on proxy) and some are proxy only.
|
||||
> [!IMPORTANT]
|
||||
> Trying to use a bukkit-only placeholder on bungeecord will not work.
|
||||
> Trying to use a backend-only placeholder on proxy will not work.
|
||||
> If you want to use those,
|
||||
> [set up PlaceholderAPI support on bungeecord](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge)
|
||||
> [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.
|
||||
|
||||
## PlaceholderAPI placeholder is not working
|
||||
If you have TAB installed on bungeecord, make sure you [set up PlaceholderAPI support on bungeecord](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) as there is no other way to retrieve data from technically a different server.
|
||||
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.
|
||||
|
||||
@@ -3,8 +3,8 @@ This is a quick guide for [PlaceholderAPI](https://www.spigotmc.org/resources/pl
|
||||
|
||||
# Installation
|
||||
PlaceholderAPI is a standalone bukkit plugin that can be installed just like any other plugin.
|
||||
It does not support BungeeCord.
|
||||
If you have TAB on BungeeCord and wish to use PlaceholderAPI placeholders,
|
||||
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.
|
||||
Alongside other features, it forwards PlaceholderAPI support to the proxy.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# About
|
||||
When TAB is installed on BungeeCord / Velocity, the plugin has very limited access to information. It is unable to retrieve internal server data, as well as hook into backend plugins, because it's a different server.
|
||||
When TAB is installed on a proxy, the plugin has very limited access to information. It is unable to retrieve internal server data, as well as hook into backend plugins, because it's a different server.
|
||||
Bridge is a backend plugin that sends this data to the proxy, allowing TAB to work better when installed on a proxy.
|
||||
You can download it from [GitHub](https://github.com/NEZNAMY/TAB-Bridge/releases) or [Spigot](https://www.spigotmc.org/resources/83966).
|
||||
You can download it from [GitHub](https://github.com/NEZNAMY/TAB-Bridge/releases), [Spigot](https://www.spigotmc.org/resources/83966) or [Modrinth](https://modrinth.com/plugin/tab-bridge).
|
||||
|
||||
# Installation
|
||||
Installation is as simple as it can be.
|
||||
@@ -13,10 +13,10 @@ Bridge itself has no config file, as everything is configured in the main plugin
|
||||
* Forwarding PlaceholderAPI support to the proxy
|
||||
* Allow detection if player is invisible to make their nametag invisible (compensation for 1.8.x client sided bug)
|
||||
* Allow detection if a player is disguised to disable collision to prevent endless push
|
||||
* Forwarding permission groups of players if no permission plugin is installed on BungeeCord
|
||||
* Forwarding permission groups of players if no permission plugin is installed on the proxy
|
||||
* Forwarding vanish status for compatibility with global playerlist / layout features
|
||||
* Forward player's world for %world% and per-world settings
|
||||
* Allow TAB's PlaceholderAPI expansion with TAB on BungeeCord
|
||||
* Allow TAB's PlaceholderAPI expansion with TAB on proxy
|
||||
* Forward player's gamemode for spectator fix feature to work
|
||||
|
||||
# Compatibility
|
||||
|
||||
+4
-1
@@ -6,4 +6,7 @@ is in quality of the report (typically providing correct steps to reproduce that
|
||||
* Test **experimental builds**, which can be found in [GitHub Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
|
||||
This way you will help make the actual releases more stable / help get stable releases sooner.
|
||||
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
|
||||
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples.
|
||||
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples.
|
||||
* Create spark reports and then send them to me. I will analyze them and try to find optimizations to make the plugin run faster. Commands:
|
||||
* `/spark profiler start --timeout 300 --thread * --interval 0.1`
|
||||
* `/spark profiler start --timeout 300 --thread * --interval 0.1 --alloc`
|
||||
@@ -1,5 +1,5 @@
|
||||
dependencies {
|
||||
compileOnlyApi("org.jetbrains:annotations:24.1.0")
|
||||
compileOnlyApi("org.jetbrains:annotations:26.0.2")
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
|
||||
@@ -77,7 +77,7 @@ public interface TabPlayer {
|
||||
* Changes expected profile name of the player. This adapts all name-bound features
|
||||
* to use this new profile name if another plugin changed profile name of the player. <p>
|
||||
* Automatic profile name change detection is available on Bukkit, BungeeCord and Fabric,
|
||||
* therefore using this is redundant there. This function is only needed on Sponge and Velocity,
|
||||
* therefore using this is redundant there. This function is only needed on Velocity,
|
||||
* where the detection is not available. <p>
|
||||
* Warning: This function does NOT change player's profile name, it only updates the tracked
|
||||
* name inside the plugin.
|
||||
@@ -96,6 +96,26 @@ public interface TabPlayer {
|
||||
@NotNull
|
||||
String getExpectedProfileName();
|
||||
|
||||
/**
|
||||
* Returns name of the server the player is connected to.
|
||||
* This requires TAB on proxy.
|
||||
* On backend installation, this method returns {@code "N/A"}.
|
||||
*
|
||||
* @return Player's server on proxy or {@code "N/A"} on backend installation
|
||||
*/
|
||||
@NotNull
|
||||
String getServer();
|
||||
|
||||
/**
|
||||
* Returns name of the world the player is in.
|
||||
* On proxy installation, TAB-Bridge must be installed to forward this info to the proxy.
|
||||
* Otherwise, it will return {@code "N/A"}.
|
||||
*
|
||||
* @return Player's world or {@code "N/A"} on proxy installation without TAB-Bridge installed
|
||||
*/
|
||||
@NotNull
|
||||
String getWorld();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this player is a bedrock player, {@code false} if not.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:8.3.6")
|
||||
implementation("io.freefair.gradle:lombok-plugin:8.13")
|
||||
implementation("dev.architectury.loom:dev.architectury.loom.gradle.plugin:1.11-SNAPSHOT")
|
||||
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")
|
||||
}
|
||||
+57
-56
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "5.3.0"
|
||||
version = "5.4.0"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
@@ -13,64 +13,65 @@ allprojects {
|
||||
ext.set("credits", "Joseph T. McQuigg (JT122406)")
|
||||
}
|
||||
|
||||
val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bukkit.paper1205,
|
||||
projects.bukkit.paper1212,
|
||||
projects.bukkit.paper1214,
|
||||
projects.bukkit.paper1219,
|
||||
projects.bukkit.v17R1,
|
||||
projects.bukkit.v17R2,
|
||||
projects.bukkit.v17R3,
|
||||
projects.bukkit.v17R4,
|
||||
projects.bukkit.v18R1,
|
||||
projects.bukkit.v18R2,
|
||||
projects.bukkit.v18R3,
|
||||
projects.bukkit.v19R1,
|
||||
projects.bukkit.v19R2,
|
||||
projects.bukkit.v110R1,
|
||||
projects.bukkit.v111R1,
|
||||
projects.bukkit.v112R1,
|
||||
projects.bukkit.v113R1,
|
||||
projects.bukkit.v113R2,
|
||||
projects.bukkit.v114R1,
|
||||
projects.bukkit.v115R1,
|
||||
projects.bukkit.v116R1,
|
||||
projects.bukkit.v116R2,
|
||||
projects.bukkit.v116R3,
|
||||
projects.bukkit.v117R1,
|
||||
projects.bukkit.v118R1,
|
||||
projects.bukkit.v118R2,
|
||||
projects.bukkit.v119R1,
|
||||
projects.bukkit.v119R2,
|
||||
projects.bukkit.v119R3,
|
||||
projects.bukkit.v120R1,
|
||||
projects.bukkit.v120R2,
|
||||
projects.bukkit.v120R3,
|
||||
projects.bukkit.v120R4,
|
||||
projects.bukkit.v121R1,
|
||||
projects.bukkit.v121R2,
|
||||
projects.bukkit.v121R3,
|
||||
projects.bukkit.v121R4,
|
||||
projects.bukkit.v121R5,
|
||||
projects.bukkit.v121R6,
|
||||
projects.bungeecord,
|
||||
projects.velocity,
|
||||
projects.sponge,
|
||||
projects.fabric,
|
||||
projects.neoforge,
|
||||
projects.forge
|
||||
).map { it.dependencyProject }
|
||||
val platformPaths = setOf(
|
||||
":bukkit",
|
||||
":bukkit:paper_1_20_5",
|
||||
":bukkit:paper_1_21_2",
|
||||
":bukkit:paper_1_21_4",
|
||||
":bukkit:paper_1_21_9",
|
||||
":bukkit:paper_1_21_11",
|
||||
":bukkit:v1_7_R1",
|
||||
":bukkit:v1_7_R2",
|
||||
":bukkit:v1_7_R3",
|
||||
":bukkit:v1_7_R4",
|
||||
":bukkit:v1_8_R1",
|
||||
":bukkit:v1_8_R2",
|
||||
":bukkit:v1_8_R3",
|
||||
":bukkit:v1_9_R1",
|
||||
":bukkit:v1_9_R2",
|
||||
":bukkit:v1_10_R1",
|
||||
":bukkit:v1_11_R1",
|
||||
":bukkit:v1_12_R1",
|
||||
":bukkit:v1_13_R1",
|
||||
":bukkit:v1_13_R2",
|
||||
":bukkit:v1_14_R1",
|
||||
":bukkit:v1_15_R1",
|
||||
":bukkit:v1_16_R1",
|
||||
":bukkit:v1_16_R2",
|
||||
":bukkit:v1_16_R3",
|
||||
":bukkit:v1_17_R1",
|
||||
":bukkit:v1_18_R1",
|
||||
":bukkit:v1_18_R2",
|
||||
":bukkit:v1_19_R1",
|
||||
":bukkit:v1_19_R2",
|
||||
":bukkit:v1_19_R3",
|
||||
":bukkit:v1_20_R1",
|
||||
":bukkit:v1_20_R2",
|
||||
":bukkit:v1_20_R3",
|
||||
":bukkit:v1_20_R4",
|
||||
":bukkit:v1_21_R1",
|
||||
":bukkit:v1_21_R2",
|
||||
":bukkit:v1_21_R3",
|
||||
":bukkit:v1_21_R4",
|
||||
":bukkit:v1_21_R5",
|
||||
":bukkit:v1_21_R6",
|
||||
":bukkit:v1_21_R7",
|
||||
":bungeecord",
|
||||
":velocity",
|
||||
":fabric",
|
||||
":neoforge",
|
||||
":forge"
|
||||
)
|
||||
|
||||
val special = setOf(
|
||||
projects.api,
|
||||
projects.shared
|
||||
).map { it.dependencyProject }
|
||||
val specialPaths = setOf(
|
||||
":api",
|
||||
":shared"
|
||||
)
|
||||
|
||||
subprojects {
|
||||
when (this) {
|
||||
in platforms -> plugins.apply("tab.platform-conventions")
|
||||
in special -> plugins.apply("tab.standard-conventions")
|
||||
when (path) {
|
||||
in platformPaths -> plugins.apply("tab.platform-conventions")
|
||||
in specialPaths -> plugins.apply("tab.standard-conventions")
|
||||
else -> plugins.apply("tab.base-conventions")
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // YamlAssist
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.viaversion.com/")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
maven {
|
||||
name = "luck-repo"
|
||||
url = uri("https://repo.lucko.me/")
|
||||
content {
|
||||
includeModule("me.lucko", "spark-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val version = "1.20.6-R0.1-SNAPSHOT" // 1.20.5 has a broken dependency, but there were no changes, so it's ok
|
||||
|
||||
+6
-3
@@ -133,7 +133,8 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
|
||||
}
|
||||
@@ -142,9 +143,11 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
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) {
|
||||
|
||||
+13
-12
@@ -3,13 +3,11 @@ package me.neznamy.tab.platforms.paper_1_20_5;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
@@ -20,7 +18,6 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -34,8 +31,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -107,8 +102,14 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
|
||||
return new ClientboundTabListPacket(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
|
||||
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
|
||||
@@ -126,9 +127,8 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(nmsData.profileId());
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -146,8 +146,9 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // YamlAssist
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.viaversion.com/")
|
||||
maven {
|
||||
name = "luck-repo"
|
||||
url = uri("https://repo.lucko.me/")
|
||||
content {
|
||||
includeModule("me.lucko", "spark-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val version = "1.21.11-R0.1-SNAPSHOT"
|
||||
|
||||
dependencies {
|
||||
implementation(projects.bukkit)
|
||||
paperweight.paperDevBundle(version)
|
||||
compileOnly("io.papermc.paper:paper-api:${version}")
|
||||
}
|
||||
|
||||
tasks.compileJava {
|
||||
options.release.set(21)
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package me.neznamy.tab.platforms.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 PaperComponentConverter 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);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package me.neznamy.tab.platforms.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 PaperPacketScoreboard 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 PaperPacketScoreboard(@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);
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
package me.neznamy.tab.platforms.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 lombok.NonNull;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct mojang-mapped code.
|
||||
*/
|
||||
public class PaperPacketTabList 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);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
* @param player
|
||||
* Player this tablist will belong to
|
||||
*/
|
||||
public PaperPacketTabList(@NotNull BukkitTabPlayer player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntry(@NonNull UUID entry) {
|
||||
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
|
||||
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLatency(@NonNull UUID entry, int latency) {
|
||||
sendPacket(updateLatency, entry, "", null, false, latency, 0, null, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGameMode(@NonNull UUID entry, int gameMode) {
|
||||
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListed(@NonNull UUID entry, boolean listed) {
|
||||
sendPacket(updateListed, entry, "", null, listed, 0, 0, null, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListOrder(@NonNull UUID entry, int listOrder) {
|
||||
sendPacket(updateListOrder, entry, "", null, false, 0, 0, null, listOrder, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHat(@NonNull UUID entry, boolean showHat) {
|
||||
sendPacket(updateHat, entry, "", null, false, 0, 0, null, 0, showHat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntry0(@NonNull Entry entry) {
|
||||
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
|
||||
entry.getGameMode(), entry.getDisplayName(), entry.getListOrder(), entry.isShowHat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@NonNull UUID entry) {
|
||||
return true; // TODO?
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Skin getSkin() {
|
||||
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().properties().get(TEXTURES_PROPERTY);
|
||||
if (properties.isEmpty()) return null; // Offline mode
|
||||
Property property = properties.iterator().next();
|
||||
return new Skin(property.value(), property.signature());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
|
||||
return new ClientboundTabListPacket(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
|
||||
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
|
||||
boolean rewriteEntry = false;
|
||||
Component displayName = nmsData.displayName();
|
||||
int latency = nmsData.latency();
|
||||
int gameMode = nmsData.gameMode().getId();
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
|
||||
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
|
||||
if (getForcedLatency() != null) {
|
||||
latency = getForcedLatency();
|
||||
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.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder, boolean showHat) {
|
||||
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
id,
|
||||
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
|
||||
listed,
|
||||
latency,
|
||||
GameType.byId(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
showHat,
|
||||
listOrder,
|
||||
null
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
* @param id
|
||||
* Profile ID
|
||||
* @param name
|
||||
* Profile name
|
||||
* @param skin
|
||||
* Player skin
|
||||
* @return GameProfile from given parameters
|
||||
*/
|
||||
@NotNull
|
||||
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
|
||||
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
|
||||
if (skin != null) {
|
||||
builder.put(TEXTURES_PROPERTY, new Property(TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
|
||||
}
|
||||
return new GameProfile(id, name, new PropertyMap(builder.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet to the player.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to send
|
||||
*/
|
||||
private void sendPacket(@NotNull Packet<?> packet) {
|
||||
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // YamlAssist
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.viaversion.com/")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
maven {
|
||||
name = "luck-repo"
|
||||
url = uri("https://repo.lucko.me/")
|
||||
content {
|
||||
includeModule("me.lucko", "spark-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val version = "1.21.3-R0.1-SNAPSHOT" // 1.21.2 causes gradle error, but the versions are interchangeable
|
||||
|
||||
+6
-3
@@ -133,7 +133,8 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
|
||||
}
|
||||
@@ -142,9 +143,11 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
|
||||
|
||||
+13
-12
@@ -3,13 +3,11 @@ package me.neznamy.tab.platforms.paper_1_21_2;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
@@ -20,7 +18,6 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -35,8 +32,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -108,8 +103,14 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
|
||||
return new ClientboundTabListPacket(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
|
||||
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
|
||||
@@ -127,9 +128,8 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(nmsData.profileId());
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -147,8 +147,9 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // YamlAssist
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.viaversion.com/")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
maven {
|
||||
name = "luck-repo"
|
||||
url = uri("https://repo.lucko.me/")
|
||||
content {
|
||||
includeModule("me.lucko", "spark-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val version = "1.21.4-R0.1-SNAPSHOT"
|
||||
|
||||
+6
-3
@@ -133,7 +133,8 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
|
||||
}
|
||||
@@ -142,9 +143,11 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
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) {
|
||||
|
||||
+13
-12
@@ -3,13 +3,11 @@ package me.neznamy.tab.platforms.paper_1_21_4;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
@@ -20,7 +18,6 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -36,8 +33,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -109,8 +104,14 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
|
||||
return new ClientboundTabListPacket(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
|
||||
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
|
||||
@@ -128,9 +129,8 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(nmsData.profileId());
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -148,8 +148,9 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
plugins {
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://jitpack.io") // YamlAssist
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/")
|
||||
maven("https://repo.viaversion.com/")
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
maven {
|
||||
name = "luck-repo"
|
||||
url = uri("https://repo.lucko.me/")
|
||||
content {
|
||||
includeModule("me.lucko", "spark-api")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val version = "1.21.9-R0.1-SNAPSHOT"
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation provider using direct Mojang-mapped NMS code for version 1.21.9.
|
||||
* Implementation provider using direct Mojang-mapped NMS code for versions 1.21.9 - 1.21.10.
|
||||
*/
|
||||
@Getter
|
||||
public class PaperImplementationProvider implements ImplementationProvider {
|
||||
|
||||
+6
-3
@@ -133,7 +133,8 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
|
||||
}
|
||||
@@ -142,9 +143,11 @@ public class PaperPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
|
||||
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) {
|
||||
|
||||
+13
-12
@@ -5,13 +5,11 @@ import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.chat.component.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
@@ -22,7 +20,6 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -38,8 +35,6 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -111,8 +106,14 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof ClientboundTabListPacket tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
|
||||
return new ClientboundTabListPacket(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
|
||||
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
|
||||
@@ -130,9 +131,8 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(nmsData.profileId());
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,9 @@ public class PaperPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -52,18 +51,6 @@ public class BukkitEventListener implements EventListener<Player>, Listener {
|
||||
worldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to command event to potentially cancel it.
|
||||
*
|
||||
* @param e
|
||||
* Command event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGH) // HIGH sounds about fair
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (e.isCancelled()) return;
|
||||
if (command(e.getPlayer().getUniqueId(), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public TabPlayer createPlayer(@NotNull Player player) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public class BukkitTAB extends JavaPlugin {
|
||||
} catch (UnsupportedOperationException 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.9. If you just updated to a new Minecraft version, check for TAB updates.");
|
||||
"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] ================================================================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import me.neznamy.tab.shared.platform.TabList;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.platform.impl.AdventureBossBar;
|
||||
import me.neznamy.tab.shared.platform.impl.DummyBossBar;
|
||||
import me.neznamy.tab.shared.util.PerformanceUtil;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
@@ -47,7 +46,8 @@ import net.milkbowl.vault.permission.Permission;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.command.defaults.BukkitCommand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
@@ -56,8 +56,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
@@ -84,13 +84,24 @@ 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 = findImplementationProvider();
|
||||
private ImplementationProvider implementationProvider;
|
||||
|
||||
private final boolean modernOnlinePlayers;
|
||||
|
||||
/** Command map for dynamic command registering */
|
||||
private final SimpleCommandMap commandMap;
|
||||
private final Map<String, Command> knownCommands;
|
||||
|
||||
/** List of custom commands registered to be able to unregister them on reload */
|
||||
private final List<Command> customCommands = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given plugin.
|
||||
*
|
||||
@@ -98,9 +109,14 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
* Plugin
|
||||
*/
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
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();
|
||||
try {
|
||||
Object server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
|
||||
recentTps = ((double[]) server.getClass().getField("recentTps").get(server));
|
||||
@@ -110,20 +126,20 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PremiumVanish")) {
|
||||
new BukkitPremiumVanishHook().register();
|
||||
}
|
||||
commandMap = (SimpleCommandMap) Bukkit.getServer().getClass().getMethod("getCommandMap").invoke(Bukkit.getServer());
|
||||
knownCommands = (Map<String, Command>) ReflectionUtils.getField(SimpleCommandMap.class, "knownCommands").get(commandMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private ImplementationProvider findImplementationProvider() {
|
||||
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
|
||||
String serverPackage = array.length > 3 ? array[3] : null;
|
||||
|
||||
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
|
||||
@@ -131,11 +147,9 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +178,10 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
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;
|
||||
}
|
||||
@@ -190,8 +207,6 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
manager.registerInternalPlayerPlaceholder("%vault-suffix%", 1000, p -> chat.getPlayerSuffix((Player) p.getPlayer()));
|
||||
}
|
||||
}
|
||||
// Override for the PAPI placeholder to prevent console errors on unsupported server versions when ping field changes
|
||||
manager.registerPlayerPlaceholder("%player_ping%", p -> PerformanceUtil.toString(((TabPlayer) p).getPing()));
|
||||
BackendPlatform.super.registerPlaceholders();
|
||||
}
|
||||
|
||||
@@ -273,7 +288,7 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getServerVersionInfo() {
|
||||
return "[Bukkit] " + Bukkit.getName() + " - " + Bukkit.getBukkitVersion().split("-")[0];
|
||||
return "[Bukkit] " + Bukkit.getName() + " - " + Bukkit.getBukkitVersion().split("-")[0] + " (" + serverPackage + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -357,6 +372,37 @@ public class BukkitPlatform implements BackendPlatform {
|
||||
return serverVersion.getMinorVersion() >= 13;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCustomCommand(@NotNull String commandName, @NotNull Consumer<TabPlayer> function) {
|
||||
Command cmd = new BukkitCommand(commandName) {
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender commandSender, @NotNull String alias, @NotNull String[] args) {
|
||||
if (commandSender instanceof ConsoleCommandSender) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
commandMap.register(commandName, cmd);
|
||||
customCommands.add(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterAllCustomCommands() {
|
||||
for (Command command : customCommands) {
|
||||
knownCommands.remove(command.getName());
|
||||
knownCommands.remove(command.getName() + ":" + command.getName());
|
||||
command.unregister(commandMap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public GroupManager detectPermissionPlugin() {
|
||||
|
||||
+14
-9
@@ -23,6 +23,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final IScoreboardCriteria INTEGER = new ScoreboardBaseCriteria("dummy");
|
||||
private static final IScoreboardCriteria HEARTS = new ScoreboardHealthCriteria("health");
|
||||
|
||||
private static final Field TeamPacket_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardTeam.class, String.class).get(0);
|
||||
private static final Field TeamPacket_ACTION = ReflectionUtils.getInstanceFields(PacketPlayOutScoreboardTeam.class, int.class).get(1);
|
||||
@@ -46,9 +48,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), IScoreboardCriteria.b);
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
@@ -65,9 +67,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@@ -121,7 +123,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -132,11 +135,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+64
-15
@@ -37,6 +37,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData, EnumGamemode.class);
|
||||
|
||||
private static final Field HEADER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(0);
|
||||
private static final Field FOOTER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(1);
|
||||
|
||||
/**
|
||||
@@ -92,11 +93,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,48 +111,99 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SneakyThrows
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
IChatBaseComponent header = (IChatBaseComponent) HEADER.get(tablist);
|
||||
IChatBaseComponent footer = (IChatBaseComponent) FOOTER.get(tablist);
|
||||
if (header != this.header.convert() || footer != this.footer.convert()) {
|
||||
return newHeaderFooter(this.header, this.footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+14
-9
@@ -23,6 +23,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final IScoreboardCriteria INTEGER = new ScoreboardBaseCriteria("dummy");
|
||||
private static final IScoreboardCriteria HEARTS = new ScoreboardHealthCriteria("health");
|
||||
|
||||
private static final Field TeamPacket_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardTeam.class, String.class).get(0);
|
||||
private static final Field TeamPacket_ACTION = ReflectionUtils.getInstanceFields(PacketPlayOutScoreboardTeam.class, int.class).get(1);
|
||||
@@ -46,9 +48,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), IScoreboardCriteria.b);
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
@@ -65,9 +67,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@@ -121,7 +123,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -132,11 +135,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+65
-19
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -40,6 +37,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData, EnumGamemode.class);
|
||||
|
||||
private static final Field HEADER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(0);
|
||||
private static final Field FOOTER = ReflectionUtils.getFields(PacketPlayOutPlayerListHeaderFooter.class, IChatBaseComponent.class).get(1);
|
||||
|
||||
/**
|
||||
@@ -95,11 +93,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,48 +111,99 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SneakyThrows
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
IChatBaseComponent header = (IChatBaseComponent) HEADER.get(tablist);
|
||||
IChatBaseComponent footer = (IChatBaseComponent) FOOTER.get(tablist);
|
||||
if (header != this.header.convert() || footer != this.footer.convert()) {
|
||||
return newHeaderFooter(this.header, this.footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+14
-9
@@ -23,6 +23,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
private static final ScoreboardTeamBase.EnumNameTagVisibility[] visibilities = ScoreboardTeamBase.EnumNameTagVisibility.values();
|
||||
private static final ScoreboardTeamBase.EnumTeamPush[] collisions = ScoreboardTeamBase.EnumTeamPush.values();
|
||||
private static final Scoreboard dummyScoreboard = new Scoreboard();
|
||||
private static final IScoreboardCriteria INTEGER = new ScoreboardBaseCriteria("dummy");
|
||||
private static final IScoreboardCriteria HEARTS = new ScoreboardHealthCriteria("health");
|
||||
|
||||
private static final Field TeamPacket_NAME = ReflectionUtils.getFields(PacketPlayOutScoreboardTeam.class, String.class).get(0);
|
||||
private static final Field TeamPacket_ACTION = ReflectionUtils.getInstanceFields(PacketPlayOutScoreboardTeam.class, int.class).get(1);
|
||||
@@ -46,9 +48,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void registerObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), IScoreboardCriteria.b);
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
objective.setPlatformObjective(obj);
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.REGISTER));
|
||||
}
|
||||
@@ -65,9 +67,9 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
public void updateObjective(@NonNull Objective objective) {
|
||||
ScoreboardObjective obj = (ScoreboardObjective) objective.getPlatformObjective();
|
||||
IScoreboardCriteria healthDisplay = objective.getHealthDisplay() == HealthDisplay.INTEGER ? INTEGER : HEARTS;
|
||||
ScoreboardObjective obj = new ScoreboardObjective(dummyScoreboard, objective.getName(), healthDisplay);
|
||||
obj.setDisplayName(maybeCut(objective.getTitle().toLegacyText(), Limitations.SCOREBOARD_TITLE_PRE_1_13));
|
||||
obj.a(IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(objective.getHealthDisplay().name()));
|
||||
sendPacket(new PacketPlayOutScoreboardObjective(obj, ObjectiveAction.UPDATE));
|
||||
}
|
||||
|
||||
@@ -121,7 +123,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -132,11 +135,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+65
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -96,12 +93,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
HEADER.set(packet, header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,48 +111,100 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SneakyThrows
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
HEADER.set(packet, header.convert());
|
||||
FOOTER.set(packet, footer.convert());
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
IChatBaseComponent header = (IChatBaseComponent) HEADER.get(tablist);
|
||||
IChatBaseComponent footer = (IChatBaseComponent) FOOTER.get(tablist);
|
||||
if (header != this.header.convert() || footer != this.footer.convert()) {
|
||||
return newHeaderFooter(this.header, this.footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.a = header.convert();
|
||||
packet.b = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.a = header.convert();
|
||||
packet.b = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, DisplayObjective_POSITION.getInt(packet),
|
||||
(String) DisplayObjective_OBJECTIVE_NAME.get(packet));
|
||||
@@ -147,11 +148,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = TeamPacket_ACTION.getInt(packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+62
-20
@@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -93,12 +90,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
sendPacket(packet);
|
||||
sendPacket(newHeaderFooter(header, footer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,48 +108,97 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PacketPlayOutPlayerListHeaderFooter newHeaderFooter(@NotNull TabComponent header, @NonNull TabComponent footer) {
|
||||
PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter();
|
||||
packet.header = header.convert();
|
||||
packet.footer = footer.convert();
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(packet);
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(packet)) {
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.header != header.convert() || tablist.footer != footer.convert()) {
|
||||
return newHeaderFooter(header, footer);
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = (EnumPlayerInfoAction) ACTION.get(info);
|
||||
List<Object> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (Object nmsData : (List<Object>) PLAYERS.get(info)) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = (GameProfile) PlayerInfoData_Profile.get(nmsData);
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = (IChatBaseComponent) PlayerInfoData_DisplayName.get(nmsData);
|
||||
int latency = PlayerInfoData_Latency.getInt(nmsData);
|
||||
int gameMode = ((EnumGamemode)PlayerInfoData_GameMode.get(nmsData)).getId();
|
||||
if (action == EnumPlayerInfoAction.UPDATE_DISPLAY_NAME || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_GAME_MODE || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.UPDATE_LATENCY || action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? newPlayerInfoData(
|
||||
(PacketPlayOutPlayerInfo) packet,
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendPacket(@NonNull EnumPlayerInfoAction action, @NonNull UUID id, @NonNull String name,
|
||||
@Nullable Skin skin, int latency, int gameMode, @Nullable TabComponent displayName) {
|
||||
PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(action);
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData.newInstance(
|
||||
PLAYERS.set(packet, Collections.singletonList(newPlayerInfoData(
|
||||
packet,
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SneakyThrows
|
||||
private Object newPlayerInfoData(@NonNull PacketPlayOutPlayerInfo packet, @NonNull GameProfile profile,
|
||||
int latency, @NonNull EnumGamemode gameMode, @Nullable IChatBaseComponent displayName) {
|
||||
return newPlayerInfoData.newInstance(packet, profile, latency, gameMode, displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates GameProfile from given parameters.
|
||||
*
|
||||
|
||||
+8
-5
@@ -138,7 +138,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -155,11 +156,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+44
-18
@@ -22,10 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -34,10 +31,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData.class, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData.class, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, EnumGamemode.class);
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
|
||||
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
|
||||
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
|
||||
@@ -117,29 +110,62 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = ((PacketPlayOutPlayerInfo)packet).c();
|
||||
for (PlayerInfoData nmsData : ((PacketPlayOutPlayerInfo)packet).b()) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = info.c();
|
||||
List<PlayerInfoData> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (PlayerInfoData nmsData : info.b()) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = nmsData.d();
|
||||
int latency = nmsData.b();
|
||||
int gameMode = nmsData.c().getId();
|
||||
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new PlayerInfoData(
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -149,7 +175,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
PLAYERS.set(packet, Collections.singletonList(new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.getById(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+44
-18
@@ -22,10 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -34,10 +31,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData.class, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData.class, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, EnumGamemode.class);
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
|
||||
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
|
||||
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
|
||||
@@ -117,29 +110,62 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = ((PacketPlayOutPlayerInfo)packet).c();
|
||||
for (PlayerInfoData nmsData : ((PacketPlayOutPlayerInfo)packet).b()) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = info.c();
|
||||
List<PlayerInfoData> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (PlayerInfoData nmsData : info.b()) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = nmsData.d();
|
||||
int latency = nmsData.b();
|
||||
int gameMode = nmsData.c().a();
|
||||
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new PlayerInfoData(
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -149,7 +175,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
PLAYERS.set(packet, Collections.singletonList(new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+44
-18
@@ -22,10 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -34,10 +31,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData.class, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData.class, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, EnumGamemode.class);
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
|
||||
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
|
||||
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
|
||||
@@ -117,29 +110,62 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = ((PacketPlayOutPlayerInfo)packet).c();
|
||||
for (PlayerInfoData nmsData : ((PacketPlayOutPlayerInfo)packet).b()) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = info.c();
|
||||
List<PlayerInfoData> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (PlayerInfoData nmsData : info.b()) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = nmsData.d();
|
||||
int latency = nmsData.b();
|
||||
int gameMode = nmsData.c().a();
|
||||
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new PlayerInfoData(
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -149,7 +175,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
PLAYERS.set(packet, Collections.singletonList(new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert())
|
||||
));
|
||||
sendPacket(packet);
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+47
-18
@@ -16,16 +16,14 @@ import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter;
|
||||
import net.minecraft.world.entity.player.ProfilePublicKey;
|
||||
import net.minecraft.world.level.EnumGamemode;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TabList implementation using direct NMS code.
|
||||
@@ -34,10 +32,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
private static final Field PLAYERS = ReflectionUtils.getOnlyField(PacketPlayOutPlayerInfo.class, List.class);
|
||||
|
||||
private static final Field PlayerInfoData_Latency = ReflectionUtils.getFields(PlayerInfoData.class, int.class).get(0);
|
||||
private static final Field PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(PlayerInfoData.class, IChatBaseComponent.class);
|
||||
private static final Field PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(PlayerInfoData.class, EnumGamemode.class);
|
||||
|
||||
private static final EnumPlayerInfoAction ADD_PLAYER = EnumPlayerInfoAction.a;
|
||||
private static final EnumPlayerInfoAction UPDATE_GAME_MODE = EnumPlayerInfoAction.b;
|
||||
private static final EnumPlayerInfoAction UPDATE_LATENCY = EnumPlayerInfoAction.c;
|
||||
@@ -117,29 +111,64 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return;
|
||||
EnumPlayerInfoAction action = ((PacketPlayOutPlayerInfo)packet).c();
|
||||
for (PlayerInfoData nmsData : ((PacketPlayOutPlayerInfo)packet).b()) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof PacketPlayOutPlayerInfo)) return packet;
|
||||
PacketPlayOutPlayerInfo info = (PacketPlayOutPlayerInfo) packet;
|
||||
EnumPlayerInfoAction action = info.c();
|
||||
List<PlayerInfoData> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
for (PlayerInfoData nmsData : info.b()) {
|
||||
boolean rewriteEntry = false;
|
||||
GameProfile profile = nmsData.a();
|
||||
UUID id = profile.getId();
|
||||
IChatBaseComponent displayName = nmsData.d();
|
||||
int latency = nmsData.b();
|
||||
int gameMode = nmsData.c().a();
|
||||
ProfilePublicKey.a publicKey = nmsData.e();
|
||||
if (action == UPDATE_DISPLAY_NAME || action == ADD_PLAYER) {
|
||||
TabComponent expectedName = getForcedDisplayNames().get(id);
|
||||
if (expectedName != null) PlayerInfoData_DisplayName.set(nmsData, expectedName.convert());
|
||||
TabComponent forcedDisplayName = getForcedDisplayNames().get(id);
|
||||
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
|
||||
displayName = forcedDisplayName.convert();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_GAME_MODE || action == ADD_PLAYER) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(id);
|
||||
if (forcedGameMode != null) PlayerInfoData_GameMode.set(nmsData, forcedGameMode);
|
||||
if (getBlockedSpectators().contains(id) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == UPDATE_LATENCY || action == ADD_PLAYER) {
|
||||
if (getForcedLatency() != null) {
|
||||
PlayerInfoData_Latency.set(nmsData, getForcedLatency());
|
||||
latency = getForcedLatency();
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
if (action == ADD_PLAYER) {
|
||||
TAB.getInstance().getFeatureManager().onEntryAdd(player, id, profile.getName());
|
||||
}
|
||||
updatedList.add(rewriteEntry ? new PlayerInfoData(
|
||||
profile,
|
||||
latency,
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName,
|
||||
publicKey
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) {
|
||||
PacketPlayOutPlayerInfo newPacket = new PacketPlayOutPlayerInfo(action, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -149,7 +178,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
PLAYERS.set(packet, Collections.singletonList(new PlayerInfoData(
|
||||
createProfile(id, name, skin),
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+19
-9
@@ -42,8 +42,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -118,8 +116,16 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return packet;
|
||||
ClientboundPlayerInfoUpdatePacket info = (ClientboundPlayerInfoUpdatePacket) packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.b();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
@@ -138,9 +144,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +163,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -170,7 +180,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+19
-9
@@ -42,8 +42,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -118,8 +116,16 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return packet;
|
||||
ClientboundPlayerInfoUpdatePacket info = (ClientboundPlayerInfoUpdatePacket) packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.a();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
@@ -138,9 +144,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +163,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -170,7 +180,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+8
-5
@@ -139,7 +139,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(
|
||||
player,
|
||||
@@ -156,11 +157,13 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam) {
|
||||
int action = getMethod((PacketPlayOutScoreboardTeam) packet);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
Collection<String> players = ((PacketPlayOutScoreboardTeam) packet).e();
|
||||
if (players == null) players = Collections.emptyList();
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, ((PacketPlayOutScoreboardTeam)packet).d(), players));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+19
-9
@@ -42,8 +42,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -118,8 +116,16 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter) {
|
||||
PacketPlayOutPlayerListHeaderFooter tablist = (PacketPlayOutPlayerListHeaderFooter) packet;
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket)) return packet;
|
||||
ClientboundPlayerInfoUpdatePacket info = (ClientboundPlayerInfoUpdatePacket) packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.a();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
@@ -138,9 +144,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +163,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -170,7 +180,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+6
-3
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.a().ordinal(), display.d());
|
||||
}
|
||||
@@ -145,9 +146,11 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.e(), team.f() == null ? Collections.emptyList() : team.f()));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.e(), team.f() == null ? Collections.emptyList() : team.f()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+18
-9
@@ -42,8 +42,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -118,8 +116,15 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.a();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
@@ -137,9 +142,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +161,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -169,7 +178,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+6
-3
@@ -134,7 +134,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.a().ordinal(), display.d());
|
||||
}
|
||||
@@ -143,9 +144,11 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.e(), team.f() == null ? Collections.emptyList() : team.f()));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.e(), team.f() == null ? Collections.emptyList() : team.f()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+18
-9
@@ -42,8 +42,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -118,8 +116,15 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.a != header.convert() || tablist.b != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.a();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
@@ -137,9 +142,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +161,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -169,7 +178,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+6
-3
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.b().ordinal(), display.e());
|
||||
}
|
||||
@@ -145,9 +146,11 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.f(), team.g() == null ? Collections.emptyList() : team.g()));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.f(), team.g() == null ? Collections.emptyList() : team.g()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
+18
-9
@@ -43,8 +43,6 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LATENCY_SET = EnumSet.of(UPDATE_LATENCY);
|
||||
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.a> UPDATE_LISTED_SET = EnumSet.of(UPDATE_LISTED);
|
||||
|
||||
private static final Field entries = ReflectionUtils.getOnlyField(ClientboundPlayerInfoUpdatePacket.class, List.class);
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
@@ -119,8 +117,15 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return;
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutPlayerListHeaderFooter tablist) {
|
||||
if (header == null || footer == null) return packet;
|
||||
if (tablist.b != header.convert() || tablist.c != footer.convert()) {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(header.convert(), footer.convert());
|
||||
}
|
||||
}
|
||||
if (!(packet instanceof ClientboundPlayerInfoUpdatePacket info)) return packet;
|
||||
EnumSet<ClientboundPlayerInfoUpdatePacket.a> actions = info.b();
|
||||
List<ClientboundPlayerInfoUpdatePacket.b> updatedList = new ArrayList<>();
|
||||
boolean rewritePacket = false;
|
||||
@@ -138,9 +143,8 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
}
|
||||
}
|
||||
if (actions.contains(UPDATE_GAME_MODE)) {
|
||||
Integer forcedGameMode = getForcedGameModes().get(profileId);
|
||||
if (forcedGameMode != null && forcedGameMode != gameMode) {
|
||||
gameMode = forcedGameMode;
|
||||
if (getBlockedSpectators().contains(profileId) && gameMode == 3) {
|
||||
gameMode = 0;
|
||||
rewriteEntry = rewritePacket = true;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +162,12 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
nmsData.g()
|
||||
) : nmsData);
|
||||
}
|
||||
if (rewritePacket) entries.set(info, updatedList);
|
||||
if (rewritePacket) {
|
||||
ClientboundPlayerInfoUpdatePacket newPacket = new ClientboundPlayerInfoUpdatePacket(actions, Collections.emptyList());
|
||||
PLAYERS.set(newPacket, updatedList);
|
||||
return newPacket;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -170,7 +179,7 @@ public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
|
||||
createProfile(id, name, skin),
|
||||
listed,
|
||||
latency,
|
||||
EnumGamemode.values()[gameMode],
|
||||
EnumGamemode.a(gameMode),
|
||||
displayName == null ? null : displayName.convert(),
|
||||
null
|
||||
)));
|
||||
|
||||
+6
-3
@@ -136,7 +136,8 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onPacketSend(@NonNull Object packet) {
|
||||
@NotNull
|
||||
public Object onPacketSend(@NonNull Object packet) {
|
||||
if (packet instanceof PacketPlayOutScoreboardDisplayObjective display) {
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.b().ordinal(), display.e());
|
||||
}
|
||||
@@ -145,9 +146,11 @@ public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
|
||||
}
|
||||
if (packet instanceof PacketPlayOutScoreboardTeam team) {
|
||||
int action = getMethod(team);
|
||||
if (action == TeamAction.UPDATE) return;
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.f(), team.g() == null ? Collections.emptyList() : team.g()));
|
||||
if (action != TeamAction.UPDATE) {
|
||||
TeamPacket_PLAYERS.set(packet, onTeamPacket(action, team.f(), team.g() == null ? Collections.emptyList() : team.g()));
|
||||
}
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private int getMethod(@NonNull PacketPlayOutScoreboardTeam team) {
|
||||
|
||||
Loaded 100 of 238 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user