From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Tue, 29 Nov 2022 23:18:57 +0100 Subject: [PATCH] Spread out sending all player info License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org 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) * Purpur copyright * MIT License Copyright (c) 2019-2022 PurpurMC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index 0f6fba34ca7bb83f40e4e47ba0fcdea754668033..78868d7b5fd0d25845937c6e63f81cb972569c04 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -13,6 +13,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.EnumSet; import java.util.Iterator; @@ -134,10 +136,11 @@ public abstract class PlayerList { public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full"); public static final Component DUPLICATE_LOGIN_DISCONNECT_MESSAGE = Component.translatable("multiplayer.disconnect.duplicate_login"); 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; @@ -328,6 +331,7 @@ public abstract class PlayerList { // entityplayer.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below 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(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer))); // CraftBukkit - replaced with loop below @@ -661,6 +665,7 @@ public abstract class PlayerList { entityplayer.retireScheduler(); // Paper - Folia schedulers 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(); @@ -817,6 +822,7 @@ public abstract class PlayerList { public ServerPlayer respawn(ServerPlayer entityplayer, boolean flag, Entity.RemovalReason entity_removalreason, RespawnReason reason, Location location) { 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.serverLevel().removePlayerImmediately(entityplayer, entity_removalreason); /* CraftBukkit start @@ -903,6 +909,7 @@ public abstract class PlayerList { if (!entityplayer.connection.isDisconnected()) { worldserver.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); } @@ -981,20 +988,55 @@ public abstract class PlayerList { this.sendPlayerPermissionLevel(player, i, recalculatePermissions); // Paper - avoid recalculating permissions if possible } - 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 ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.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 81fdab6a5fbc39cb942f7b07a132a8f1ef18cb37..a3051cafbab789d1ca8c9af2f3486d77a1c5470b 100644 --- a/src/main/java/net/minecraft/world/entity/player/Player.java +++ b/src/main/java/net/minecraft/world/entity/player/Player.java @@ -39,6 +39,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; @@ -208,12 +209,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) { super(EntityType.PLAYER, world); this.lastItemInMainHand = ItemStack.EMPTY; this.cooldowns = this.createItemCooldowns(); this.lastDeathLocation = Optional.empty(); this.setUUID(gameProfile.getId()); + this.sendAllPlayerInfoBucketIndex = Math.floorMod(this.uuid.hashCode(), PlayerList.SEND_PLAYER_INFO_INTERVAL); // Gale - Purpur - spread out sending all player info this.gameProfile = gameProfile; this.inventoryMenu = new InventoryMenu(this.inventory, !world.isClientSide, this); this.containerMenu = this.inventoryMenu;