Fix entities vanishing upon respawn (#1497)

This commit is contained in:
GreatWyrm 2022-11-26 15:23:39 -05:00 committed by GitHub
parent 77be05085d
commit 27f3b5d4ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 17 additions and 7 deletions

View File

@ -455,7 +455,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
Pos respawnPosition = respawnEvent.getRespawnPosition(); Pos respawnPosition = respawnEvent.getRespawnPosition();
// The client unloads chunks when respawning, so resend all chunks next to spawn // The client unloads chunks when respawning, so resend all chunks next to spawn
ChunkUtils.forChunksInRange(respawnPosition, MinecraftServer.getChunkViewDistance(), (chunkX, chunkZ) -> ChunkUtils.forChunksInRange(respawnPosition, Math.min(MinecraftServer.getChunkViewDistance(), settings.getViewDistance()), (chunkX, chunkZ) ->
this.instance.loadOptionalChunk(chunkX, chunkZ).thenAccept(chunk -> { this.instance.loadOptionalChunk(chunkX, chunkZ).thenAccept(chunk -> {
try { try {
if (chunk != null) { if (chunk != null) {
@ -466,6 +466,14 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
} }
})); }));
chunksLoadedByClient = new Vec(respawnPosition.chunkX(), respawnPosition.chunkZ()); chunksLoadedByClient = new Vec(respawnPosition.chunkX(), respawnPosition.chunkZ());
// Client also needs all entities resent to them, since those are unloaded as well
this.instance.getEntityTracker().nearbyEntitiesByChunkRange(respawnPosition, Math.min(MinecraftServer.getChunkViewDistance(), settings.getViewDistance()),
EntityTracker.Target.ENTITIES, entity -> {
// Skip refreshing self with a new viewer
if (!entity.getUuid().equals(uuid)) {
entity.updateNewViewer(this);
}
});
teleport(respawnPosition).thenRun(this::refreshAfterTeleport); teleport(respawnPosition).thenRun(this::refreshAfterTeleport);
} }
@ -2181,7 +2189,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
public void refresh(String locale, byte viewDistance, ChatMessageType chatMessageType, boolean chatColors, public void refresh(String locale, byte viewDistance, ChatMessageType chatMessageType, boolean chatColors,
byte displayedSkinParts, MainHand mainHand, boolean enableTextFiltering, boolean allowServerListings) { byte displayedSkinParts, MainHand mainHand, boolean enableTextFiltering, boolean allowServerListings) {
this.locale = locale; this.locale = locale;
this.viewDistance = viewDistance; // Clamp viewDistance to valid bounds
this.viewDistance = (byte) MathUtils.clamp(viewDistance, 2, 32);
this.chatMessageType = chatMessageType; this.chatMessageType = chatMessageType;
this.chatColors = chatColors; this.chatColors = chatColors;
this.displayedSkinParts = displayedSkinParts; this.displayedSkinParts = displayedSkinParts;

View File

@ -8,8 +8,8 @@ import net.minestom.server.network.packet.client.play.ClientSettingsPacket;
public final class SettingsListener { public final class SettingsListener {
public static void listener(ClientSettingsPacket packet, Player player) { public static void listener(ClientSettingsPacket packet, Player player) {
Player.PlayerSettings settings = player.getSettings(); Player.PlayerSettings settings = player.getSettings();
final byte viewDistance = (byte) Math.abs(packet.viewDistance()); // Since viewDistance bounds checking is performed in the refresh function, it is not necessary to check it here
settings.refresh(packet.locale(), viewDistance, packet.chatMessageType(), packet.chatColors(), packet.displayedSkinParts(), packet.mainHand(), packet.enableTextFiltering(), packet.allowsListing()); settings.refresh(packet.locale(), packet.viewDistance(), packet.chatMessageType(), packet.chatColors(), packet.displayedSkinParts(), packet.mainHand(), packet.enableTextFiltering(), packet.allowsListing());
EventDispatcher.call(new PlayerSettingsChangeEvent(player)); EventDispatcher.call(new PlayerSettingsChangeEvent(player));
} }
} }

View File

@ -45,7 +45,7 @@ public class PlayerRespawnChunkIntegrationTest {
player.setHealth(0); player.setHealth(0);
player.respawn(); player.respawn();
// Player should have all their chunks reloaded // Player should have all their chunks reloaded
int chunkLoads = ChunkUtils.getChunkCount(MinecraftServer.getChunkViewDistance()); int chunkLoads = ChunkUtils.getChunkCount(Math.min(MinecraftServer.getChunkViewDistance(), player.getSettings().getViewDistance()));
loadChunkTracker.assertCount(chunkLoads); loadChunkTracker.assertCount(chunkLoads);
} }
@ -61,12 +61,13 @@ public class PlayerRespawnChunkIntegrationTest {
player.interpretPacketQueue(); player.interpretPacketQueue();
List<ChunkDataPacket> dataPacketList = loadChunkTracker.collect(); List<ChunkDataPacket> dataPacketList = loadChunkTracker.collect();
Set<ChunkDataPacket> duplicateCheck = new HashSet<>(); Set<ChunkDataPacket> duplicateCheck = new HashSet<>();
int chunkLoads = ChunkUtils.getChunkCount(MinecraftServer.getChunkViewDistance()); int actualViewDistance = Math.min(MinecraftServer.getChunkViewDistance(), player.getSettings().getViewDistance());
int chunkLoads = ChunkUtils.getChunkCount(actualViewDistance);
loadChunkTracker.assertCount(chunkLoads); loadChunkTracker.assertCount(chunkLoads);
for (ChunkDataPacket packet : dataPacketList) { for (ChunkDataPacket packet : dataPacketList) {
assertFalse(duplicateCheck.contains(packet)); assertFalse(duplicateCheck.contains(packet));
duplicateCheck.add(packet); duplicateCheck.add(packet);
assertTrue(Math.abs(packet.chunkX()) <= MinecraftServer.getChunkViewDistance() && Math.abs(packet.chunkZ()) <= MinecraftServer.getChunkViewDistance()); assertTrue(Math.abs(packet.chunkX()) <= actualViewDistance && Math.abs(packet.chunkZ()) <= actualViewDistance);
} }
} }
} }