Compare commits

...
17 Commits
Author SHA1 Message Date
NEZNAMY 4af57308d3 6.0.1 2026-04-13 16:44:57 +02:00
NEZNAMY 28ecf8666c [Velocity] Update to VelocityScoreboardAPI 2.0.0 2026-04-13 16:43:08 +02:00
NEZNAMY 1f3d1beb8f Merge pull request #1643 from lokspel/master
26.1.2 is supported
2026-04-12 15:45:57 +02:00
MISHAandNEZNAMY 96d21bc544 Update bukkit/src/main/java/me/neznamy/tab/platforms/bukkit/ServerVersionInfo.java
Co-authored-by: NEZNAMY <martinmiklovicmm@gmail.com>
2026-04-12 14:27:54 +04:00
MISHA f5cb2b5a1d Update ServerVersionInfo.java 2026-04-12 09:32:08 +04:00
MISHA d1d6e73bbc Update ProtocolVersion.java 2026-04-12 09:31:13 +04:00
NEZNAMY 2321ae1c0c [Dump] Include floodgate install status next to player's bedrock status 2026-04-09 19:01:48 +02:00
NEZNAMY 982ee5b6bf [MySQL] Do not relocate driver due to a startup error on NeoForge 2026-04-09 18:11:35 +02:00
NEZNAMY dbe699843b [Dump] Sort plugins/mods alphabetically 2026-04-09 18:09:59 +02:00
NEZNAMY fda19760bd [Bukkit] Compile v26_1 module with Java 21 instead of 25 (#1640) 2026-04-09 17:23:48 +02:00
NEZNAMY 97d42152cd [Configuration] Fix NPE in config conversion 2026-04-07 17:57:21 +02:00
NEZNAMY 5981607783 Fix memory leak 2026-04-06 13:14:58 +02:00
NEZNAMY 7b7927f328 [Code style] Separate methods from class to clean it up 2026-04-05 12:47:07 +02:00
NEZNAMY a8e2730f2f [Performance] Do not send plugin messages about placeholder values if tab expansion is disabled 2026-04-05 12:05:06 +02:00
NEZNAMY 7ae18187bc 6.0.1-SNAPSHOT 2026-04-05 12:03:38 +02:00
NEZNAMY 02634c531f [Wiki] Monthly wiki backup for changes made in March 2026 2026-04-04 16:55:16 +02:00
NEZNAMY 5e8ff89c43 [Description] Update badges 2026-04-04 14:04:35 +02:00
32 changed files with 203 additions and 147 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 5.5.0"><title>Release: 5.5.0</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.5.0</text><text x="715" y="140" transform="scale(.1)" fill="#fff" textLength="290">5.5.0</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20" role="img" aria-label="Release: 6.0.0"><title>Release: 6.0.0</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="92" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="53" height="20" fill="#555"/><rect x="53" width="39" height="20" fill="#007ec6"/><rect width="92" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="275" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">Release</text><text x="275" y="140" transform="scale(.1)" fill="#fff" textLength="430">Release</text><text aria-hidden="true" x="715" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">6.0.0</text><text x="715" y="140" transform="scale(.1)" fill="#fff" textLength="290">6.0.0</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+1 -1
View File
@@ -274,7 +274,7 @@ if __name__ == "__main__":
]
)
release = "5.5.0"
release = "6.0.0"
download_image(f"https://img.shields.io/badge/Release-{release}-blue.svg", "Badge_Release.svg")
minecraft = "1.7.x - 1.21.11".replace(" ", "%20").replace("-", "--")
+4 -8
View File
@@ -13,7 +13,7 @@ Commands have required permissions associated with them.
* [/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 dump \[player\]](#tab-dump-player)
* [/tab group \<group\>](#tab-group-group)
* [/tab groups](#tab-groups)
* [Bossbar](#bossbar)
@@ -65,13 +65,9 @@ Commands have required permissions associated with them.
* **Permission:** `tab.reload`
* **Description:** Unloads the plugin, loads configuration files including changes and loads the plugin.
## /tab debug [player]
* **Permission:** `tab.debug`
* **Description:** Shows the server version, plugin version, permission group choice logic, and sorting type. If player argument is filled, shows info about that player:
* On proxy, shows whether player is connected to the backend server with [Bridge](https://github.com/NEZNAMY/TAB/wiki/TAB-Bridge) plugin or not.
* Sorting value & explanation, useful to see what went wrong if players are not sorted correctly.
* Primary group set using [How to assign players into groups](https://github.com/NEZNAMY/TAB/wiki/How-to-assign-players-into-groups)
* List of all configured properties, their values and source.
## /tab dump \<player\>
* **Permission:** `tab.dump`
* **Description:** Creates a very detailed dump about the specified player to help with troubleshooting, uploads it to a paste site and shows the generated URL.
## /tab group \<group\>
* **Permission:** `tab.groupinfo`
+1 -1
View File
@@ -28,7 +28,7 @@ TAB's shared module is compiled with Java 8. For the platforms themselves, TAB d
<tr>
<td rowspan=2>Vanilla</td>
<td rowspan=1><a href="https://getbukkit.org/">Bukkit</a> (+forge hybrids)</td>
<td>✔ (1.7.x - 1.21.11)</td>
<td>✔ (1.7.10 ~ 26.1.1) (not all versions included)</td>
</tr>
<tr>
<td rowspan=1><a href="https://www.spongepowered.org/">Sponge</a></td>
+12 -1
View File
@@ -7,6 +7,7 @@
* [Additional note 1 - Copying nametag visibility rule](#additional-note-1---copying-nametag-visibility-rule)
* [Additional note 2 - Hidden on sneak on 1.8](#additional-note-2---hidden-on-sneak-on-18)
* [Additional note 3 - Visible on NPCs](#additional-note-3---visible-on-npcs)
* [[26.1] Visible on all entities](#261-visible-on-all-entities)
* [Additional note 4 - Compatibility with modified clients](#additional-note-4---compatibility-with-modified-clients)
* [Examples](#examples)
* [Example 1 - Per-world values](#example-1---per-world-values)
@@ -102,7 +103,17 @@ This is how you can achieve it using the following popular NPC plugins:
* 3 - Change the NPC's name by editing your hologram's line(s): `/hologram edit hi setline 1 <text>`.
You can also add more lines if you want. See [FancyNpcs's wiki](https://docs.fancyinnovations.com/fancyholograms/commands/hologram/#text-hologram-modification) for more commands & info.
# Additional note 4 - Compatibility with modified clients
### [26.1] Visible on all entities
In Minecraft version 26.1, Mojang "fixed" [MC-99647](https://bugs.mojang.com/browse/MC/issues/MC-99647), which reported that belowname is not visible on non-player entities. As a result, the belowname is now visible on all entities with a custom name (this even includes invisible armor stands, holograms and more). A new bug report was made for this: [MC-307012](https://bugs.mojang.com/browse/MC/issues/MC-307012).
**This is a client-sided bug. You will experience it on 26.1 regardless of the server version**.
If your server is on 26.1 or you support players with this version through ViaVersion, this might be a deal breaker for you. If that's the case, either disable this feature entirely, or set
```
disable-condition: "%player-version-id%>=775"
```
which will make 26.1 players not see this feature on anyone (remember: disabling this feature disables it for the viewer, not target players).
## Additional note 4 - Compatibility with modified clients
Sadly, this feature is suffering from bugs introduced by third party clients such as Feather and Lunar. These two completely ignore `fancy-value` as if it was never added into the game, even on 1.20.3+. This is just an example, and it's not limited to these two clients and this one issue. If you experience issues with the feature and believe you configured it correctly, use vanilla client to make sure it's not caused by a broken client.
# Examples
+1 -1
View File
@@ -169,7 +169,7 @@ Currently, TAB supports these skin formats:
|--------|---------|-------------|
| `mineskin:<UUID>` | `mineskin:37e93c8e12cd426cb28fce31969e0674` | Takes UUID from [MineSkin](https://mineskin.org). |
| `player:<name>` | `player:Notch` | Displays skin of specified player. |
| `texture:<texture>` | `texture:469b3bac406b51ba0e76c2c218aa4d45fde9ea7c101c85fbd8106c92c4aa36dd` | Uses skin texture from `textures.minecraft.net`. Skin browsing websites show this value. |
| `texture:<texture>` | `texture:469b3bac406b51ba0e76c2c218aa4d45fde9ea7c101c85fbd8106c92c4aa36dd` | Uses skin texture from `textures.minecraft.net`. Skin browsing websites often show this value (for example `Texture URL` on mineskin.org). |
# Compatibility with other plugins
This feature does not have any compatibility issues with other plugins.
+2 -2
View File
@@ -142,8 +142,8 @@ This list is browsed through until the first match is found. If no match is foun
Values are taken independently of each other.
This means you can set per-world tagprefix, but only keep one global tagsuffix, for example.
You can see the source of a value displayed on player by using `/tab debug <player>` and checking "source"
part of the value you are looking for.
You can see the source of a value displayed on player by using `/tab dump <player>`, opening the link and checking "source"
part of the value you are looking for (tagprefix / tagsuffix).
## Name color
On versions 1.12.2 and below, name color follows the last color of prefix. This also includes magic codes. On 1.13+, this is no longer the case. Instead, a new field called "team color" was created. This is an enum constant, where we need to pick from 22 options - 16 colors, 5 magic codes and reset. **As you can see, it doesn't allow for RGB colors**. This also means name can no longer have color and magic codes simultaneously.
-19
View File
@@ -21,9 +21,6 @@
# Enabling
To enable sorting, you must have either [Nametags](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags) or [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) (or both) enabled.
> [!TIP]
> To verify you enabled sorting, run `/tab debug`. It will say `Sorting type:` followed by anything except `DISABLED`.
# Methods of sorting
Below are all the different methods that you can use to sort players.
@@ -33,10 +30,6 @@ This is the default and recommended method. Players will be sorted by their prim
First, put your players into groups in your permission plugin.
All the ways to do it can be found
at [How to assign players into groups](https://github.com/NEZNAMY/TAB/wiki/How-to-assign-players-into-groups).
> [!TIP]
> Verify player's group using `/tab debug <player>`.
It should say `Primary permission group:` followed by group you configured.
If not, you did not assign players into groups correctly.
Second, place all of your groups in to a comma separated list **in order of priority** into `sorting-types`. Example:
```yaml
@@ -44,15 +37,6 @@ scoreboard-teams:
sorting-types:
- "GROUPS:owner,admin,mod,default"
```
> [!TIP]
> Verify configured sorting priority of players using `/tab debug <player>`.
It should show a message like this:
![image](https://github.com/NEZNAMY/TAB/assets/6338394/095d8523-e8c0-4eff-8935-b0b8e6e0d5ff)
If a group is not in the list, you will get the following:
![image](https://github.com/NEZNAMY/TAB/assets/6338394/9c530873-87c9-4db7-8916-a6121dbeed37)
If that's the case, you forgot to add that group into the list.
## PERMISSIONS
> [!CAUTION]
@@ -71,8 +55,6 @@ scoreboard-teams:
sorting-types:
- "PERMISSIONS:my.permission.1,my.permission.2,my.permission.3"
```
> [!TIP]
> Verify sorting permissions of a player using `/tab debug <player>`
## PLACEHOLDER
This method sorts players using the output of a placeholder and comparing that to pre-defined values.
@@ -233,7 +215,6 @@ The most common mistakes include:
* Installing TAB on a proxy without having any permission plugin on the proxy.
* Enabling `assign-groups-by-permissions` without giving those permissions (or the opposite - giving away OP to users, resulting in the highest group being taken).
[Debug command](https://github.com/NEZNAMY/TAB/wiki/Commands-&-Permissions#tab-debug-player) will help you identify if this is your case.
* Using a plugin that causes TAB to fail to apply teams (such as Tablisknu). This includes installing TAB plugin on both backend and proxy, causing the installations to conflict.
* Using TAB on Velocity without installing [VelocityScoreboardAPI](https://github.com/NEZNAMY/VelocityScoreboardAPI/releases)
@@ -133,8 +133,8 @@ This list is browsed through until the first match is found. If no match is foun
Values are taken independently of each other.
This means you can set per-world tabprefix, but only keep one global tabsuffix, for example.
You can see the source of a value displayed on player by using `/tab debug <player>` and checking "source"
part of the value you are looking for.
You can see the source of a value displayed on player by using `/tab dump <player>`, opening the link and checking "source"
part of the value you are looking for (tabprefix / customtabname / tabsuffix).
# Placeholders
This feature does not offer any internal placeholders, only PlaceholderAPI placeholders.
+1 -1
View File
@@ -1,5 +1,5 @@
# About the wiki
The wiki explains the functionality of the plugin as of version **5.5.0**.
The wiki explains the functionality of the plugin as of version **6.0.0**.
If your config looks different or is missing some features, you are using an old version of the plugin.
To get the latest version, check [releases](https://github.com/NEZNAMY/TAB/releases/).
Wiki for older plugin versions is not available.
@@ -22,7 +22,7 @@ There are 2 simple steps to this:
> (for luckperms /lp group \<group\> setweight \<number\>).
> [!TIP]
> Verify player's group using `/tab debug <player>`. It should say `Primary permission group: <your group>`.
> Verify player's group using `/tab dump <player>`, opening the link and check "permissionGroup" in player info.
## Option 2: Permission nodes
Instead of taking whole groups, TAB will check for permission nodes and assign groups based on the highest permission.
+1
View File
@@ -35,6 +35,7 @@ Advantages of proxy installation compared to installing on all backend servers i
* [Layout](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Layout) can work with all players connected to the proxy
* Avoid [header/footer](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer#additional-note-1---not-resetting-on-server-switch) and [bossbar](https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar#additional-note-2---not-hiding-on-server-switch) not disappearing on server switch
* Configuration files in a single place for easier editing / no need to use MySQL for syncing data
* Can use new features for new players even if backend is using an older version (RGB, 1.20.3+ NumberFormat and more).
Disadvantages compared to installing on all backend servers instead:
+1 -6
View File
@@ -25,9 +25,4 @@ older software will not be compatible with the new one.
You should always be running the latest versions of both TAB and Bridge.
# Confirming it works
You have many ways to verify TAB and Bridge are properly connected. Besides checking if the features above work, the easiest one is to run `/btab debug <player>`.
If it's properly connected, you'll get a message saying this:
![image](https://github.com/NEZNAMY/TAB/assets/6338394/3f8dd978-a8bd-4613-9ea1-a56340194859)
If it's not properly connected, you'll get this message:
![image](https://github.com/NEZNAMY/TAB/assets/6338394/a90018bc-2c3b-4bf5-b54c-56621178ce80)
If this is the case, it means Bridge is either not installed or versions are not compatible. Update both to the latest version.
You have many ways to verify TAB and Bridge are properly connected. Besides checking if the features above work, the easiest one is to run `/btab dump <player>` and check if bridge connection says connected. If not, it means Bridge is either not installed or versions are not compatible. Update both to the latest version.
@@ -20,7 +20,5 @@ tasks {
relocate("org.json", "me.neznamy.tab.libs.org.json")
relocate("com.rabbitmq", "me.neznamy.tab.libs.com.rabbitmq")
relocate("com.saicone.delivery4j", "me.neznamy.tab.libs.com.saicone.delivery4j")
relocate("com.mysql", "me.neznamy.tab.libs.com.mysql")
relocate("com.google.protobuf", "me.neznamy.tab.libs.com.google.protobuf")
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "6.0.0"
version = "6.0.1"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -85,6 +85,7 @@ public class ServerVersionInfo {
Map<ProtocolVersion, String> spigotVersions = new LinkedHashMap<>();
spigotVersions.put(ProtocolVersion.V26_1, "v26_1");
spigotVersions.put(ProtocolVersion.V26_1_1, "v26_1");
spigotVersions.put(ProtocolVersion.V26_1_2, "v26_1");
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
@@ -103,6 +104,7 @@ public class ServerVersionInfo {
paperVersions.put(ProtocolVersion.V1_21_11, "paper_1_21_11");
paperVersions.put(ProtocolVersion.V26_1, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_1_1, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_1_2, "paper_1_21_11"); // v26_1 works too
if (serverVersion == ProtocolVersion.UNKNOWN) {
throw new IllegalStateException(String.format(
@@ -329,6 +329,7 @@ public class BukkitPlatform implements BackendPlatform {
knownCommands.remove(command.getName() + ":" + command.getName());
command.unregister(commandMap);
}
customCommands.clear();
}
@Override
@@ -438,15 +439,20 @@ public class BukkitPlatform implements BackendPlatform {
map.put("server-name", Bukkit.getName());
map.put("server-version", serverVersionInfo.getMinecraftVersion());
map.put("craftbukkit-package", serverVersionInfo.getServerPackage());
map.put("nms-implementation", serverVersionInfo.getImplementationProvider().getClass().getName());
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> plugins = new LinkedHashMap<>();
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
Plugin[] pluginArray = Bukkit.getPluginManager().getPlugins();
Arrays.sort(pluginArray, Comparator.comparing(p -> p.getDescription().getName(), String.CASE_INSENSITIVE_ORDER));
for (Plugin p : pluginArray) {
plugins.put(p.getDescription().getName(), p.getDescription().getVersion());
}
map.put("plugins", plugins);
if (placeholderAPI) {
Map<String, String> expansions = new LinkedHashMap<>();
for (PlaceholderExpansion p : PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getExpansions()) {
PlaceholderExpansion[] expansionArray = PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getExpansions().toArray(new PlaceholderExpansion[0]);
Arrays.sort(expansionArray, Comparator.comparing(PlaceholderExpansion::getIdentifier, String.CASE_INSENSITIVE_ORDER));
for (PlaceholderExpansion p : expansionArray) {
expansions.put(p.getIdentifier(), p.getVersion());
}
map.put("placeholderapi-expansions", expansions);
+1 -1
View File
@@ -4,5 +4,5 @@ dependencies {
}
tasks.compileJava {
options.release.set(25) // Required to compile the code
options.release.set(21) // Included library "jtracy" requires Java 21
}
@@ -327,6 +327,7 @@ public class BungeePlatform extends ProxyPlatform {
for (Command cmd : customCommands) {
ProxyServer.getInstance().getPluginManager().unregisterCommand(cmd);
}
customCommands.clear();
}
@Override
@@ -338,7 +339,9 @@ public class BungeePlatform extends ProxyPlatform {
map.put("server-version", plugin.getProxy().getVersion());
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> plugins = new LinkedHashMap<>();
for (Plugin p : plugin.getProxy().getPluginManager().getPlugins()) {
Plugin[] pluginArray = plugin.getProxy().getPluginManager().getPlugins().toArray(new Plugin[0]);
Arrays.sort(pluginArray, Comparator.comparing(p -> p.getDescription().getName(), String.CASE_INSENSITIVE_ORDER));
for (Plugin p : pluginArray) {
plugins.put(p.getDescription().getName(), p.getDescription().getVersion());
}
map.put("plugins", plugins);
@@ -286,7 +286,9 @@ public record FabricPlatform(MinecraftServer server) implements BackendPlatform
map.put("server-version", SharedConstants.getCurrentVersion().name());
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> mods = new LinkedHashMap<>();
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
ModContainer[] modArray = FabricLoader.getInstance().getAllMods().toArray(new ModContainer[0]);
Arrays.sort(modArray, Comparator.comparing(mod -> mod.getMetadata().getId(), String.CASE_INSENSITIVE_ORDER));
for (ModContainer mod : modArray) {
mods.put(mod.getMetadata().getId(), mod.getMetadata().getVersion().getFriendlyString());
}
map.put("mods", mods);
@@ -271,7 +271,9 @@ public record ForgePlatform(MinecraftServer server) implements BackendPlatform {
map.put("server-version", SharedConstants.getCurrentVersion().name());
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> mods = new LinkedHashMap<>();
for (IModInfo mod : ModList.get().getMods()) {
IModInfo[] modArray = ModList.get().getMods().toArray(new IModInfo[0]);
Arrays.sort(modArray, Comparator.comparing(IModInfo::getModId, String.CASE_INSENSITIVE_ORDER));
for (IModInfo mod : modArray) {
mods.put(mod.getModId(), mod.getVersion().toString());
}
map.put("mods", mods);
@@ -262,7 +262,9 @@ public record NeoForgePlatform(MinecraftServer server) implements BackendPlatfor
map.put("server-version", SharedConstants.getCurrentVersion().name());
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> mods = new LinkedHashMap<>();
for (IModInfo mod : ModList.get().getMods()) {
IModInfo[] modArray = ModList.get().getMods().toArray(new IModInfo[0]);
Arrays.sort(modArray, Comparator.comparing(IModInfo::getModId, String.CASE_INSENSITIVE_ORDER));
for (IModInfo mod : modArray) {
mods.put(mod.getModId(), mod.getVersion().toString());
}
map.put("mods", mods);
@@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V26_1_2 (775),
V26_1_1 (775),
V26_1 (775),
V1_21_11 (774),
@@ -157,4 +158,4 @@ public enum ProtocolVersion {
}
return UNKNOWN;
}
}
}
@@ -78,8 +78,8 @@ public class Configs {
}
if (config.getMysql() != null) {
try {
// Load relocated shaded driver for java connector to recognize it and avoid "No suitable driver found" error
Class.forName("me.neznamy.tab.libs.com.mysql.cj.jdbc.Driver");
//Avoid "No suitable driver found" error
Class.forName("com.mysql.cj.jdbc.Driver");
mysql = new MySQL(config.getMysql());
mysql.openConnection();
@@ -15,7 +15,6 @@ import me.neznamy.tab.shared.data.World;
import me.neznamy.tab.shared.features.proxy.ProxyPlayer;
import me.neznamy.tab.shared.features.proxy.ProxySupport;
import me.neznamy.tab.shared.features.types.*;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.Scoreboard.CollisionRule;
import me.neznamy.tab.shared.platform.Scoreboard.NameVisibility;
import me.neznamy.tab.shared.platform.TabPlayer;
@@ -45,6 +44,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
private final int teamOptions;
private final DisableChecker disableChecker;
@Nullable private final ProxySupport proxy = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PROXY_SUPPORT);
private final NameTagProxyHandler proxyHandler = new NameTagProxyHandler(this);
/**
* Constructs new instance and registers sub-features.
@@ -77,7 +77,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
continue;
}
all.expansionData.setNameTagVisibility(true);
sendProxyMessage(all);
proxyHandler.sendProxyMessage(all);
}
for (TabPlayer viewer : onlinePlayers.getPlayers()) {
for (TabPlayer target : onlinePlayers.getPlayers()) {
@@ -165,7 +165,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
lastColorCache.get(proxied.getNametag().getPrefix()).getLastStyle().toEnumChatFormat()
);
}
sendProxyMessage(connectedPlayer);
proxyHandler.sendProxyMessage(connectedPlayer);
}
}
@@ -259,7 +259,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
lastColorCache.get(player.teamData.prefix.getFormat(viewer)).getLastStyle().toEnumChatFormat()
);
}
sendProxyMessage(player);
proxyHandler.sendProxyMessage(player);
}
/**
@@ -300,7 +300,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
getTeamVisibility(player, viewer) ? NameVisibility.ALWAYS : NameVisibility.NEVER
);
}
sendProxyMessage(player);
proxyHandler.sendProxyMessage(player);
}, getFeatureName(), "Updating visibility"));
}
@@ -375,7 +375,7 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
viewer.getScoreboard().renameTeam(player.teamData.teamName, newTeamName);
}
player.teamData.teamName = newTeamName;
sendProxyMessage(player);
proxyHandler.sendProxyMessage(player);
}, getFeatureName(), "Updating team name"));
}
@@ -453,57 +453,19 @@ public class NameTag extends RefreshableFeature implements NameTagManager, JoinL
}, getFeatureName(), cpuReason));
}
// ------------------
// ProxySupport
// ------------------
private void sendProxyMessage(@NotNull TabPlayer player) {
if (proxy != null) {
proxy.sendMessage(new NameTagProxyPlayerData(
this,
proxy.getIdCounter().incrementAndGet(),
player.getUniqueId(),
player.teamData.teamName,
player.teamData.prefix.get(),
player.teamData.suffix.get(),
getTeamVisibility(player, player) ? NameVisibility.ALWAYS : NameVisibility.NEVER
));
}
}
@Override
public void onProxyLoadRequest() {
for (TabPlayer all : onlinePlayers.getPlayers()) {
sendProxyMessage(all);
}
proxyHandler.onProxyLoadRequest();
}
@Override
public void onQuit(@NotNull ProxyPlayer player) {
if (player.getNametag() == null) {
// One of the two options is being forcibly unregistered when real player joined
return;
}
for (TabPlayer viewer : onlinePlayers.getPlayers()) {
((SafeScoreboard<?>)viewer.getScoreboard()).unregisterTeamSafe(player.getNametag().getResolvedTeamName());
}
proxyHandler.onQuit(player);
}
@Override
public void onJoin(@NotNull ProxyPlayer player) {
if (player.getNametag() == null) return; // Player not loaded yet
for (TabPlayer viewer : onlinePlayers.getPlayers()) {
viewer.getScoreboard().registerTeam(
player.getNametag().getResolvedTeamName(),
prefixCache.get(player.getNametag().getPrefix()),
suffixCache.get(player.getNametag().getSuffix()),
player.getNametag().getNameVisibility(),
Scoreboard.CollisionRule.ALWAYS,
Collections.singletonList(player.getNickname()),
teamOptions,
lastColorCache.get(player.getNametag().getPrefix()).getLastStyle().toEnumChatFormat()
);
}
proxyHandler.onJoin(player);
}
@NotNull
@@ -0,0 +1,71 @@
package me.neznamy.tab.shared.features.nametags;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.features.proxy.ProxyPlayer;
import me.neznamy.tab.shared.features.types.ProxyFeature;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
/**
* Class for handling proxy players in NameTag feature.
* Separated to avoid the main class getting too massive.
*/
@RequiredArgsConstructor
public class NameTagProxyHandler implements ProxyFeature {
@NotNull
private final NameTag feature;
public void sendProxyMessage(@NotNull TabPlayer player) {
if (feature.getProxy() != null) {
feature.getProxy().sendMessage(new NameTagProxyPlayerData(
feature,
feature.getProxy().getIdCounter().incrementAndGet(),
player.getUniqueId(),
player.teamData.teamName,
player.teamData.prefix.get(),
player.teamData.suffix.get(),
feature.getTeamVisibility(player, player) ? Scoreboard.NameVisibility.ALWAYS : Scoreboard.NameVisibility.NEVER
));
}
}
@Override
public void onProxyLoadRequest() {
for (TabPlayer all : feature.getOnlinePlayers().getPlayers()) {
sendProxyMessage(all);
}
}
@Override
public void onQuit(@NotNull ProxyPlayer player) {
if (player.getNametag() == null) {
// One of the two options is being forcibly unregistered when real player joined
return;
}
for (TabPlayer viewer : feature.getOnlinePlayers().getPlayers()) {
((SafeScoreboard<?>)viewer.getScoreboard()).unregisterTeamSafe(player.getNametag().getResolvedTeamName());
}
}
@Override
public void onJoin(@NotNull ProxyPlayer player) {
if (player.getNametag() == null) return; // Player not loaded yet
for (TabPlayer viewer : feature.getOnlinePlayers().getPlayers()) {
viewer.getScoreboard().registerTeam(
player.getNametag().getResolvedTeamName(),
feature.getPrefixCache().get(player.getNametag().getPrefix()),
feature.getSuffixCache().get(player.getNametag().getSuffix()),
player.getNametag().getNameVisibility(),
Scoreboard.CollisionRule.ALWAYS,
Collections.singletonList(player.getNickname()),
feature.getTeamOptions(),
feature.getLastColorCache().get(player.getNametag().getPrefix()).getLastStyle().toEnumChatFormat()
);
}
}
}
@@ -15,9 +15,11 @@ import java.util.UUID;
public class FloodgateHook {
/** Instance of the class */
@Getter private static final FloodgateHook instance = new FloodgateHook();
@Getter
private static final FloodgateHook instance = new FloodgateHook();
/** Flag tracking if floodgate is installed or not */
@Getter
private final boolean installed = ReflectionUtils.classExists("org.geysermc.floodgate.api.FloodgateApi");
/**
@@ -70,7 +70,12 @@ public class ConditionPlaceholder {
isRelational = placeholderDefinition.startsWith("%rel_");
}
}
this.realPlaceholder = TAB.getInstance().getPlaceholderManager().getPlaceholderReference(realPlaceholder);
// Can be null during plugin startup and config conversion, this value is not actually used, just avoid NPE
if (TAB.getInstance().getPlaceholderManager() != null) {
this.realPlaceholder = TAB.getInstance().getPlaceholderManager().getPlaceholderReference(realPlaceholder);
} else {
this.realPlaceholder = null;
}
}
/**
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.placeholders.expansion;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import me.neznamy.tab.shared.proxy.message.outgoing.ExpansionPlaceholder;
@@ -110,7 +111,7 @@ public class ExpansionData {
*/
private void setValue(@NotNull String key, @NotNull String value) {
data.put(key, value);
if (player instanceof ProxyTabPlayer) {
if (player instanceof ProxyTabPlayer && TAB.getInstance().getConfiguration().getConfig().getPlaceholders().isRegisterTabExpansion()) {
((ProxyTabPlayer)player).sendPluginMessage(new ExpansionPlaceholder(key, value));
}
}
@@ -131,7 +132,7 @@ public class ExpansionData {
* Resends all values to the player, typically on server switch on proxy.
*/
public void resendAllValues() {
if (player instanceof ProxyTabPlayer) {
if (player instanceof ProxyTabPlayer && TAB.getInstance().getConfiguration().getConfig().getPlaceholders().isRegisterTabExpansion()) {
for (Map.Entry<String, String> entry : data.entrySet()) {
((ProxyTabPlayer)player).sendPluginMessage(new ExpansionPlaceholder(entry.getKey(), entry.getValue()));
}
@@ -383,7 +383,7 @@ public abstract class TabPlayer implements me.neznamy.tab.api.TabPlayer {
data.put("world", world.getName());
data.put("permissionGroup", permissionGroup);
data.put("temporaryGroup", temporaryGroup);
data.put("bedrockPlayer", bedrockPlayer);
data.put("bedrockPlayer", bedrockPlayer + " (floodgate is installed: " + FloodgateHook.getInstance().isInstalled() + ")");
data.put("protocolVersion", versionId);
data.put("gameVersion", version.getFriendlyName());
data.put("loaded", loaded);
+1 -1
View File
@@ -7,7 +7,7 @@ dependencies {
compileOnlyApi("net.kyori:adventure-nbt:4.17.0")
annotationProcessor("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT")
compileOnly("com.github.LeonMangler:PremiumVanishAPI:2.9.0-4")
compileOnly("net.william278:velocityscoreboardapi:1.0.1")
compileOnly("net.william278:velocityscoreboardapi:2.0.0")
}
tasks.compileJava {
@@ -67,47 +67,59 @@ public class VelocityPlatform extends ProxyPlatform {
*/
public VelocityPlatform(@NotNull VelocityTAB plugin) {
this.plugin = plugin;
if (plugin.getServer().getPluginManager().isLoaded("velocity-scoreboard-api")) {
try {
ScoreboardManager.getInstance();
scoreboardAPI = true;
plugin.getServer().getEventManager().register(plugin, ObjectiveEvent.Display.class, e -> {
TAB tab = TAB.getInstance();
if (tab.isPluginDisabled()) return;
tab.getCPUManager().runTask(() -> {
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
if (player != null) tab.getFeatureManager().onDisplayObjective(player, e.getNewSlot().ordinal(), e.getObjective().getName());
});
});
plugin.getServer().getEventManager().register(plugin, ObjectiveEvent.Unregister.class, e -> {
TAB tab = TAB.getInstance();
if (tab.isPluginDisabled()) return;
tab.getCPUManager().runTask(() -> {
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
if (player != null) tab.getFeatureManager().onObjective(player, Scoreboard.ObjectiveAction.UNREGISTER, e.getObjective().getName());
});
});
} catch (IllegalStateException ignored) {
// Scoreboard API failed to enable due to an error
}
} else {
logInfo(new TabTextComponent("==============================================================================", TabTextColor.RED));
logInfo(new TabTextComponent("Velocity does not have any sort of scoreboard API.", TabTextColor.RED));
logInfo(new TabTextComponent("As a result, many features cannot be implemented using the standard Velocity API.", TabTextColor.RED));
logInfo(new TabTextComponent("In order to enhance your experience, please consider installing VelocityScoreboardAPI " +
"(https://github.com/NEZNAMY/VelocityScoreboardAPI/releases/) plugin.", TabTextColor.RED));
logInfo(new TabTextComponent("Until then, the following features will not work:", TabTextColor.RED));
logInfo(new TabTextComponent("- scoreboard-teams", TabTextColor.RED));
logInfo(new TabTextComponent("- belowname-objective", TabTextColor.RED));
logInfo(new TabTextComponent("- playerlist-objective", TabTextColor.RED));
logInfo(new TabTextComponent("- scoreboard", TabTextColor.RED));
logInfo(new TabTextComponent("==============================================================================", TabTextColor.RED));
}
loadVSAPIHook();
if (plugin.getServer().getPluginManager().isLoaded("premiumvanish")) {
new VelocityPremiumVanishHook().register();
}
}
private void loadVSAPIHook() {
Optional<PluginContainer> vsapi = plugin.getServer().getPluginManager().getPlugin("velocity-scoreboard-api");
if (vsapi.isEmpty()) {
logWarn(new TabTextComponent("==============================================================================", TabTextColor.RED));
logWarn(new TabTextComponent("Velocity does not have any sort of scoreboard API.", TabTextColor.RED));
logWarn(new TabTextComponent("As a result, many features cannot be implemented using the standard Velocity API.", TabTextColor.RED));
logWarn(new TabTextComponent("In order to enhance your experience, please consider installing VelocityScoreboardAPI " +
"(https://github.com/NEZNAMY/VelocityScoreboardAPI/releases/) plugin.", TabTextColor.RED));
logWarn(new TabTextComponent("Until then, the following features will not work:", TabTextColor.RED));
logWarn(new TabTextComponent("- scoreboard-teams", TabTextColor.RED));
logWarn(new TabTextComponent("- belowname-objective", TabTextColor.RED));
logWarn(new TabTextComponent("- playerlist-objective", TabTextColor.RED));
logWarn(new TabTextComponent("- scoreboard", TabTextColor.RED));
logWarn(new TabTextComponent("==============================================================================", TabTextColor.RED));
return;
}
String vsapiVersion = vsapi.get().getDescription().getVersion().orElse("null");
try {
ScoreboardManager.getInstance();
scoreboardAPI = true;
if (vsapiVersion.startsWith("1.")) {
logWarn(new TabTextComponent("Please update VelocityScoreboardAPI to version 2.0.0+ for optimal experience (" +
"current version: " + vsapiVersion + ").", TabTextColor.RED));
return;
}
} catch (IllegalStateException ignored) {
// Scoreboard API failed to enable due to an error
return;
}
plugin.getServer().getEventManager().register(plugin, ObjectiveEvent.Display.class, e -> {
TAB tab = TAB.getInstance();
if (tab.isPluginDisabled()) return;
tab.getCPUManager().runTask(() -> {
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
if (player != null) tab.getFeatureManager().onDisplayObjective(player, e.getNewSlot().ordinal(), e.getObjectiveName());
});
});
plugin.getServer().getEventManager().register(plugin, ObjectiveEvent.Unregister.class, e -> {
TAB tab = TAB.getInstance();
if (tab.isPluginDisabled()) return;
tab.getCPUManager().runTask(() -> {
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
if (player != null) tab.getFeatureManager().onObjective(player, Scoreboard.ObjectiveAction.UNREGISTER, e.getObjectiveName());
});
});
}
@Override
public void loadPlayers() {
for (Player p : plugin.getServer().getAllPlayers()) {
@@ -233,6 +245,7 @@ public class VelocityPlatform extends ProxyPlatform {
for (String cmd : customCommands) {
plugin.getServer().getCommandManager().unregister(cmd);
}
customCommands.clear();
}
@Override
@@ -254,7 +267,9 @@ public class VelocityPlatform extends ProxyPlatform {
}
map.put("VelocityScoreboardAPI", vsapiString);
Map<String, Object> plugins = new LinkedHashMap<>();
for (PluginContainer p : plugin.getServer().getPluginManager().getPlugins()) {
PluginContainer[] pluginArray = plugin.getServer().getPluginManager().getPlugins().toArray(new PluginContainer[0]);
Arrays.sort(pluginArray, Comparator.comparing(p -> p.getDescription().getName().orElse("null"), String.CASE_INSENSITIVE_ORDER));
for (PluginContainer p : pluginArray) {
plugins.put(p.getDescription().getId(), p.getDescription().getVersion().orElse("null"));
}
map.put("plugins", plugins);