Optimize block connections on neighbour chunk calculation (#3228)

This commit is contained in:
Pablo Herrera 2023-03-06 12:21:17 +01:00 committed by GitHub
parent 3ced95903a
commit 68d1843496
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 81 deletions

View File

@ -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<BlockChangeRecord1_8> 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<BlockChangeRecord1_8> 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<String> 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<BlockChangeRecord1_8> 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<BlockChangeRecord1_8> 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));
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers;
public interface UserBlockData {
int getBlockData(int x, int y, int z);
}

View File

@ -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);
}
}
});

View File

@ -35,6 +35,10 @@ public class BlockConnectionStorage implements StorableObject {
private final Map<Long, SectionData> 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);
}