From 68d1843496be3c14ec17eca8b14e229dfccb9221 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Mon, 6 Mar 2023 12:21:17 +0100 Subject: [PATCH] Optimize block connections on neighbour chunk calculation (#3228) --- .../blockconnections/ConnectionData.java | 157 +++++++++--------- .../providers/BlockConnectionProvider.java | 5 + .../PacketBlockConnectionProvider.java | 5 + .../providers/UserBlockData.java | 24 +++ .../packets/WorldPackets.java | 3 +- .../storage/BlockConnectionStorage.java | 34 +++- 6 files changed, 147 insertions(+), 81 deletions(-) create mode 100644 common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/UserBlockData.java diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/ConnectionData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/ConnectionData.java index 535bc53b4..5e24f1dfe 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/ConnectionData.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/ConnectionData.java @@ -18,7 +18,6 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections; import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.viaversion.viaversion.api.Via; @@ -37,6 +36,7 @@ import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPacke import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider; import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.PacketBlockConnectionProvider; +import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.UserBlockData; import com.viaversion.viaversion.util.Key; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -77,80 +77,6 @@ public final class ConnectionData { } } - public static void updateChunkSectionNeighbours(UserConnection user, int chunkX, int chunkZ, int chunkSectionY) { - int chunkMinY = chunkSectionY << 4; - List updates = new ArrayList<>(); - for (int chunkDeltaX = -1; chunkDeltaX <= 1; chunkDeltaX++) { - for (int chunkDeltaZ = -1; chunkDeltaZ <= 1; chunkDeltaZ++) { - int distance = Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ); - if (distance == 0) continue; - - int chunkMinX = (chunkX + chunkDeltaX) << 4; - int chunkMinZ = (chunkZ + chunkDeltaZ) << 4; - if (distance == 2) { // Corner - for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) { - int blockPosX = chunkDeltaX == 1 ? 0 : 15; - int blockPosZ = chunkDeltaZ == 1 ? 0 : 15; - updateBlock(user, new Position(chunkMinX + blockPosX, blockY, chunkMinZ + blockPosZ), updates); - } - } else { - for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) { - int xStart, xEnd; - int zStart, zEnd; - if (chunkDeltaX == 1) { - xStart = 0; - xEnd = 2; - zStart = 0; - zEnd = 16; - } else if (chunkDeltaX == -1) { - xStart = 14; - xEnd = 16; - zStart = 0; - zEnd = 16; - } else if (chunkDeltaZ == 1) { - xStart = 0; - xEnd = 16; - zStart = 0; - zEnd = 2; - } else { - xStart = 0; - xEnd = 16; - zStart = 14; - zEnd = 16; - } - for (int blockX = xStart; blockX < xEnd; blockX++) { - for (int blockZ = zStart; blockZ < zEnd; blockZ++) { - updateBlock(user, new Position(chunkMinX + blockX, blockY, chunkMinZ + blockZ), updates); - } - } - } - } - - if (!updates.isEmpty()) { - PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.MULTI_BLOCK_CHANGE, null, user); - wrapper.write(Type.INT, chunkX + chunkDeltaX); - wrapper.write(Type.INT, chunkZ + chunkDeltaZ); - wrapper.write(Type.BLOCK_CHANGE_RECORD_ARRAY, updates.toArray(EMPTY_RECORDS)); - try { - wrapper.send(Protocol1_13To1_12_2.class); - } catch (Exception e) { - e.printStackTrace(); - } - updates.clear(); - } - } - } - } - - public static void updateBlock(UserConnection user, Position pos, List records) { - int blockState = blockConnectionProvider.getBlockData(user, pos.x(), pos.y(), pos.z()); - ConnectionHandler handler = getConnectionHandler(blockState); - if (handler == null) return; - - int newBlockState = handler.connect(user, pos, blockState); - records.add(new BlockChangeRecord1_8(pos.x() & 0xF, pos.y(), pos.z() & 0xF, newBlockState)); - } - public static void updateBlockStorage(UserConnection userConnection, int x, int y, int z, int blockState) { if (!needStoreBlocks()) return; if (ConnectionData.isWelcome(blockState)) { @@ -693,4 +619,85 @@ public final class ConnectionData { public static Object2IntMap getKeyToId() { return keyToId; } + + public static class NeighbourUpdater { + private final UserConnection user; + private final UserBlockData userBlockData; + + public NeighbourUpdater(UserConnection user) { + this.user = user; + this.userBlockData = blockConnectionProvider.forUser(user); + } + + public void updateChunkSectionNeighbours(int chunkX, int chunkZ, int chunkSectionY) throws Exception { + int chunkMinY = chunkSectionY << 4; + List updates = new ArrayList<>(); + for (int chunkDeltaX = -1; chunkDeltaX <= 1; chunkDeltaX++) { + for (int chunkDeltaZ = -1; chunkDeltaZ <= 1; chunkDeltaZ++) { + int distance = Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ); + if (distance == 0) continue; + + int chunkMinX = (chunkX + chunkDeltaX) << 4; + int chunkMinZ = (chunkZ + chunkDeltaZ) << 4; + if (distance == 2) { // Corner + for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) { + int blockPosX = chunkDeltaX == 1 ? 0 : 15; + int blockPosZ = chunkDeltaZ == 1 ? 0 : 15; + updateBlock(chunkMinX + blockPosX, blockY, chunkMinZ + blockPosZ, updates); + } + } else { + for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) { + int xStart, xEnd; + int zStart, zEnd; + if (chunkDeltaX == 1) { + xStart = 0; + xEnd = 2; + zStart = 0; + zEnd = 16; + } else if (chunkDeltaX == -1) { + xStart = 14; + xEnd = 16; + zStart = 0; + zEnd = 16; + } else if (chunkDeltaZ == 1) { + xStart = 0; + xEnd = 16; + zStart = 0; + zEnd = 2; + } else { + xStart = 0; + xEnd = 16; + zStart = 14; + zEnd = 16; + } + for (int blockX = xStart; blockX < xEnd; blockX++) { + for (int blockZ = zStart; blockZ < zEnd; blockZ++) { + updateBlock(chunkMinX + blockX, blockY, chunkMinZ + blockZ, updates); + } + } + } + } + + if (!updates.isEmpty()) { + PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.MULTI_BLOCK_CHANGE, null, user); + wrapper.write(Type.INT, chunkX + chunkDeltaX); + wrapper.write(Type.INT, chunkZ + chunkDeltaZ); + wrapper.write(Type.BLOCK_CHANGE_RECORD_ARRAY, updates.toArray(EMPTY_RECORDS)); + wrapper.send(Protocol1_13To1_12_2.class); + updates.clear(); + } + } + } + } + + private void updateBlock(int x, int y, int z, List records) { + int blockState = userBlockData.getBlockData(x, y, z); + ConnectionHandler handler = getConnectionHandler(blockState); + if (handler == null) return; + + Position pos = new Position(x, y, z); + int newBlockState = handler.connect(user, pos, blockState); + records.add(new BlockChangeRecord1_8(pos.x() & 0xF, pos.y(), pos.z() & 0xF, newBlockState)); + } + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/BlockConnectionProvider.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/BlockConnectionProvider.java index 7afa7436e..4254f062a 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/BlockConnectionProvider.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/BlockConnectionProvider.java @@ -55,4 +55,9 @@ public class BlockConnectionProvider implements Provider { public boolean storesBlocks() { return false; } + + public UserBlockData forUser(UserConnection connection) { + return (x, y, z) -> getBlockData(connection, x, y, z); + } + } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/PacketBlockConnectionProvider.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/PacketBlockConnectionProvider.java index c1b76e857..95a71a787 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/PacketBlockConnectionProvider.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/PacketBlockConnectionProvider.java @@ -56,4 +56,9 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider { public boolean storesBlocks() { return true; } + + @Override + public UserBlockData forUser(UserConnection connection) { + return connection.get(BlockConnectionStorage.class)::get; + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/UserBlockData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/UserBlockData.java new file mode 100644 index 000000000..c4cd4c863 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/providers/UserBlockData.java @@ -0,0 +1,24 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2023 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers; + +public interface UserBlockData { + + int getBlockData(int x, int y, int z); + +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/WorldPackets.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/WorldPackets.java index 3b960a0a2..9398f6253 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/WorldPackets.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/WorldPackets.java @@ -448,10 +448,11 @@ public class WorldPackets { // Workaround for packet order issue wrapper.send(Protocol1_13To1_12_2.class); wrapper.cancel(); + ConnectionData.NeighbourUpdater updater = new ConnectionData.NeighbourUpdater(wrapper.user()); for (int i = 0; i < chunk.getSections().length; i++) { ChunkSection section = chunk.getSections()[i]; if (section == null) continue; - ConnectionData.updateChunkSectionNeighbours(wrapper.user(), chunk.getX(), chunk.getZ(), i); + updater.updateChunkSectionNeighbours(chunk.getX(), chunk.getZ(), i); } } }); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/storage/BlockConnectionStorage.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/storage/BlockConnectionStorage.java index 7631cf81a..8edd3d78e 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/storage/BlockConnectionStorage.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/storage/BlockConnectionStorage.java @@ -35,6 +35,10 @@ public class BlockConnectionStorage implements StorableObject { private final Map blockStorage = createLongObjectMap(); + // Cache to retrieve section quicker + private Long lastIndex; + private SectionData lastSection; + static { try { //noinspection StringBufferReplaceableByString - prevent relocation @@ -73,7 +77,7 @@ public class BlockConnectionStorage implements StorableObject { public int get(int x, int y, int z) { long pair = getChunkSectionIndex(x, y, z); - SectionData map = blockStorage.get(pair); + SectionData map = getSection(pair); if (map == null) return 0; short blockPosition = encodeBlockPos(x, y, z); NibbleArray nibbleArray = map.nibbleArray(); @@ -85,7 +89,7 @@ public class BlockConnectionStorage implements StorableObject { public void remove(int x, int y, int z) { long pair = getChunkSectionIndex(x, y, z); - SectionData map = blockStorage.get(pair); + SectionData map = getSection(pair); if (map == null) return; int blockIndex = encodeBlockPos(x, y, z); NibbleArray nibbleArray = map.nibbleArray(); @@ -104,11 +108,13 @@ public class BlockConnectionStorage implements StorableObject { for (short entry : map.blockIds()) { if (entry != 0) return; } - blockStorage.remove(pair); + removeSection(pair); } public void clear() { blockStorage.clear(); + lastSection = null; + lastIndex = null; } public void unloadChunk(int x, int z) { @@ -118,14 +124,16 @@ public class BlockConnectionStorage implements StorableObject { } public void unloadSection(int x, int y, int z) { - blockStorage.remove(getChunkSectionIndex(x << 4, y << 4, z << 4)); + removeSection(getChunkSectionIndex(x << 4, y << 4, z << 4)); } private SectionData getChunkSection(long index, boolean requireNibbleArray) { - SectionData map = blockStorage.get(index); + SectionData map = getSection(index); if (map == null) { map = new SectionData(new byte[4096]); blockStorage.put(index, map); + lastSection = map; + lastIndex = index; } if (map.nibbleArray() == null && requireNibbleArray) { map.setNibbleArray(new NibbleArray(4096)); @@ -133,6 +141,22 @@ public class BlockConnectionStorage implements StorableObject { return map; } + private SectionData getSection(long index) { + if (lastIndex != null && lastIndex == index) { + return lastSection; + } + lastIndex = index; + return lastSection = blockStorage.get(index); + } + + private void removeSection(long index) { + blockStorage.remove(index); + if (lastIndex != null && lastIndex == index) { + lastIndex = null; + lastSection = null; + } + } + private long getChunkSectionIndex(int x, int y, int z) { return (((x >> 4) & 0x3FFFFFFL) << 38) | (((y >> 4) & 0xFFFL) << 26) | ((z >> 4) & 0x3FFFFFFL); }