From 047016fbeffb21b1ad9f7a1a28c3ce87858de915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=80=E5=93=A5?= <3523206925@qq.com> Date: Sat, 2 Sep 2023 03:27:03 +0800 Subject: [PATCH] refactor: chunk --- .../api/container/FullContainerType.java | 1 - .../allay/api/world/chunk/ChunkService.java | 2 + .../cn/allay/server/client/AllayClient.java | 18 ++++---- .../cn/allay/server/world/AllayWorld.java | 4 +- .../server/world/chunk/AllayChunkService.java | 45 +++++++++++++++---- 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/Allay-API/src/main/java/cn/allay/api/container/FullContainerType.java b/Allay-API/src/main/java/cn/allay/api/container/FullContainerType.java index 838ba1d80..124b78e28 100644 --- a/Allay-API/src/main/java/cn/allay/api/container/FullContainerType.java +++ b/Allay-API/src/main/java/cn/allay/api/container/FullContainerType.java @@ -16,7 +16,6 @@ public record FullContainerType(int id, boolean canBeOpened Set heldSlotTypes) { public static final Map> SLOT_TYPE_TO_TYPE_MAP = new EnumMap<>(ContainerSlotType.class); - public static final int UNKNOWN_NETWORK_ID = -1; public static final FullContainerType CURSOR = builder() diff --git a/Allay-API/src/main/java/cn/allay/api/world/chunk/ChunkService.java b/Allay-API/src/main/java/cn/allay/api/world/chunk/ChunkService.java index 75b6cfec1..3ccbdf58c 100644 --- a/Allay-API/src/main/java/cn/allay/api/world/chunk/ChunkService.java +++ b/Allay-API/src/main/java/cn/allay/api/world/chunk/ChunkService.java @@ -18,6 +18,8 @@ public interface ChunkService extends ChunkAccessible { CompletableFuture getOrLoadChunk(int x, int z); + CompletableFuture> getOrLoadRangedChunk(int x, int z, int range); + CompletableFuture loadChunk(int x, int z); @SlowOperation diff --git a/Allay-Server/src/main/java/cn/allay/server/client/AllayClient.java b/Allay-Server/src/main/java/cn/allay/server/client/AllayClient.java index 9b25d2834..c35712933 100644 --- a/Allay-Server/src/main/java/cn/allay/server/client/AllayClient.java +++ b/Allay-Server/src/main/java/cn/allay/server/client/AllayClient.java @@ -21,8 +21,6 @@ import cn.allay.api.item.type.ItemTypeRegistry; import cn.allay.api.math.location.Location3f; import cn.allay.api.math.position.Position3ic; -import cn.allay.api.client.data.AdventureSettings; -import cn.allay.api.client.data.LoginData; import cn.allay.api.server.Server; import cn.allay.api.utils.MathUtils; import cn.allay.api.world.biome.BiomeTypeRegistry; @@ -202,13 +200,15 @@ public void initializePlayer() { initPlayerEntity(); sendBasicGameData(); online = true; - if (playerEntity.getCurrentChunk() == null) { - getWorld().getChunkService().loadChunk( - (int) playerEntity.getLocation().x() >> 4, - (int) playerEntity.getLocation().z() >> 4 - ).join(); - } - getWorld().addClient(this); + getWorld().getChunkService().getOrLoadChunk( + (int) playerEntity.getLocation().x() >> 4, + (int) playerEntity.getLocation().z() >> 4 + ).thenAcceptAsync((c) -> getWorld().addClient(this), Server.getInstance().getVirtualThreadPool()).exceptionally( + t -> { + log.error("Error while initialize player " + getName() + "!", t); + return null; + } + ); } @Override diff --git a/Allay-Server/src/main/java/cn/allay/server/world/AllayWorld.java b/Allay-Server/src/main/java/cn/allay/server/world/AllayWorld.java index b8e10c6e5..d977456ee 100644 --- a/Allay-Server/src/main/java/cn/allay/server/world/AllayWorld.java +++ b/Allay-Server/src/main/java/cn/allay/server/world/AllayWorld.java @@ -159,15 +159,15 @@ public void removeEntity(Entity entity) { @Override public void addClient(Client client) { clients.add(client); - addEntity(client.getPlayerEntity()); chunkService.addChunkLoader(client); + addEntity(client.getPlayerEntity()); } @Override public void removeClient(Client client) { - clients.remove(client); removeEntity(client.getPlayerEntity()); chunkService.removeChunkLoader(client); + clients.remove(client); } @Override diff --git a/Allay-Server/src/main/java/cn/allay/server/world/chunk/AllayChunkService.java b/Allay-Server/src/main/java/cn/allay/server/world/chunk/AllayChunkService.java index 1a915b8bb..00341e199 100644 --- a/Allay-Server/src/main/java/cn/allay/server/world/chunk/AllayChunkService.java +++ b/Allay-Server/src/main/java/cn/allay/server/world/chunk/AllayChunkService.java @@ -25,7 +25,6 @@ import org.cloudburstmc.protocol.bedrock.data.SubChunkData; import org.cloudburstmc.protocol.bedrock.data.SubChunkRequestResult; import org.cloudburstmc.protocol.bedrock.packet.SubChunkPacket; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; import org.joml.Vector3i; @@ -34,6 +33,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * Allay Project 2023/7/1 @@ -186,7 +186,8 @@ public void addChunkLoader(ChunkLoader chunkLoader) { @Override public void removeChunkLoader(ChunkLoader chunkLoader) { - this.chunkLoaderManagers.remove(chunkLoader).onRemoved(); + var removed = this.chunkLoaderManagers.remove(chunkLoader); + if (removed != null) removed.onRemoved(); } @Override @@ -203,6 +204,29 @@ public CompletableFuture getOrLoadChunk(int x, int z) { return loadChunk(x, z); } + @Override + public CompletableFuture> getOrLoadRangedChunk(int x, int z, int range) { + // 用于存储CompletableFuture的集合 + Set> futureSet = new HashSet<>(); + + // 遍历(x, z)为中心,半径为range的区块 + for (int dx = -range; dx <= range; dx++) { + for (int dz = -range; dz <= range; dz++) { + if (dx * dx + dz * dz <= range * range) { + // 获取或加载每一个块,并将返回的CompletableFuture添加到集合中 + futureSet.add(getOrLoadChunk(x + dx, z + dz)); + } + } + } + + // 当所有的CompletableFuture都完成时,返回一个新的CompletableFuture + return CompletableFuture.allOf(futureSet.toArray(new CompletableFuture[0])) + .thenApplyAsync(v -> futureSet.stream() + .map(CompletableFuture::join) + .collect(Collectors.toSet()), Server.getInstance().getVirtualThreadPool()); + } + + @Override public CompletableFuture loadChunk(int x, int z) { var hashXZ = HashUtils.hashXZ(x, z); @@ -213,9 +237,14 @@ public CompletableFuture loadChunk(int x, int z) { if (loadingChunk != null) { return loadingChunk; } - var future = worldStorage.readChunk(x, z); + var future = worldStorage.readChunk(x, z).exceptionally(t -> { + log.error("Error while reading chunk (" + x + "," + z + ") !", t); + return null; + }).thenApplyAsync(loadedChunk -> generateChunkIfNullAndSetChunk(x, z, loadedChunk), Server.getInstance().getComputeThreadPool()).exceptionally(t -> { + log.error("Error while generating chunk (" + x + "," + z + ") !", t); + return null; + }); loadingChunks.put(hashXZ, future); - future.thenApplyAsync(loadedChunk -> generateChunkIfNull(x, z, loadedChunk), Server.getInstance().getComputeThreadPool()); return future; } @@ -223,16 +252,16 @@ public CompletableFuture loadChunk(int x, int z) { @SlowOperation @Override public Chunk loadChunkImmediately(int x, int z) { - if (isChunkLoaded(x, z)) { - throw new IllegalStateException("Chunk is already loaded"); + if (isChunkLoaded(x, z) || isChunkLoading(x, z)) { + throw new IllegalStateException("Chunk is already loaded or under loading"); } - return generateChunkIfNull( + return generateChunkIfNullAndSetChunk( x, z, worldStorage.readChunk(x, z).get() ); } - private Chunk generateChunkIfNull(int x, int z, Chunk loadedChunk) { + private Chunk generateChunkIfNullAndSetChunk(int x, int z, Chunk loadedChunk) { long hashXZ = HashUtils.hashXZ(x, z); if (loadedChunk == null) { loadedChunk = generateChunkImmediately(x, z);