Skip to content

Commit

Permalink
Changed: Recoded NameTagAPI to be more thread-safe and optimized
Browse files Browse the repository at this point in the history
Signed-off-by: DevDrizzy <fistergaming123@gmail.com>
  • Loading branch information
DevDrizzy committed Sep 16, 2024
1 parent 2ab5f58 commit d143a41
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 330 deletions.
29 changes: 18 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
</properties>

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.io/repository/maven-public/</url>
Expand All @@ -34,20 +38,16 @@
<id>nms-repo</id>
<url>https://repo.codemc.io/repository/nms/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>refine-public</id>
<url>https://maven.refinedev.xyz/repository/public-repo/</url>
<url>https://maven.refinedev.xyz/public-repo</url>
</repository>
</repositories>

<distributionManagement>
<repository>
<id>refine-releases</id>
<url>https://maven.refinedev.xyz/repository/maven-releases/</url>
<url>https://maven.refinedev.xyz/maven-releases</url>
</repository>
</distributionManagement>

Expand Down Expand Up @@ -92,14 +92,13 @@
<dependency>
<groupId>xyz.refinedev.api</groupId>
<artifactId>TablistAPI</artifactId>
<version>2.1</version>
<version>2.2</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.github.retrooper</groupId>
<artifactId>packetevents-spigot</artifactId>
<version>2.4.0</version>
<groupId>com.github.retrooper.packetevents</groupId>
<artifactId>spigot</artifactId>
<version>2.3.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
Expand All @@ -110,6 +109,14 @@
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.retrooper.packetevents</groupId>
<artifactId>api</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.retrooper.packetevents</groupId>
<artifactId>netty-common</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Expand Down
105 changes: 56 additions & 49 deletions src/main/java/xyz/refinedev/api/nametag/NameTagHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import xyz.refinedev.api.nametag.listener.GlitchFixListener;
import xyz.refinedev.api.nametag.listener.NameTagListener;
import xyz.refinedev.api.nametag.update.impl.NameTagRemove;
import xyz.refinedev.api.nametag.util.packet.ScoreboardPacket;
import xyz.refinedev.api.nametag.setup.NameTagTeam;
import xyz.refinedev.api.nametag.update.impl.NameTagRefresh;
import xyz.refinedev.api.nametag.thread.NameTagThread;
Expand All @@ -36,6 +35,7 @@
import xyz.refinedev.api.nametag.util.VersionUtil;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* This Project is property of Refine Development.
Expand All @@ -62,11 +62,12 @@ public class NameTagHandler {
* <center>Viewer -> Target -> {@link NameTagTeam}</center>
* </p>
*/
private final Map<UUID, Map<UUID, NameTagTeam>> teamMap = new Object2ObjectOpenHashMap<>();
private final Map<UUID, Map<UUID, String>> teamMap = new ConcurrentHashMap<>();
/**
* All registered teams are stored here
*/
private final List<NameTagTeam> registeredTeams = new ObjectArrayList<>();
private final Map<String, NameTagTeam> teamCache = new ConcurrentHashMap<>();
private static final String SEPARATOR = "::"; // Separator for prefix-suffix keys
/**
* The plugin registering this NameTag Handler
*/
Expand All @@ -86,7 +87,6 @@ public class NameTagHandler {
public NameTagHandler(JavaPlugin plugin) {
instance = this;
this.plugin = plugin;
this.debugMode = Boolean.getBoolean("BDebug");
}

/**
Expand All @@ -96,10 +96,9 @@ public NameTagHandler(JavaPlugin plugin) {
public void init(PacketEventsAPI<?> packetEventsAPI) {
this.packetEvents = packetEventsAPI;
this.adapter = new DefaultNameTagAdapter();

this.packetEvents.getEventManager().registerListener(new DisguiseListener(this));
Bukkit.getPluginManager().registerEvents(new NameTagListener(this), this.plugin);

Bukkit.getPluginManager().registerEvents(new NameTagListener(this), this.plugin);
try {
Class.forName("xyz.refinedev.api.tablist.util.GlitchFixEvent");
Bukkit.getPluginManager().registerEvents(new GlitchFixListener(this), this.plugin);
Expand All @@ -116,7 +115,7 @@ public void unload() {
this.thread.stopExecuting();

for ( Player player : Bukkit.getOnlinePlayers() ) {
for ( NameTagTeam team : registeredTeams ) {
for ( NameTagTeam team : this.teamCache.values() ) {
team.destroyFor(player);
}
}
Expand All @@ -133,7 +132,6 @@ public void registerAdapter(NameTagAdapter adapter, long ticks) {
}

this.thread = new NameTagThread(this, ticks);
this.thread.start();
}

/**
Expand All @@ -157,12 +155,13 @@ public void createTeams(Player player) {
public void initiatePlayer(Player player) {
if (this.thread == null) return;

for ( NameTagTeam teamInfo : this.registeredTeams ) {
if (VersionUtil.MINOR_VERSION > 8) {
PacketUtil.sendPacket(player, teamInfo.getPECreatePacket());
} else {
ScoreboardPacket.deliverPacket(player, teamInfo.getCreatePacket());
}
if (this.teamCache.isEmpty()) {
this.adapter.fetchNameTag(player, player);
return;
}

for ( NameTagTeam teamInfo : this.teamCache.values() ) {
PacketUtil.sendPacket(player, teamInfo.getCreatePacket());
}
}

Expand All @@ -174,7 +173,7 @@ public void initiatePlayer(Player player) {
public void unloadPlayer(Player player) {
if (this.thread == null) return;

for ( NameTagTeam team : registeredTeams ) {
for ( NameTagTeam team : this.teamCache.values() ) {
team.destroyFor(player);
}
thread.addUpdate(new NameTagRemove(player.getUniqueId()));
Expand All @@ -189,7 +188,7 @@ public void unloadPlayer(Player player) {
public void reloadPlayer(Player toRefresh, Player refreshFor) {
if (this.thread == null) return;

if (!Bukkit.isPrimaryThread()) {
if (Thread.currentThread().getName().contains("Bolt - NameTag")) {
this.reloadPlayerInternal(toRefresh, refreshFor);
return;
}
Expand All @@ -205,7 +204,7 @@ public void reloadPlayer(Player toRefresh, Player refreshFor) {
public void reloadPlayer(Player toRefresh) {
if (this.thread == null) return;

if (!Bukkit.isPrimaryThread()) {
if (Thread.currentThread().getName().contains("Bolt - NameTag")) {
this.applyUpdate(new NameTagRefresh(toRefresh));
return;
}
Expand All @@ -221,7 +220,7 @@ public void reloadPlayer(Player toRefresh) {
public void reloadOthersFor(Player refreshFor) {
if (this.thread == null) return;

if (!Bukkit.isPrimaryThread()) {
if (Thread.currentThread().getName().contains("Bolt - NameTag")) {
for (Player toRefresh : Bukkit.getOnlinePlayers()) {
if (refreshFor == toRefresh) continue;
this.reloadPlayerInternal(toRefresh, refreshFor);
Expand All @@ -239,9 +238,6 @@ public void reloadOthersFor(Player refreshFor) {
* @param nameTagRefresh {@link NameTagRefresh} update
*/
public void applyUpdate(NameTagRefresh nameTagRefresh) {
if (nameTagRefresh.getToRefresh() == null) return;

Player toRefreshPlayer = Bukkit.getPlayer(nameTagRefresh.getToRefresh());
if (nameTagRefresh.isGlobal()) {
Player refreshFor = Bukkit.getPlayer(nameTagRefresh.getRefreshFor());
for (Player player : Bukkit.getOnlinePlayers()) {
Expand All @@ -250,6 +246,7 @@ public void applyUpdate(NameTagRefresh nameTagRefresh) {
return;
}

Player toRefreshPlayer = Bukkit.getPlayer(nameTagRefresh.getToRefresh());
if (toRefreshPlayer == null) {
return;
}
Expand All @@ -271,15 +268,19 @@ public void reloadPlayerInternal(Player toRefresh, Player refreshFor) {
NameTagTeam provided = this.adapter.fetchNameTag(toRefresh, refreshFor);
if (provided == null) return;

//TODO: Sort Priority system, by sending remove packets!!
if (VersionUtil.MINOR_VERSION > 8) {
WrapperPlayServerTeams packet = new WrapperPlayServerTeams(provided.getName(), WrapperPlayServerTeams.TeamMode.ADD_ENTITIES, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null, toRefresh.getName());
PacketUtil.sendPacket(refreshFor, packet);
} else {
Object packet = ScoreboardPacket.additionPacket(provided.getName(), Collections.singletonList(toRefresh.getName()));
ScoreboardPacket.deliverPacket(refreshFor, packet);
Map<UUID, String> teamInfoMap = this.teamMap.computeIfAbsent(refreshFor.getUniqueId(), (t) -> new HashMap<>());

// Don't spam repeat the same NameTag to the client
// Netty wakeup calls are expensive!!
String previousName = teamInfoMap.get(toRefresh.getUniqueId());
NameTagTeam previous = previousName == null ? null : this.getByName(previousName);
if (previous != null && previous.getSuffix().equals(provided.getSuffix()) && previous.getPrefix().equals(provided.getPrefix())) {
return;
}

WrapperPlayServerTeams packet = new WrapperPlayServerTeams(ColorUtil.color(provided.getName()), WrapperPlayServerTeams.TeamMode.ADD_ENTITIES, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null, toRefresh.getName());
PacketUtil.sendPacket(refreshFor, packet);

// In 1.16, the issue arises that hex color does not apply to the name of the player.
// This is due to the ScoreboardTeam color being applied to the name, which is a plain enum with normal colors.
// It does not support Hex Colors, for Teams, I get the nearest ChatColor to the hex color but for displaying in tablist,
Expand Down Expand Up @@ -315,13 +316,10 @@ public void reloadPlayerInternal(Player toRefresh, Player refreshFor) {
}

// Update and store the new team for this target according to the viewer
Map<UUID, NameTagTeam> teamInfoMap = this.teamMap.computeIfAbsent(refreshFor.getUniqueId(), (t) -> new HashMap<>());
teamInfoMap.put(toRefresh.getUniqueId(), provided);
teamInfoMap.put(toRefresh.getUniqueId(), provided.getName());
this.teamMap.put(refreshFor.getUniqueId(), teamInfoMap);
}

private NameTagTeam cachedTeam;

/**
* Get a {@link NameTagTeam} associated with the given prefix and suffix.
* If we don't have one for these prefixes and suffixes, then we make one and send it to everyone.
Expand All @@ -335,32 +333,41 @@ public NameTagTeam getOrCreate(String prefix, String suffix) {
log.info("[NameTagAPI-Debug] Trying to fetch a team with prefix {} and suffix {}", ColorUtil.getRaw(prefix), ColorUtil.getRaw(suffix));
}

if (cachedTeam != null && cachedTeam.getPrefix().equals(prefix) && cachedTeam.getSuffix().equals(suffix)) {
return (cachedTeam);
}
// Create the unique key for caching
String key = prefix + SEPARATOR + suffix;

for ( NameTagTeam teamInfo : registeredTeams) {
if (teamInfo.getPrefix().equals(prefix) && teamInfo.getSuffix().equals(suffix)) {
return (teamInfo);
}
// Attempt to get the team from the cache
NameTagTeam team = teamCache.get(key);
if (team != null) {
return team;
}
TEAM_INDEX++;

// Team doesn't exist; create a new one
TEAM_INDEX++;
NameTagTeam newTeam = new NameTagTeam("boltNT" + TEAM_INDEX, prefix, suffix, collisionEnabled);
cachedTeam = newTeam;

// Cache the newly created team
teamCache.put(key, newTeam);

if (debugMode) {
log.info("[NameTagAPI-Debug] Creating Team with Name: {} with Prefix {} and Suffix {}", newTeam.getName(), ColorUtil.getRaw(newTeam.getPrefix()), ColorUtil.getRaw(newTeam.getSuffix()));
}
this.registeredTeams.add(newTeam);

if (VersionUtil.MINOR_VERSION > 8) {
PacketUtil.broadcast(newTeam.getPECreatePacket());
} else {
for ( Player target : Bukkit.getOnlinePlayers() ) {
ScoreboardPacket.deliverPacket(target, newTeam.getCreatePacket());
}
}
PacketUtil.broadcast(newTeam.getCreatePacket());

return newTeam;
}

/**
* Get a {@link NameTagTeam} by its name.
*
* @param name The name of the team to retrieve.
* @return The {@link NameTagTeam} with the specified name, or null if not found.
*/
public NameTagTeam getByName(String name) {
return teamCache.values().stream()
.filter(team -> team.getName().equals(name))
.findFirst()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.bukkit.event.player.PlayerQuitEvent;

import xyz.refinedev.api.nametag.NameTagHandler;
import xyz.refinedev.api.nametag.update.impl.NameTagInitiate;
import xyz.refinedev.api.nametag.util.VersionUtil;

import java.util.concurrent.CompletableFuture;
Expand All @@ -28,34 +29,20 @@
@RequiredArgsConstructor
public final class NameTagListener implements Listener {

private static boolean firstJoin;

private final NameTagHandler handler;


@EventHandler(priority = EventPriority.HIGH)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();

Runnable wrapper = () -> {
if (!NameTagListener.firstJoin) {
this.handler.createTeams(player);
NameTagListener.firstJoin = true;
} else {
this.handler.initiatePlayer(player);
}
this.handler.reloadPlayer(player);
this.handler.reloadOthersFor(player);
};

if (VersionUtil.MINOR_VERSION < 16) {
CompletableFuture.runAsync(wrapper);
} else {
// PacketEvents or maybe even bukkit is making first join
// miss the packets, they're getting sent before the player has logged in.
// So to counter this, we simply send the packets 2 ticks later (which should be enough).
Bukkit.getScheduler().runTaskLaterAsynchronously(this.handler.getPlugin(), wrapper, 20L);
}
// PacketEvents or maybe even bukkit is making first join
// miss the packets, they're getting sent before the player has logged in.
// So to counter this, we simply send the packets 2 ticks later (which should be enough).
Bukkit.getScheduler().runTaskLater(this.handler.getPlugin(), () -> {
NameTagInitiate initiate = new NameTagInitiate(player);
this.handler.getThread().addUpdate(initiate);
}, 20L);
}

@EventHandler
Expand Down
Loading

0 comments on commit d143a41

Please sign in to comment.