Compare commits

...
22 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
NEZNAMY 2f307c492f 6.0.0 2026-04-04 13:24:48 +02:00
NEZNAMY e4b3e17c14 [Bukkit] Update unsupported server version message 2026-04-04 13:24:32 +02:00
NEZNAMY 66d14a0754 [Bukkit] Refactor server version detection for new Paper format and mark 26.1(.1) as compatible 2026-04-04 13:21:01 +02:00
NEZNAMY cb47dcbe12 [Bukkit] [Code style] Create variable instead of repeatedly calculating it 2026-04-03 18:43:25 +02:00
NEZNAMY ea15b40e05 Acknowledge 26.1.1 2026-04-03 18:01:34 +02:00
36 changed files with 362 additions and 249 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-SNAPSHOT"
version = "6.0.1"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -44,8 +44,8 @@ public class BukkitEventListener implements EventListener<Player>, Listener {
public void onJoin(PlayerJoinEvent e) {
TAB.getInstance().addTablistTracker(
e.getPlayer().getUniqueId(),
platform.getImplementationProvider().getChannel(e.getPlayer()),
platform.getImplementationProvider().newTabListEntryTracker()
platform.getServerVersionInfo().getImplementationProvider().getChannel(e.getPlayer()),
platform.getServerVersionInfo().getImplementationProvider().newTabListEntryTracker()
);
join(e.getPlayer());
}
@@ -21,6 +21,6 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
@Override
@NotNull
protected Channel getChannel(@NotNull TabPlayer player) {
return ((BukkitPlatform)player.getPlatform()).getImplementationProvider().getChannel(((BukkitTabPlayer) player).getPlayer());
return ((BukkitPlatform)player.getPlatform()).getServerVersionInfo().getImplementationProvider().getChannel(((BukkitTabPlayer) player).getPlayer());
}
}
}
@@ -19,8 +19,8 @@ public class BukkitTAB extends JavaPlugin {
TAB.create(folia ? new FoliaPlatform(this) : new BukkitPlatform(this));
} catch (IllegalStateException e) {
Bukkit.getConsoleSender().sendMessage("§c[TAB] ================================================================================");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion().split("-")[0] + ") is not supported.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.9.4 - 1.15.x, 1.16.5, 1.17.1, 1.18.2 - 1.21.11.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion() + ") is not supported.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.9.4 - 1.15.x, 1.16.5, 1.17.1, 1.18.2 - 26.1.1");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you just updated to a new Minecraft version, check for TAB updates.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you are using an unsupported 1.x version, use an older version of TAB (latest TAB 5.x supports all MC 1.x versions).");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Thrown error message: " + e.getMessage());
@@ -25,7 +25,7 @@ public class BukkitTabPlayer extends BackendTabPlayer {
* bukkit player
*/
public BukkitTabPlayer(@NotNull BukkitPlatform platform, @NotNull Player p) {
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersion().getNetworkId());
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersionInfo().getServerVersion().getNetworkId());
}
@Override
@@ -35,7 +35,7 @@ public class BukkitTabPlayer extends BackendTabPlayer {
@Override
public int getPing() {
return getPlatform().getImplementationProvider().getPing(this);
return getPlatform().getServerVersionInfo().getImplementationProvider().getPing(this);
}
@Override
@@ -0,0 +1,136 @@
package me.neznamy.tab.platforms.bukkit;
import lombok.Getter;
import lombok.Setter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
@Getter
public class ServerVersionInfo {
/** Package name of the server implementation, null on Paper 1.20.5+ / Spigot 26+ */
@Nullable
private final String serverPackage;
/** Minecraft version as returned by the platform */
@NotNull
private final String minecraftVersion;
@NotNull
private final String serverName;
/** Server version */
private final ProtocolVersion serverVersion;
/** Implementation for creating new instances using content available on the server */
@NotNull
@Setter
private ImplementationProvider implementationProvider;
/**
* Constructs new instance and detects server version info.
*/
public ServerVersionInfo() {
// Server package
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
serverPackage = array.length > 3 ? array[3] : null;
// Minecraft version
if (ReflectionUtils.methodExists(Bukkit.class, "getMinecraftVersion")) {
serverName = "Paper";
minecraftVersion = Bukkit.getMinecraftVersion();
} else {
serverName = "Spigot";
minecraftVersion = Bukkit.getBukkitVersion().split("-")[0];
}
// Minecraft version to server version
serverVersion = ProtocolVersion.fromFriendlyName(minecraftVersion);
// Find implementation
implementationProvider = findImplementationProvider();
}
/**
* Finds implementation provider for current server software and version.
*
* @return Implementation provider for current server
* @throws IllegalStateException
* If no implementation was found
*/
@NotNull
private ImplementationProvider findImplementationProvider() {
if (serverPackage != null) {
// Paper <1.20.5 or Spigot 1.x
try {
// Does not actually support flat 1.19, but whatever, no one is using it anyway
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + serverPackage + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ReflectiveOperationException ignored) {
throw new IllegalStateException(String.format(
"Your server version (%s - %s) is no longer supported.",
minecraftVersion, serverPackage
));
}
}
// Paper 1.20.5+ or Spigot 26+
Map<ProtocolVersion, String> spigotVersions = new LinkedHashMap<>();
spigotVersions.put(ProtocolVersion.V26_1, "v26_1");
spigotVersions.put(ProtocolVersion.V26_1_1, "v26_1");
spigotVersions.put(ProtocolVersion.V26_1_2, "v26_1");
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_20_6, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_1, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_2, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_3, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_4, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_5, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_6, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_7, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_8, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_9, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_10, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_11, "paper_1_21_11");
paperVersions.put(ProtocolVersion.V26_1, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_1_1, "paper_1_21_11"); // v26_1 works too
paperVersions.put(ProtocolVersion.V26_1_2, "paper_1_21_11"); // v26_1 works too
if (serverVersion == ProtocolVersion.UNKNOWN) {
throw new IllegalStateException(String.format(
"Unknown server version (%s), can not find implementation.",
minecraftVersion
));
}
String implementation = (serverName.equals("Paper") ? paperVersions : spigotVersions).get(serverVersion);
if (implementation == null) {
throw new IllegalStateException(String.format(
"Your server version (%s %s) is no longer supported.",
serverName, minecraftVersion
));
}
try {
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + implementation + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format(
"Your server version (%s %s) is marked as compatible, but the implementation does not exist. This is probably a bug.",
serverName, minecraftVersion
), e);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(String.format(
"Failed to initialize implementation for %s %s. This is probably a bug.",
serverName, minecraftVersion
), e);
}
}
}
@@ -1,21 +1,16 @@
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.neznamy.tab.platforms.bukkit.BukkitEventListener;
import me.neznamy.tab.platforms.bukkit.BukkitPipelineInjector;
import me.neznamy.tab.platforms.bukkit.BukkitTabCommand;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.*;
import me.neznamy.tab.platforms.bukkit.bossbar.BukkitBossBar;
import me.neznamy.tab.platforms.bukkit.bossbar.ViaBossBar;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.hook.BukkitPremiumVanishHook;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.*;
import me.neznamy.tab.shared.backend.BackendPlatform;
import me.neznamy.tab.shared.chat.TabTextColor;
@@ -72,8 +67,9 @@ public class BukkitPlatform implements BackendPlatform {
@NotNull
private final JavaPlugin plugin;
/** Server version */
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
/** Information about server version */
@NotNull
private final ServerVersionInfo serverVersionInfo = new ServerVersionInfo();
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = ReflectionUtils.classExists("me.clip.placeholderapi.PlaceholderAPI");
@@ -87,15 +83,6 @@ public class BukkitPlatform implements BackendPlatform {
/** Detection for presence of Paper's MSPT getter */
private final boolean paperMspt = ReflectionUtils.methodExists(Bukkit.class, "getAverageTickTime");
/** Package name of the server implementation, null on Paper 1.20.5+ / Spigot 26+ */
@Nullable
private final String serverPackage;
/** Implementation for creating new instances using content available on the server */
@NotNull
@Setter
private ImplementationProvider implementationProvider;
private final boolean modernOnlinePlayers;
/** Command map for dynamic command registering */
@@ -116,11 +103,7 @@ public class BukkitPlatform implements BackendPlatform {
public BukkitPlatform(@NotNull JavaPlugin plugin) {
this.plugin = plugin;
modernOnlinePlayers = Bukkit.class.getMethod("getOnlinePlayers").getReturnType() == Collection.class;
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
serverPackage = array.length > 3 ? array[3] : null;
implementationProvider = findImplementationProvider();
logInfo(new TabTextComponent("Found NMS implementation: " + implementationProvider.getClass().getName(), TabTextColor.GRAY));
logInfo(new TabTextComponent("Found NMS implementation: " + serverVersionInfo.getImplementationProvider().getClass().getName(), TabTextColor.GRAY));
try {
Object server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
recentTps = ((double[]) server.getClass().getField("recentTps").get(server));
@@ -134,67 +117,6 @@ public class BukkitPlatform implements BackendPlatform {
knownCommands = (Map<String, Command>) ReflectionUtils.getField(SimpleCommandMap.class, "knownCommands").get(commandMap);
}
/**
* Finds implementation provider for current server software and version.
*
* @return Implementation provider for current server
* @throws IllegalStateException
* If no implementation was found
*/
@NotNull
private ImplementationProvider findImplementationProvider() {
if (serverPackage != null) {
// Paper <1.20.5 or Spigot 1.x
try {
// Does not actually support flat 1.19, but whatever, no one is using it anyway
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + serverPackage + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ReflectiveOperationException ignored) {
throw new IllegalStateException("Implementation for version \"" + serverPackage + "\" was not found.");
}
}
Map<ProtocolVersion, String> spigotVersions = new LinkedHashMap<>();
spigotVersions.put(ProtocolVersion.V26_1, "v26_1");
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_20_6, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_1, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_2, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_3, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_4, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_5, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_6, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_7, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_8, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_9, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_10, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_11, "paper_1_21_11");
String software;
Map<ProtocolVersion, String> versions;
if (paperTps) { // This needs a better check
software = "Paper";
versions = paperVersions;
} else {
software = "Spigot";
versions = spigotVersions;
}
String serverVersionString = Bukkit.getBukkitVersion().split("-")[0];
String implementation = versions.get(serverVersion);
if (implementation == null) {
throw new IllegalStateException(String.format("No implementation is available for your server version (%s %s).", software, serverVersionString));
}
try {
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + implementation + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format("Your server version (%s %s) is marked as compatible, but the implementation does not exist. This is probably a bug.", software, serverVersionString), e);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(String.format("Failed to initialize implementation for %s %s. This is probably a bug.", software, serverVersionString), e);
}
}
@Override
public void loadPlayers() {
for (Player p : getOnlinePlayers()) {
@@ -315,7 +237,7 @@ public class BukkitPlatform implements BackendPlatform {
Metrics metrics = new Metrics(plugin, TabConstants.BSTATS_PLUGIN_ID_BUKKIT);
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM,
() -> TAB.getInstance().getGroupManager().getPermissionPlugin()));
metrics.addCustomChart(new SimplePie("tab_6_0_0_servers", serverVersion::getFriendlyName));
metrics.addCustomChart(new SimplePie("tab_6_0_0_servers", serverVersionInfo.getServerVersion()::getFriendlyName));
}
@Override
@@ -327,13 +249,13 @@ public class BukkitPlatform implements BackendPlatform {
@Override
@NotNull
public Object convertComponent(@NotNull TabComponent component) {
return implementationProvider.getComponentConverter().convert(component);
return serverVersionInfo.getImplementationProvider().getComponentConverter().convert(component);
}
@Override
@NotNull
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
return implementationProvider.newScoreboard((BukkitTabPlayer) player);
return serverVersionInfo.getImplementationProvider().newScoreboard((BukkitTabPlayer) player);
}
@Override
@@ -355,7 +277,7 @@ public class BukkitPlatform implements BackendPlatform {
@Override
@NotNull
public TabList createTabList(@NotNull TabPlayer player) {
return implementationProvider.newTabList((BukkitTabPlayer) player);
return serverVersionInfo.getImplementationProvider().newTabList((BukkitTabPlayer) player);
}
@Override
@@ -365,17 +287,17 @@ public class BukkitPlatform implements BackendPlatform {
@Override
public boolean supportsListed() {
return serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId();
return serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId();
}
@Override
public boolean supportsListOrder() {
return serverVersion.getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId();
return serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId();
}
@Override
public boolean isSafeFromPacketEventsBug() {
return serverVersion.getMinorVersion() >= 13;
return serverVersionInfo.getServerVersion().getMinorVersion() >= 13;
}
@Override
@@ -407,6 +329,7 @@ public class BukkitPlatform implements BackendPlatform {
knownCommands.remove(command.getName() + ":" + command.getName());
command.unregister(commandMap);
}
customCommands.clear();
}
@Override
@@ -465,7 +388,7 @@ public class BukkitPlatform implements BackendPlatform {
public String toBukkitFormat(@NotNull TabComponent component) {
StringBuilder sb = new StringBuilder();
if (component.getModifier().getColor() != null) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
if (serverVersionInfo.getServerVersion().getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
String hexCode = component.getModifier().getColor().getHexCode();
sb.append('§').append("x").append('§').append(hexCode.charAt(0)).append('§').append(hexCode.charAt(1))
.append('§').append(hexCode.charAt(2)).append('§').append(hexCode.charAt(3))
@@ -514,17 +437,22 @@ public class BukkitPlatform implements BackendPlatform {
Map<String, Object> map = new LinkedHashMap<>();
map.put("server-type", "Bukkit");
map.put("server-name", Bukkit.getName());
map.put("server-version", Bukkit.getBukkitVersion().split("-")[0]);
map.put("craftbukkit-package", serverPackage);
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,7 +11,9 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V26_1 (775),
V26_1_2 (775),
V26_1_1 (775),
V26_1 (775),
V1_21_11 (774),
V1_21_10 (773),
V1_21_9 (773),
@@ -156,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);