Compare commits

..
42 Commits
Author SHA1 Message Date
NEZNAMY 4c3bd66209 Update README.md 2024-10-11 20:27:07 +02:00
NEZNAMY 0827d083bf 4.1.9 2024-10-11 19:56:06 +02:00
NEZNAMY 2f7b42ab85 [Bukkit] Fix NPE caused by another plugin making a not-null field null (#1368) 2024-10-07 19:20:14 +02:00
NEZNAMY 735aa666d6 I just love Velocity 2024-10-06 18:21:54 +02:00
NEZNAMY c2ceb643e5 [Bukkit] Bump dependency 2024-10-03 18:22:18 +02:00
NEZNAMY 5c166f647f [Velocity] Fix exploit (not specifying more to keep users safe) 2024-10-02 20:48:10 +02:00
NEZNAMY 03a8fb9aa2 4.1.9-SNAPSHOT 2024-08-30 19:11:38 +02:00
NEZNAMY 1a44c97d5a 4.1.8 2024-08-30 18:57:45 +02:00
NEZNAMY 29cf0a26d6 4.1.8-SNAPSHOT 2024-08-25 20:11:13 +02:00
NEZNAMY 33d172598d [BungeeCord] Avoid sending UPDATE_LIST_ORDER tablist action to <1.21.2 players (#1359) 2024-08-25 20:10:53 +02:00
NEZNAMY af1e685c2b Merge pull request #1357 from hans362/fix-get-skin
Get skin from profile properties by property name
2024-08-20 16:01:48 +02:00
hans362 09129974db Get skin from profile properties by property name 2024-08-20 21:26:08 +08:00
NEZNAMY 0efb69fb4b [Fabric] Add 1.21.1 to supported versions 2024-08-10 09:46:22 +02:00
NEZNAMY 5cbec52ea3 4.1.7 2024-08-10 09:38:23 +02:00
NEZNAMY 8db9e64121 Update to 1.21.1 2024-08-10 09:37:20 +02:00
NEZNAMY 05009b17f0 [Layout] Fix API usage causing placeholders to not refresh (#1347) 2024-08-06 17:57:41 +02:00
NEZNAMY 032af707dd [Sorting] Fix sorting placeholder using old instance when a placeholder is overridden 2024-07-25 20:52:33 +02:00
NEZNAMY cf227a97f3 [BungeeCord] Add try/catch for yet another NPE in BungeeCord code (#1338) 2024-07-25 15:56:21 +02:00
NEZNAMY 1a2075dfe2 [Bukkit] Fix compatibility with random modded server 2024-07-22 19:26:57 +02:00
NEZNAMY 1de7665309 [Bug fix] Colors not displaying correctly in Fabric and Velocity console (#1298) 2024-06-26 14:25:05 +02:00
NEZNAMY 9473037127 Merge pull request #1323 from sunmisc/master
represent getOnlinePlayers as a collection
2024-06-22 23:37:47 +02:00
sunmisc dcb234dc08 stream api to collection 2024-06-23 00:35:16 +03:00
sunmisc 79eb34fc32 Revert "stream api to collection"
This reverts commit 40544ef5d3.
2024-06-23 00:31:38 +03:00
sunmisc 40544ef5d3 stream api to collection 2024-06-23 00:19:33 +03:00
sunmisc 14dfa0154a Stream api for getOnlinePlayers 2024-06-22 23:44:51 +03:00
NEZNAMY 7df2aba2d1 [RedisBungee] Override %serveronline% to count redis players as well (#1316) 2024-06-16 20:10:25 +02:00
NEZNAMY 9edf3176ea [MiniMessage] Add dummy character to parsed text to disable color compacting (#1312) 2024-06-16 09:16:57 +02:00
NEZNAMY ec73aa13b4 4.1.7-SNAPSHOT 2024-06-16 09:16:48 +02:00
NEZNAMY 6f002cd61b Change action builds to only upload from v5 branch 2024-06-13 21:11:12 +02:00
NEZNAMY cf065ba27d 4.1.6 2024-06-13 19:19:54 +02:00
NEZNAMY f96e263bd7 Add 1.21 support 2024-06-13 19:19:41 +02:00
NEZNAMY a22f8eccaf Update message about unlimited nametags 2024-06-13 15:28:44 +02:00
NEZNAMY 718ee89211 [BungeeCord] Fix another issue leading to "Network Protocol Error" disconnect on 1.20.5+ 2024-06-13 11:33:04 +02:00
NEZNAMY 54b44ce6cd [Unlimited nametags] Print console warn if the feature is enabled 2024-06-13 11:30:26 +02:00
NEZNAMY 29da28e955 [BungeeCord] Fix error in pipeline injector (#1310) 2024-06-12 17:44:24 +02:00
NEZNAMY fa195389ab [Sorting] Fix sorting by pre-defined values space trimming inconsistency (#1308) 2024-06-11 15:39:39 +02:00
NEZNAMY 9e3864be53 [Bukkit] Add missing support for mojang mapped jars 2024-06-06 15:16:17 +02:00
NEZNAMY 29b89b3bb7 [Commands] Fix error when setting property value to ' or " 2024-06-01 16:50:02 +02:00
NEZNAMY e741608261 [Placeholders] Fix server placeholders not returning proper value on request sometimes 2024-05-30 16:10:38 +02:00
NEZNAMY 8e8a4cc9e8 [Teams] [Bukkit] Fix team action not being taken correctly on 1.8 - 1.12, causing issues with previous commit 2024-05-29 13:34:35 +02:00
NEZNAMY 3bb6e0c3f2 [Teams] Fix another instance of "Network Protocol Error" disconnect on 1.20.5+ 2024-05-28 14:40:17 +02:00
NEZNAMY 4c3b13a81c 4.1.6-SNAPSHOT 2024-05-28 14:40:04 +02:00
49 changed files with 285 additions and 197 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ name: Build
on:
push:
branches:
- master
- v5
jobs:
build:
name: Build
+2 -58
View File
@@ -1,58 +1,2 @@
# About
TAB aims to be a superior all-in-one minecraft plugin for displaying information that outperforms all
similar plugins in terms of features, performance and compatibility.
More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/Why-TAB%3F) wiki page.
# Download
### Releases
You can download full releases from [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
[SpigotMC](https://www.spigotmc.org/resources/57806/), [Modrinth](https://modrinth.com/plugin/tab-was-taken) or [BuiltByBit](https://builtbybit.com/resources/20631).
### Dev builds
Plugin's development is continuous and contains lots of small changes/fixes not worthy of being called a full
update, therefore development builds are being created and eventually a full update is released when enough changes are made.
If you want to benefit from a recent change in the code before official release, you can download development builds
from [GitHub actions](https://github.com/NEZNAMY/TAB/actions) (you must be logged into GitHub to be able to download it).
# Compiling
Compilation requires JDK 21 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
The universal jar contains all modules for all supported platforms.
# Documentation
You can find everything about the plugin on the [Wiki](https://github.com/NEZNAMY/TAB/wiki). This includes a detailed description
of all features, as well as information regarding compatibility or limitations of each feature.
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is an issue tracker for all the kind of problems covered in issue templates.
Please refrain from attempting to use it to receive help with your server, such issues will be closed immediately.
When opening an issue, provide as much information as possible. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report. Bug reports where steps to reproduce are "idk" have no value and will not help to get the bug fixed at all.
# Contributing
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
# Provided services
The plugin is being used by a large amount of servers, which also means the demand for my attention is high. Unfortunately,
I am no longer able to keep up with the demand since I don't want to dedicate my whole existence to a free plugin.
In order to provide the best quality of all provided services, the amount of services provided may vary over time.
Here is the full list of potential services and their status:
| Service type | Availability |
|-------------------------------|--------------|
| Updates for new MC versions | ✔ |
| Bug fixes | ✔ |
| Wiki updates<sup>1</sup> | ✔ |
| Feature requests | ❌ |
| Customer support<sup>2</sup> | ✔ |
| Free user support<sup>3</sup> | ❌* |
| Optimization<sup>4</sup> | ✔ |
<sup>1</sup> - Adding new examples, guides and other kind of useful information people ask about.
<sup>2</sup> - Unrestricted assistance with the plugin for those, who bought a copy of the plugin when it was purchasable.
<sup>3</sup> - Unlimited access to my free time for everyone in the world.
<sup>4</sup> - Improving plugin's performance while not losing out on any features. This was done
proactively a lot in the past, so there isn't much to do anymore, only if someone actually finds and reports something.
*Instead, you can use [this fresh new community discord](https://discord.gg/YPqXt63YQj) made by a nice person who will provide this service.
# Warning
This branch is abandoned and NO support will be provided. It is an archived source code of the latest release with unlimited nametags and 1.8 bossbars (4.1.9) for MC 1.5 - 1.21.1.
@@ -1,5 +1,6 @@
package me.neznamy.tab.api;
import java.util.Collection;
import java.util.UUID;
import lombok.NonNull;
@@ -67,8 +68,17 @@ public abstract class TabAPI {
*
* @return collection of online players
*/
@Deprecated
public abstract @NotNull TabPlayer[] getOnlinePlayers();
/**
* Returns a stream of all online players
*
* @return stream of online players
*/
public abstract @NotNull Collection<? extends TabPlayer> onlinePlayers();
/**
* Return BossBar manager instance if the feature is enabled. If not, returns {@code null}.
*
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "4.1.5"
version = "4.1.9"
description = "An all-in-one solution that works"
ext.set("id", "tab")
+1 -1
View File
@@ -2,7 +2,7 @@ dependencies {
implementation(projects.shared)
implementation("org.bstats:bstats-bukkit:3.0.1")
compileOnly("org.purpurmc.purpur:purpur-api:1.16.5-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.2")
compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("com.github.MilkBowl:VaultAPI:1.7") {
exclude("org.bukkit", "bukkit")
@@ -97,7 +97,7 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
}
if (player == null) return "<Player cannot be null>";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
if (p == null) return "<Player is not loaded>";
return p.expansionValues.values.get(identifier);
}
@@ -29,8 +29,8 @@ public class ComponentConverter {
private final FunctionWithException<String, Object> newTextComponent;
private final BiFunction<ChatModifier, Boolean, Object> convertModifier;
private final Class<?> ChatModifier = BukkitReflection.getClass("network.chat.ChatModifier", "ChatModifier");
private final Class<Enum> EnumChatFormat = (Class<Enum>) BukkitReflection.getClass("EnumChatFormat");
private final Class<?> ChatModifier = BukkitReflection.getClass("network.chat.Style", "network.chat.ChatModifier", "ChatModifier");
private final Class<Enum> EnumChatFormat = (Class<Enum>) BukkitReflection.getClass("ChatFormatting", "EnumChatFormat");
private final Constructor<?> newChatModifier;
private final Method ChatBaseComponent_addSibling;
private final Field Component_modifier;
@@ -50,28 +50,28 @@ public class ComponentConverter {
* If something failed
*/
private ComponentConverter() throws ReflectiveOperationException {
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.IChatBaseComponent", "IChatBaseComponent");
Class<?> IChatBaseComponent = BukkitReflection.getClass("network.chat.Component", "network.chat.IChatBaseComponent", "IChatBaseComponent");
if (BukkitReflection.getMinorVersion() >= 19) {
Method IChatBaseComponent_b = ReflectionUtils.getMethod(IChatBaseComponent, new String[] {"b", "literal"}, String.class);
newTextComponent = text -> IChatBaseComponent_b.invoke(null, text);
Class<?> IChatMutableComponent = BukkitReflection.getClass("network.chat.IChatMutableComponent", "IChatMutableComponent");
Class<?> IChatMutableComponent = BukkitReflection.getClass("network.chat.MutableComponent", "network.chat.IChatMutableComponent", "IChatMutableComponent");
Component_modifier = ReflectionUtils.getOnlyField(IChatMutableComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(IChatMutableComponent, IChatMutableComponent, IChatBaseComponent);
} else {
Class<?> ChatComponentText = BukkitReflection.getClass("network.chat.ChatComponentText", "ChatComponentText");
Class<?> ChatComponentText = BukkitReflection.getClass("network.chat.TextComponent", "network.chat.ChatComponentText", "ChatComponentText");
Constructor<?> newChatComponentText = ChatComponentText.getConstructor(String.class);
newTextComponent = newChatComponentText::newInstance;
Class<?> ChatBaseComponent = BukkitReflection.getClass("network.chat.ChatBaseComponent", "ChatBaseComponent");
Class<?> ChatBaseComponent = BukkitReflection.getClass("network.chat.BaseComponent", "network.chat.ChatBaseComponent", "ChatBaseComponent");
Component_modifier = ReflectionUtils.getOnlyField(ChatBaseComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(ChatComponentText, IChatBaseComponent, IChatBaseComponent);
}
if (BukkitReflection.getMinorVersion() >= 16) {
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.ChatHexColor", "ChatHexColor");
Class<?> ResourceLocation = BukkitReflection.getClass("resources.MinecraftKey", "MinecraftKey");
Class<?> chatClickable = BukkitReflection.getClass("network.chat.ChatClickable", "ChatClickable");
Class<?> chatHoverable = BukkitReflection.getClass("network.chat.ChatHoverable", "ChatHoverable");
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.TextColor", "network.chat.ChatHexColor", "ChatHexColor");
Class<?> ResourceLocation = BukkitReflection.getClass("resources.ResourceLocation", "resources.MinecraftKey", "MinecraftKey");
Class<?> chatClickable = BukkitReflection.getClass("network.chat.ClickEvent", "network.chat.ChatClickable", "ChatClickable");
Class<?> chatHoverable = BukkitReflection.getClass("network.chat.HoverEvent", "network.chat.ChatHoverable", "ChatHoverable");
ResourceLocation_tryParse = ReflectionUtils.getMethod(ResourceLocation, new String[]{"tryParse", "m_135820_", "a"}, String.class);
ChatHexColor_fromRGB = ReflectionUtils.getOnlyMethod(chatHexColor, chatHexColor, int.class);
ChatHexColor_fromRGB = ReflectionUtils.getMethods(chatHexColor, chatHexColor, int.class).get(0); // There should only be 1, but some mods add more
newChatModifier = ReflectionUtils.setAccessible(ChatModifier.getDeclaredConstructor(chatHexColor, Boolean.class, Boolean.class, Boolean.class,
Boolean.class, Boolean.class, chatClickable, chatHoverable, String.class, ResourceLocation));
convertModifier = this::createModifierModern;
@@ -16,6 +16,8 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Class storing all team related fields and methods.
@@ -70,7 +72,12 @@ public class TeamPacketData {
emptyScoreboard = Scoreboard.getConstructor().newInstance();
newScoreboardTeam = scoreboardTeam.getConstructor(Scoreboard, String.class);
TeamPacket_NAME = ReflectionUtils.getFields(TeamPacketClass, String.class).get(0);
TeamPacket_ACTION = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class).get(0);
List<Field> intFields = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class);
if (minorVersion >= 8 && minorVersion <= 12) {
TeamPacket_ACTION = intFields.get(1);
} else {
TeamPacket_ACTION = intFields.get(0);
}
TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(TeamPacketClass, Collection.class);
ScoreboardTeam_getPlayerNameSet = ReflectionUtils.getOnlyMethod(scoreboardTeam, Collection.class);
chatFormats = (Enum<?>[]) enumChatFormatClass.getMethod("values").invoke(null);
@@ -302,8 +309,9 @@ public class TeamPacketData {
public void onPacketSend(@NonNull TabPlayer player, @NonNull Object packet) {
if (!TeamPacketClass.isInstance(packet)) return;
int action = TeamPacket_ACTION.getInt(packet);
if (action == TeamAction.REMOVE || action == TeamAction.UPDATE) return;
TeamPacket_PLAYERS.set(packet, player.getScoreboard().onTeamPacket(
action, (String) TeamPacket_NAME.get(packet), (Collection<String>) TeamPacket_PLAYERS.get(packet)));
if (action == TeamAction.UPDATE) return;
Collection<String> players = (Collection<String>) TeamPacket_PLAYERS.get(packet);
if (players == null) players = Collections.emptyList();
TeamPacket_PLAYERS.set(packet, player.getScoreboard().onTeamPacket(action, (String) TeamPacket_NAME.get(packet), players));
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
dependencies {
implementation(projects.shared)
implementation("org.bstats:bstats-bungeecord:3.0.1")
compileOnly("net.md-5:bungeecord-proxy:1.20-R0.2-SNAPSHOT")
compileOnly("net.md-5:bungeecord-proxy:1.20-R0.3-SNAPSHOT")
compileOnly("com.github.limework.redisbungee:RedisBungee-Bungee:0.11.0")
compileOnly("com.github.LeonMangler:PremiumVanishAPI:2.8.8")
}
@@ -59,6 +59,7 @@ public class BungeeEventListener implements EventListener<ProxiedPlayer>, Listen
// Sending these packets before login packet will also crash the client on 1.20.3
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
player.getScoreboard().freeze();
player.getBossBar().freeze();
}
tab.getFeatureManager().onJoin(player);
@@ -14,6 +14,8 @@ import net.md_5.bungee.protocol.packet.*;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Scoreboard handler for BungeeCord. Because it does not offer
@@ -158,8 +160,9 @@ public class BungeeScoreboard extends Scoreboard<BungeeTabPlayer, BaseComponent>
}
if (isAntiOverrideTeams() && packet instanceof Team) {
Team team = (Team) packet;
if (team.getMode() == TeamAction.REMOVE || team.getMode() == TeamAction.UPDATE) return;
team.setPlayers(onTeamPacket(team.getMode(), team.getName(), Lists.newArrayList(team.getPlayers())).toArray(new String[0]));
if (team.getMode() == TeamAction.UPDATE) return;
List<String> players = team.getPlayers() == null ? Collections.emptyList() : Lists.newArrayList(team.getPlayers());
team.setPlayers(onTeamPacket(team.getMode(), team.getName(), players).toArray(new String[0]));
}
}
}
@@ -71,7 +71,20 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public void sendMessage(@NotNull TabComponent message) {
getPlayer().sendMessage((BaseComponent) message.convert(getVersion()));
try {
getPlayer().sendMessage((BaseComponent) message.convert(getVersion()));
} catch (NullPointerException BungeeCordBug) {
// java.lang.NullPointerException: Cannot invoke "net.md_5.bungee.protocol.MinecraftEncoder.getProtocol()" because the return value of "io.netty.channel.ChannelPipeline.get(java.lang.Class)" is null
// at net.md_5.bungee.netty.ChannelWrapper.getEncodeProtocol(ChannelWrapper.java:51)
// at net.md_5.bungee.UserConnection.sendPacketQueued(UserConnection.java:198)
// at net.md_5.bungee.UserConnection.sendMessage(UserConnection.java:565)
// at net.md_5.bungee.UserConnection.sendMessage(UserConnection.java:520)
// at net.md_5.bungee.UserConnection.sendMessage(UserConnection.java:508)
if (TAB.getInstance().getConfiguration().isDebugMode()) {
TAB.getInstance().getErrorManager().printError("Failed to send message to player " + getName() +
" (online = " + getPlayer().isConnected() + "): " + message.convert(getVersion()), BungeeCordBug);
}
}
}
@Override
@@ -80,8 +93,13 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
LoginResult loginResult = ((InitialHandler)getPlayer().getPendingConnection()).getLoginProfile();
if (loginResult == null) return null;
Property[] properties = loginResult.getProperties();
if (properties == null || properties.length == 0) return null; //Offline mode
return new TabList.Skin(properties[0].getValue(), properties[0].getSignature());
if (properties == null) return null; //Offline mode
for (Property property : properties) {
if (property.getName().equals(TabList.TEXTURES_PROPERTY)) {
return new TabList.Skin(property.getValue(), property.getSignature());
}
}
return null;
}
@Override
@@ -20,7 +20,15 @@ public class BungeeTabList1193 extends BungeeTabList {
private static final Map<Action, EnumSet<PlayerListItemUpdate.Action>> actions = new EnumMap<>(Action.class);
static {
actions.put(Action.ADD_PLAYER, EnumSet.allOf(PlayerListItemUpdate.Action.class));
// Do not use allOf because <1.21.2 does not support UPDATE_LIST_ORDER
// TODO Once bungeecord dependency is updated, support it properly
actions.put(Action.ADD_PLAYER, EnumSet.of(
PlayerListItemUpdate.Action.ADD_PLAYER,
PlayerListItemUpdate.Action.UPDATE_GAMEMODE,
PlayerListItemUpdate.Action.UPDATE_LISTED,
PlayerListItemUpdate.Action.UPDATE_LATENCY,
PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME
));
actions.put(Action.UPDATE_GAME_MODE, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_GAMEMODE));
actions.put(Action.UPDATE_DISPLAY_NAME, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_DISPLAY_NAME));
actions.put(Action.UPDATE_LATENCY, EnumSet.of(PlayerListItemUpdate.Action.UPDATE_LATENCY));
+2 -2
View File
@@ -12,11 +12,11 @@ repositories {
dependencies {
api(projects.shared)
minecraft("com.mojang:minecraft:24w19a")
minecraft("com.mojang:minecraft:1.21.1")
mappings(loom.officialMojangMappings())
modImplementation("me.lucko:fabric-permissions-api:0.2-SNAPSHOT")
modImplementation("net.fabricmc:fabric-loader:0.15.9")
val version = "0.97.8+1.20.6"
val version = "0.100.1+1.21"
modImplementation(fabricApi.module("fabric-api-base", version))
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", version))
modImplementation(fabricApi.module("fabric-networking-api-v1", version))
@@ -4,7 +4,7 @@ import com.mojang.authlib.properties.Property;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.fabric.loader.Loader;
import me.neznamy.tab.platforms.fabric.loader.Loader_1_20_5;
import me.neznamy.tab.platforms.fabric.loader.Loader_1_21_1;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
@@ -38,7 +38,7 @@ public class FabricMultiVersion {
private static final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(FabricTAB.minecraftVersion);
/** Method loader using latest supported MC version */
private static final Loader loaderNew = new Loader_1_20_5();
private static final Loader loaderNew = new Loader_1_21_1();
/** Method loader using 1.18.2 */
private static final Loader loader1_18_2 = createLoader("1_18_2");
@@ -42,14 +42,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Method loader compiled using Minecraft 1.20.5.
* Method loader compiled using Minecraft 1.21.1.
*/
@SuppressWarnings({
"unchecked", // Java generic types
"DataFlowIssue", // Profile is not null on add action
"unused" // Actually used, just via reflection
"DataFlowIssue" // Profile is not null on add action
})
public class Loader_1_20_5 implements Loader {
public class Loader_1_21_1 implements Loader {
@Override
@NotNull
@@ -96,7 +95,7 @@ public class Loader_1_20_5 implements Loader {
null,
null,
null,
modifier.getFont() == null ? null : new ResourceLocation(modifier.getFont())
modifier.getFont() == null ? null : ResourceLocation.tryParse(modifier.getFont())
);
}
@@ -147,7 +146,7 @@ public class Loader_1_20_5 implements Loader {
@Override
public void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
if (team.method == Scoreboard.TeamAction.REMOVE || team.method == Scoreboard.TeamAction.UPDATE) return;
if (team.method == Scoreboard.TeamAction.UPDATE) return;
team.players = scoreboard.onTeamPacket(team.method, team.getName(), team.players);
}
}
@@ -320,12 +319,12 @@ public class Loader_1_20_5 implements Loader {
@Override
public void logInfo(@NotNull TabComponent message) {
MinecraftServer.LOGGER.info("[TAB] " + message.toLegacyText());
MinecraftServer.LOGGER.info("[TAB] " + message.toRawText());
}
@Override
public void logWarn(@NotNull TabComponent message) {
MinecraftServer.LOGGER.warn("[TAB] " + message.toLegacyText());
MinecraftServer.LOGGER.warn("[TAB] " + message.toRawText());
}
/**
@@ -361,10 +360,6 @@ public class Loader_1_20_5 implements Loader {
static final Map<TabList.Action, EnumSet<ClientboundPlayerInfoUpdatePacket.Action>> actionMap = createActionMap();
public static EnumSet<ClientboundPlayerInfoUpdatePacket.Action> convertAction(TabList.Action action) {
return EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.valueOf(action.name()));
}
private static Map<TabList.Action, EnumSet<ClientboundPlayerInfoUpdatePacket.Action>> createActionMap() {
Map<TabList.Action, EnumSet<ClientboundPlayerInfoUpdatePacket.Action>> actions = new EnumMap<>(TabList.Action.class);
actions.put(TabList.Action.ADD_PLAYER, EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class));
+1 -1
View File
@@ -20,7 +20,7 @@
"1.18", "1.18.1", "1.18.2",
"1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4",
"1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6",
"1.21-alpha.24.19.a"
"1.21", "1.21.1"
]
},
"recommends": {
@@ -147,7 +147,7 @@ public class Loader_1_14_4 implements Loader {
public void checkTeamPacket(@NotNull Packet<?> packet, @NotNull FabricScoreboard scoreboard) {
if (packet instanceof ClientboundSetPlayerTeamPacket) {
int action = ReflectionUtils.getInstanceFields(packet.getClass(), int.class).get(0).getInt(packet);
if (action == Scoreboard.TeamAction.REMOVE || action == Scoreboard.TeamAction.UPDATE) return;
if (action == Scoreboard.TeamAction.UPDATE) return;
Field playersField = ReflectionUtils.getFields(packet.getClass(), Collection.class).get(0);
Collection<String> players = (Collection<String>) playersField.get(packet);
String teamName = String.valueOf(ReflectionUtils.getFields(packet.getClass(), String.class).get(0).get(packet));
@@ -319,13 +319,13 @@ public class Loader_1_14_4 implements Loader {
@SneakyThrows
public void logInfo(@NotNull TabComponent message) {
Object logger = ReflectionUtils.getFields(MinecraftServer.class, Class.forName("org.apache.logging.log4j.Logger")).get(0).get(null);
logger.getClass().getMethod("info", String.class).invoke(logger, "[TAB] " + message.toLegacyText());
logger.getClass().getMethod("info", String.class).invoke(logger, "[TAB] " + message.toRawText());
}
@Override
@SneakyThrows
public void logWarn(@NotNull TabComponent message) {
Object logger = ReflectionUtils.getFields(MinecraftServer.class, Class.forName("org.apache.logging.log4j.Logger")).get(0).get(null);
logger.getClass().getMethod("warn", String.class).invoke(logger, "[TAB] " + message.toLegacyText());
logger.getClass().getMethod("warn", String.class).invoke(logger, "[TAB] " + message.toRawText());
}
}
@@ -3,7 +3,7 @@ package me.neznamy.tab.shared;
import lombok.Getter;
import me.neznamy.tab.api.event.TabEvent;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -118,13 +118,13 @@ public class ErrorManager {
if (file.length() < TabConstants.MAX_LOG_SIZE)
buf.write(dateFormat.format(new Date()) + "[TAB v" + TabConstants.PLUGIN_VERSION + "] " + EnumChatFormat.decolor(message) + System.lineSeparator());
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().getPlatform().logWarn(new SimpleComponent(message));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(message));
}
for (String line : error) {
if (file.length() < TabConstants.MAX_LOG_SIZE)
buf.write(dateFormat.format(new Date()) + line + System.lineSeparator());
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().getPlatform().logWarn(new SimpleComponent(line));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(line));
}
}
} catch (IOException ex) {
@@ -134,7 +134,7 @@ public class ErrorManager {
lines.add("Original error: " + message);
lines.addAll(error);
for (String line : lines) {
TAB.getInstance().getPlatform().logWarn(new SimpleComponent(line));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(line));
}
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared;
import java.util.*;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.config.Configs;
import me.neznamy.tab.shared.config.mysql.MySQLUserConfiguration;
import me.neznamy.tab.shared.features.*;
@@ -487,6 +488,18 @@ public class FeatureManager {
NameTag unlimited = TAB.getInstance().getPlatform().getUnlimitedNameTags();
if (unlimited instanceof NameTagX) {
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, unlimited);
for (String message : new String[]{
"---------------------------------------------------------------------",
"You have unlimited nametag mode feature enabled.",
"This feature is scheduled for removal in a future TAB release.",
"Please considering the alternative solutions available to achieve your desired result.",
"No bug reports with this feature will be accepted anymore.",
"No support with this feature will be provided anymore.",
"See https://gist.github.com/NEZNAMY/f4cabf2fd9251a836b5eb877720dee5c/ for more info.",
"---------------------------------------------------------------------"
}) {
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(message));
}
} else {
featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, unlimited);
}
@@ -11,6 +11,8 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V1_21_1 (767),
V1_21 (767),
V1_20_6 (766),
V1_20_5 (766),
V1_20_4 (765),
@@ -81,7 +83,7 @@ public enum ProtocolVersion {
private static final ProtocolVersion[] VALUES = values();
/** Newest MC version this plugin jar knows */
public static final ProtocolVersion LATEST_KNOWN_VERSION = V1_20_6;
public static final ProtocolVersion LATEST_KNOWN_VERSION = VALUES[1];
/** Version's network id found at <a href="https://wiki.vg/Protocol_version_numbers">wiki.vg</a> */
private final int networkId;
@@ -10,7 +10,7 @@ import me.neznamy.tab.api.scoreboard.ScoreboardManager;
import me.neznamy.tab.api.tablist.HeaderFooterManager;
import me.neznamy.tab.api.tablist.TabListFormatManager;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.config.helper.ConfigHelper;
import me.neznamy.tab.shared.cpu.CpuManager;
import me.neznamy.tab.shared.features.nametags.NameTag;
@@ -28,6 +28,8 @@ import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -184,10 +186,10 @@ public class TAB extends TabAPI {
cpu.enable();
configHelper.startup().checkErrorLog();
configHelper.startup().printWarnCount();
platform.logInfo(new SimpleComponent(EnumChatFormat.GREEN + "Enabled in " + (System.currentTimeMillis()-time) + "ms"));
platform.logInfo(TabComponent.fromColoredText(EnumChatFormat.GREEN + "Enabled in " + (System.currentTimeMillis()-time) + "ms"));
return configuration.getMessages().getReloadSuccess();
} catch (YAMLException e) {
platform.logWarn(new SimpleComponent(EnumChatFormat.RED + "Did not enable due to a broken configuration file."));
platform.logWarn(TabComponent.fromColoredText(EnumChatFormat.RED + "Did not enable due to a broken configuration file."));
kill();
return (configuration == null ? "&4Failed to reload, file %file% has broken syntax. Check console for more info."
: configuration.getMessages().getReloadFailBrokenFile()).replace("%file%", brokenFile);
@@ -208,7 +210,7 @@ public class TAB extends TabAPI {
long time = System.currentTimeMillis();
if (configuration.getMysql() != null) configuration.getMysql().closeConnection();
featureManager.unload();
platform.logInfo(new SimpleComponent(EnumChatFormat.GREEN + "Disabled in " + (System.currentTimeMillis()-time) + "ms"));
platform.logInfo(TabComponent.fromColoredText(EnumChatFormat.GREEN + "Disabled in " + (System.currentTimeMillis()-time) + "ms"));
} catch (Throwable e) {
errorManager.criticalError("Failed to disable", e);
}
@@ -293,6 +295,11 @@ public class TAB extends TabAPI {
return featureManager.getFeature(TabConstants.Feature.HEADER_FOOTER);
}
@Override
public @NotNull Collection<? extends TabPlayer> onlinePlayers() {
return Collections.unmodifiableCollection(data.values());
}
@Override
public @Nullable TabListFormatManager getTabListFormatManager() {
return featureManager.getFeature(TabConstants.Feature.PLAYER_LIST);
@@ -317,6 +324,6 @@ public class TAB extends TabAPI {
*/
public void debug(@NotNull String message) {
if (configuration != null && configuration.isDebugMode())
platform.logInfo(new SimpleComponent(EnumChatFormat.BLUE + "[DEBUG] " + message));
platform.logInfo(TabComponent.fromColoredText(EnumChatFormat.BLUE + "[DEBUG] " + message));
}
}
@@ -19,4 +19,11 @@ public class SimpleComponent extends TabComponent {
public String toLegacyText() {
return text;
}
@Override
@NotNull
public String toRawText() {
return text;
}
}
@@ -115,17 +115,14 @@ public class StructuredComponent extends TabComponent {
return builder.toString();
}
/**
* Returns raw text without colors, only works correctly when component is organized
*
* @return raw text in this component and all child components
*/
public @NotNull String toRawText() {
StringBuilder builder = new StringBuilder();
builder.append(text);
for (StructuredComponent child : getExtra()) {
builder.append(child.text);
@Override
@NotNull
public String toRawText() {
StringBuilder builder = new StringBuilder(text);
for (StructuredComponent extra : getExtra()) {
builder.append(extra.toRawText());
}
return builder.toString();
}
}
@@ -64,6 +64,15 @@ public abstract class TabComponent {
*/
public abstract String toLegacyText();
/**
* Converts this component into a string that only consists of text without any formatting.
*
* @return String containing text of the component and extras
*/
@NotNull
public abstract String toRawText();
/**
* Returns the most optimized component based on text. Returns null if text is null,
* organized component if RGB colors are used or simple component with only text field
@@ -14,13 +14,17 @@ public class MiniMessageFormat implements RGBFormatter {
private static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder()
.hexColors().useUnusualXRepeatedCharacterHexFormat().build();
/** Dummy character to append to disable MiniMessage's color compacting that prevents team color from being detected as last color of prefix */
private static final char dummyChar = Character.MAX_VALUE;
@Override
@NotNull
public String reformat(@NotNull String text) {
if (!text.contains("<")) return text; // User did not even attempt to use MiniMessage
if (text.contains(EnumChatFormat.COLOR_STRING)) return text;
try {
return SERIALIZER.serialize(MiniMessage.miniMessage().deserialize(text));
String serialized = SERIALIZER.serialize(MiniMessage.miniMessage().deserialize(text + dummyChar));
return serialized.substring(0, serialized.length()-1); // Remove the dummy char back
} catch (Throwable ignored) {
return text;
}
@@ -4,7 +4,7 @@ import java.text.DecimalFormat;
import java.util.Map;
import java.util.Map.Entry;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.cpu.CpuReport;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.chat.EnumChatFormat;
@@ -74,13 +74,13 @@ public class CpuCommand extends SubCommand {
}
public void sendToConsole(@NotNull Map<String, Map<String, Float>> features) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.color("&8&l" + LINE_CHAR + " &6Features:")));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.color("&8&l" + LINE_CHAR + " &6Features:")));
for (Entry<String, Map<String, Float>> entry : features.entrySet()) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.color(
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.color(
String.format("&8&l%s &7%s &7(%s%%&7):", LINE_CHAR, entry.getKey(),
colorize(decimal3.format(entry.getValue().values().stream().mapToDouble(Float::floatValue).sum()), 5, 1)))));
for (Entry<String, Float> type : entry.getValue().entrySet()) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.color(
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.color(
String.format("&8&l%s &7%s - %s%%", LINE_CHAR, type.getKey(), colorize(decimal3.format(type.getValue()), 5, 1)))));
}
}
@@ -91,7 +91,7 @@ public class CpuCommand extends SubCommand {
for (Entry<String, Map<String, Float>> entry : features.entrySet()) {
double featureTotal = entry.getValue().values().stream().mapToDouble(Float::floatValue).sum();
String core = String.format("&8&l%s &7%s &7(%s%%&7):", LINE_CHAR, entry.getKey(), colorize(decimal3.format(featureTotal), 5, 1));
sender.sendMessage(new SimpleComponent(EnumChatFormat.color(core)));
sender.sendMessage(TabComponent.fromColoredText(EnumChatFormat.color(core)));
}
}
@@ -42,7 +42,7 @@ public abstract class PropertyCommand extends SubCommand {
server = args[args.length-1];
value = value.startsWith("-s") ? "" : value.substring(0, value.length()-server.length()-4);
}
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
if (((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) && value.length() > 1) {
value = value.substring(1, value.length()-1);
}
String property = args[1].toLowerCase();
@@ -3,7 +3,7 @@ package me.neznamy.tab.shared.config;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.config.file.ConfigurationFile;
import me.neznamy.tab.shared.config.file.YamlConfigurationFile;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +25,7 @@ public class Converter {
public void convert2810to290(@NotNull ConfigurationFile animations) {
if (animations.getValues().size() == 1 && animations.getValues().containsKey("animations")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 2.8.10 to 2.9.0"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 2.8.10 to 2.9.0"));
animations.setValues(animations.getConfigurationSection("animations"));
animations.save();
}
@@ -42,7 +42,7 @@ public class Converter {
*/
public void convert292to300(@NotNull ConfigurationFile currentConfig) throws IOException {
if (!currentConfig.hasConfigOption("change-nametag-prefix-suffix")) return;
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 2.9.2 to 3.0.0"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 2.9.2 to 3.0.0"));
File folder = TAB.getInstance().getDataFolder();
moveOldFiles();
@@ -346,13 +346,13 @@ public class Converter {
public void convert301to302(@NotNull ConfigurationFile config) {
if (config.removeOption("placeholders.remove-strings")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.0.1 to 3.0.2"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.0.1 to 3.0.2"));
}
}
public void convert331to332(@NotNull ConfigurationFile config) {
if (config.removeOption("scoreboard-teams.unlimited-nametag-mode.use-marker-tag-for-1-8-x-clients")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.3.1 to 3.3.2)"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.3.1 to 3.3.2)"));
}
}
@@ -360,7 +360,7 @@ public class Converter {
public void convert332to400(@NotNull ConfigurationFile config) throws IOException {
// Removed config options
if (config.hasConfigOption("fix-pet-names")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.3.2 to 4.0.0"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 3.3.2 to 4.0.0"));
config.set("fix-pet-names", null);
config.set("bossbar.disable-in-worlds", null);
config.set("bossbar.disable-in-servers", null);
@@ -425,13 +425,13 @@ public class Converter {
public void convert403to404(@NotNull ConfigurationFile config) {
if (config.removeOption("global-playerlist.update-latency")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 4.0.3 to 4.0.4"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 4.0.3 to 4.0.4"));
}
}
public void convert409to410(@NotNull ConfigurationFile config) {
if (config.hasConfigOption("yellow-number-in-tablist")) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.YELLOW + "Performing configuration conversion from 4.0.9 to 4.1.0"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.YELLOW + "Performing configuration conversion from 4.0.9 to 4.1.0"));
Map<Object, Object> section = config.getConfigurationSection("yellow-number-in-tablist");
section.put("fancy-value", "&7Ping: %ping%");
config.set("yellow-number-in-tablist", null);
@@ -14,7 +14,7 @@ import java.util.List;
import lombok.NonNull;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
@@ -56,13 +56,13 @@ public class YamlConfigurationFile extends ConfigurationFile {
if (input != null) input.close();
TAB tab = TAB.getInstance();
tab.setBrokenFile(destination.getName());
tab.getPlatform().logWarn(new SimpleComponent("File " + destination + " has broken syntax."));
tab.getPlatform().logInfo(new SimpleComponent(EnumChatFormat.GOLD + "Error message from yaml parser: " + e.getMessage()));
tab.getPlatform().logWarn(TabComponent.fromColoredText("File " + destination + " has broken syntax."));
tab.getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.GOLD + "Error message from yaml parser: " + e.getMessage()));
List<String> suggestions = YamlAssist.getSuggestions(file);
if (!suggestions.isEmpty()) {
tab.getPlatform().logInfo(new SimpleComponent(EnumChatFormat.LIGHT_PURPLE + "Suggestions to fix yaml syntax:"));
tab.getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.LIGHT_PURPLE + "Suggestions to fix yaml syntax:"));
for (String suggestion : suggestions) {
tab.getPlatform().logInfo(new SimpleComponent(EnumChatFormat.LIGHT_PURPLE + "- " + suggestion));
tab.getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.LIGHT_PURPLE + "- " + suggestion));
}
}
throw e;
@@ -79,7 +79,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
writer.close();
fixHeader();
} catch (IOException e) {
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("Failed to save yaml file " + file.getPath() + " with content " + values.toString()));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText("Failed to save yaml file " + file.getPath() + " with content " + values.toString()));
}
}
}
@@ -2,7 +2,7 @@ package me.neznamy.tab.shared.config.helper;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@@ -100,6 +100,6 @@ public class HintPrinter {
* Message to log
*/
private void hint(@NotNull String message) {
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.GOLD + "[Hint] " + message));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.GOLD + "[Hint] " + message));
}
}
@@ -3,7 +3,7 @@ package me.neznamy.tab.shared.config.helper;
import me.neznamy.tab.api.bossbar.BossBar;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.sorting.types.SortingType;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
@@ -196,6 +196,6 @@ public class RuntimeErrorPrinter {
* Message to log
*/
private void error(@NotNull String message) {
TAB.getInstance().getPlatform().logWarn(new SimpleComponent(message));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(message));
}
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.layout.GroupPattern;
import me.neznamy.tab.shared.features.layout.LayoutManagerImpl;
import me.neznamy.tab.shared.features.sorting.types.SortingType;
@@ -389,7 +389,7 @@ public class StartupWarnPrinter {
private void startupWarn(@NotNull String... messages) {
warnCount++;
for (String message : messages) {
TAB.getInstance().getPlatform().logWarn(new SimpleComponent(message));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText(message));
}
}
@@ -398,7 +398,7 @@ public class StartupWarnPrinter {
*/
public void printWarnCount() {
if (warnCount == 0) return;
TAB.getInstance().getPlatform().logWarn(new SimpleComponent("Found a total of " + warnCount + " issues."));
TAB.getInstance().getPlatform().logWarn(TabComponent.fromColoredText("Found a total of " + warnCount + " issues."));
// Reset after printing to prevent count going up on each reload
warnCount = 0;
}
@@ -14,7 +14,7 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,7 +37,7 @@ public class MySQL {
properties.setProperty("useSSL", String.valueOf(useSSL));
properties.setProperty("characterEncoding", "UTF-8");
con = DriverManager.getConnection(String.format("jdbc:mysql://%s:%d/%s", host, port, database), properties);
TAB.getInstance().getPlatform().logInfo(new SimpleComponent(EnumChatFormat.GREEN + "Successfully connected to MySQL"));
TAB.getInstance().getPlatform().logInfo(TabComponent.fromColoredText(EnumChatFormat.GREEN + "Successfully connected to MySQL"));
}
public void closeConnection() throws SQLException {
@@ -111,7 +111,7 @@ public class LayoutPattern extends TabFeature implements Refreshable, Layout {
public void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping) {
ensureActive();
fixedSlots.put(slot, new FixedSlot(manager, slot, this, manager.getUUID(slot), text,
"Layout-" + text + "-SLOT-" + slot, skin, "Layout-" + text + "-SLOT-" + slot+ "-skin", ping));
"Layout-" + name + "-SLOT-" + slot, skin, "Layout-" + name + "-SLOT-" + slot+ "-skin", ping));
}
@Override
@@ -227,6 +227,16 @@ public abstract class RedisSupport extends TabFeature implements JoinListener, Q
}
return count;
});
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.SERVER_ONLINE, 1000, p -> {
int count = 0;
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
if (((TabPlayer)p).getServer().equals(player.getServer()) && !player.isVanished()) count++;
}
for (RedisPlayer player : redisPlayers.values()) {
if (((TabPlayer)p).getServer().equals(player.getServer()) && !player.isVanished()) count++;
}
return count;
});
}
@Override
@@ -61,14 +61,15 @@ public class Placeholder extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = EnumChatFormat.color(setPlaceholders(p));
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\"";
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\"";
int position;
if (!sortingMap.containsKey(output.toLowerCase(Locale.US))) {
TAB.getInstance().getConfigHelper().runtime().valueNotInPredefinedValues(sortingPlaceholder.getIdentifier(), sortingMap.keySet(), output, p);
String cleanOutput = output.trim().toLowerCase(Locale.US);
if (!sortingMap.containsKey(cleanOutput)) {
TAB.getInstance().getConfigHelper().runtime().valueNotInPredefinedValues(sortingPlaceholder, sortingMap.keySet(), cleanOutput, p);
position = sortingMap.size()+1;
p.sortingData.teamNameNote += "&c (not in list)&r. ";
} else {
position = sortingMap.get(output.toLowerCase(Locale.US));
position = sortingMap.get(cleanOutput);
p.sortingData.teamNameNote += "&r &a(#" + position + " in list). &r";
}
return String.valueOf((char) (position + 47));
@@ -24,7 +24,7 @@ public class PlaceholderAtoZ extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r";
return sorting.isCaseSensitiveSorting() ? output : output.toLowerCase();
}
}
@@ -24,7 +24,7 @@ public class PlaceholderHighToLow extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER - parseDouble(sortingPlaceholder.getIdentifier(), output, 0, p));
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER - parseDouble(sortingPlaceholder, output, 0, p));
}
}
@@ -24,7 +24,7 @@ public class PlaceholderLowToHigh extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
String output = setPlaceholders(p);
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER + parseDouble(sortingPlaceholder.getIdentifier(), output, 0, p));
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r";
return compressNumber(DEFAULT_NUMBER + parseDouble(sortingPlaceholder, output, 0, p));
}
}
@@ -24,7 +24,7 @@ public class PlaceholderZtoA extends SortingType {
@Override
public String getChars(@NotNull TabPlayer p) {
char[] chars = setPlaceholders(p).toCharArray();
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " returned \"&e" + new String(chars) + "&r\". &r";
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder + " returned \"&e" + new String(chars) + "&r\". &r";
for (int i=0; i<chars.length; i++) {
char c = chars[i];
if (c >= 65 && c <= 90) {
@@ -28,7 +28,7 @@ public abstract class SortingType {
protected final int DEFAULT_NUMBER = Integer.MAX_VALUE / 2;
/** Placeholder to sort by, if sorting type uses it */
protected TabPlaceholder sortingPlaceholder;
protected String sortingPlaceholder;
/**
* Constructs new instance with given parameter
@@ -43,7 +43,7 @@ public abstract class SortingType {
TAB.getInstance().getConfigHelper().startup().invalidSortingPlaceholder(sortingPlaceholder, this);
} else {
sorting.addUsedPlaceholder(sortingPlaceholder);
this.sortingPlaceholder = TAB.getInstance().getPlaceholderManager().getPlaceholder(sortingPlaceholder);
this.sortingPlaceholder = sortingPlaceholder;
}
}
@@ -56,7 +56,8 @@ public abstract class SortingType {
*/
protected String setPlaceholders(TabPlayer player) {
if (sortingPlaceholder == null) return "";
return sortingPlaceholder.set(sortingPlaceholder.getIdentifier(), player);
TabPlaceholder placeholder = TAB.getInstance().getPlaceholderManager().getPlaceholder(sortingPlaceholder);
return placeholder.set(placeholder.getIdentifier(), player);
}
/**
@@ -90,7 +90,7 @@ public class ServerPlaceholderImpl extends TabPlaceholder implements ServerPlace
@Override
@NotNull
public String getLastValueSafe(@NotNull TabPlayer player) {
return identifier;
return lastValue;
}
/**
@@ -37,6 +37,12 @@ public abstract class Scoreboard<T extends TabPlayer, C> {
/** Player-to-Team map of expected teams of players */
private final Map<String, String> expectedTeams = new HashMap<>();
/** Map of blocked team adds, key is player and value is team name */
private final Map<String, String> blockedTeamAdds = new HashMap<>();
/** Map of allowed team adds, key is player and value is team name */
private final Map<String, String> allowedTeamAdds = new HashMap<>();
/** Flag tracking time between Login packet send and its processing */
private boolean frozen;
@@ -372,26 +378,48 @@ public abstract class Scoreboard<T extends TabPlayer, C> {
@NotNull
public Collection<String> onTeamPacket(int action, @NonNull String teamName, @NonNull Collection<String> players) {
Collection<String> newList = new ArrayList<>();
for (String entry : players) {
String expectedTeam = expectedTeams.get(entry);
if (expectedTeam == null) {
newList.add(entry);
continue;
}
if (!teamName.equals(expectedTeam)) {
// on REMOVE_PLAYER don't warn, just remove the player (required to prevent FPS drop, #1193)
// will still throw error and freeze if removing after team handling was paused (API, disable condition),
// since expected team is null, but add action was blocked previously but remove will not be blocked anymore,
// but that is a very specific case that people are unlikely to even reproduce
// workaround would be complicated, just let it be for now
if (action == TeamAction.CREATE || action == TeamAction.ADD_PLAYER) {
if (action == TeamAction.CREATE || action == TeamAction.ADD_PLAYER) {
for (String entry : players) {
String expectedTeam = expectedTeams.get(entry);
if (expectedTeam == null) {
blockedTeamAdds.remove(entry);
allowedTeamAdds.put(entry, teamName);
newList.add(entry);
continue;
}
if (teamName.equals(expectedTeam)) {
newList.add(entry);
allowedTeamAdds.remove(entry);
} else {
blockedTeamAdds.put(entry, teamName);
logTeamOverride(teamName, entry, expectedTeam);
}
} else {
newList.add(entry);
}
return newList;
}
return newList;
if (action == TeamAction.REMOVE_PLAYER) {
// TAB does not send remove player, making checks easier
for (String entry : players) {
String expectedTeam = expectedTeams.get(entry);
if (expectedTeam != null) {
allowedTeamAdds.remove(entry);
blockedTeamAdds.remove(entry);
continue;
}
if (allowedTeamAdds.containsKey(entry)) {
allowedTeamAdds.remove(entry);
newList.add(entry);
continue;
}
blockedTeamAdds.remove(entry);
}
return newList;
}
if (action == TeamAction.REMOVE) {
allowedTeamAdds.entrySet().removeIf(entry -> entry.getValue().equals(teamName));
blockedTeamAdds.entrySet().removeIf(entry -> entry.getValue().equals(teamName));
}
return players;
}
/**
@@ -403,7 +431,7 @@ public abstract class Scoreboard<T extends TabPlayer, C> {
* @param player
* Player who was about to be added into the team
* @param expectedTeam
* Expected name of the team
* Expected team
*/
public static void logTeamOverride(@NonNull String team, @NonNull String player, @NonNull String expectedTeam) {
String message = "Blocked attempt to add player " + player + " into team " + team + " (expected team: " + expectedTeam + ")";
@@ -84,7 +84,12 @@ public class SpongeTabPlayer extends BackendTabPlayer {
public TabList.Skin getSkin() {
List<ProfileProperty> list = getPlayer().profile().properties();
if (list.isEmpty()) return null; // Offline mode
return new TabList.Skin(list.get(0).value(), list.get(0).signature().orElse(null));
for (ProfileProperty property : list) {
if (property.name().equals(TabList.TEXTURES_PROPERTY)) {
return new TabList.Skin(property.value(), property.signature().orElse(null));
}
}
return null;
}
@Override
@@ -18,12 +18,19 @@ import me.neznamy.tab.shared.platform.EventListener;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* The core for Velocity forwarding events into all enabled features
*/
@SuppressWarnings("UnstableApiUsage")
public class VelocityEventListener implements EventListener<Player> {
/** Map for tracking online players */
private final Map<Player, UUID> players = new ConcurrentHashMap<>();
/**
* Listens to player disconnecting from the server.
*
@@ -32,7 +39,10 @@ public class VelocityEventListener implements EventListener<Player> {
*/
@Subscribe
public void onQuit(@NotNull DisconnectEvent e) {
quit(e.getPlayer().getUniqueId());
// Check if the player was actually connected to the server in the first place to avoid processing
// disconnect of an existing player who is still there (because players are mapped by UUID in TAB)
UUID id = players.remove(e.getPlayer());
if (id != null) quit(id);
}
/**
@@ -66,6 +76,7 @@ public class VelocityEventListener implements EventListener<Player> {
tab.getCPUManager().runTask(() -> {
TabPlayer player = tab.getPlayer(e.getPlayer().getUniqueId());
if (player == null) {
players.put(e.getPlayer(), e.getPlayer().getUniqueId());
tab.getFeatureManager().onJoin(createPlayer(e.getPlayer()));
} else {
player.getScoreboard().freeze(); // Prevent server switch listeners from sending packets before re-registering objectives in onLoginPacket
@@ -9,7 +9,6 @@ import me.neznamy.tab.platforms.velocity.features.VelocityRedisSupport;
import me.neznamy.tab.platforms.velocity.hook.VelocityPremiumVanishHook;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisSupport;
@@ -18,6 +17,8 @@ import me.neznamy.tab.shared.hook.PremiumVanishHook;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import org.bstats.charts.SimplePie;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,6 +33,9 @@ public class VelocityPlatform extends ProxyPlatform {
@NotNull
private final VelocityTAB plugin;
/** Logger for components */
private static final ComponentLogger logger = ComponentLogger.logger("TAB");
/** Plugin message channel */
@Getter
private final MinecraftChannelIdentifier MCI = MinecraftChannelIdentifier.from(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME);
@@ -68,12 +72,12 @@ public class VelocityPlatform extends ProxyPlatform {
@Override
public void logInfo(@NotNull TabComponent message) {
plugin.getLogger().info(message.toLegacyText());
logger.info(AdventureHook.toAdventureComponent(message, true));
}
@Override
public void logWarn(@NotNull TabComponent message) {
plugin.getLogger().warn(EnumChatFormat.RED + message.toLegacyText());
logger.warn(Component.text("").color(NamedTextColor.RED).append(AdventureHook.toAdventureComponent(message, true)));
}
@Override
@@ -7,7 +7,6 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.TabList;
import lombok.Getter;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
@@ -18,7 +17,6 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.util.UUID;
/**
* Main class for Velocity.
@@ -54,9 +52,8 @@ public class VelocityTAB {
*/
@Subscribe
public void onProxyInitialization(@Nullable ProxyInitializeEvent event) {
if (!ReflectionUtils.methodExists(TabList.class, "getEntry", UUID.class)) {
logger.warn(EnumChatFormat.RED + "The plugin requires Velocity build #229 " +
"(released on February 20th, 2023) and up to work.");
if (!ReflectionUtils.classExists("net.kyori.adventure.text.logger.slf4j.ComponentLogger")) {
logger.warn(EnumChatFormat.RED + "The plugin requires Velocity build #274 (released on October 12th, 2023) and up to work.");
return;
}
TAB.create(new VelocityPlatform(this));
@@ -65,7 +65,12 @@ public class VelocityTabPlayer extends ProxyTabPlayer {
public TabList.Skin getSkin() {
List<GameProfile.Property> properties = getPlayer().getGameProfile().getProperties();
if (properties.isEmpty()) return null; //Offline mode
return new TabList.Skin(properties.get(0).getValue(), properties.get(0).getSignature());
for (GameProfile.Property property : properties) {
if (property.getName().equals(TabList.TEXTURES_PROPERTY)) {
return new TabList.Skin(property.getValue(), property.getSignature());
}
}
return null;
}
@Override