mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0827d083bf | ||
|
|
2f7b42ab85 | ||
|
|
735aa666d6 | ||
|
|
c2ceb643e5 | ||
|
|
5c166f647f | ||
|
|
03a8fb9aa2 | ||
|
|
1a44c97d5a | ||
|
|
29cf0a26d6 | ||
|
|
33d172598d | ||
|
|
af1e685c2b | ||
|
|
09129974db | ||
|
|
0efb69fb4b | ||
|
|
5cbec52ea3 | ||
|
|
8db9e64121 | ||
|
|
05009b17f0 | ||
|
|
032af707dd | ||
|
|
cf227a97f3 | ||
|
|
1a2075dfe2 | ||
|
|
1de7665309 | ||
|
|
9473037127 | ||
|
|
dcb234dc08 | ||
|
|
79eb34fc32 | ||
|
|
40544ef5d3 | ||
|
|
14dfa0154a | ||
|
|
7df2aba2d1 | ||
|
|
9edf3176ea | ||
|
|
ec73aa13b4 | ||
|
|
6f002cd61b | ||
|
|
cf065ba27d | ||
|
|
f96e263bd7 | ||
|
|
a22f8eccaf | ||
|
|
718ee89211 | ||
|
|
54b44ce6cd | ||
|
|
29da28e955 | ||
|
|
fa195389ab | ||
|
|
9e3864be53 | ||
|
|
29b89b3bb7 | ||
|
|
e741608261 | ||
|
|
8e8a4cc9e8 | ||
|
|
3bb6e0c3f2 | ||
|
|
4c3b13a81c | ||
|
|
6b9063c789 | ||
|
|
ce0a45b76c | ||
|
|
41a0bd37ce | ||
|
|
449d3cb3ee | ||
|
|
a1a3852b08 | ||
|
|
ef63df5175 | ||
|
|
17e57f9d0c |
No files matched your search
@@ -2,7 +2,7 @@ name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v5
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
|
||||
@@ -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
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "me.neznamy"
|
||||
version = "4.1.5-SNAPSHOT"
|
||||
version = "4.1.9"
|
||||
description = "An all-in-one solution that works"
|
||||
|
||||
ext.set("id", "tab")
|
||||
|
||||
@@ -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")
|
||||
|
||||
+1
-1
@@ -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;
|
||||
@@ -41,7 +41,7 @@ public class ComponentConverter {
|
||||
|
||||
// 1.16+
|
||||
private Method ChatHexColor_fromRGB;
|
||||
private Constructor<?> newMinecraftKey;
|
||||
private Method ResourceLocation_tryParse;
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads all NMS classes, constructors and methods.
|
||||
@@ -50,30 +50,30 @@ 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<?> MinecraftKey = BukkitReflection.getClass("resources.MinecraftKey", "MinecraftKey");
|
||||
Class<?> chatClickable = BukkitReflection.getClass("network.chat.ChatClickable", "ChatClickable");
|
||||
Class<?> chatHoverable = BukkitReflection.getClass("network.chat.ChatHoverable", "ChatHoverable");
|
||||
newMinecraftKey = MinecraftKey.getConstructor(String.class);
|
||||
ChatHexColor_fromRGB = ReflectionUtils.getOnlyMethod(chatHexColor, chatHexColor, int.class);
|
||||
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.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, MinecraftKey));
|
||||
Boolean.class, Boolean.class, chatClickable, chatHoverable, String.class, ResourceLocation));
|
||||
convertModifier = this::createModifierModern;
|
||||
} else {
|
||||
newChatModifier = ChatModifier.getConstructor();
|
||||
@@ -124,7 +124,7 @@ public class ComponentConverter {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
modifier.getFont() == null ? null : newMinecraftKey.newInstance(modifier.getFont())
|
||||
modifier.getFont() == null ? null : ResourceLocation_tryParse.invoke(null, modifier.getFont())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -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,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")
|
||||
}
|
||||
@@ -29,10 +29,12 @@ public class BungeeBossBar implements BossBar {
|
||||
@NotNull
|
||||
private final Map<UUID, Integer> styles = new HashMap<>();
|
||||
|
||||
/** Flag tracking whether boss bars should be frozen or not */
|
||||
private boolean frozen;
|
||||
|
||||
@Override
|
||||
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 0);
|
||||
packet.setHealth(progress);
|
||||
packet.setTitle(TabComponent.optimized(title).convert(player.getVersion()));
|
||||
@@ -46,8 +48,7 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull String title) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 3);
|
||||
packet.setTitle(TabComponent.optimized(title).convert(player.getVersion()));
|
||||
player.sendPacket(packet);
|
||||
@@ -55,8 +56,7 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, float progress) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 2);
|
||||
packet.setHealth(progress);
|
||||
player.sendPacket(packet);
|
||||
@@ -64,8 +64,7 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull BarStyle style) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
|
||||
packet.setDivision(style.ordinal());
|
||||
packet.setColor(colors.get(id));
|
||||
@@ -76,8 +75,7 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull BarColor color) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
net.md_5.bungee.protocol.packet.BossBar packet = new net.md_5.bungee.protocol.packet.BossBar(id, 4);
|
||||
packet.setDivision(styles.get(id));
|
||||
packet.setColor(color.ordinal());
|
||||
@@ -88,11 +86,20 @@ public class BungeeBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull UUID id) {
|
||||
if (player.getVersion().getMinorVersion() < 9) return;
|
||||
|
||||
if (frozen) return; // Server switch
|
||||
player.sendPacket(new net.md_5.bungee.protocol.packet.BossBar(id, 1));
|
||||
|
||||
colors.remove(id);
|
||||
styles.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unfreeze() {
|
||||
frozen = false;
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -42,10 +42,12 @@ public class BungeeEventListener implements EventListener<ProxiedPlayer>, Listen
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.isPluginDisabled()) return;
|
||||
|
||||
// Avoid 1.20.3 client crash on scoreboard packets, do it sync to prevent packet being sent after event, but before processing
|
||||
// Avoid 1.20.3+ client crash on scoreboard packets, do it sync to prevent packet being sent after event, but before processing
|
||||
// Avoid 1.20.5+ client disconnect with "Network Protocol Error"
|
||||
TabPlayer p = tab.getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p != null && p.getVersion().getNetworkId() >= ProtocolVersion.V1_20_3.getNetworkId()) {
|
||||
if (p != null && p.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
p.getScoreboard().freeze();
|
||||
p.getBossBar().freeze();
|
||||
}
|
||||
|
||||
tab.getCPUManager().runTask(() -> {
|
||||
@@ -57,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
|
||||
|
||||
+9
-1
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
|
||||
+7
-12
@@ -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));
|
||||
@@ -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": {
|
||||
|
||||
+3
-3
@@ -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 {
|
||||
|
||||
@@ -237,7 +237,7 @@ public class PlayerList extends TabFeature implements TabListFormatManager, Join
|
||||
connectedPlayer.getTabList().setAntiOverride(antiOverrideTabList);
|
||||
updateProperties(connectedPlayer);
|
||||
if (disableChecker.isDisableConditionMet(connectedPlayer)) {
|
||||
connectedPlayer.disabledHeaderFooter.set(true);
|
||||
connectedPlayer.disabledPlayerList.set(true);
|
||||
return;
|
||||
}
|
||||
Runnable r = () -> {
|
||||
|
||||
@@ -264,12 +264,12 @@ public class BossBarLine implements BossBar {
|
||||
public void addPlayer(@NonNull me.neznamy.tab.api.TabPlayer p) {
|
||||
TabPlayer player = (TabPlayer) p;
|
||||
if (players.contains(player)) return;
|
||||
players.add(player);
|
||||
player.setProperty(textRefresher, propertyTitle, title);
|
||||
player.setProperty(progressRefresher, propertyProgress, progress);
|
||||
player.setProperty(colorRefresher, propertyColor, color);
|
||||
player.setProperty(styleRefresher, propertyStyle, style);
|
||||
sendToPlayerRaw(player);
|
||||
players.add(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,8 +83,11 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100,
|
||||
() -> TimeUnit.MILLISECONDS.toSeconds(announceEndTime - System.currentTimeMillis()));
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> {
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(announceEndTime - System.currentTimeMillis());
|
||||
if (seconds < 0) return 0;
|
||||
return seconds;
|
||||
});
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
onJoin(p);
|
||||
}
|
||||
@@ -165,12 +168,11 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager, Jo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoginPacket(TabPlayer player) {
|
||||
// Since 1.20.2, Login packet clears BossBars as well
|
||||
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
player.getBossBar().unfreeze();
|
||||
for (BossBar bar : lineValues) {
|
||||
if (bar.containsPlayer(player)) {
|
||||
((BossBarLine)bar).sendToPlayerRaw(player);
|
||||
|
||||
@@ -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));
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+12
-11
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,27 +81,27 @@ public abstract class SortingType {
|
||||
|
||||
/**
|
||||
* Compresses a number to ### format, where # is a character symbol representing
|
||||
* a number in a base of 65536. The first two represent the whole part, the third one decimal part.
|
||||
* a number in a base of 65534. The first two represent the whole part, the third one decimal part.
|
||||
* The maximum number it will work properly with is {@link Integer#MAX_VALUE}.
|
||||
*
|
||||
* @param number
|
||||
* Number to convert
|
||||
* @return 3 characters long String of converted number with a base of 65536.
|
||||
* @return 3 characters long String of converted number with a base of 65534.
|
||||
*/
|
||||
public String compressNumber(double number) {
|
||||
int wholePart = (int) number;
|
||||
char decimalChar = (char) ((number - wholePart) * (Character.MAX_VALUE - 1));
|
||||
int base = Character.MAX_VALUE - 1;
|
||||
char decimalChar = (char) ((number - wholePart) * base);
|
||||
// The \ symbol breaks json syntax, skip it (and reduce range) (why is it not being escaped by json writer?)
|
||||
if (decimalChar >= '\\') decimalChar++;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (wholePart > 0) {
|
||||
char digit = (char) (wholePart % (Character.MAX_VALUE - 1));
|
||||
char digit = (char) (wholePart % base);
|
||||
if (digit >= '\\') digit++;
|
||||
sb.append(digit);
|
||||
wholePart /= Character.MAX_VALUE;
|
||||
sb.insert(0, digit);
|
||||
wholePart /= base;
|
||||
}
|
||||
sb.reverse();
|
||||
if (sb.length() == 1) sb.insert(0, (char) 0); // Avoid a single # if number is < 65535
|
||||
if (sb.length() == 1) sb.insert(0, (char) 0); // Avoid a single # if number is < base
|
||||
sb.append(decimalChar);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class ServerPlaceholderImpl extends TabPlaceholder implements ServerPlace
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLastValueSafe(@NotNull TabPlayer player) {
|
||||
return identifier;
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,4 +74,19 @@ public interface BossBar {
|
||||
* Unique identifier to match the bossbar with other functions
|
||||
*/
|
||||
void remove(@NotNull UUID id);
|
||||
|
||||
|
||||
/**
|
||||
* Freezes the class, not letting any packets through.
|
||||
*/
|
||||
default void freeze() {
|
||||
// Empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfreezes the class, enabling it back.
|
||||
*/
|
||||
default void unfreeze() {
|
||||
// Empty by default
|
||||
}
|
||||
}
|
||||
@@ -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 + ")";
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
|
||||
package me.neznamy.tab.shared.platform.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.shared.chat.TabComponent;
|
||||
import me.neznamy.tab.shared.platform.BossBar;
|
||||
import me.neznamy.tab.shared.platform.TabPlayer;
|
||||
import me.neznamy.tab.shared.util.ReflectionUtils;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.bossbar.BossBar.Color;
|
||||
import net.kyori.adventure.bossbar.BossBar.Overlay;
|
||||
@@ -17,23 +20,25 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Shared BossBar implementation using Adventure API.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class AdventureBossBar implements BossBar {
|
||||
|
||||
/** Flag tracking whether this implementation is available on the server or not */
|
||||
@Getter
|
||||
private static final boolean available = ReflectionUtils.classExists("net.kyori.adventure.bossbar.BossBar");
|
||||
|
||||
/** Player this BossBar belongs to */
|
||||
private final TabPlayer player;
|
||||
|
||||
/** BossBars currently visible to the player */
|
||||
private final Map<UUID, net.kyori.adventure.bossbar.BossBar> bossBars = new HashMap<>();
|
||||
|
||||
/** Flag tracking whether boss bars should be frozen or not */
|
||||
private boolean frozen;
|
||||
|
||||
@Override
|
||||
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
|
||||
if (bossBars.containsKey(id)) {
|
||||
// Can happen on 1.20.2+ on Velocity on server switch
|
||||
((Audience)player.getPlayer()).hideBossBar(bossBars.get(id));
|
||||
((Audience)player.getPlayer()).showBossBar(bossBars.get(id));
|
||||
return;
|
||||
}
|
||||
if (frozen) return; // Server switch
|
||||
net.kyori.adventure.bossbar.BossBar bar = net.kyori.adventure.bossbar.BossBar.bossBar(
|
||||
TabComponent.optimized(title).convert(player.getVersion()),
|
||||
progress,
|
||||
@@ -46,26 +51,42 @@ public class AdventureBossBar implements BossBar {
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull String title) {
|
||||
if (frozen) return; // Server switch
|
||||
bossBars.get(id).name(TabComponent.optimized(title).convert(player.getVersion()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, float progress) {
|
||||
if (frozen) return; // Server switch
|
||||
bossBars.get(id).progress(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull BarStyle style) {
|
||||
if (frozen) return; // Server switch
|
||||
bossBars.get(id).overlay(Overlay.valueOf(style.name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull UUID id, @NotNull BarColor color) {
|
||||
if (frozen) return; // Server switch
|
||||
bossBars.get(id).color(Color.valueOf(color.name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull UUID id) {
|
||||
if (frozen) return; // Server switch
|
||||
((Audience)player.getPlayer()).hideBossBar(bossBars.remove(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
bossBars.clear();
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unfreeze() {
|
||||
frozen = false;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+33
-2
@@ -1,12 +1,15 @@
|
||||
package me.neznamy.tab.platforms.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
@@ -15,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.
|
||||
*
|
||||
@@ -29,7 +39,27 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes Boss bar for 1.20.2+ players due to bug with adventure that causes disconnect
|
||||
* on 1.20.5+ with "Network Protocol Error"
|
||||
*
|
||||
* @param e
|
||||
* Event fired before player switches server for proper freezing
|
||||
*/
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void preConnect(@NotNull ServerPreConnectEvent e) {
|
||||
if (e.getResult().isAllowed()) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p != null && p.getVersion().getNetworkId() >= ProtocolVersion.V1_20_2.getNetworkId()) {
|
||||
p.getBossBar().freeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,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
|
||||
@@ -97,4 +128,4 @@ public class VelocityEventListener implements EventListener<Player> {
|
||||
public TabPlayer createPlayer(@NotNull Player player) {
|
||||
return new VelocityTabPlayer((VelocityPlatform) TAB.getInstance().getPlatform(), player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user