615 lines
41 KiB
Diff
615 lines
41 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
|
Date: Sun, 16 Nov 2025 15:48:12 +0800
|
|
Subject: [PATCH] Leaves: Replay Mod API
|
|
|
|
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
|
As a part of : Leaves (https://github.com/LeavesMC/Leaves) & ReplayMod (https://github.com/ReplayMod)
|
|
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
|
|
|
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 587ec673a0821e91de67abaf3a559c78f4aa8016..4fc1aad19fd9321c44aae99c118f40ec67d78020 100644
|
|
--- a/net/minecraft/commands/CommandSourceStack.java
|
|
+++ b/net/minecraft/commands/CommandSourceStack.java
|
|
@@ -562,7 +562,7 @@ public class CommandSourceStack implements SharedSuggestionProvider, ExecutionCo
|
|
|
|
@Override
|
|
public Collection<String> 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().name()).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().name()).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 6c05b9276bdc267a36376f6c64592a50e4b2b36e..21d2c97b3d1e1d29c9de02b6acd58985c7c74231 100644
|
|
--- a/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
+++ b/net/minecraft/commands/arguments/selector/EntitySelector.java
|
|
@@ -144,13 +144,14 @@ public class EntitySelector {
|
|
|
|
if (this.playerName != null) {
|
|
ServerPlayer result = sender.getServer().getPlayerList().getPlayerByName(this.playerName);
|
|
+ result = result instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : result; // Leaves - skip photographer
|
|
return result == null ? List.of() : List.of(result);
|
|
}
|
|
|
|
if (this.entityUUID != null) {
|
|
for (ServerLevel level : sender.getServer().getAllLevels()) {
|
|
Entity entity = level.getEntity(this.entityUUID);
|
|
- if (entity != null) {
|
|
+ if (entity != null && !(entity instanceof org.leavesmc.leaves.replay.ServerPhotographer)) {
|
|
if (entity.getType().isEnabled(sender.enabledFeatures())) {
|
|
return List.of(entity);
|
|
}
|
|
@@ -164,7 +165,7 @@ public class EntitySelector {
|
|
AABB absoluteAabb = this.getAbsoluteAabb(pos);
|
|
if (this.currentEntity) {
|
|
Predicate<Entity> predicate = this.getPredicate(pos, absoluteAabb, null);
|
|
- return sender.getEntity() != null && predicate.test(sender.getEntity()) ? List.of(sender.getEntity()) : List.of();
|
|
+ return sender.getEntity() != null && !(sender.getEntity() instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(sender.getEntity()) ? List.of(sender.getEntity()) : List.of(); // Leaves - skip photographer
|
|
}
|
|
|
|
Predicate<Entity> predicate = this.getPredicate(pos, absoluteAabb, sender.enabledFeatures());
|
|
@@ -176,6 +177,7 @@ public class EntitySelector {
|
|
this.addEntities(result, level, absoluteAabb, predicate);
|
|
}
|
|
}
|
|
+ result.removeIf(entity -> entity instanceof org.leavesmc.leaves.replay.ServerPhotographer); // Leaves - skip photographer
|
|
|
|
return this.sortAndLimit(pos, result);
|
|
}
|
|
@@ -210,11 +212,13 @@ public class EntitySelector {
|
|
this.checkPermissions(sender);
|
|
if (this.playerName != null) {
|
|
ServerPlayer result = sender.getServer().getPlayerList().getPlayerByName(this.playerName);
|
|
+ result = result instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : result; // Leaves - skip photographer
|
|
return result == null ? List.of() : List.of(result);
|
|
}
|
|
|
|
if (this.entityUUID != null) {
|
|
ServerPlayer result = sender.getServer().getPlayerList().getPlayer(this.entityUUID);
|
|
+ result = result instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : result; // Leaves - skip photographer
|
|
return result == null ? List.of() : List.of(result);
|
|
}
|
|
|
|
@@ -228,11 +232,11 @@ public class EntitySelector {
|
|
int limit = this.getResultLimit();
|
|
List<ServerPlayer> result;
|
|
if (this.isWorldLimited()) {
|
|
- result = sender.getLevel().getPlayers(predicate, limit);
|
|
+ result = sender.getLevel().getPlayers((player -> !(player instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(player)), limit); // Leaves - skip photographer
|
|
} else {
|
|
result = new ObjectArrayList<>();
|
|
|
|
- for (ServerPlayer player : sender.getServer().getPlayerList().getPlayers()) {
|
|
+ for (ServerPlayer player : sender.getServer().getPlayerList().realPlayers) { // Leaves - only real players
|
|
if (predicate.test(player)) {
|
|
result.add(player);
|
|
if (result.size() >= limit) {
|
|
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
|
index f6ace71d151d428149e8c9615c3b91e0f213ae7c..d7a7f7e7daf45bc0912aae008f3fed35201925fe 100644
|
|
--- a/net/minecraft/server/MinecraftServer.java
|
|
+++ b/net/minecraft/server/MinecraftServer.java
|
|
@@ -1894,7 +1894,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
|
|
private ServerStatus.Players buildPlayerStatus() {
|
|
- List<ServerPlayer> players = new java.util.ArrayList<>(this.playerList.getPlayers()); // Folia - region threading
|
|
+ List<ServerPlayer> players = new java.util.ArrayList<>(this.playerList.realPlayers); // Folia - region threading // Leaves - only real player
|
|
int maxPlayers = this.getMaxPlayers();
|
|
if (this.hidesOnlinePlayers()) {
|
|
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
|
|
@@ -2115,7 +2115,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
@Override
|
|
public int getPlayerCount() {
|
|
- return this.playerList.getPlayerCount();
|
|
+ return this.playerList.realPlayers.size(); // Leaves - only real player
|
|
}
|
|
|
|
public String[] getPlayerNames() {
|
|
@@ -2332,7 +2332,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
|
|
int count = 0;
|
|
|
|
- for (ServerPlayer player : this.getPlayerList().getPlayers()) {
|
|
+ for (ServerPlayer player : this.getPlayerList().realPlayers) { // Leaves - only real player
|
|
player.getBukkitEntity().taskScheduler.schedule((ServerPlayer updatedPlayer) -> { // Folia - region threading
|
|
// Paper start - Expand PlayerGameModeChangeEvent
|
|
org.bukkit.event.player.PlayerGameModeChangeEvent event = updatedPlayer.setGameMode(gameType, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null); // Folia - region threading
|
|
@@ -2523,7 +2523,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
if (Thread.currentThread() != this.serverThread) return; // Paper
|
|
// Paper start - we don't need to save everything, just advancements
|
|
// this.getPlayerList().saveAll();
|
|
- for (final ServerPlayer player : this.getPlayerList().getPlayers()) {
|
|
+ for (final ServerPlayer player : this.getPlayerList().realPlayers) { // Leaves - only real players
|
|
player.getAdvancements().save();
|
|
}
|
|
// Paper end - we don't need to save everything, just advancements
|
|
@@ -2659,7 +2659,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
PlayerList playerList = this.getPlayerList();
|
|
UserWhiteList whiteList = playerList.getWhiteList();
|
|
|
|
- for (ServerPlayer player : Lists.newArrayList(playerList.getPlayers())) {
|
|
+ for (ServerPlayer player : Lists.newArrayList(playerList.realPlayers)) { // Leaves - only real player
|
|
if (!whiteList.isWhiteListed(player.nameAndId()) && !this.getPlayerList().isOp(player.nameAndId())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
|
|
player.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause
|
|
}
|
|
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
|
|
index cdafcb724106807c6bc77c9930090839bd2cc007..d916021ed42046d1aa37dc687ef80f6c5eb5c879 100644
|
|
--- a/net/minecraft/server/PlayerAdvancements.java
|
|
+++ b/net/minecraft/server/PlayerAdvancements.java
|
|
@@ -167,7 +167,7 @@ public class PlayerAdvancements {
|
|
|
|
public boolean award(final AdvancementHolder holder, final String criterion) {
|
|
// Leaves start - bot can't get advancement
|
|
- if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
|
|
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot || player instanceof org.leavesmc.leaves.replay.ServerPhotographer) { // Leaves - and photographer
|
|
return false;
|
|
}
|
|
// Leaves end - bot can't get advancement
|
|
diff --git a/net/minecraft/server/ServerScoreboard.java b/net/minecraft/server/ServerScoreboard.java
|
|
index 7ffd683c42557052d7bfa963d87e68f4e8f62d6c..9d0f55250ec6613f7ad8252dd028468967953521 100644
|
|
--- a/net/minecraft/server/ServerScoreboard.java
|
|
+++ b/net/minecraft/server/ServerScoreboard.java
|
|
@@ -245,7 +245,7 @@ public class ServerScoreboard extends Scoreboard {
|
|
public void startTrackingObjective(final Objective objective) {
|
|
List<Packet<?>> packets = this.getStartTrackingPackets(objective);
|
|
|
|
- for (ServerPlayer player : this.server.getPlayerList().getPlayers()) {
|
|
+ for (ServerPlayer player : this.server.getPlayerList().realPlayers) { // Leaves - only real players
|
|
if (player.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
|
|
for (Packet<?> packet : packets) {
|
|
player.connection.send(packet);
|
|
@@ -271,7 +271,7 @@ public class ServerScoreboard extends Scoreboard {
|
|
public void stopTrackingObjective(final Objective objective) {
|
|
List<Packet<?>> packets = this.getStopTrackingPackets(objective);
|
|
|
|
- for (ServerPlayer player : this.server.getPlayerList().getPlayers()) {
|
|
+ for (ServerPlayer player : this.server.getPlayerList().realPlayers) { // Leaves - only real players
|
|
if (player.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
|
|
for (Packet<?> packet : packets) {
|
|
player.connection.send(packet);
|
|
diff --git a/net/minecraft/server/commands/ListPlayersCommand.java b/net/minecraft/server/commands/ListPlayersCommand.java
|
|
index b850c908a594f839ce124c6bd01762d31cc3aa1e..b2240fd3f5ace03168fd679932675a72c09e81d7 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(final CommandSourceStack source, final Function<ServerPlayer, Component> formatter) {
|
|
PlayerList playerList = source.getServer().getPlayerList();
|
|
// CraftBukkit start
|
|
- List<ServerPlayer> playersTemp = playerList.getPlayers();
|
|
+ List<ServerPlayer> playersTemp = playerList.realPlayers;
|
|
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 20cbc0e65fea3fc2d0033259c2f35e158ea81926..6bcd09b8d39333bd5f2c0d7c0a9a91bb52f9314d 100644
|
|
--- a/net/minecraft/server/commands/OpCommand.java
|
|
+++ b/net/minecraft/server/commands/OpCommand.java
|
|
@@ -25,7 +25,8 @@ public class OpCommand {
|
|
(c, p) -> {
|
|
PlayerList list = c.getSource().getServer().getPlayerList();
|
|
return SharedSuggestionProvider.suggest(
|
|
- list.getPlayers().stream().filter(player -> !list.isOp(player.nameAndId())).map(pl -> pl.getGameProfile().name()), p
|
|
+ list.realPlayers // Leaves - only real player
|
|
+ .stream().filter(player -> !list.isOp(player.nameAndId())).map(pl -> pl.getGameProfile().name()), p
|
|
);
|
|
}
|
|
)
|
|
diff --git a/net/minecraft/server/commands/ParticleCommand.java b/net/minecraft/server/commands/ParticleCommand.java
|
|
index ce8a8e665ca68f14158739dd7a07f932a472203c..390464ac4dcf698f27646efbdb034a873a8725af 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,
|
|
- c.getSource().getServer().getPlayerList().getPlayers()
|
|
+ c.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
|
|
)
|
|
)
|
|
.then(
|
|
@@ -50,7 +50,7 @@ public class ParticleCommand {
|
|
0.0F,
|
|
0,
|
|
false,
|
|
- c.getSource().getServer().getPlayerList().getPlayers()
|
|
+ c.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
|
|
)
|
|
)
|
|
.then(
|
|
@@ -68,7 +68,7 @@ public class ParticleCommand {
|
|
FloatArgumentType.getFloat(c, "speed"),
|
|
IntegerArgumentType.getInteger(c, "count"),
|
|
false,
|
|
- c.getSource().getServer().getPlayerList().getPlayers()
|
|
+ c.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
|
|
)
|
|
)
|
|
.then(
|
|
@@ -82,7 +82,7 @@ public class ParticleCommand {
|
|
FloatArgumentType.getFloat(c, "speed"),
|
|
IntegerArgumentType.getInteger(c, "count"),
|
|
true,
|
|
- c.getSource().getServer().getPlayerList().getPlayers()
|
|
+ c.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
|
|
)
|
|
)
|
|
.then(
|
|
@@ -112,7 +112,7 @@ public class ParticleCommand {
|
|
FloatArgumentType.getFloat(c, "speed"),
|
|
IntegerArgumentType.getInteger(c, "count"),
|
|
false,
|
|
- c.getSource().getServer().getPlayerList().getPlayers()
|
|
+ c.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 6a79944b040307acef3d1c66bd0dacaba514c708..4fa37a9772a10aea5eacaa1a02d9f834eda0877b 100644
|
|
--- a/net/minecraft/server/commands/TeamMsgCommand.java
|
|
+++ b/net/minecraft/server/commands/TeamMsgCommand.java
|
|
@@ -41,7 +41,7 @@ public class TeamMsgCommand {
|
|
|
|
List<ServerPlayer> receivers = source.getServer()
|
|
.getPlayerList()
|
|
- .getPlayers()
|
|
+ .realPlayers // Leaves - only real players
|
|
.stream()
|
|
.filter(receiver -> receiver == entity || receiver.getTeam() == team)
|
|
.toList();
|
|
diff --git a/net/minecraft/server/commands/WhitelistCommand.java b/net/minecraft/server/commands/WhitelistCommand.java
|
|
index 3bc559b636288d38192c64a2aba15c062898182f..99aac6df1725db48b63ce68e74f6a3a18caf7956 100644
|
|
--- a/net/minecraft/server/commands/WhitelistCommand.java
|
|
+++ b/net/minecraft/server/commands/WhitelistCommand.java
|
|
@@ -45,7 +45,7 @@ public class WhitelistCommand {
|
|
(c, p) -> {
|
|
PlayerList list = c.getSource().getServer().getPlayerList();
|
|
return SharedSuggestionProvider.suggest(
|
|
- list.getPlayers()
|
|
+ list.realPlayers // Leaves - only real player
|
|
.stream()
|
|
.map(Player::nameAndId)
|
|
.filter(nameAndId -> !list.getWhiteList().isWhiteListed(nameAndId))
|
|
diff --git a/net/minecraft/server/gui/PlayerListComponent.java b/net/minecraft/server/gui/PlayerListComponent.java
|
|
index 37c9f983ae89c497f44a1b7967d741abf909e0a0..d7fb2505f2a86ca0d81f144624ef2e1a4b4bddbb 100644
|
|
--- a/net/minecraft/server/gui/PlayerListComponent.java
|
|
+++ b/net/minecraft/server/gui/PlayerListComponent.java
|
|
@@ -17,8 +17,8 @@ public class PlayerListComponent extends JList<String> {
|
|
if (this.tickCount++ % 20 == 0) {
|
|
Vector<String> players = new Vector<>();
|
|
|
|
- for (int i = 0; i < this.server.getPlayerList().getPlayers().size(); i++) {
|
|
- players.add(this.server.getPlayerList().getPlayers().get(i).getGameProfile().name());
|
|
+ for (int i = 0; i < this.server.getPlayerList().realPlayers.size(); i++) { // Leaves - only real players
|
|
+ players.add(this.server.getPlayerList().realPlayers.get(i).getGameProfile().name()); // Leaves - only real players
|
|
}
|
|
|
|
this.setListData(players);
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index 41c7179cf2b530d8e4b0fc8161aec75fc9d6c4df..f75249ba1b02efec0f4fc381b02803228e3e1d7c 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -3103,7 +3103,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
|
if (entity instanceof ServerPlayer player) {
|
|
ServerLevel.this.players.add(player);
|
|
// Leaves start - skip
|
|
- if (!(player instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ if (!(player instanceof org.leavesmc.leaves.bot.ServerBot) && !(player instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer
|
|
ServerLevel.this.realPlayers.add(player);
|
|
}
|
|
// Leaves end - skip
|
|
@@ -3189,7 +3189,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
|
if (entity instanceof ServerPlayer player) {
|
|
ServerLevel.this.players.remove(player);
|
|
// Leaves start - skip
|
|
- if (!(player instanceof org.leavesmc.leaves.bot.ServerBot)) {
|
|
+ if (!(player instanceof org.leavesmc.leaves.bot.ServerBot) && !(player instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer
|
|
ServerLevel.this.realPlayers.remove(player);
|
|
}
|
|
// Leaves end - skip
|
|
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
|
index b5a22510fede9521a0d8330272876d2ceedfb1cc..049648756f9053895e773efdca33fe65b3555b10 100644
|
|
--- a/net/minecraft/server/players/PlayerList.java
|
|
+++ b/net/minecraft/server/players/PlayerList.java
|
|
@@ -129,6 +129,7 @@ public abstract class PlayerList {
|
|
private int simulationDistance;
|
|
private boolean allowCommandsForAllPlayers;
|
|
private int sendAllPlayerInfoIn;
|
|
+ public final List<ServerPlayer> realPlayers = new java.util.concurrent.CopyOnWriteArrayList<>(); // Leaves - replay api
|
|
|
|
// CraftBukkit start
|
|
private org.bukkit.craftbukkit.CraftServer cserver;
|
|
@@ -191,6 +192,7 @@ public abstract class PlayerList {
|
|
|
|
private boolean countConnection(Connection conn, int limit) {
|
|
synchronized (this.connectionsStateLock) {
|
|
+ if (conn instanceof org.leavesmc.leaves.bot.ServerBotPacketListenerImpl.BotConnection) return true;
|
|
int count = this.usersCountedAgainstLimit.size();
|
|
if (count >= limit) {
|
|
return false;
|
|
@@ -221,6 +223,173 @@ 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 - Replace OfflinePlayer#getLastPlayed
|
|
+ NameAndId gameProfile = player.nameAndId();
|
|
+ UserNameToIdResolver profileCache = this.server.services().nameToIdCache();
|
|
+ Optional<NameAndId> oldProfile = profileCache.get(gameProfile.id());
|
|
+ String oldName = oldProfile.map(NameAndId::name).orElse(gameProfile.name());
|
|
+ if (player.lastKnownName != null) { oldName = player.lastKnownName; player.lastKnownName = null; } // CraftBukkit - Better rename detection
|
|
+ profileCache.add(gameProfile);
|
|
+ ServerLevel level = player.level();
|
|
+ LevelData levelData = level.getLevelData();
|
|
+ CommonListenerCookie cookie = CommonListenerCookie.createInitial(player.gameProfile, false);
|
|
+ ServerGamePacketListenerImpl playerConnection = new ServerGamePacketListenerImpl(this.server, connection, player, cookie);
|
|
+ // Folia start - rewrite login process
|
|
+ // only after setting the connection listener to game type, add the connection to this regions list
|
|
+ level.getCurrentWorldData().connections.add(connection);
|
|
+ // Folia end - rewrite login process
|
|
+ if (!me.earthme.luminol.config.modules.optimizations.AsyncProtocolChangeConfig.enabled) { // Luminol - Async protocol switch // we will run async switch once these main thread logics became done
|
|
+ connection.setupInboundProtocol(
|
|
+ GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), playerConnection), playerConnection
|
|
+ );
|
|
+ } // Luminol - Async protocol switch
|
|
+ playerConnection.suspendFlushing();
|
|
+ GameRules gameRules = level.getGameRules();
|
|
+ boolean immediateRespawn = gameRules.get(GameRules.IMMEDIATE_RESPAWN);
|
|
+ boolean reducedDebugInfo = gameRules.get(GameRules.REDUCED_DEBUG_INFO);
|
|
+ boolean doLimitedCrafting = gameRules.get(GameRules.LIMITED_CRAFTING);
|
|
+ playerConnection.send(
|
|
+ new ClientboundLoginPacket(
|
|
+ player.getId(),
|
|
+ levelData.isHardcore(),
|
|
+ this.server.levelKeys(),
|
|
+ this.getMaxPlayers(),
|
|
+ io.papermc.paper.FeatureHooks.getViewDistance(level), // Paper - view distance
|
|
+ io.papermc.paper.FeatureHooks.getSimulationDistance(level), // Paper - simulation distance
|
|
+ reducedDebugInfo,
|
|
+ !immediateRespawn,
|
|
+ doLimitedCrafting,
|
|
+ player.createCommonSpawnInfo(level),
|
|
+ this.server.usesAuthentication(),
|
|
+ this.server.enforceSecureProfile()
|
|
+ )
|
|
+ );
|
|
+ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
|
|
+ playerConnection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
|
|
+ playerConnection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities()));
|
|
+ playerConnection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot()));
|
|
+ RecipeManager recipeManager = this.server.getRecipeManager();
|
|
+ playerConnection.send(
|
|
+ new ClientboundUpdateRecipesPacket(recipeManager.getSynchronizedItemProperties(), recipeManager.getSynchronizedStonecutterRecipes())
|
|
+ );
|
|
+ this.sendPlayerPermissionLevel(player);
|
|
+ player.getStats().markAllDirty();
|
|
+ player.getRecipeBook().sendInitialRecipeBook(player);
|
|
+ //this.updateEntireScoreboard(level.getScoreboard(), player); // Folia - region threading
|
|
+ this.server.invalidateStatus();
|
|
+
|
|
+ playerConnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
|
|
+ ServerStatus status = this.server.getStatus();
|
|
+ if (status != null && !cookie.transferred()) {
|
|
+ player.sendServerStatus(status);
|
|
+ }
|
|
+
|
|
+ // player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below
|
|
+ this.players.add(player);
|
|
+ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
|
|
+ this.playersByUUID.put(player.getUUID(), player);
|
|
+ // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
|
|
+ // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
|
|
+ player.suppressTrackerForLogin = true;
|
|
+ this.sendLevelInfo(player, level);
|
|
+ level.addNewPlayer(player);
|
|
+ this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player);
|
|
+ // Paper end - Fire PlayerJoinEvent when Player is actually ready
|
|
+ player.initInventoryMenu();
|
|
+ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity();
|
|
+
|
|
+ if (!player.connection.isAcceptingMessages()) {
|
|
+ //return; // Folia - region threading - must still allow the player to connect, as we must add to chunk map before handling disconnect
|
|
+ }
|
|
+
|
|
+ // org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol
|
|
+
|
|
+ // Leaves start - bot support
|
|
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
|
|
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
|
|
+ if (bot != null) {
|
|
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false, false);
|
|
+ }
|
|
+ this.server.getBotList().bots.forEach(bot1 -> {
|
|
+ bot1.sendPlayerInfo(player);
|
|
+ bot1.sendFakeDataIfNeed(player, true);
|
|
+ }); // Leaves - render bot
|
|
+ }
|
|
+ // Leaves end - bot support
|
|
+
|
|
+ // CraftBukkit start - sendAll above replaced with this loop
|
|
+ ClientboundPlayerInfoUpdatePacket packet = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player)); // Paper - Add Listing API for Player
|
|
+
|
|
+ final List<ServerPlayer> onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1); // Paper - Use single player info update packet on join
|
|
+ for (ServerPlayer entityplayer1 : this.players) { // Folia - region threading
|
|
+
|
|
+ if (entityplayer1.getBukkitEntity().canSee(bukkitPlayer)) {
|
|
+ // Paper start - Add Listing API for Player
|
|
+ if (entityplayer1.getBukkitEntity().isListed(bukkitPlayer)) {
|
|
+ // Paper end - Add Listing API for Player
|
|
+ entityplayer1.connection.send(packet);
|
|
+ // Paper start - Add Listing API for Player
|
|
+ } else {
|
|
+ entityplayer1.connection.send(ClientboundPlayerInfoUpdatePacket.createSinglePlayerInitializing(player, false));
|
|
+ }
|
|
+ // Paper end - Add Listing API for Player
|
|
+ }
|
|
+
|
|
+ if (entityplayer1 == player || !bukkitPlayer.canSee(entityplayer1.getBukkitEntity())) { // Paper - Use single player info update packet on join; Don't include joining player
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ onlinePlayers.add(entityplayer1); // Paper - Use single player info update packet on join
|
|
+ }
|
|
+ // Paper start - Use single player info update packet on join
|
|
+ if (!onlinePlayers.isEmpty()) {
|
|
+ player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player)); // Paper - Add Listing API for Player
|
|
+ }
|
|
+ // Paper end - Use single player info update packet on join
|
|
+ player.sentListPacket = true;
|
|
+ player.suppressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready
|
|
+ player.level().getChunkSource().addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
|
|
+ // CraftBukkit end
|
|
+
|
|
+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
|
|
+
|
|
+ this.sendLevelInfo(player, level);
|
|
+
|
|
+ // CraftBukkit start - Only add if the player wasn't moved in the event
|
|
+ if (player.level() == level && !level.players().contains(player)) {
|
|
+ level.addNewPlayer(player);
|
|
+ this.server.getCustomBossEvents().onPlayerConnect(player);
|
|
+ }
|
|
+
|
|
+ level = player.level(); // CraftBukkit - Update in case join event changed it
|
|
+ // CraftBukkit end
|
|
+ this.sendActivePlayerEffects(player);
|
|
+ // Paper - move loading pearls / parent vehicle up
|
|
+ player.initInventoryMenu();
|
|
+ this.server.notificationManager().playerJoined(player);
|
|
+ playerConnection.resumeFlushing();
|
|
+ // Paper start - Configurable player collision; Add to collideRule team if needed
|
|
+ final net.minecraft.world.scores.Scoreboard scoreboard = this.getServer().getLevel(Level.OVERWORLD).getScoreboard();
|
|
+ final PlayerTeam collideRuleTeam = scoreboard.getPlayerTeam(this.collideRuleTeamName);
|
|
+ if (false && this.collideRuleTeamName != null && collideRuleTeam != null && player.getTeam() == null) { // Folia - region threading
|
|
+ scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam);
|
|
+ }
|
|
+
|
|
+ if (player.isDeadOrDying()) {
|
|
+ net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> plains = level.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(level, player.chunkPosition(), plains),
|
|
+ level.getLightEngine(), null, null, true) // Paper - Anti-Xray
|
|
+ );
|
|
+ }
|
|
+ // Paper end - Send empty chunk
|
|
+ }
|
|
+ // Leaves end - replay mod api
|
|
+
|
|
public void placeNewPlayer(final Connection connection, final ServerPlayer player, final CommonListenerCookie cookie) {
|
|
player.isRealPlayer = true; // Paper
|
|
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
|
|
@@ -295,6 +464,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.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
|
|
@@ -510,6 +680,7 @@ public abstract class PlayerList {
|
|
}
|
|
|
|
protected void save(final ServerPlayer player) {
|
|
+ if (player instanceof org.leavesmc.leaves.replay.ServerPhotographer) return; // Leaves - skip photographer
|
|
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
|
|
player.lastSave = System.nanoTime(); // Folia - region threading - changed to nanoTime tracking
|
|
this.playerIo.save(player);
|
|
@@ -524,6 +695,48 @@ 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.getBukkitEntity().packetProcessor.close(); // Folia - region threading
|
|
+ entityplayer.getAdvancements().clearTriggers();
|
|
+ this.players.remove(entityplayer);
|
|
+ // Leaves - guard name removal so a photographer sharing a real player's name won't evict the real player's mapping
|
|
+ final String nameKey = entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT);
|
|
+ if (this.playersByName.get(nameKey) == entityplayer) {
|
|
+ this.playersByName.remove(nameKey);
|
|
+ }
|
|
+ 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 end - replay mod api
|
|
+
|
|
public net.kyori.adventure.text.@Nullable Component remove(final 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())));
|
|
@@ -597,6 +810,7 @@ public abstract class PlayerList {
|
|
player.getBukkitEntity().packetProcessor.close(); // Folia - region threading
|
|
player.getAdvancements().clearTriggers();
|
|
this.players.remove(player);
|
|
+ this.realPlayers.remove(player); // Leaves - replay api
|
|
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
|
|
if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().name()); // Leaf - Configurable vanilla username check
|
|
this.server.getCustomBossEvents().onPlayerDisconnect(player);
|
|
@@ -910,15 +1124,15 @@ public abstract class PlayerList {
|
|
}
|
|
|
|
public String[] getPlayerNamesArray() {
|
|
- List<ServerPlayer> players = new java.util.ArrayList<>(this.players); // Folia - region threading
|
|
- String[] names = new String[players.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support
|
|
+ List<ServerPlayer> players = new java.util.ArrayList<>(this.realPlayers); // Folia - region threading // Lophine - fakeplayer support
|
|
+ String[] names = new String[this.realPlayers.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support
|
|
|
|
- for (int i = 0; i < players.size(); i++) { // Folia - region threading
|
|
- names[i] = players.get(i).getGameProfile().name(); // Folia - region threading
|
|
+ for (int i = 0; i < this.realPlayers.size(); i++) { // Folia - region threading // Leaves - only real players
|
|
+ names[i] = this.realPlayers.get(i).getGameProfile().name(); // Folia - region threading // Leaves - only real players
|
|
}
|
|
// Leaves start - fakeplayer support
|
|
- for (int i = this.players.size(); i < names.length; ++i) {
|
|
- names[i] = this.server.getBotList().bots.get(i - this.players.size()).gameProfile.name();
|
|
+ for (int i = this.realPlayers.size(); i < names.length; ++i) { // Leaves - only real players
|
|
+ names[i] = this.server.getBotList().bots.get(i - this.realPlayers.size()).gameProfile.name(); // Leaves - only real players
|
|
}
|
|
// Leaves end - fakeplayer support
|
|
|
|
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
|
index d64c68651b7142869a06ce0200fb625cd45ae235..1fe317cf3251bff7bb3e0335452a5714d4d31017 100644
|
|
--- a/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -276,7 +276,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
|
public int useItemRemaining;
|
|
protected int fallFlyTicks;
|
|
private long lastKineticHitFeedbackTime = -2147483648L;
|
|
- private BlockPos lastPos;
|
|
+ protected BlockPos lastPos;
|
|
private Optional<BlockPos> lastClimbablePos = Optional.empty();
|
|
private @Nullable DamageSource lastDamageSource;
|
|
private long lastDamageStamp = Long.MIN_VALUE; // Folia - region threading
|