From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MartijnMuijsers Date: Tue, 29 Nov 2022 23:18:57 +0100 Subject: [PATCH] Spread out sending all player info License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) This patch is based on the following patch: "Spread out and optimise player list ticksSpread out and optimise player list ticks" By: James Lyne As part of: Purpur (https://github.com/PurpurMC/Purpur) Licensed under: MIT (https://opensource.org/licenses/MIT) diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index 507017c1ea03cd028be2149b18c8de7f8353e37e..d2e6a92a44416bfaa161c78dd4b9a7b3ad78792a 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -15,6 +15,8 @@ import java.net.SocketAddress; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -132,10 +134,11 @@ public abstract class PlayerList { public static final File WHITELIST_FILE = new File("whitelist.json"); public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full"); private static final Logger LOGGER = LogUtils.getLogger(); - private static final int SEND_PLAYER_INFO_INTERVAL = 600; + public static final int SEND_PLAYER_INFO_INTERVAL = 600; // Gale - Purpur - spread out sending all player info - private -> public private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); private final MinecraftServer server; public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + private final ServerPlayer[][] sendAllPlayerInfoBuckets = new ServerPlayer[SEND_PLAYER_INFO_INTERVAL][]; // Gale - Purpur - spread out sending all player info private final Map playersByUUID = Maps.newHashMap(); private final UserBanList bans; private final IpBanList ipBans; @@ -354,6 +357,7 @@ public abstract class PlayerList { playerconnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); this.players.add(player); + this.addToSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot this.playersByUUID.put(player.getUUID(), player); // this.broadcastAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[]{entityplayer})); // CraftBukkit - replaced with loop below @@ -675,6 +679,7 @@ public abstract class PlayerList { worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER); entityplayer.getAdvancements().stopListening(); this.players.remove(entityplayer); + this.removeFromSendAllPlayerInfoBuckets(entityplayer); // Gale - Purpur - spread out sending all player info this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot this.server.getCustomBossEvents().onPlayerDisconnect(entityplayer); UUID uuid = entityplayer.getUUID(); @@ -842,6 +847,7 @@ public abstract class PlayerList { // Paper end entityplayer.stopRiding(); // CraftBukkit this.players.remove(entityplayer); + this.removeFromSendAllPlayerInfoBuckets(entityplayer); // Gale - Purpur - spread out sending all player info this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot entityplayer.getLevel().removePlayerImmediately(entityplayer, Entity.RemovalReason.DISCARDED); BlockPos blockposition = entityplayer.getRespawnPosition(); @@ -977,6 +983,7 @@ public abstract class PlayerList { if (!entityplayer.connection.isDisconnected()) { worldserver1.addRespawnedPlayer(entityplayer1); this.players.add(entityplayer1); + this.addToSendAllPlayerInfoBuckets(entityplayer1); // Gale - Purpur - spread out sending all player info this.playersByName.put(entityplayer1.getScoreboardName().toLowerCase(java.util.Locale.ROOT), entityplayer1); // Spigot this.playersByUUID.put(entityplayer1.getUUID(), entityplayer1); } @@ -1027,20 +1034,55 @@ public abstract class PlayerList { this.sendPlayerPermissionLevel(player, i); } - public void tick() { - if (++this.sendAllPlayerInfoIn > 600) { - // CraftBukkit start - for (int i = 0; i < this.players.size(); ++i) { - final ServerPlayer target = (ServerPlayer) this.players.get(i); + // Gale start - Purpur - spread out sending all player info + + private void addToSendAllPlayerInfoBuckets(ServerPlayer player) { + ServerPlayer[] sendAllPlayerInfoBucket = this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex]; + if (sendAllPlayerInfoBucket == null) { + this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex] = new ServerPlayer[]{player}; + } else { + this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex] = sendAllPlayerInfoBucket = Arrays.copyOf(sendAllPlayerInfoBucket, sendAllPlayerInfoBucket.length + 1); + sendAllPlayerInfoBucket[sendAllPlayerInfoBucket.length - 1] = player; + } + } + + private void removeFromSendAllPlayerInfoBuckets(ServerPlayer player) { + ServerPlayer[] sendAllPlayerInfoBucket = this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex]; + if (sendAllPlayerInfoBucket != null) { + if (sendAllPlayerInfoBucket.length == 1) { + if (sendAllPlayerInfoBucket[0] == player) { + this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex] = null; + } + return; + } + for (int i = 0; i < sendAllPlayerInfoBucket.length; i++) { + if (sendAllPlayerInfoBucket[i] == player) { + sendAllPlayerInfoBucket[i] = sendAllPlayerInfoBucket[sendAllPlayerInfoBucket.length - 1]; + this.sendAllPlayerInfoBuckets[player.sendAllPlayerInfoBucketIndex] = Arrays.copyOf(sendAllPlayerInfoBucket, sendAllPlayerInfoBucket.length - 1); + } + } + } + } + // Gale end - Purpur - spread out sending all player info + + public void tick() { + // Gale start - Purpur - spread out sending all player info + ServerPlayer[] sendAllPlayerInfoBucket = this.sendAllPlayerInfoBuckets[this.sendAllPlayerInfoIn]; + if (sendAllPlayerInfoBucket != null) { + for (ServerPlayer target : sendAllPlayerInfoBucket) { + // Gale end - Purpur - spread out sending all player info target.connection.send(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.UPDATE_LATENCY, this.players.stream().filter(new Predicate() { @Override public boolean test(ServerPlayer input) { return target.getBukkitEntity().canSee(input.getBukkitEntity()); } }).collect(Collectors.toList()))); + // Gale start - Purpur - spread out sending all player info } - // CraftBukkit end + } + if (++this.sendAllPlayerInfoIn >= SEND_PLAYER_INFO_INTERVAL) { + // Gale end - Purpur - spread out sending all player info this.sendAllPlayerInfoIn = 0; } diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java index 0ea101f155936f7c340d9e1d1077c58b72551027..8818ca46af000926ed9eacb47953f440bdac1342 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java @@ -42,6 +42,7 @@ import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; @@ -198,12 +199,15 @@ public abstract class Player extends LivingEntity { } // CraftBukkit end + public final int sendAllPlayerInfoBucketIndex; // Gale - Purpur - spread out sending all player info + public Player(Level world, BlockPos pos, float yaw, GameProfile gameProfile, @Nullable ProfilePublicKey publicKey) { super(EntityType.PLAYER, world); this.lastItemInMainHand = ItemStack.EMPTY; this.cooldowns = this.createItemCooldowns(); this.lastDeathLocation = Optional.empty(); this.setUUID(UUIDUtil.getOrCreatePlayerUUID(gameProfile)); + this.sendAllPlayerInfoBucketIndex = this.uuid.hashCode() % PlayerList.SEND_PLAYER_INFO_INTERVAL; // Gale - Purpur - spread out sending all player info this.gameProfile = gameProfile; this.profilePublicKey = publicKey; this.inventoryMenu = new InventoryMenu(this.inventory, !world.isClientSide, this);