From 4ae453cf0075bff04585aaa4b9525853214b096e Mon Sep 17 00:00:00 2001 From: opt Date: Wed, 12 Aug 2026 21:47:50 +0000 Subject: [PATCH 1/2] Use _ variable names & fix @NonNull annotation --- .../extras/modules/player/PlayerMessaging.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java b/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java index 461add50..eb072566 100644 --- a/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java +++ b/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java @@ -11,7 +11,7 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.messaging.Messenger; import org.bukkit.plugin.messaging.PluginMessageListener; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import pw.kaboom.extras.Main; import java.io.*; @@ -52,7 +52,7 @@ private static String readString(final DataInput dataInput) throws IOException { } private void handleRegister(final Player player, final DataInput input) throws IOException { - this.listening.compute(readString(input), (k, v) -> { + this.listening.compute(readString(input), (_, v) -> { v = v == null ? Collections.synchronizedSet( Collections.newSetFromMap( @@ -68,7 +68,7 @@ private void handleRegister(final Player player, final DataInput input) throws I private void handleUnregister(final Player player, final DataInput input) throws IOException { - this.listening.computeIfPresent(readString(input), (k, v) -> { + this.listening.computeIfPresent(readString(input), (_, v) -> { v.remove(player); return v; }); @@ -142,9 +142,9 @@ private void handleMessage(final Player player, } @Override - public void onPluginMessageReceived(final @NotNull String channelName, - final @NotNull Player player, - final byte[] bytes) { + public void onPluginMessageReceived(final @NonNull String channelName, + final @NonNull Player player, + final byte @NonNull [] bytes) { try { switch (channelName) { case REGISTER -> handleRegister( From e2f0d2baa0bb54ac96a620fba7dda6427b7f02be Mon Sep 17 00:00:00 2001 From: opt Date: Wed, 12 Aug 2026 23:18:02 +0000 Subject: [PATCH 2/2] Remove unnecessary synchronization, implement optional payload length limit --- .../modules/player/PlayerMessaging.java | 162 +++++++++--------- 1 file changed, 80 insertions(+), 82 deletions(-) diff --git a/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java b/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java index eb072566..5c577d08 100644 --- a/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java +++ b/src/main/java/pw/kaboom/extras/modules/player/PlayerMessaging.java @@ -33,7 +33,7 @@ public PlayerMessaging(final Main plugin) { this.plugin = plugin; } - private final Map> listening = Collections.synchronizedMap(new HashMap<>()); + private final Map> listening = new HashMap<>(); private static String readString(final DataInput dataInput) throws IOException { final byte[] buf = new byte[255]; @@ -51,26 +51,24 @@ private static String readString(final DataInput dataInput) throws IOException { return new String(buf, 0, idx, StandardCharsets.US_ASCII); } - private void handleRegister(final Player player, final DataInput input) throws IOException { - this.listening.compute(readString(input), (_, v) -> { - v = v == null ? - Collections.synchronizedSet( - Collections.newSetFromMap( - new WeakHashMap<>() - ) - ) - : - v; - v.add(player); - return v; - }); + private void handleRegister(final Player player, + final DataInputStream input) throws IOException { + final var name = readString(input); + // 2 bytes = short + Short limit = input.available() >= 2 ? (short) Math.max(0, input.readShort()) : null; + if (Objects.equals(limit, Short.MAX_VALUE)) limit = null; + + this.listening.computeIfAbsent( + name, + _ -> new IdentityHashMap<>() + ).put(player, limit); } private void handleUnregister(final Player player, final DataInput input) throws IOException { this.listening.computeIfPresent(readString(input), (_, v) -> { v.remove(player); - return v; + return v.isEmpty() ? null : v; }); } @@ -78,66 +76,72 @@ private void handleMessage(final Player player, final DataInputStream input) throws IOException { final String channelName = readString(input); - final Set players = this.listening.get(channelName); - if (players == null) return; + final Map playerToLimit = this.listening.get(channelName); + if (playerToLimit == null) return; + final Set> players = playerToLimit.entrySet(); - synchronized (players) { - // we initialize as null so that we do not read the incoming - // data and serialize the payload if the only recipient + // we initialize as null so that we do not read the incoming + // data and serialize the payload if the only recipient // would be the sender, who we do not send to - byte[] msg = null; - - for (final Player playerInSet : players) { - if (playerInSet == player) continue; - if (msg == null) { - final int remaining = input.available(); - - // remaining count + channel name + uuid - // note: calls to channelName.length() are safe because we only read ASCII - final int realLength = remaining + channelName.length() + 16; - if (realLength > Messenger.MAX_MESSAGE_SIZE) { - player.sendMessage(ERROR); - return; - } - - msg = new byte[realLength]; - int offset = 0; - - final byte[] nameBytes = channelName.getBytes(StandardCharsets.US_ASCII); - nameBytes[nameBytes.length - 1] |= END_CHAR_MASK; - System.arraycopy( - nameBytes, - 0, - msg, - offset, - channelName.length() - ); - offset += channelName.length(); - - final UUID uuid = player.getUniqueId(); - System.arraycopy( - Longs.toByteArray(uuid.getMostSignificantBits()), - 0, - msg, - offset, - 8) - ; - offset += 8; - - System.arraycopy( - Longs.toByteArray(uuid.getLeastSignificantBits()), - 0, - msg, - offset, - 8 - ); - offset += 8; - - input.readFully(msg, offset, remaining); + byte[] msg = null; + int len = -1; + + for (final Map.Entry entry: players) { + final Player playerInSet = entry.getKey(); + if (playerInSet == player) continue; + if (msg == null) { + final int remaining = input.available(); + len = remaining; + + // remaining count + channel name + uuid + // note: calls to channelName.length() are safe because we only read ASCII + final int realLength = remaining + channelName.length() + 16; + if (realLength > Messenger.MAX_MESSAGE_SIZE) { + player.sendMessage(ERROR); + return; } - playerInSet.sendPluginMessage(this.plugin, MESSAGE, msg); + msg = new byte[realLength]; + int offset = 0; + + final byte[] nameBytes = channelName.getBytes(StandardCharsets.US_ASCII); + nameBytes[nameBytes.length - 1] |= END_CHAR_MASK; + + System.arraycopy( + nameBytes, + 0, + msg, + offset, + channelName.length() + ); + offset += channelName.length(); + + final UUID uuid = player.getUniqueId(); + System.arraycopy( + Longs.toByteArray(uuid.getMostSignificantBits()), + 0, + msg, + offset, + 8 + ); + offset += 8; + + System.arraycopy( + Longs.toByteArray(uuid.getLeastSignificantBits()), + 0, + msg, + offset, + 8 + ); + offset += 8; + + input.readFully(msg, offset, remaining); } + + final Short limit = entry.getValue(); + // does the message exceed this listener's limit? + if (limit != null && len > limit) continue; + playerInSet.sendPluginMessage(this.plugin, MESSAGE, msg); } } @@ -169,19 +173,13 @@ public void onPluginMessageReceived(final @NonNull String channelName, public void onPlayerQuit(PlayerQuitEvent event) { final Player removedPlayer = event.getPlayer(); - synchronized (this.listening) { - final Iterator>> listeningIterator = - this.listening.entrySet().iterator(); - - while (listeningIterator.hasNext()) { - final Map.Entry> entry = listeningIterator.next(); - final Set players = entry.getValue(); - synchronized (players) { - players.remove(removedPlayer); + final var iterator = this.listening.entrySet().iterator(); - if (players.isEmpty()) listeningIterator.remove(); - } - } + while (iterator.hasNext()) { + final var entry = iterator.next(); + final var listeners = entry.getValue(); + if (listeners.remove(removedPlayer) == null || !listeners.isEmpty()) continue; + iterator.remove(); } } }