From b8ea9890269432d5c30db3b5922d4563d56336ad Mon Sep 17 00:00:00 2001 From: ChomeNS <95471003+ChomeNS@users.noreply.github.com> Date: Mon, 21 Apr 2025 20:15:47 +0700 Subject: [PATCH] fix: make the lists actually thread-safe (maybe, but pretty likely) https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Collections.html#synchronizedList(java.util.List) > It is imperative that the user manually synchronize on the returned list when traversing it via Iterator, Spliterator or Stream > Failure to follow this advice may result in non-deterministic behavior. --- build-number.txt | 2 +- .../commands/CommandBlockCommand.java | 68 ++++++++++--------- .../chomens_bot/commands/TestCommand.java | 21 +++--- .../plugins/BossbarManagerPlugin.java | 5 +- .../chomens_bot/plugins/IPFilterPlugin.java | 4 +- .../chomens_bot/plugins/PlayersPlugin.java | 56 ++++++++------- .../chomens_bot/plugins/PositionPlugin.java | 8 +-- .../chomens_bot/plugins/TeamPlugin.java | 24 ++++--- .../chomens_bot/plugins/WhitelistPlugin.java | 24 ++++--- 9 files changed, 121 insertions(+), 91 deletions(-) diff --git a/build-number.txt b/build-number.txt index c211c986..7b34ebbf 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -2892 \ No newline at end of file +2900 \ No newline at end of file diff --git a/src/main/java/me/chayapak1/chomens_bot/commands/CommandBlockCommand.java b/src/main/java/me/chayapak1/chomens_bot/commands/CommandBlockCommand.java index 6506435b..8f84489c 100644 --- a/src/main/java/me/chayapak1/chomens_bot/commands/CommandBlockCommand.java +++ b/src/main/java/me/chayapak1/chomens_bot/commands/CommandBlockCommand.java @@ -63,48 +63,52 @@ public class CommandBlockCommand extends Command { if (userFound) pattern = Pattern.compile(userMatcher.group(1)); else pattern = Pattern.compile(uuidMatcher.group(1)); - for (final PlayerEntry entry : bot.players.list) { - final String username = entry.profile.getName(); - final String uuid = entry.profile.getIdAsString(); + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) { + final String username = entry.profile.getName(); + final String uuid = entry.profile.getIdAsString(); - if (!pattern.matcher(userFound ? username : uuid).matches()) continue; + if (!pattern.matcher(userFound ? username : uuid).matches()) continue; - String replacedCommand; + String replacedCommand; - if (userFound) - replacedCommand = new StringBuilder(command).replace(userMatcher.start(), userMatcher.end(), username).toString(); - else - replacedCommand = new StringBuilder(command).replace(uuidMatcher.start(), uuidMatcher.end(), uuid).toString(); + if (userFound) + replacedCommand = new StringBuilder(command).replace(userMatcher.start(), userMatcher.end(), username).toString(); + else + replacedCommand = new StringBuilder(command).replace(uuidMatcher.start(), uuidMatcher.end(), uuid).toString(); - replacedCommand = replacedCommand - .replace("{username}", username) - .replace("{uuid}", uuid); + replacedCommand = replacedCommand + .replace("{username}", username) + .replace("{uuid}", uuid); - if ( - !replacedCommand.contains("{username}") && - !replacedCommand.contains("{uuid}") && - !USER_PATTERN.matcher(username).find() && - !UUID_PATTERN.matcher(username).find() - ) { - runCommand(bot, context, replacedCommand, entry); + if ( + !replacedCommand.contains("{username}") && + !replacedCommand.contains("{uuid}") && + !USER_PATTERN.matcher(username).find() && + !UUID_PATTERN.matcher(username).find() + ) { + runCommand(bot, context, replacedCommand, entry); + } } } } else if (command.contains("{username}") || command.contains("{uuid}")) { - for (final PlayerEntry entry : bot.players.list) { - final String username = entry.profile.getName(); - final String uuid = entry.profile.getIdAsString(); + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) { + final String username = entry.profile.getName(); + final String uuid = entry.profile.getIdAsString(); - final String replacedCommand = command - .replace("{username}", username) - .replace("{uuid}", uuid); + final String replacedCommand = command + .replace("{username}", username) + .replace("{uuid}", uuid); - if ( - !replacedCommand.contains("{username}") && - !replacedCommand.contains("{uuid}") && - !USER_PATTERN.matcher(username).find() && - !UUID_PATTERN.matcher(username).find() - ) { - runCommand(bot, context, replacedCommand, entry); + if ( + !replacedCommand.contains("{username}") && + !replacedCommand.contains("{uuid}") && + !USER_PATTERN.matcher(username).find() && + !UUID_PATTERN.matcher(username).find() + ) { + runCommand(bot, context, replacedCommand, entry); + } } } } else { diff --git a/src/main/java/me/chayapak1/chomens_bot/commands/TestCommand.java b/src/main/java/me/chayapak1/chomens_bot/commands/TestCommand.java index 2891ecb1..9d5da94b 100644 --- a/src/main/java/me/chayapak1/chomens_bot/commands/TestCommand.java +++ b/src/main/java/me/chayapak1/chomens_bot/commands/TestCommand.java @@ -5,7 +5,6 @@ import me.chayapak1.chomens_bot.command.CommandContext; import me.chayapak1.chomens_bot.command.CommandException; import me.chayapak1.chomens_bot.command.TrustLevel; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; public class TestCommand extends Command { public TestCommand () { @@ -20,13 +19,17 @@ public class TestCommand extends Command { @Override public Component execute (final CommandContext context) throws CommandException { - return Component.translatable( - "Hello, World! Username: %s, Sender UUID: %s, Prefix: %s, Flags: %s, Args: %s", - Component.text(context.sender.profile.getName()), - Component.text(context.sender.profile.getIdAsString()), - Component.text(context.prefix), - Component.text(String.join(" ", context.getFlags())), - Component.text(context.getString(true, false)) - ).color(NamedTextColor.GREEN); + for (final Thread thread : Thread.getAllStackTraces().keySet()) { + System.out.println(thread); + } + return null; +// return Component.translatable( +// "Hello, World! Username: %s, Sender UUID: %s, Prefix: %s, Flags: %s, Args: %s", +// Component.text(context.sender.profile.getName()), +// Component.text(context.sender.profile.getIdAsString()), +// Component.text(context.prefix), +// Component.text(String.join(" ", context.getFlags())), +// Component.text(context.getString(true, false)) +// ).color(NamedTextColor.GREEN); } } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java index a965cc4d..f2c5cb4a 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java @@ -15,13 +15,14 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.Clientbound import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; // yes this has been rewritten to be not spammy public class BossbarManagerPlugin implements Listener { private final Bot bot; - public final Map serverBossBars = new HashMap<>(); - private final Map bossBars = new HashMap<>(); + public final Map serverBossBars = new ConcurrentHashMap<>(); + private final Map bossBars = new ConcurrentHashMap<>(); public boolean enabled = true; public boolean actionBar = false; diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/IPFilterPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/IPFilterPlugin.java index b772892f..e9932a88 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/IPFilterPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/IPFilterPlugin.java @@ -109,7 +109,9 @@ public class IPFilterPlugin implements Listener { if (localList.isEmpty()) return; bot.executorService.submit(() -> { - for (final PlayerEntry entry : bot.players.list) check(entry); + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) check(entry); + } }); } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/PlayersPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/PlayersPlugin.java index f5b5bf59..924127a0 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/PlayersPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/PlayersPlugin.java @@ -67,7 +67,11 @@ public class PlayersPlugin implements Listener { for (final UUID uuid : uuids) removePlayer(uuid); } - private void queryPlayersIP () { for (final PlayerEntry target : list) queryPlayersIP(target); } + private void queryPlayersIP () { + synchronized (list) { + for (final PlayerEntry target : list) queryPlayersIP(target); + } + } private void queryPlayersIP (final PlayerEntry target) { if (target.ip != null) return; @@ -132,40 +136,46 @@ public class PlayersPlugin implements Listener { } public final PlayerEntry getEntry (final UUID uuid) { - for (final PlayerEntry candidate : list) { - if (candidate != null && candidate.profile.getId().equals(uuid)) { - return candidate; + synchronized (list) { + for (final PlayerEntry candidate : list) { + if (candidate != null && candidate.profile.getId().equals(uuid)) { + return candidate; + } } - } - return null; + return null; + } } public final PlayerEntry getEntry (final String username) { - for (final PlayerEntry candidate : list) { - if ( - candidate != null && - ( - candidate.profile.getIdAsString().equals(username) || // checks for a string UUID - candidate.profile.getName().equals(username) || - candidate.usernames.contains(username) - ) - ) { - return candidate; + synchronized (list) { + for (final PlayerEntry candidate : list) { + if ( + candidate != null && + ( + candidate.profile.getIdAsString().equals(username) || // checks for a string UUID + candidate.profile.getName().equals(username) || + candidate.usernames.contains(username) + ) + ) { + return candidate; + } } - } - return null; + return null; + } } public final PlayerEntry getEntry (final Component displayName) { - for (final PlayerEntry candidate : list) { - if (candidate != null && candidate.displayName != null && candidate.displayName.equals(displayName)) { - return candidate; + synchronized (list) { + for (final PlayerEntry candidate : list) { + if (candidate != null && candidate.displayName != null && candidate.displayName.equals(displayName)) { + return candidate; + } } - } - return null; + return null; + } } public PlayerEntry getBotEntry () { return getEntry(bot.username); } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java index a4bbd428..622f141a 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java @@ -15,8 +15,8 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spaw import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; // some part of the code used to be in a test plugin but i thought it would be useful in the future so i moved it here public class PositionPlugin implements Listener { @@ -28,9 +28,9 @@ public class PositionPlugin implements Listener { private long tpCommandCooldownTime = 0; - private final Map entityIdMap = new HashMap<>(); - private final Map positionMap = new HashMap<>(); - private final Map rotationMap = new HashMap<>(); + private final Map entityIdMap = new ConcurrentHashMap<>(); + private final Map positionMap = new ConcurrentHashMap<>(); + private final Map rotationMap = new ConcurrentHashMap<>(); public PositionPlugin (final Bot bot) { this.bot = bot; diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/TeamPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/TeamPlugin.java index b9bee36b..c7b863fe 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/TeamPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/TeamPlugin.java @@ -25,20 +25,24 @@ public class TeamPlugin implements Listener { teams.clear(); } - public synchronized Team findTeamByName (final String name) { - for (final Team team : new ArrayList<>(teams)) { - if (team.teamName.equals(name)) return team; - } + public Team findTeamByName (final String name) { + synchronized (teams) { + for (final Team team : new ArrayList<>(teams)) { + if (team.teamName.equals(name)) return team; + } - return null; + return null; + } } - public synchronized Team findTeamByMember (final String member) { - for (final Team team : new ArrayList<>(teams)) { - if (team.players.contains(member)) return team; - } + public Team findTeamByMember (final String member) { + synchronized (teams) { + for (final Team team : new ArrayList<>(teams)) { + if (team.players.contains(member)) return team; + } - return null; + return null; + } } @Override diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/WhitelistPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/WhitelistPlugin.java index 40db8fa6..ad991757 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/WhitelistPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/WhitelistPlugin.java @@ -23,20 +23,24 @@ public class WhitelistPlugin implements Listener { public void enable () { enabled = true; - for (final PlayerEntry entry : bot.players.list) { - if (list.contains(entry.profile.getName())) continue; + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) { + if (list.contains(entry.profile.getName())) continue; - list.add(entry.profile.getName()); + list.add(entry.profile.getName()); - bot.filterManager.remove(entry.profile.getName()); + bot.filterManager.remove(entry.profile.getName()); + } } } public void disable () { enabled = false; - for (final PlayerEntry entry : bot.players.list) { - bot.filterManager.remove(entry.profile.getName()); + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) { + bot.filterManager.remove(entry.profile.getName()); + } } } @@ -57,10 +61,12 @@ public class WhitelistPlugin implements Listener { public void clear () { list.removeIf(eachPlayer -> !eachPlayer.equals(bot.profile.getName())); - for (final PlayerEntry entry : bot.players.list) { - if (entry.profile.equals(bot.profile)) continue; + synchronized (bot.players.list) { + for (final PlayerEntry entry : bot.players.list) { + if (entry.profile.equals(bot.profile)) continue; - bot.filterManager.add(entry, ""); + bot.filterManager.add(entry, ""); + } } }