1212import net .swofty .type .generic .HypixelGenericLoader ;
1313import net .swofty .type .generic .user .HypixelPlayer ;
1414
15+ import java .nio .charset .StandardCharsets ;
1516import java .util .*;
16- import java .util .concurrent .atomic .AtomicReference ;
17+ import java .util .concurrent .atomic .AtomicInteger ;
1718
1819public abstract class TablistManager {
19- private static Map <HypixelPlayer , List <UUID >> tablistEntries = new HashMap <>();
20+ private static final Map <HypixelPlayer , PlayerTabCache > tablistEntries = new HashMap <>();
21+
22+ private static final class PlayerTabCache {
23+ private final List <UUID > tabEntries = new ArrayList <>();
24+ private final Set <String > createdTeams = new HashSet <>();
25+ }
2026
2127 public abstract List <TablistModule > getModules ();
2228
@@ -25,89 +31,89 @@ public void deleteTablistEntries(HypixelPlayer player) {
2531 }
2632
2733 public void nullifyCache (HypixelPlayer player ) {
28- if (tablistEntries .containsKey (player ) && tablistEntries .get (player ) != null ) {
29- player .sendPacket (new PlayerInfoRemovePacket (tablistEntries .get (player )));
30- tablistEntries .put (player , null );
34+ PlayerTabCache cache = tablistEntries .get (player );
35+ if (cache != null && !cache .tabEntries .isEmpty ()) {
36+ player .sendPacket (new PlayerInfoRemovePacket (cache .tabEntries ));
37+ cache .tabEntries .clear ();
3138 }
3239 }
3340
3441 public void runScheduler (Scheduler scheduler ) {
3542 scheduler .scheduleTask (() -> {
3643 HypixelGenericLoader .getLoadedPlayers ().forEach (player -> {
37- if (!tablistEntries .containsKey (player )) {
38- tablistEntries .put (player , new ArrayList <>());
39- } else {
40- if (tablistEntries .get (player ) == null ) return ;
41- player .sendPacket (new PlayerInfoRemovePacket (tablistEntries .get (player )));
44+ PlayerTabCache cache = tablistEntries .computeIfAbsent (player , ignored -> new PlayerTabCache ());
45+
46+ if (!cache .tabEntries .isEmpty ()) {
47+ player .sendPacket (new PlayerInfoRemovePacket (cache .tabEntries ));
4248 }
43- tablistEntries . get ( player ) .clear ();
49+ cache . tabEntries .clear ();
4450
45- AtomicReference < Map . Entry < String , Integer >> charPrefix = new AtomicReference <>( Map . entry ( "§" , 0 ) );
51+ AtomicInteger slot = new AtomicInteger ( 0 );
4652
4753 getModules ().forEach (module -> {
4854 try {
4955 List <TablistModule .TablistEntry > entries = module .getEntries (player );
5056
5157 entries .forEach (entry -> {
58+ int slotIndex = slot .getAndIncrement ();
59+ String teamName = getTeamName (slotIndex );
60+ String fakeProfileName = getFakeProfileName (slotIndex );
61+
5262 List <PlayerInfoUpdatePacket .Property > properties = new ArrayList <>();
5363 properties .add (new PlayerInfoUpdatePacket .Property (
54- "textures" ,
55- entry .registry ().getTexture (),
56- entry .registry ().getSignature ()));
64+ "textures" ,
65+ entry .registry ().getTexture (),
66+ entry .registry ().getSignature ()));
5767
58- if (!charPrefix .get ().getKey ().equals (entry .content ())) {
59- charPrefix .set (Map .entry (entry .content (), charPrefix .get ().getValue () + 1 ));
60- }
61-
62- // 0 is AA, 1 is AB, 2 is AC, etc.
63- // 26 is BA, 27 is BB, 28 is BC, etc.
64- StringBuilder prefix = new StringBuilder ();
65- int value = charPrefix .get ().getValue ();
66- do {
67- // 'A' has an ASCII value of 65, so adding value % 26 gives us the letter we want.
68- // We subtract by 1 before the modulus operation because we want 'A' to represent 0, 'B' to represent 1, and so on.
69- char charToAdd = (char ) ('A' + (value - 1 ) % 26 );
70- prefix .insert (0 , charToAdd ); // Prepend the character
71- value = (value - 1 ) / 26 ; // Move to the next 'digit'
72- } while (value > 0 );
73-
74- UUID uuid = UUID .randomUUID ();
75- tablistEntries .get (player ).add (uuid );
76-
77- String randomName = UUID .randomUUID ().toString ().substring (0 , 8 );
78-
79- TeamsPacket teamPacket = new TeamsPacket (prefix .toString (), new TeamsPacket .CreateTeamAction (
80- Component .text (prefix .toString ()),
68+ if (cache .createdTeams .add (teamName )) {
69+ TeamsPacket teamPacket = new TeamsPacket (teamName , new TeamsPacket .CreateTeamAction (
70+ Component .text (teamName ),
8171 (byte ) 0x01 ,
8272 TeamsPacket .NameTagVisibility .ALWAYS ,
8373 TeamsPacket .CollisionRule .ALWAYS ,
8474 NamedTextColor .RED ,
85- Component .text (prefix . toString () ),
75+ Component .text (teamName ),
8676 Component .empty (),
87- new ArrayList <>(Collections .singletonList (randomName ))
88- ));
77+ new ArrayList <>(Collections .singletonList (fakeProfileName ))
78+ ));
79+
80+ player .sendPacket (teamPacket );
81+ }
82+
83+ UUID uuid = UUID .nameUUIDFromBytes ((player .getUuid ().toString () + "#tab#" + slotIndex )
84+ .getBytes (StandardCharsets .UTF_8 ));
85+ cache .tabEntries .add (uuid );
8986
9087 player .sendPackets (
91- teamPacket ,
92- new PlayerInfoUpdatePacket (EnumSet .of (
93- PlayerInfoUpdatePacket .Action .ADD_PLAYER ,
94- PlayerInfoUpdatePacket .Action .UPDATE_DISPLAY_NAME ,
95- PlayerInfoUpdatePacket .Action .UPDATE_LISTED
96- ), Collections .singletonList (new PlayerInfoUpdatePacket .Entry (
97- uuid ,
98- randomName ,
99- properties ,
100- true ,
101- 0 ,
102- GameMode .CREATIVE ,
103- Component .text (entry .content ()),
104- null ,
105- 1 , true )))
88+ new PlayerInfoUpdatePacket (EnumSet .of (
89+ PlayerInfoUpdatePacket .Action .ADD_PLAYER ,
90+ PlayerInfoUpdatePacket .Action .UPDATE_DISPLAY_NAME ,
91+ PlayerInfoUpdatePacket .Action .UPDATE_LISTED
92+ ), Collections .singletonList (new PlayerInfoUpdatePacket .Entry (
93+ uuid ,
94+ fakeProfileName ,
95+ properties ,
96+ true ,
97+ 0 ,
98+ GameMode .CREATIVE ,
99+ Component .text (entry .content ()),
100+ null ,
101+ 1 , true )))
106102 );
107103 });
108- } catch (Exception e ) {}
104+ } catch (Exception _) {
105+ }
109106 });
110107 });
111108 }, TaskSchedule .seconds (5 ), TaskSchedule .seconds (3 ), ExecutionType .TICK_END );
112109 }
110+
111+ private static String getTeamName (int slotIndex ) {
112+ return String .format (Locale .ROOT , "TAB%03d" , slotIndex );
113+ }
114+
115+ private static String getFakeProfileName (int slotIndex ) {
116+ return "tab" + Integer .toString (slotIndex , 36 );
117+ }
118+
113119}
0 commit comments