Compare commits

..
13 Commits
18 changed files with 103 additions and 50 deletions

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
allprojects {
group = "me.neznamy"
version = "4.1.5"
version = "4.1.6"
description = "An all-in-one solution that works"
ext.set("id", "tab")
@@ -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,26 +50,26 @@ 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);
newChatModifier = ReflectionUtils.setAccessible(ChatModifier.getDeclaredConstructor(chatHexColor, Boolean.class, Boolean.class, Boolean.class,
@@ -16,6 +16,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
/**
* Class storing all team related fields and methods.
@@ -70,7 +71,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,7 +308,7 @@ 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;
if (action == TeamAction.UPDATE) return;
TeamPacket_PLAYERS.set(packet, player.getScoreboard().onTeamPacket(
action, (String) TeamPacket_NAME.get(packet), (Collection<String>) TeamPacket_PLAYERS.get(packet)));
}
+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]));
}
}
}
+2 -2
View File
@@ -12,11 +12,11 @@ repositories {
dependencies {
api(projects.shared)
minecraft("com.mojang:minecraft:24w19a")
minecraft("com.mojang:minecraft:1.21")
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;
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();
/** Method loader using 1.18.2 */
private static final Loader loader1_18_2 = createLoader("1_18_2");
@@ -42,14 +42,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Method loader compiled using Minecraft 1.20.5.
* Method loader compiled using Minecraft 1.21.
*/
@SuppressWarnings({
"unchecked", // Java generic types
"DataFlowIssue", // Profile is not null on add action
"unused" // Actually used, just via reflection
})
public class Loader_1_20_5 implements Loader {
public class Loader_1_21 implements Loader {
@Override
@NotNull
@@ -96,7 +96,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 +147,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);
}
}
+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"
]
},
"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));
@@ -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,7 @@ import org.jetbrains.annotations.NotNull;
public enum ProtocolVersion {
UNKNOWN,
V1_21 (767),
V1_20_6 (766),
V1_20_5 (766),
V1_20_4 (765),
@@ -81,7 +82,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;
@@ -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();
@@ -63,12 +63,13 @@ public class Placeholder extends SortingType {
String output = EnumChatFormat.color(setPlaceholders(p));
p.sortingData.teamNameNote += "\n-> " + sortingPlaceholder.getIdentifier() + " 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.getIdentifier(), 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));
@@ -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 + ")";