From d4db9705fdd71956b7773cad3616db294bb853a8 Mon Sep 17 00:00:00 2001 From: syldium Date: Sun, 7 Jun 2026 18:37:09 +0200 Subject: [PATCH 1/5] Move away from final field mutations --- .../fr/mrmicky/fastboard/FastBoardBase.java | 178 +++++++++++------- 1 file changed, 113 insertions(+), 65 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 4ec97a9..5677721 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -29,6 +29,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -60,11 +61,14 @@ public abstract class FastBoardBase { private static final MethodHandle SEND_PACKET; private static final MethodHandle PLAYER_GET_HANDLE; private static final MethodHandle FIXED_NUMBER_FORMAT; + // Scoreboard teams + private static final MethodHandle OBJECTIVE; + private static final MethodHandle PLAYER_TEAM; // Scoreboard packets - private static final FastReflection.PacketConstructor PACKET_SB_OBJ; - private static final FastReflection.PacketConstructor PACKET_SB_DISPLAY_OBJ; - private static final FastReflection.PacketConstructor PACKET_SB_TEAM; - private static final FastReflection.PacketConstructor PACKET_SB_SERIALIZABLE_TEAM; + private static final MethodHandle PACKET_SB_OBJ; + private static final MethodHandle PACKET_SB_DISPLAY_OBJ; + private static final MethodHandle PACKET_SB_TEAM; + private static final MethodHandle PACKET_SB_SERIALIZABLE_TEAM; private static final MethodHandle PACKET_SB_SET_SCORE; private static final MethodHandle PACKET_SB_RESET_SCORE; private static final boolean SCORE_OPTIONAL_COMPONENTS; @@ -72,15 +76,12 @@ public abstract class FastBoardBase { private static final Class DISPLAY_SLOT_TYPE; private static final Class ENUM_SB_HEALTH_DISPLAY; private static final Class ENUM_SB_ACTION; - private static final Class ENUM_VISIBILITY; - private static final Class ENUM_COLLISION_RULE; private static final Object BLANK_NUMBER_FORMAT; private static final Object SIDEBAR_DISPLAY_SLOT; private static final Object ENUM_SB_HEALTH_DISPLAY_INTEGER; private static final Object ENUM_SB_ACTION_CHANGE; private static final Object ENUM_SB_ACTION_REMOVE; - private static final Object ENUM_VISIBILITY_ALWAYS; - private static final Object ENUM_COLLISION_RULE_ALWAYS; + private static final Object DUMMY_SCOREBOARD_CRITERIA; static { try { @@ -127,8 +128,14 @@ public abstract class FastBoardBase { PLAYER_GET_HANDLE = lookup.findVirtual(craftPlayerClass, "getHandle", MethodType.methodType(entityPlayerClass)); PLAYER_CONNECTION = lookup.unreflectGetter(playerConnectionField); SEND_PACKET = lookup.unreflect(sendPacketMethod); - PACKET_SB_OBJ = FastReflection.findPacketConstructor(packetSbObjClass, lookup); - PACKET_SB_DISPLAY_OBJ = FastReflection.findPacketConstructor(packetSbDisplayObjClass, lookup); + + Class scoreboardClass = FastReflection.nmsClass("world.scores", "Scoreboard"); + Class playerTeamClass = FastReflection.nmsClass("world.scores", "ScoreboardTeam", "PlayerTeam"); + Class objectiveClass = FastReflection.nmsClass("world.scores", "ScoreboardObjective", "Objective"); + Class objectiveCriteriaClass = FastReflection.nmsClass("world.scores.criteria", "IScoreboardCriteria", "ObjectiveCriteria"); + PLAYER_TEAM = lookup.unreflectConstructor(playerTeamClass.getConstructor(scoreboardClass, String.class)); + + Class objectiveRenderTypeClass = FastReflection.nmsOptionalClass("world.scores.criteria", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "ObjectiveCriteria$RenderType").orElse(null); Optional> numberFormat = FastReflection.nmsOptionalClass("network.chat.numbers", "NumberFormat"); MethodHandle packetSbSetScore; @@ -138,6 +145,10 @@ public abstract class FastBoardBase { boolean scoreOptionalComponents = false; if (numberFormat.isPresent()) { // 1.20.3 + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass, boolean.class, numberFormat.get())); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(DISPLAY_SLOT_TYPE, objectiveClass)); + Class blankFormatClass = FastReflection.nmsClass("network.chat.numbers", "BlankFormat"); Class fixedFormatClass = FastReflection.nmsClass("network.chat.numbers", "FixedFormat"); Class resetScoreClass = FastReflection.nmsClass(gameProtocolPackage, "ClientboundResetScorePacket"); @@ -158,31 +169,31 @@ public abstract class FastBoardBase { Class enumSbAction = FastReflection.nmsClass("server", "ScoreboardServer$Action", "ServerScoreboard$Method"); MethodType scoreType = MethodType.methodType(void.class, enumSbAction, String.class, String.class, int.class); packetSbSetScore = lookup.findConstructor(packetSbScoreClass, scoreType); + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(int.class, objectiveClass)); } else { packetSbSetScore = lookup.findConstructor(packetSbScoreClass, MethodType.methodType(void.class)); + if (VersionType.V1_13.isHigherOrEqual()) { + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); + } else { + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass)); + } + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(int.class, objectiveClass)); } PACKET_SB_SET_SCORE = packetSbSetScore; PACKET_SB_RESET_SCORE = packetSbResetScore; - PACKET_SB_TEAM = FastReflection.findPacketConstructor(packetSbTeamClass, lookup); - PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass != null ? FastReflection.findPacketConstructor(sbTeamClass, lookup) : null; + Constructor packetSbTeamConstructor = sbTeamClass != null ? packetSbTeamClass.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class) : packetSbTeamClass.getDeclaredConstructor(); + packetSbTeamConstructor.setAccessible(true); + PACKET_SB_TEAM = lookup.unreflectConstructor(packetSbTeamConstructor); + PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass != null ? lookup.unreflectConstructor(sbTeamClass.getConstructor(playerTeamClass)) : null; FIXED_NUMBER_FORMAT = fixedFormatConstructor; BLANK_NUMBER_FORMAT = blankNumberFormat; SCORE_OPTIONAL_COMPONENTS = scoreOptionalComponents; - if (VersionType.V1_17.isHigherOrEqual()) { - ENUM_VISIBILITY = FastReflection.nmsClass("world.scores", "ScoreboardTeamBase$EnumNameTagVisibility", "Team$Visibility"); - ENUM_COLLISION_RULE = FastReflection.nmsClass("world.scores", "ScoreboardTeamBase$EnumTeamPush", "Team$CollisionRule"); - ENUM_VISIBILITY_ALWAYS = FastReflection.enumValueOf(ENUM_VISIBILITY, "ALWAYS", 0); - ENUM_COLLISION_RULE_ALWAYS = FastReflection.enumValueOf(ENUM_COLLISION_RULE, "ALWAYS", 0); - } else { - ENUM_VISIBILITY = null; - ENUM_COLLISION_RULE = null; - ENUM_VISIBILITY_ALWAYS = null; - ENUM_COLLISION_RULE_ALWAYS = null; - } - - for (Class clazz : Arrays.asList(packetSbObjClass, packetSbDisplayObjClass, packetSbScoreClass, packetSbTeamClass, sbTeamClass)) { + for (Class clazz : Arrays.asList(packetSbScoreClass, packetSbTeamClass, sbTeamClass, playerTeamClass, objectiveClass)) { if (clazz == null) { continue; } @@ -211,7 +222,12 @@ public abstract class FastBoardBase { ENUM_SB_ACTION_CHANGE = null; ENUM_SB_ACTION_REMOVE = null; } - } catch (Throwable t) { + if (VersionType.V1_13.isHigherOrEqual()) { + DUMMY_SCOREBOARD_CRITERIA = null; + } else { + DUMMY_SCOREBOARD_CRITERIA = FastReflection.nmsClass("world.scores.criteria", "ScoreboardBaseCriteria").getConstructor(String.class).newInstance("dummy"); + } + } catch (Throwable t) { throw new ExceptionInInitializerError(t); } } @@ -235,8 +251,8 @@ protected FastBoardBase(Player player) { this.id = "fb-" + Integer.toHexString(ThreadLocalRandom.current().nextInt()); try { - sendObjectivePacket(ObjectiveMode.CREATE); - sendDisplayObjectivePacket(); + Object objective = sendObjectivePacket(ObjectiveMode.CREATE); + sendDisplayObjectivePacket(objective); } catch (Throwable t) { throw new RuntimeException("Unable to create scoreboard", t); } @@ -641,32 +657,53 @@ protected T getLineByScore(List lines, int score) { return score < lines.size() ? lines.get(lines.size() - score - 1) : null; } - protected void sendObjectivePacket(ObjectiveMode mode) throws Throwable { - Object packet = PACKET_SB_OBJ.invoke(); - - setField(packet, String.class, this.id); - setField(packet, int.class, mode.ordinal()); - - if (mode != ObjectiveMode.REMOVE) { - setComponentField(packet, this.title, 1); - setField(packet, Optional.class, Optional.empty()); // Number format for 1.20.5+, previously nullable - - if (VersionType.V1_8.isHigherOrEqual()) { - setField(packet, ENUM_SB_HEALTH_DISPLAY, ENUM_SB_HEALTH_DISPLAY_INTEGER); - } - } else if (VERSION_TYPE == VersionType.V1_7) { - setField(packet, String.class, "", 1); + protected Object sendObjectivePacket(ObjectiveMode mode) throws Throwable { + Object objective; + if (BLANK_NUMBER_FORMAT != null) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER, // Render type + false, // Auto-update, unused + null // Number format + ); + } else if (VersionType.V1_17.isHigherOrEqual()) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER // Render type + ); + } else if (VersionType.V1_13.isHigherOrEqual()) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER // Render type + ); + } else { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + DUMMY_SCOREBOARD_CRITERIA // Criteria + ); + setComponentField(objective, this.title, 1); } + Object packet = PACKET_SB_OBJ.invoke(objective, mode.ordinal()); sendPacket(packet); + return objective; } - protected void sendDisplayObjectivePacket() throws Throwable { - Object packet = PACKET_SB_DISPLAY_OBJ.invoke(); - - setField(packet, DISPLAY_SLOT_TYPE, SIDEBAR_DISPLAY_SLOT); // Position - setField(packet, String.class, this.id); // Score Name - + protected void sendDisplayObjectivePacket(Object objective) throws Throwable { + Object packet = PACKET_SB_DISPLAY_OBJ.invoke( + SIDEBAR_DISPLAY_SLOT, // Position + objective // Score Name + ); sendPacket(packet); } @@ -732,29 +769,40 @@ protected void sendTeamPacket(int score, TeamMode mode, T prefix, T suffix) throw new UnsupportedOperationException(); } - Object packet = PACKET_SB_TEAM.invoke(); - - setField(packet, String.class, this.id + ':' + score); // Team name - setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode - + Object packet; if (mode == TeamMode.REMOVE) { + if (VersionType.V1_17.isHigherOrEqual()) { + packet = PACKET_SB_TEAM.invoke( + this.id + ':' + score, // Team name + mode.ordinal(), // Update mode + Optional.empty(), // Serializable team, unused + Collections.emptyList() // Players + ); + } else { + packet = PACKET_SB_TEAM.invoke(); + setField(packet, String.class, this.id + ':' + score); // Team name + setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode + } sendPacket(packet); return; } if (VersionType.V1_17.isHigherOrEqual()) { - Object team = PACKET_SB_SERIALIZABLE_TEAM.invoke(); - // Since the packet is initialized with null values, we need to change more things. - setComponentField(team, null, 0); // Display name - setField(team, CHAT_FORMAT_ENUM, RESET_FORMATTING); // Color - setComponentField(team, prefix, 1); // Prefix - setComponentField(team, suffix, 2); // Suffix - setField(team, String.class, "always", 0); // Visibility before 1.21.5 - setField(team, String.class, "always", 1); // Collisions before 1.21.5 - setField(team, ENUM_VISIBILITY, ENUM_VISIBILITY_ALWAYS, 0); // 1.21.5+ - setField(team, ENUM_COLLISION_RULE, ENUM_COLLISION_RULE_ALWAYS, 0); // 1.21.5+ - setField(packet, Optional.class, Optional.of(team)); + Object team = PLAYER_TEAM.invoke(null, this.id + ':' + score); + setComponentField(team, null, 1); // Display name + setComponentField(team, prefix, 2); // Prefix + setComponentField(team, suffix, 3); // Suffix + Object serializableTeam = PACKET_SB_SERIALIZABLE_TEAM.invoke(team); + packet = PACKET_SB_TEAM.invoke( + this.id + ':' + score, // Team name + mode.ordinal(), // Update mode + Optional.of(serializableTeam), // Serializable team + Collections.emptyList() // Players + ); } else { + packet = PACKET_SB_TEAM.invoke(); + setField(packet, String.class, this.id + ':' + score); // Team name + setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode setComponentField(packet, prefix, 2); // Prefix setComponentField(packet, suffix, 3); // Suffix setField(packet, String.class, "always", 4); // Visibility for 1.8+ From b3b6ef3a9204f7f6bed03b5d54df72e792db8cf7 Mon Sep 17 00:00:00 2001 From: syldium Date: Sun, 7 Jun 2026 18:46:02 +0200 Subject: [PATCH 2/5] Remove PacketConstructor --- .../fr/mrmicky/fastboard/FastReflection.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastReflection.java b/src/main/java/fr/mrmicky/fastboard/FastReflection.java index e14c3a6..3b0db2c 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastReflection.java +++ b/src/main/java/fr/mrmicky/fastboard/FastReflection.java @@ -28,7 +28,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.Field; import java.util.Optional; import java.util.function.Predicate; @@ -135,33 +134,4 @@ static Optional optionalConstructor(Class declaringClass, Metho return Optional.empty(); } } - - public static PacketConstructor findPacketConstructor(Class packetClass, MethodHandles.Lookup lookup) throws Exception { - try { - MethodHandle constructor = lookup.findConstructor(packetClass, VOID_METHOD_TYPE); - return constructor::invoke; - } catch (NoSuchMethodException | IllegalAccessException e) { - // try below with Unsafe - } - - if (theUnsafe == null) { - synchronized (FastReflection.class) { - if (theUnsafe == null) { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); - Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - theUnsafe = theUnsafeField.get(null); - } - } - } - - MethodType allocateMethodType = MethodType.methodType(Object.class, Class.class); - MethodHandle allocateMethod = lookup.findVirtual(theUnsafe.getClass(), "allocateInstance", allocateMethodType); - return () -> allocateMethod.invoke(theUnsafe, packetClass); - } - - @FunctionalInterface - interface PacketConstructor { - Object invoke() throws Throwable; - } } From 93a6d3d195e71efecd0348712f500c8d00ddf170 Mon Sep 17 00:00:00 2001 From: MrMicky Date: Thu, 11 Jun 2026 22:08:24 -0400 Subject: [PATCH 3/5] Remove unused code --- src/main/java/fr/mrmicky/fastboard/FastReflection.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastReflection.java b/src/main/java/fr/mrmicky/fastboard/FastReflection.java index 3b0db2c..f972bb3 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastReflection.java +++ b/src/main/java/fr/mrmicky/fastboard/FastReflection.java @@ -42,12 +42,9 @@ public final class FastReflection { private static final String OBC_PACKAGE = Bukkit.getServer().getClass().getPackage().getName(); private static final String NMS_PACKAGE = OBC_PACKAGE.replace("org.bukkit.craftbukkit", NM_PACKAGE + ".server"); - private static final MethodType VOID_METHOD_TYPE = MethodType.methodType(void.class); private static final boolean NMS_REPACKAGED = optionalClass(NM_PACKAGE + ".network.protocol.Packet").isPresent(); private static final boolean MOJANG_MAPPINGS = optionalClass(NM_PACKAGE + ".network.chat.Component").isPresent(); - private static volatile Object theUnsafe; - private FastReflection() { throw new UnsupportedOperationException(); } From 22284a75e80b60ff33c517a0c7022d124c78f6fb Mon Sep 17 00:00:00 2001 From: MrMicky Date: Thu, 11 Jun 2026 22:19:30 -0400 Subject: [PATCH 4/5] Remove unused code (and minor formatting) --- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 5677721..ea85dea 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -55,8 +55,6 @@ public abstract class FastBoardBase { private static final VersionType VERSION_TYPE; // Packets and components private static final Class CHAT_COMPONENT_CLASS; - private static final Class CHAT_FORMAT_ENUM; - private static final Object RESET_FORMATTING; private static final MethodHandle PLAYER_CONNECTION; private static final MethodHandle SEND_PACKET; private static final MethodHandle PLAYER_GET_HANDLE; @@ -120,10 +118,8 @@ public abstract class FastBoardBase { .filter(m -> m.getParameterCount() == 1 && m.getParameterTypes()[0] == packetClass) .findFirst().orElseThrow(NoSuchMethodException::new); Optional> displaySlotEnum = FastReflection.nmsOptionalClass("world.scores", "DisplaySlot"); - CHAT_COMPONENT_CLASS = FastReflection.nmsClass("network.chat", "IChatBaseComponent","Component"); - CHAT_FORMAT_ENUM = FastReflection.nmsClass(null, "EnumChatFormat", "ChatFormatting"); + CHAT_COMPONENT_CLASS = FastReflection.nmsClass("network.chat", "IChatBaseComponent", "Component"); DISPLAY_SLOT_TYPE = displaySlotEnum.orElse(int.class); - RESET_FORMATTING = FastReflection.enumValueOf(CHAT_FORMAT_ENUM, "RESET", 21); SIDEBAR_DISPLAY_SLOT = displaySlotEnum.isPresent() ? FastReflection.enumValueOf(DISPLAY_SLOT_TYPE, "SIDEBAR", 1) : 1; PLAYER_GET_HANDLE = lookup.findVirtual(craftPlayerClass, "getHandle", MethodType.methodType(entityPlayerClass)); PLAYER_CONNECTION = lookup.unreflectGetter(playerConnectionField); @@ -227,7 +223,7 @@ public abstract class FastBoardBase { } else { DUMMY_SCOREBOARD_CRITERIA = FastReflection.nmsClass("world.scores.criteria", "ScoreboardBaseCriteria").getConstructor(String.class).newInstance("dummy"); } - } catch (Throwable t) { + } catch (Throwable t) { throw new ExceptionInInitializerError(t); } } From e26936ac2d202a373e91d185073a56a4c373b815 Mon Sep 17 00:00:00 2001 From: syldium Date: Mon, 15 Jun 2026 22:07:07 +0200 Subject: [PATCH 5/5] Fix final field mutation when sending a team packet --- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index ea85dea..65d639e 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -793,7 +793,7 @@ protected void sendTeamPacket(int score, TeamMode mode, T prefix, T suffix) this.id + ':' + score, // Team name mode.ordinal(), // Update mode Optional.of(serializableTeam), // Serializable team - Collections.emptyList() // Players + mode == TeamMode.CREATE ? Collections.singletonList(COLOR_CODES[score]) : Collections.emptyList() // Players ); } else { packet = PACKET_SB_TEAM.invoke(); @@ -803,10 +803,9 @@ protected void sendTeamPacket(int score, TeamMode mode, T prefix, T suffix) setComponentField(packet, suffix, 3); // Suffix setField(packet, String.class, "always", 4); // Visibility for 1.8+ setField(packet, String.class, "always", 5); // Collisions for 1.9+ - } - - if (mode == TeamMode.CREATE) { - setField(packet, Collection.class, Collections.singletonList(COLOR_CODES[score])); // Players in the team + if (mode == TeamMode.CREATE) { + setField(packet, Collection.class, Collections.singletonList(COLOR_CODES[score])); // Players in the team + } } sendPacket(packet);