From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Thu, 3 Aug 2023 20:36:38 +0800 Subject: [PATCH] Leaves: Replay Mod API Co-authored-by: alazeprt Original license: GPLv3 Original project: https://github.com/LeavesMC/Leaves This patch is Powered by ReplayMod(https://github.com/ReplayMod) diff --git a/net/minecraft/commands/CommandSourceStack.java b/net/minecraft/commands/CommandSourceStack.java index 51caf352e77df49fc04bf84f1fab29b6f4f4fc14..3fb3afb4171e6ff57c73cd228c2b41b2cb543ea5 100644 --- a/net/minecraft/commands/CommandSourceStack.java +++ b/net/minecraft/commands/CommandSourceStack.java @@ -617,7 +617,7 @@ public class CommandSourceStack implements ExecutionCommandSource getOnlinePlayerNames() { - return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players + return this.entity instanceof ServerPlayer sourcePlayer && !(sourcePlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players // Leaves - only real player } @Override diff --git a/net/minecraft/commands/arguments/selector/EntitySelector.java b/net/minecraft/commands/arguments/selector/EntitySelector.java index b305ba9bab617bf4e52d0e6ddf160bacc5751a94..6fb844999589ac17c8586c9b3f202345aa80d7fb 100644 --- a/net/minecraft/commands/arguments/selector/EntitySelector.java +++ b/net/minecraft/commands/arguments/selector/EntitySelector.java @@ -128,11 +128,12 @@ public class EntitySelector { return this.findPlayers(source); } else if (this.playerName != null) { ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName); + playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer return playerByName == null ? List.of() : List.of(playerByName); } else if (this.entityUUID != null) { for (ServerLevel serverLevel : source.getServer().getAllLevels()) { Entity entity = serverLevel.getEntity(this.entityUUID); - if (entity != null) { + if (entity != null && !(entity instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // Leaves - skip photographer if (entity.getType().isEnabled(source.enabledFeatures())) { return List.of(entity); } @@ -146,7 +147,7 @@ public class EntitySelector { AABB absoluteAabb = this.getAbsoluteAabb(vec3); if (this.currentEntity) { Predicate predicate = this.getPredicate(vec3, absoluteAabb, null); - return source.getEntity() != null && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of(); + return source.getEntity() != null && !(source.getEntity() instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of(); // Leaves - skip photographer } else { Predicate predicate = this.getPredicate(vec3, absoluteAabb, source.enabledFeatures()); List list = new ObjectArrayList<>(); @@ -157,6 +158,7 @@ public class EntitySelector { this.addEntities(list, serverLevel1, absoluteAabb, predicate); } } + list.removeIf(entity -> entity instanceof org.leavesmc.leaves.replay.ServerPhotographer); // Leaves - skip photographer return this.sortAndLimit(vec3, list); } @@ -192,26 +194,28 @@ public class EntitySelector { this.checkPermissions(source); if (this.playerName != null) { ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName); + playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector } else if (this.entityUUID != null) { ServerPlayer playerByName = source.getServer().getPlayerList().getPlayer(this.entityUUID); + playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector } else { Vec3 vec3 = this.position.apply(source.getPosition()); AABB absoluteAabb = this.getAbsoluteAabb(vec3); Predicate predicate = this.getPredicate(vec3, absoluteAabb, null); if (this.currentEntity) { - return source.getEntity() instanceof ServerPlayer serverPlayer && predicate.test(serverPlayer) && canSee(source, serverPlayer) ? List.of(serverPlayer) : List.of(); // Purpur - Hide hidden players from entity selector + return source.getEntity() instanceof ServerPlayer serverPlayer && !(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(serverPlayer) && canSee(source, serverPlayer) ? List.of(serverPlayer) : List.of(); // Purpur - Hide hidden players from entity selector // Leaves - skip photographer } else { int resultLimit = this.getResultLimit(); List players; if (this.isWorldLimited()) { players = source.getLevel().getPlayers(predicate, resultLimit); - players.removeIf(entityplayer3 -> !canSee(source, entityplayer3)); // Purpur - Hide hidden players from entity selector + players.removeIf(entityplayer3 -> entityplayer3 instanceof org.leavesmc.leaves.replay.ServerPhotographer || !canSee(source, entityplayer3)); // Purpur - Hide hidden players from entity selector // Leaves - skip photographer } else { players = new ObjectArrayList<>(); - for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().getPlayers()) { + for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().realPlayers) { // Leaves - only real players if (predicate.test(serverPlayer1) && canSee(source, serverPlayer1)) { // Purpur - Hide hidden players from entity selector players.add(serverPlayer1); if (players.size() >= resultLimit) { diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 73c63330b67b3d16ceebc644ce177980af25266b..688e6684f6e0890dda8124b85b24a3da8c5b3292 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -1642,7 +1642,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop players = this.playerList.getPlayers(); + List players = this.playerList.realPlayers; // Leaves - only real player int maxPlayers = this.getMaxPlayers(); if (this.hidesOnlinePlayers()) { return new ServerStatus.Players(maxPlayers, players.size(), List.of()); @@ -1828,7 +1828,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> startTrackingPackets = this.getStartTrackingPackets(objective); - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { + for (ServerPlayer serverPlayer : this.server.getPlayerList().realPlayers) { // Leaves - only real players if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board for (Packet packet : startTrackingPackets) { serverPlayer.connection.send(packet); @@ -268,7 +268,7 @@ public class ServerScoreboard extends Scoreboard { public void stopTrackingObjective(Objective objective) { List> stopTrackingPackets = this.getStopTrackingPackets(objective); - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { + for (ServerPlayer serverPlayer : this.server.getPlayerList().realPlayers) { // Leaves - only real players if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board for (Packet packet : stopTrackingPackets) { serverPlayer.connection.send(packet); diff --git a/net/minecraft/server/commands/DefaultGameModeCommands.java b/net/minecraft/server/commands/DefaultGameModeCommands.java index 84baf29ad8af6a8af0cd00ce3921b3c5b8b89451..17f5543b802d88811c3e373e579a6e8b4c9f263a 100644 --- a/net/minecraft/server/commands/DefaultGameModeCommands.java +++ b/net/minecraft/server/commands/DefaultGameModeCommands.java @@ -27,7 +27,7 @@ public class DefaultGameModeCommands { server.setDefaultGameType(gamemode); GameType forcedGameType = server.getForcedGameType(); if (forcedGameType != null) { - for (ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) { + for (ServerPlayer serverPlayer : server.getPlayerList().realPlayers) { // Leaves - only real players // Paper start - Expand PlayerGameModeChangeEvent org.bukkit.event.player.PlayerGameModeChangeEvent event = serverPlayer.setGameMode(gamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, net.kyori.adventure.text.Component.empty()); if (event != null && event.isCancelled()) { diff --git a/net/minecraft/server/commands/ListPlayersCommand.java b/net/minecraft/server/commands/ListPlayersCommand.java index c6ae34f91b3629990294fc5e69237a1e600ef038..712d7b66f1b701acf2f411e811648e5c18112ee9 100644 --- a/net/minecraft/server/commands/ListPlayersCommand.java +++ b/net/minecraft/server/commands/ListPlayersCommand.java @@ -33,7 +33,7 @@ public class ListPlayersCommand { private static int format(CommandSourceStack source, Function nameExtractor) { PlayerList playerList = source.getServer().getPlayerList(); // CraftBukkit start - List playersTemp = playerList.getPlayers(); + List playersTemp = playerList.realPlayers; // Leaves - only real players if (source.getBukkitSender() instanceof org.bukkit.entity.Player) { org.bukkit.entity.Player sender = (org.bukkit.entity.Player) source.getBukkitSender(); playersTemp = playersTemp.stream().filter((ep) -> sender.canSee(ep.getBukkitEntity())).collect(java.util.stream.Collectors.toList()); diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java index f2286b96b8f40b4588f817913c42ae7b4a92340f..dbe6c37642d35ac6ee8b428cf1e45878a5dfa9da 100644 --- a/net/minecraft/server/commands/OpCommand.java +++ b/net/minecraft/server/commands/OpCommand.java @@ -25,7 +25,7 @@ public class OpCommand { (commandContext, suggestionsBuilder) -> { PlayerList playerList = commandContext.getSource().getServer().getPlayerList(); return SharedSuggestionProvider.suggest( - playerList.getPlayers() + playerList.realPlayers // Leaves - only real player .stream() .filter(serverPlayer -> !playerList.isOp(serverPlayer.getGameProfile())) .map(serverPlayer -> serverPlayer.getGameProfile().getName()), diff --git a/net/minecraft/server/commands/ParticleCommand.java b/net/minecraft/server/commands/ParticleCommand.java index 33d96239f4b72a5587dc70f9602847a870d6d6a5..a83ce2cd112fca02cf3545f8c38e5cafae2c7c0e 100644 --- a/net/minecraft/server/commands/ParticleCommand.java +++ b/net/minecraft/server/commands/ParticleCommand.java @@ -36,7 +36,7 @@ public class ParticleCommand { 0.0F, 0, false, - commandContext.getSource().getServer().getPlayerList().getPlayers() + commandContext.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player ) ) .then( @@ -50,7 +50,7 @@ public class ParticleCommand { 0.0F, 0, false, - context1.getSource().getServer().getPlayerList().getPlayers() + context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player ) ) .then( @@ -68,7 +68,7 @@ public class ParticleCommand { FloatArgumentType.getFloat(context1, "speed"), IntegerArgumentType.getInteger(context1, "count"), false, - context1.getSource().getServer().getPlayerList().getPlayers() + context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player ) ) .then( @@ -82,7 +82,7 @@ public class ParticleCommand { FloatArgumentType.getFloat(context1, "speed"), IntegerArgumentType.getInteger(context1, "count"), true, - context1.getSource().getServer().getPlayerList().getPlayers() + context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player ) ) .then( @@ -112,7 +112,7 @@ public class ParticleCommand { FloatArgumentType.getFloat(context1, "speed"), IntegerArgumentType.getInteger(context1, "count"), false, - context1.getSource().getServer().getPlayerList().getPlayers() + context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player ) ) .then( diff --git a/net/minecraft/server/commands/TeamMsgCommand.java b/net/minecraft/server/commands/TeamMsgCommand.java index 134d7b1a9d5a5a47ebf4aabff110dde914cd6fe1..894dd1d048904b8a775416ea6cb3112215c567bc 100644 --- a/net/minecraft/server/commands/TeamMsgCommand.java +++ b/net/minecraft/server/commands/TeamMsgCommand.java @@ -40,7 +40,7 @@ public class TeamMsgCommand { } else { List list = commandSourceStack.getServer() .getPlayerList() - .getPlayers() + .realPlayers // Leaves - only real players .stream() .filter(player -> player == entityOrException || player.getTeam() == team) .toList(); diff --git a/net/minecraft/server/commands/WhitelistCommand.java b/net/minecraft/server/commands/WhitelistCommand.java index 763a51d9a0859296eb4ea52b6879d6c7703db673..25b4fcf79dbf3e227451b3321d8fd38bab55ffe9 100644 --- a/net/minecraft/server/commands/WhitelistCommand.java +++ b/net/minecraft/server/commands/WhitelistCommand.java @@ -43,7 +43,7 @@ public class WhitelistCommand { (commandContext, suggestionsBuilder) -> { PlayerList playerList = commandContext.getSource().getServer().getPlayerList(); return SharedSuggestionProvider.suggest( - playerList.getPlayers() + playerList.realPlayers // Leaves - only real player .stream() .filter(serverPlayer -> !playerList.getWhiteList().isWhiteListed(serverPlayer.getGameProfile())) .map(serverPlayer -> serverPlayer.getGameProfile().getName()), diff --git a/net/minecraft/server/gui/PlayerListComponent.java b/net/minecraft/server/gui/PlayerListComponent.java index f5ba0c9a4c3f9eaa38eeb689de915c25c7165433..24bbc32bc17802edbd9cc14310fe8141c0ad85b0 100644 --- a/net/minecraft/server/gui/PlayerListComponent.java +++ b/net/minecraft/server/gui/PlayerListComponent.java @@ -17,8 +17,8 @@ public class PlayerListComponent extends JList { if (this.tickCount++ % 20 == 0) { Vector list = new Vector<>(); - for (int i = 0; i < this.server.getPlayerList().getPlayers().size(); i++) { - list.add(this.server.getPlayerList().getPlayers().get(i).getGameProfile().getName()); + for (int i = 0; i < this.server.getPlayerList().realPlayers.size(); i++) { // Leaves - only real players + list.add(this.server.getPlayerList().realPlayers.get(i).getGameProfile().getName()); // Leaves - only real players } this.setListData(list); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 95c128f028c3cce7d0b37821a6e75208323fb4e9..66b7205ea7da8b452e6e66676d75d2b29ed6b6d5 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -215,6 +215,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) public boolean hasRidableMoveEvent = false; // Purpur - Ridables + final List realPlayers; // Leaves - skip @Override public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { @@ -694,6 +695,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - rewrite chunk system this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle + this.realPlayers = Lists.newArrayList(); // Leaves - skip } // Paper start @@ -2735,6 +2737,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.add(serverPlayer); + // Leaves start - skip + if (!(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer + ServerLevel.this.realPlayers.add(serverPlayer); + } + // Leaves end - skip if (serverPlayer.isReceivingWaypoints()) { ServerLevel.this.getWaypointManager().addPlayer(serverPlayer); } @@ -2813,6 +2820,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ServerLevel.this.getChunkSource().removeEntity(entity); if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.remove(serverPlayer); + // Leaves start - skip + if (!(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer + ServerLevel.this.realPlayers.remove(serverPlayer); + } + // Leaves end - skip ServerLevel.this.getWaypointManager().removePlayer(serverPlayer); ServerLevel.this.updateSleepingPlayerList(); } diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java index a2d5b58eef4b6def8032d2421f8f3d34e64cb70e..5812f425870c8db5a48d3717bba40ff6ace2f0c6 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -131,6 +131,7 @@ public abstract class PlayerList { private boolean allowCommandsForAllPlayers; private static final boolean ALLOW_LOGOUTIVATOR = false; private int sendAllPlayerInfoIn; + public final List realPlayers = new java.util.concurrent.CopyOnWriteArrayList(); // Leaves - replay api // CraftBukkit start private org.bukkit.craftbukkit.CraftServer cserver; @@ -149,6 +150,112 @@ public abstract class PlayerList { abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor + // Leaves start - replay mod api + public void placeNewPhotographer(Connection connection, org.leavesmc.leaves.replay.ServerPhotographer player, ServerLevel worldserver) { + player.isRealPlayer = true; // Paper + player.loginTime = System.currentTimeMillis(); // Paper + + ServerLevel worldserver1 = worldserver; + + player.setServerLevel(worldserver1); + player.spawnIn(worldserver1); + player.gameMode.setLevel((ServerLevel) player.level()); + + LevelData worlddata = worldserver1.getLevelData(); + + player.loadGameTypes(null); + ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, CommonListenerCookie.createInitial(player.gameProfile, false)); + GameRules gamerules = worldserver1.getGameRules(); + boolean flag = gamerules.getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN); + boolean flag1 = gamerules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO); + boolean flag2 = gamerules.getBoolean(GameRules.RULE_LIMITED_CRAFTING); + + playerconnection.send(new ClientboundLoginPacket(player.getId(), worlddata.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), worldserver1.getWorld().getSendViewDistance(), worldserver1.getWorld().getSimulationDistance(), flag1, !flag, flag2, player.createCommonSpawnInfo(worldserver1), this.server.enforceSecureProfile())); // Paper - replace old player chunk management + player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + playerconnection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); + playerconnection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); + playerconnection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); + RecipeManager craftingmanager = this.server.getRecipeManager(); + playerconnection.send(new ClientboundUpdateRecipesPacket(craftingmanager.getSynchronizedItemProperties(), craftingmanager.getSynchronizedStonecutterRecipes())); + + this.sendPlayerPermissionLevel(player); + player.getStats().markAllDirty(); + player.getRecipeBook().sendInitialRecipeBook(player); + this.updateEntireScoreboard(worldserver1.getScoreboard(), player); + this.server.invalidateStatus(); + + playerconnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); + ServerStatus serverping = this.server.getStatus(); + + if (serverping != null) { + player.sendServerStatus(serverping); + } + + this.players.add(player); + this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot + this.playersByUUID.put(player.getUUID(), player); + + player.supressTrackerForLogin = true; + worldserver1.addNewPlayer(player); + this.server.getCustomBossEvents().onPlayerConnect(player); + org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity(); + + player.containerMenu.transferTo(player.containerMenu, bukkitPlayer); + if (!player.connection.isAcceptingMessages()) { + return; + } + + // org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol + + final List onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1); + for (int i = 0; i < this.players.size(); ++i) { + ServerPlayer entityplayer1 = this.players.get(i); + + if (entityplayer1 == player || !bukkitPlayer.canSee(entityplayer1.getBukkitEntity())) { + continue; + } + + // Leaves start - skip photographer + if (entityplayer1 instanceof org.leavesmc.leaves.replay.ServerPhotographer) { + continue; + } + // Leaves end - skip photographer + + onlinePlayers.add(entityplayer1); + } + if (!onlinePlayers.isEmpty()) { + player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player)); + } + + player.sentListPacket = true; + player.supressTrackerForLogin = false; + ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); + + this.sendLevelInfo(player, worldserver1); + + if (player.level() == worldserver1 && !worldserver1.players().contains(player)) { + worldserver1.addNewPlayer(player); + this.server.getCustomBossEvents().onPlayerConnect(player); + } + + worldserver1 = player.level(); + java.util.Iterator iterator = player.getActiveEffects().iterator(); + while (iterator.hasNext()) { + MobEffectInstance mobeffect = iterator.next(); + playerconnection.send(new ClientboundUpdateMobEffectPacket(player.getId(), mobeffect, false)); + } + + if (player.isDeadOrDying()) { + net.minecraft.core.Holder plains = worldserver1.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) + .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS); + player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( + new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains), + worldserver1.getLightEngine(), null, null, false) + ); + } + } + // Leaves end - replay mod api + public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) { player.isRealPlayer = true; // Paper player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed @@ -318,6 +425,7 @@ public abstract class PlayerList { // player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below this.players.add(player); + this.realPlayers.add(player); // Leaves - replay api this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot this.playersByUUID.put(player.getUUID(), player); this.addToSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info @@ -508,6 +616,7 @@ public abstract class PlayerList { } protected void save(ServerPlayer player) { + if (player instanceof org.leavesmc.leaves.replay.ServerPhotographer) return; // Leaves - skip photographer if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving this.playerIo.save(player); @@ -522,6 +631,43 @@ public abstract class PlayerList { } } + // Leaves start - replay mod api + public void removePhotographer(org.leavesmc.leaves.replay.ServerPhotographer entityplayer) { + ServerLevel worldserver = entityplayer.level(); + + entityplayer.awardStat(Stats.LEAVE_GAME); + + if (entityplayer.containerMenu != entityplayer.inventoryMenu) { + entityplayer.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); + } + + if (server.isSameThread()) entityplayer.doTick(); + + if (this.collideRuleTeamName != null) { + final net.minecraft.world.scores.Scoreboard scoreBoard = this.server.getLevel(Level.OVERWORLD).getScoreboard(); + final PlayerTeam team = scoreBoard.getPlayersTeam(this.collideRuleTeamName); + if (entityplayer.getTeam() == team && team != null) { + scoreBoard.removePlayerFromTeam(entityplayer.getScoreboardName(), team); + } + } + + worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER); + entityplayer.retireScheduler(); + entityplayer.getAdvancements().stopListening(); + this.players.remove(entityplayer); + this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); + this.server.getCustomBossEvents().onPlayerDisconnect(entityplayer); + UUID uuid = entityplayer.getUUID(); + ServerPlayer entityplayer1 = this.playersByUUID.get(uuid); + + if (entityplayer1 == entityplayer) { + this.playersByUUID.remove(uuid); + } + + this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity()); + } + // Leaves stop - replay mod api + public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player) { // CraftBukkit - return string // Paper - return Component // Paper start - Fix kick event leave message not being sent return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName()))); @@ -596,6 +742,7 @@ public abstract class PlayerList { player.retireScheduler(); // Paper - Folia schedulers player.getAdvancements().stopListening(); this.players.remove(player); + this.realPlayers.remove(player); // Leaves - replay api this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot this.removeFromSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info this.server.getCustomBossEvents().onPlayerDisconnect(player); @@ -1009,10 +1156,10 @@ public abstract class PlayerList { } public String[] getPlayerNamesArray() { - String[] strings = new String[this.players.size()]; + String[] strings = new String[this.realPlayers.size()]; // Leaves - fakeplayer support, and skip photographer - for (int i = 0; i < this.players.size(); i++) { - strings[i] = this.players.get(i).getGameProfile().getName(); + for (int i = 0; i < this.realPlayers.size(); i++) { // Leaves - only real players + strings[i] = this.realPlayers.get(i).getGameProfile().getName(); // Leaves - only real players } return strings; @@ -1092,7 +1239,7 @@ public abstract class PlayerList { // Paper start - whitelist verify event / login event public LoginResult canBypassFullServerLogin(final GameProfile profile, final LoginResult currentResult) { - final boolean shouldKick = this.players.size() >= this.maxPlayers && !(/*player.hasPermission("purpur.joinfullserver") || */this.canBypassPlayerLimit(profile)); // Purpur - Allow player join full server by permission TODO: this hasn't worked for a while, so comment it out until we can reliably check perms of the player joining + final boolean shouldKick = this.realPlayers.size() >= this.maxPlayers && !(/*player.hasPermission("purpur.joinfullserver") || */this.canBypassPlayerLimit(profile)); // Purpur - Allow player join full server by permission TODO: this hasn't worked for a while, so comment it out until we can reliably check perms of the player joining // Leaves - only real player final io.papermc.paper.event.player.PlayerServerFullCheckEvent fullCheckEvent = new io.papermc.paper.event.player.PlayerServerFullCheckEvent( com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(profile), io.papermc.paper.adventure.PaperAdventure.asAdventure(currentResult.message),