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.
This commit is contained in:
ChomeNS
2025-04-21 20:15:47 +07:00
parent 1bb4d1713e
commit b8ea989026
9 changed files with 121 additions and 91 deletions

View File

@@ -1 +1 @@
2892
2900

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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<UUID, BossBar> serverBossBars = new HashMap<>();
private final Map<UUID, BotBossBar> bossBars = new HashMap<>();
public final Map<UUID, BossBar> serverBossBars = new ConcurrentHashMap<>();
private final Map<UUID, BotBossBar> bossBars = new ConcurrentHashMap<>();
public boolean enabled = true;
public boolean actionBar = false;

View File

@@ -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);
}
});
}

View File

@@ -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); }

View File

@@ -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<Integer, PlayerEntry> entityIdMap = new HashMap<>();
private final Map<Integer, Vector3d> positionMap = new HashMap<>();
private final Map<Integer, Rotation> rotationMap = new HashMap<>();
private final Map<Integer, PlayerEntry> entityIdMap = new ConcurrentHashMap<>();
private final Map<Integer, Vector3d> positionMap = new ConcurrentHashMap<>();
private final Map<Integer, Rotation> rotationMap = new ConcurrentHashMap<>();
public PositionPlugin (final Bot bot) {
this.bot = bot;

View File

@@ -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

View File

@@ -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, "");
}
}
}