Fix respawning players falling into the void (#1454)

This commit is contained in:
GreatWyrm 2022-10-24 21:04:41 -04:00 committed by GitHub
parent 9dab3183e5
commit 6316eeb90e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 3 deletions

View File

@ -431,7 +431,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
setOnFire(false);
refreshHealth();
sendPacket(new RespawnPacket(getDimensionType().toString(), getDimensionType().getName().asString(),
0, gameMode, gameMode, false, levelFlat, true));
0, gameMode, gameMode, false, levelFlat, true));
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(this);
EventDispatcher.call(respawnEvent);
@ -439,8 +439,21 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
refreshIsDead(false);
updatePose();
// Runnable called when teleportation is successful (after loading and sending necessary chunk)
teleport(respawnEvent.getRespawnPosition()).thenRun(this::refreshAfterTeleport);
Pos respawnPosition = respawnEvent.getRespawnPosition();
// The client unloads chunks when respawning, so resend all chunks next to spawn
ChunkUtils.forChunksInRange(respawnPosition, MinecraftServer.getChunkViewDistance(), (chunkX, chunkZ) ->
this.instance.loadOptionalChunk(chunkX, chunkZ).thenAccept(chunk -> {
try {
if (chunk != null) {
chunk.sendChunk(this);
}
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
}));
chunksLoadedByClient = new Vec(respawnPosition.chunkX(), respawnPosition.chunkZ());
teleport(respawnPosition).thenRun(this::refreshAfterTeleport);
}
/**

View File

@ -0,0 +1,73 @@
package net.minestom.server.entity.player;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.client.play.ClientStatusPacket;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
@EnvTest
public class PlayerRespawnChunkTest {
@Test
public void testChunkUnloadsOnRespawn(Env env) {
var instance = env.createFlatInstance();
var connection = env.createConnection();
Player player = connection.connect(instance, new Pos(0, 40, 0)).join();
player.teleport(new Pos(32, 40, 32)).join();
var unloadChunkTracker = connection.trackIncoming(UnloadChunkPacket.class);
player.setHealth(0);
player.respawn();
// Since client unloads the chunks, we shouldn't receive any unload packets
unloadChunkTracker.assertCount(0);
}
@Test
public void testChunkReloadCount(Env env) {
var instance = env.createFlatInstance();
var connection = env.createConnection();
Player player = connection.connect(instance, new Pos(0, 40, 0)).join();
var loadChunkTracker = connection.trackIncoming(ChunkDataPacket.class);
player.setHealth(0);
player.respawn();
// Player should have all their chunks reloaded
int chunkLoads = ChunkUtils.getChunkCount(MinecraftServer.getChunkViewDistance());
loadChunkTracker.assertCount(chunkLoads);
}
@Test
public void testPlayerTryRespawn(Env env) {
var instance = env.createFlatInstance();
var connection = env.createConnection();
Player player = connection.connect(instance, new Pos(0, 40, 0)).join();
var loadChunkTracker = connection.trackIncoming(ChunkDataPacket.class);
player.setHealth(0);
player.addPacketToQueue(new ClientStatusPacket(ClientStatusPacket.Action.PERFORM_RESPAWN));
player.interpretPacketQueue();
List<ChunkDataPacket> dataPacketList = loadChunkTracker.collect();
Set<ChunkDataPacket> duplicateCheck = new HashSet<>();
int chunkLoads = ChunkUtils.getChunkCount(MinecraftServer.getChunkViewDistance());
loadChunkTracker.assertCount(chunkLoads);
for(ChunkDataPacket packet : dataPacketList) {
assertFalse(duplicateCheck.contains(packet));
duplicateCheck.add(packet);
assertTrue(Math.abs(packet.chunkX()) <= MinecraftServer.getChunkViewDistance() && Math.abs(packet.chunkZ()) <= MinecraftServer.getChunkViewDistance());
}
}
}