Lots of small and less small blockconnection optimization

- blockConnections.json data is now stored more compact and with direct block state ids in blockConnections.nbt
- For PacketBlockConnectionProvider, the server now longer spams unnecessary block change packets if the connectable block did not actually change
- Some other, small optimizations within the individual connection handlers
This commit is contained in:
Nassim Jahnke 2023-03-09 17:46:03 +01:00
parent 9781b7962f
commit 72197ddca8
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
26 changed files with 377 additions and 24854 deletions

View File

@ -21,29 +21,33 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.HashSet; import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Map; import java.util.Arrays;
import java.util.Set;
public abstract class AbstractFenceConnectionHandler extends ConnectionHandler { public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
private static final StairConnectionHandler STAIR_CONNECTION_HANDLER = new StairConnectionHandler(); private static final StairConnectionHandler STAIR_CONNECTION_HANDLER = new StairConnectionHandler();
private final String blockConnections; private final IntSet blockStates = new IntOpenHashSet();
private final Set<Integer> blockStates = new HashSet<>(); private final int[] connectedBlockStates = new int[statesSize()];
private final Map<Byte, Integer> connectedBlockStates = new HashMap<>(); private final int blockConnectionsTypeId;
protected AbstractFenceConnectionHandler(String blockConnections) { protected AbstractFenceConnectionHandler(String blockConnections) {
this.blockConnections = blockConnections; this.blockConnectionsTypeId = blockConnections != null ? BlockData.connectionTypeId(blockConnections) : -1;
Arrays.fill(connectedBlockStates, -1);
} }
public ConnectionData.ConnectorInitAction getInitAction(final String key) { public ConnectionData.ConnectorInitAction getInitAction(final String key) {
final AbstractFenceConnectionHandler handler = this; final AbstractFenceConnectionHandler handler = this;
return blockData -> { return blockData -> {
if (key.equals(blockData.getMinecraftKey())) { if (key.equals(blockData.getMinecraftKey())) {
if (blockData.hasData("waterlogged") && blockData.getValue("waterlogged").equals("true")) return; if (blockData.hasData("waterlogged") && blockData.getValue("waterlogged").equals("true")) {
return;
}
blockStates.add(blockData.getSavedBlockStateId()); blockStates.add(blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler);
connectedBlockStates.put(getStates(blockData), blockData.getSavedBlockStateId()); byte internalStateId = getStates(blockData);
connectedBlockStates[internalStateId] = blockData.getSavedBlockStateId();
} }
}; };
} }
@ -67,6 +71,10 @@ public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
return states; return states;
} }
protected byte statesSize() {
return 16;
}
@Override @Override
public int getBlockData(UserConnection user, Position position) { public int getBlockData(UserConnection user, Position position) {
return STAIR_CONNECTION_HANDLER.connect(user, position, super.getBlockData(user, position)); return STAIR_CONNECTION_HANDLER.connect(user, position, super.getBlockData(user, position));
@ -74,19 +82,19 @@ public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
final Integer newBlockState = connectedBlockStates.get(getStates(user, position, blockState)); final int newBlockState = connectedBlockStates[getStates(user, position, blockState)];
return newBlockState == null ? blockState : newBlockState; return newBlockState == -1 ? blockState : newBlockState;
} }
protected boolean connects(BlockFace side, int blockState, boolean pre1_12) { protected boolean connects(BlockFace side, int blockState, boolean pre1_12) {
if (blockStates.contains(blockState)) return true; if (blockStates.contains(blockState)) return true;
if (blockConnections == null) return false; if (blockConnectionsTypeId == -1) return false;
BlockData blockData = ConnectionData.blockConnectionData.get(blockState); BlockData blockData = ConnectionData.blockConnectionData.get(blockState);
return blockData != null && blockData.connectsTo(blockConnections, side.opposite(), pre1_12); return blockData != null && blockData.connectsTo(blockConnectionsTypeId, side.opposite(), pre1_12);
} }
public Set<Integer> getBlockStates() { public IntSet getBlockStates() {
return blockStates; return blockStates;
} }
} }

View File

@ -20,17 +20,17 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
public abstract class AbstractStempConnectionHandler extends ConnectionHandler { public abstract class AbstractStempConnectionHandler extends ConnectionHandler {
private static final BlockFace[] BLOCK_FACES = {BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST}; private static final BlockFace[] BLOCK_FACES = {BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST};
private final IntSet blockId = new IntOpenHashSet();
private final int baseStateId; private final int baseStateId;
private final Set<Integer> blockId = new HashSet<>();
private final Map<BlockFace, Integer> stemps = new EnumMap<>(BlockFace.class); private final Map<BlockFace, Integer> stemps = new EnumMap<>(BlockFace.class);

View File

@ -24,12 +24,12 @@ public class BasicFenceConnectionHandler extends AbstractFenceConnectionHandler
static List<ConnectionData.ConnectorInitAction> init() { static List<ConnectionData.ConnectorInitAction> init() {
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(); List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>();
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:oak_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:oak_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:birch_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:birch_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:jungle_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:jungle_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:dark_oak_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:dark_oak_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:acacia_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:acacia_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:spruce_fence")); actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:spruce_fence"));
return actions; return actions;
} }

View File

@ -17,25 +17,34 @@
*/ */
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections; package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import java.util.Map; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.Arrays;
import java.util.List;
public class BlockData { public final class BlockData {
private final Map<String, boolean[]> connectData = new HashMap<>(); private static final List<String> CONNECTION_TYPES = Arrays.asList("fence", "netherFence", "pane", "cobbleWall", "redstone", "allFalseIfStairPre1_12");
private static final int MAGIC_STAIRS_ID = connectionTypeId("allFalseIfStairPre1_12");
private final Int2ObjectMap<boolean[]> connectData = new Int2ObjectArrayMap<>();
public void put(String key, boolean[] booleans) { public void put(final int blockConnectionTypeId, final boolean[] booleans) {
connectData.put(key, booleans); connectData.put(blockConnectionTypeId, booleans);
} }
public boolean connectsTo(String blockConnection, BlockFace face, boolean pre1_12AbstractFence) { public boolean connectsTo(final int blockConnectionTypeId, final BlockFace face, final boolean pre1_12AbstractFence) {
boolean[] booleans = null; if (pre1_12AbstractFence && connectData.containsKey(MAGIC_STAIRS_ID)) {
if (pre1_12AbstractFence) { return false;
booleans = connectData.get("allFalseIfStairPre1_12"); // https://minecraft.gamepedia.com/Java_Edition_1.12
}
if (booleans == null) {
booleans = connectData.get(blockConnection);
} }
final boolean[] booleans = connectData.get(blockConnectionTypeId);
return booleans != null && booleans[face.ordinal()]; return booleans != null && booleans[face.ordinal()];
} }
public static int connectionTypeId(final String blockConnection) {
final int connectionTypeId = CONNECTION_TYPES.indexOf(blockConnection);
Preconditions.checkArgument(connectionTypeId != -1, "Unknown connection type: " + blockConnection);
return connectionTypeId;
}
} }

View File

@ -20,28 +20,33 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.HashSet; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set;
class ChestConnectionHandler extends ConnectionHandler { class ChestConnectionHandler extends ConnectionHandler {
private static final Map<Integer, BlockFace> chestFacings = new HashMap<>(); private static final Int2ObjectMap<BlockFace> CHEST_FACINGS = new Int2ObjectOpenHashMap<>();
private static final Map<Byte, Integer> connectedStates = new HashMap<>(); private static final int[] CONNECTED_STATES = new int[32];
private static final Set<Integer> trappedChests = new HashSet<>(); private static final IntSet TRAPPED_CHESTS = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
Arrays.fill(CONNECTED_STATES, -1);
final ChestConnectionHandler connectionHandler = new ChestConnectionHandler(); final ChestConnectionHandler connectionHandler = new ChestConnectionHandler();
return blockData -> { return blockData -> {
if (!blockData.getMinecraftKey().equals("minecraft:chest") && !blockData.getMinecraftKey().equals("minecraft:trapped_chest")) if (!blockData.getMinecraftKey().equals("minecraft:chest") && !blockData.getMinecraftKey().equals("minecraft:trapped_chest")) {
return; return;
if (blockData.getValue("waterlogged").equals("true")) return;
chestFacings.put(blockData.getSavedBlockStateId(), BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT)));
if (blockData.getMinecraftKey().equalsIgnoreCase("minecraft:trapped_chest")) {
trappedChests.add(blockData.getSavedBlockStateId());
} }
connectedStates.put(getStates(blockData), blockData.getSavedBlockStateId()); if (blockData.getValue("waterlogged").equals("true")) {
return;
}
CHEST_FACINGS.put(blockData.getSavedBlockStateId(), BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT)));
if (blockData.getMinecraftKey().equalsIgnoreCase("minecraft:trapped_chest")) {
TRAPPED_CHESTS.add(blockData.getSavedBlockStateId());
}
CONNECTED_STATES[getStates(blockData)] = blockData.getSavedBlockStateId();
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
}; };
} }
@ -58,22 +63,27 @@ class ChestConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
BlockFace facing = chestFacings.get(blockState); BlockFace facing = CHEST_FACINGS.get(blockState);
byte states = 0; byte states = 0;
states |= (facing.ordinal() << 2); states |= (facing.ordinal() << 2);
boolean trapped = trappedChests.contains(blockState);
if (trapped) states |= 16; boolean trapped = TRAPPED_CHESTS.contains(blockState);
if (trapped) {
states |= 16;
}
int relative; int relative;
if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.NORTH))) && trapped == trappedChests.contains(relative)) { if (CHEST_FACINGS.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.NORTH))) && trapped == TRAPPED_CHESTS.contains(relative)) {
states |= facing == BlockFace.WEST ? 1 : 2; states |= facing == BlockFace.WEST ? 1 : 2;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.SOUTH))) && trapped == trappedChests.contains(relative)) { } else if (CHEST_FACINGS.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.SOUTH))) && trapped == TRAPPED_CHESTS.contains(relative)) {
states |= facing == BlockFace.EAST ? 1 : 2; states |= facing == BlockFace.EAST ? 1 : 2;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.WEST))) && trapped == trappedChests.contains(relative)) { } else if (CHEST_FACINGS.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.WEST))) && trapped == TRAPPED_CHESTS.contains(relative)) {
states |= facing == BlockFace.NORTH ? 2 : 1; states |= facing == BlockFace.NORTH ? 2 : 1;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.EAST))) && trapped == trappedChests.contains(relative)) { } else if (CHEST_FACINGS.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.EAST))) && trapped == TRAPPED_CHESTS.contains(relative)) {
states |= facing == BlockFace.SOUTH ? 2 : 1; states |= facing == BlockFace.SOUTH ? 2 : 1;
} }
Integer newBlockState = connectedStates.get(states);
return newBlockState == null ? blockState : newBlockState; int newBlockState = CONNECTED_STATES[states];
return newBlockState == -1 ? blockState : newBlockState;
} }
} }

View File

@ -55,6 +55,11 @@ public class ChorusPlantConnectionHandler extends AbstractFenceConnectionHandler
return states; return states;
} }
@Override
protected byte statesSize() {
return 64;
}
@Override @Override
protected byte getStates(UserConnection user, Position position, int blockState) { protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState); byte states = super.getStates(user, position, blockState);

View File

@ -17,9 +17,12 @@
*/ */
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections; package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.google.gson.JsonElement; import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.google.gson.JsonObject; import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingDataLoader; import com.viaversion.viaversion.api.data.MappingDataLoader;
@ -46,17 +49,19 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map.Entry; import java.util.Map.Entry;
public final class ConnectionData { public final class ConnectionData {
private static final BlockChangeRecord1_8[] EMPTY_RECORDS = new BlockChangeRecord1_8[0];
public static BlockConnectionProvider blockConnectionProvider; public static BlockConnectionProvider blockConnectionProvider;
static Int2ObjectMap<String> idToKey = new Int2ObjectOpenHashMap<>(8582, .99F); static final Object2IntMap<String> KEY_TO_ID = new Object2IntOpenHashMap<>(8582, .99F);
static Object2IntMap<String> keyToId = new Object2IntOpenHashMap<>(8582, .99F); static final IntSet OCCLUDING_STATES = new IntOpenHashSet(377, .99F);
static Int2ObjectMap<ConnectionHandler> connectionHandlerMap = new Int2ObjectOpenHashMap<>(1); static Int2ObjectMap<ConnectionHandler> connectionHandlerMap = new Int2ObjectOpenHashMap<>();
static Int2ObjectMap<BlockData> blockConnectionData = new Int2ObjectOpenHashMap<>(1); static Int2ObjectMap<BlockData> blockConnectionData = new Int2ObjectOpenHashMap<>();
static IntSet occludingStates = new IntOpenHashSet(377, .99F); private static final BlockChangeRecord1_8[] EMPTY_RECORDS = new BlockChangeRecord1_8[0];
static {
KEY_TO_ID.defaultReturnValue(-1);
}
public static void update(UserConnection user, Position position) throws Exception { public static void update(UserConnection user, Position position) throws Exception {
for (BlockFace face : BlockFace.values()) { for (BlockFace face : BlockFace.values()) {
@ -68,10 +73,12 @@ public final class ConnectionData {
} }
int newBlockState = handler.connect(user, pos, blockState); int newBlockState = handler.connect(user, pos, blockState);
if (newBlockState == blockState) { if (newBlockState == blockState && blockConnectionProvider.storesBlocks()) {
continue; continue;
} }
updateBlockStorage(user, pos.x(), pos.y(), pos.z(), newBlockState);
PacketWrapper blockUpdatePacket = PacketWrapper.create(ClientboundPackets1_13.BLOCK_CHANGE, null, user); PacketWrapper blockUpdatePacket = PacketWrapper.create(ClientboundPackets1_13.BLOCK_CHANGE, null, user);
blockUpdatePacket.write(Type.POSITION, pos); blockUpdatePacket.write(Type.POSITION, pos);
blockUpdatePacket.write(Type.VAR_INT, newBlockState); blockUpdatePacket.write(Type.VAR_INT, newBlockState);
@ -103,7 +110,10 @@ public final class ConnectionData {
for (int s = 0; s < chunk.getSections().length; s++) { for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s]; ChunkSection section = chunk.getSections()[s];
if (section == null) continue; if (section == null) {
continue;
}
DataPalette blocks = section.palette(PaletteType.BLOCKS); DataPalette blocks = section.palette(PaletteType.BLOCKS);
boolean willConnect = false; boolean willConnect = false;
@ -114,16 +124,25 @@ public final class ConnectionData {
break; break;
} }
} }
if (!willConnect) continue; if (!willConnect) {
continue;
}
int yOff = s << 4; int yOff = s << 4;
for (int idx = 0; idx < ChunkSection.SIZE; idx++) { for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx); int id = blocks.idAt(idx);
ConnectionHandler handler = ConnectionData.getConnectionHandler(id); ConnectionHandler handler = ConnectionData.getConnectionHandler(id);
if (handler == null) continue; if (handler == null) {
id = handler.connect(user, new Position(xOff + ChunkSection.xFromIndex(idx), yOff + ChunkSection.yFromIndex(idx), zOff + ChunkSection.zFromIndex(idx)), id); continue;
blocks.setIdAt(idx, id); }
Position position = new Position(xOff + ChunkSection.xFromIndex(idx), yOff + ChunkSection.yFromIndex(idx), zOff + ChunkSection.zFromIndex(idx));
int connectedId = handler.connect(user, position, id);
if (connectedId != id) {
blocks.setIdAt(idx, connectedId);
updateBlockStorage(user, position.x(), position.y(), position.z(), connectedId);
}
} }
} }
} }
@ -137,39 +156,49 @@ public final class ConnectionData {
ListTag blockStates = MappingDataLoader.loadNBT("blockstates-1.13.nbt").get("blockstates"); ListTag blockStates = MappingDataLoader.loadNBT("blockstates-1.13.nbt").get("blockstates");
for (int id = 0; id < blockStates.size(); id++) { for (int id = 0; id < blockStates.size(); id++) {
String key = (String) blockStates.get(id).getValue(); String key = (String) blockStates.get(id).getValue();
idToKey.put(id, key); KEY_TO_ID.put(key, id);
keyToId.put(key, id);
} }
connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650, .99F); connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650, .99F);
if (!Via.getConfig().isReduceBlockStorageMemory()) { if (!Via.getConfig().isReduceBlockStorageMemory()) {
blockConnectionData = new Int2ObjectOpenHashMap<>(1146, .99F); blockConnectionData = new Int2ObjectOpenHashMap<>(2048);
JsonObject mappingBlockConnections = MappingDataLoader.loadData("blockConnections.json");
for (Entry<String, JsonElement> entry : mappingBlockConnections.entrySet()) { ListTag blockConnectionMappings = MappingDataLoader.loadNBT("blockConnections.nbt").get("data");
int id = keyToId.getInt(entry.getKey()); for (Tag blockTag : blockConnectionMappings) {
CompoundTag blockCompoundTag = (CompoundTag) blockTag;
BlockData blockData = new BlockData(); BlockData blockData = new BlockData();
for (Entry<String, JsonElement> type : entry.getValue().getAsJsonObject().entrySet()) { for (Entry<String, Tag> entry : blockCompoundTag.entrySet()) {
String name = type.getKey(); String key = entry.getKey();
JsonObject object = type.getValue().getAsJsonObject(); if (key.equals("id") || key.equals("ids")) {
boolean[] data = new boolean[6]; continue;
for (BlockFace value : BlockFace.values()) {
String face = value.toString().toLowerCase(Locale.ROOT);
if (object.has(face)) {
data[value.ordinal()] = object.getAsJsonPrimitive(face).getAsBoolean();
} }
boolean[] attachingFaces = new boolean[4];
ByteArrayTag connections = (ByteArrayTag) entry.getValue();
for (byte blockFaceId : connections.getValue()) {
attachingFaces[blockFaceId] = true;
} }
blockData.put(name, data);
} int connectionTypeId = Integer.parseInt(key);
if (entry.getKey().contains("stairs")) { blockData.put(connectionTypeId, attachingFaces);
blockData.put("allFalseIfStairPre1_12", new boolean[6]);
} }
NumberTag idTag = blockCompoundTag.get("id");
if (idTag != null) {
blockConnectionData.put(idTag.asInt(), blockData);
} else {
IntArrayTag idsTag = blockCompoundTag.get("ids");
for (int id : idsTag.getValue()) {
blockConnectionData.put(id, blockData); blockConnectionData.put(id, blockData);
} }
} }
}
}
for (String state : occludingBlockStates()) { for (String state : occludingBlockStates()) {
occludingStates.add(keyToId.getInt(state)); OCCLUDING_STATES.add(KEY_TO_ID.getInt(state));
} }
List<ConnectorInitAction> initActions = new ArrayList<>(); List<ConnectorInitAction> initActions = new ArrayList<>();
@ -192,7 +221,7 @@ public final class ConnectionData {
initActions.add(VineConnectionHandler.init()); initActions.add(VineConnectionHandler.init());
} }
for (String key : keyToId.keySet()) { for (String key : KEY_TO_ID.keySet()) {
WrappedBlockData wrappedBlockData = WrappedBlockData.fromString(key); WrappedBlockData wrappedBlockData = WrappedBlockData.fromString(key);
for (ConnectorInitAction action : initActions) { for (ConnectorInitAction action : initActions) {
action.check(wrappedBlockData); action.check(wrappedBlockData);
@ -223,11 +252,7 @@ public final class ConnectionData {
} }
public static int getId(String key) { public static int getId(String key) {
return keyToId.getOrDefault(Key.stripMinecraftNamespace(key), -1); return KEY_TO_ID.getOrDefault(Key.stripMinecraftNamespace(key), -1);
}
public static String getKey(int id) {
return idToKey.get(id);
} }
private static String[] occludingBlockStates() { private static String[] occludingBlockStates() {
@ -619,10 +644,10 @@ public final class ConnectionData {
} }
public static Object2IntMap<String> getKeyToId() { public static Object2IntMap<String> getKeyToId() {
return keyToId; return KEY_TO_ID;
} }
public static class NeighbourUpdater { public static final class NeighbourUpdater {
private final UserConnection user; private final UserConnection user;
private final UserBlockData userBlockData; private final UserBlockData userBlockData;
@ -701,8 +726,9 @@ public final class ConnectionData {
Position pos = new Position(x, y, z); Position pos = new Position(x, y, z);
int newBlockState = handler.connect(user, pos, blockState); int newBlockState = handler.connect(user, pos, blockState);
if (blockState != newBlockState) { if (blockState != newBlockState || !blockConnectionProvider.storesBlocks()) {
records.add(new BlockChangeRecord1_8(x & 0xF, y, z & 0xF, newBlockState)); records.add(new BlockChangeRecord1_8(x & 0xF, y, z & 0xF, newBlockState));
updateBlockStorage(user, x, y, z, newBlockState);
} }
} }
} }

View File

@ -17,16 +17,14 @@
*/ */
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections; package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
public abstract class ConnectionHandler { public abstract class ConnectionHandler {
public abstract int connect(UserConnection user, Position position, int blockState); public abstract int connect(UserConnection user, Position position, int blockState);
public int getBlockData(UserConnection user, Position position) { public int getBlockData(UserConnection user, Position position) {
return Via.getManager().getProviders().get(BlockConnectionProvider.class).getBlockData(user, position.x(), position.y(), position.z()); return ConnectionData.blockConnectionProvider.getBlockData(user, position.x(), position.y(), position.z());
} }
} }

View File

@ -20,6 +20,8 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -27,8 +29,8 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
public class DoorConnectionHandler extends ConnectionHandler { public class DoorConnectionHandler extends ConnectionHandler {
private static final Map<Integer, DoorData> doorDataMap = new HashMap<>(); private static final Int2ObjectMap<DoorData> DOOR_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<Short, Integer> connectedStates = new HashMap<>(); private static final Map<Short, Integer> CONNECTED_STATES = new HashMap<>();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final List<String> baseDoors = new LinkedList<>(); final List<String> baseDoors = new LinkedList<>();
@ -56,9 +58,9 @@ public class DoorConnectionHandler extends ConnectionHandler {
type type
); );
doorDataMap.put(id, doorData); DOOR_DATA_MAP.put(id, doorData);
connectedStates.put(getStates(doorData), id); CONNECTED_STATES.put(getStates(doorData), id);
ConnectionData.connectionHandlerMap.put(id, connectionHandler); ConnectionData.connectionHandlerMap.put(id, connectionHandler);
}; };
@ -77,12 +79,12 @@ public class DoorConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
DoorData doorData = doorDataMap.get(blockState); DoorData doorData = DOOR_DATA_MAP.get(blockState);
if (doorData == null) return blockState; if (doorData == null) return blockState;
short s = 0; short s = 0;
s |= (doorData.getType() & 0x7) << 6; s |= (doorData.getType() & 0x7) << 6;
if (doorData.isLower()) { if (doorData.isLower()) {
DoorData upperHalf = doorDataMap.get(getBlockData(user, position.getRelative(BlockFace.TOP))); DoorData upperHalf = DOOR_DATA_MAP.get(getBlockData(user, position.getRelative(BlockFace.TOP)));
if (upperHalf == null) return blockState; if (upperHalf == null) return blockState;
s |= 1; s |= 1;
if (doorData.isOpen()) s |= 2; if (doorData.isOpen()) s |= 2;
@ -90,7 +92,7 @@ public class DoorConnectionHandler extends ConnectionHandler {
if (upperHalf.isRightHinge()) s |= 8; if (upperHalf.isRightHinge()) s |= 8;
s |= doorData.getFacing().ordinal() << 4; s |= doorData.getFacing().ordinal() << 4;
} else { } else {
DoorData lowerHalf = doorDataMap.get(getBlockData(user, position.getRelative(BlockFace.BOTTOM))); DoorData lowerHalf = DOOR_DATA_MAP.get(getBlockData(user, position.getRelative(BlockFace.BOTTOM)));
if (lowerHalf == null) return blockState; if (lowerHalf == null) return blockState;
if (lowerHalf.isOpen()) s |= 2; if (lowerHalf.isOpen()) s |= 2;
if (doorData.isPowered()) s |= 4; if (doorData.isPowered()) s |= 4;
@ -98,7 +100,7 @@ public class DoorConnectionHandler extends ConnectionHandler {
s |= lowerHalf.getFacing().ordinal() << 4; s |= lowerHalf.getFacing().ordinal() << 4;
} }
Integer newBlockState = connectedStates.get(s); Integer newBlockState = CONNECTED_STATES.get(s);
return newBlockState == null ? blockState : newBlockState; return newBlockState == null ? blockState : newBlockState;
} }

View File

@ -20,15 +20,15 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class FireConnectionHandler extends ConnectionHandler { public class FireConnectionHandler extends ConnectionHandler {
private static final String[] WOOD_TYPES = {"oak", "spruce", "birch", "jungle", "acacia", "dark_oak"}; private static final String[] WOOD_TYPES = {"oak", "spruce", "birch", "jungle", "acacia", "dark_oak"};
private static final Map<Byte, Integer> connectedBlocks = new HashMap<>(); private static final int[] CONNECTED_BLOCKS = new int[32];
private static final Set<Integer> flammableBlocks = new HashSet<>(); private static final IntSet FLAMMABLE_BLOCKS = new IntOpenHashSet();
private static void addWoodTypes(Set<String> set, String suffix) { private static void addWoodTypes(Set<String> set, String suffix) {
for (String woodType : WOOD_TYPES) { for (String woodType : WOOD_TYPES) {
@ -55,10 +55,10 @@ public class FireConnectionHandler extends ConnectionHandler {
return blockData -> { return blockData -> {
String key = blockData.getMinecraftKey(); String key = blockData.getMinecraftKey();
if (key.contains("_wool") || key.contains("_carpet") || flammabeIds.contains(key)) { if (key.contains("_wool") || key.contains("_carpet") || flammabeIds.contains(key)) {
flammableBlocks.add(blockData.getSavedBlockStateId()); FLAMMABLE_BLOCKS.add(blockData.getSavedBlockStateId());
} else if (key.equals("minecraft:fire")) { } else if (key.equals("minecraft:fire")) {
int id = blockData.getSavedBlockStateId(); int id = blockData.getSavedBlockStateId();
connectedBlocks.put(getStates(blockData), id); CONNECTED_BLOCKS[getStates(blockData)] = id;
ConnectionData.connectionHandlerMap.put(id, connectionHandler); ConnectionData.connectionHandlerMap.put(id, connectionHandler);
} }
}; };
@ -77,11 +77,11 @@ public class FireConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
byte states = 0; byte states = 0;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.EAST)))) states |= 1; if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.EAST)))) states |= 1;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.NORTH)))) states |= 2; if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.NORTH)))) states |= 2;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.SOUTH)))) states |= 4; if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.SOUTH)))) states |= 4;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) states |= 8; if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) states |= 8;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.WEST)))) states |= 16; if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.WEST)))) states |= 16;
return connectedBlocks.get(states); return CONNECTED_BLOCKS[states];
} }
} }

View File

@ -28,7 +28,7 @@ import java.util.Set;
public class FlowerConnectionHandler extends ConnectionHandler { public class FlowerConnectionHandler extends ConnectionHandler {
private static final Int2IntMap flowers = new Int2IntOpenHashMap(); private static final Int2IntMap FLOWERS = new Int2IntOpenHashMap();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final Set<String> baseFlower = new HashSet<>(); final Set<String> baseFlower = new HashSet<>();
@ -45,7 +45,7 @@ public class FlowerConnectionHandler extends ConnectionHandler {
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler);
if (blockData.getValue("half").equals("lower")) { if (blockData.getValue("half").equals("lower")) {
blockData.set("half", "upper"); blockData.set("half", "upper");
flowers.put(blockData.getSavedBlockStateId(), blockData.getBlockStateId()); FLOWERS.put(blockData.getSavedBlockStateId(), blockData.getBlockStateId());
} }
} }
}; };
@ -54,14 +54,14 @@ public class FlowerConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
int blockBelowId = getBlockData(user, position.getRelative(BlockFace.BOTTOM)); int blockBelowId = getBlockData(user, position.getRelative(BlockFace.BOTTOM));
int connectBelow = flowers.get(blockBelowId); int connectBelow = FLOWERS.get(blockBelowId);
if (connectBelow != 0) { if (connectBelow != 0) {
int blockAboveId = getBlockData(user, position.getRelative(BlockFace.TOP)); int blockAboveId = getBlockData(user, position.getRelative(BlockFace.TOP));
if (Via.getConfig().isStemWhenBlockAbove()) { if (Via.getConfig().isStemWhenBlockAbove()) {
if (blockAboveId == 0) { if (blockAboveId == 0) {
return connectBelow; return connectBelow;
} }
} else if (!flowers.containsKey(blockAboveId)) { } else if (!FLOWERS.containsKey(blockAboveId)) {
return connectBelow; return connectBelow;
} }
} }

View File

@ -27,24 +27,24 @@ public class GlassConnectionHandler extends AbstractFenceConnectionHandler {
static List<ConnectionData.ConnectorInitAction> init() { static List<ConnectionData.ConnectorInitAction> init() {
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(18); List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(18);
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:white_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:white_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:orange_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:orange_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:magenta_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:magenta_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:light_blue_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:light_blue_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:yellow_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:yellow_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:lime_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:lime_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:pink_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:pink_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:gray_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:gray_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:light_gray_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:light_gray_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:cyan_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:cyan_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:purple_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:purple_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:blue_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:blue_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:brown_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:brown_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:green_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:green_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:red_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:red_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:black_stained_glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:black_stained_glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:glass_pane")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:glass_pane"));
actions.add(new GlassConnectionHandler("paneConnections").getInitAction("minecraft:iron_bars")); actions.add(new GlassConnectionHandler("pane").getInitAction("minecraft:iron_bars"));
return actions; return actions;
} }

View File

@ -20,7 +20,7 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
public class NetherFenceConnectionHandler extends AbstractFenceConnectionHandler { public class NetherFenceConnectionHandler extends AbstractFenceConnectionHandler {
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
return new NetherFenceConnectionHandler("netherFenceConnections").getInitAction("minecraft:nether_brick_fence"); return new NetherFenceConnectionHandler("netherFence").getInitAction("minecraft:nether_brick_fence");
} }
public NetherFenceConnectionHandler(String blockConnections) { public NetherFenceConnectionHandler(String blockConnections) {

View File

@ -22,23 +22,27 @@ import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import java.util.HashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.Set; import it.unimi.dsi.fastutil.ints.IntSet;
public class RedstoneConnectionHandler extends ConnectionHandler { public class RedstoneConnectionHandler extends ConnectionHandler {
private static final Set<Integer> redstone = new HashSet<>(); private static final IntSet REDSTONE = new IntOpenHashSet();
private static final Int2IntMap connectedBlockStates = new Int2IntOpenHashMap(1296); private static final Int2IntMap CONNECTED_BLOCK_STATES = new Int2IntOpenHashMap(1296);
private static final Int2IntMap powerMappings = new Int2IntOpenHashMap(1296); private static final Int2IntMap POWER_MAPPINGS = new Int2IntOpenHashMap(1296);
private static final int BLOCK_CONNECTION_TYPE_ID = BlockData.connectionTypeId("redstone");
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final RedstoneConnectionHandler connectionHandler = new RedstoneConnectionHandler(); final RedstoneConnectionHandler connectionHandler = new RedstoneConnectionHandler();
final String redstoneKey = "minecraft:redstone_wire"; final String redstoneKey = "minecraft:redstone_wire";
return blockData -> { return blockData -> {
if (!redstoneKey.equals(blockData.getMinecraftKey())) return; if (!redstoneKey.equals(blockData.getMinecraftKey())) {
redstone.add(blockData.getSavedBlockStateId()); return;
}
REDSTONE.add(blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
connectedBlockStates.put(getStates(blockData), blockData.getSavedBlockStateId()); CONNECTED_BLOCK_STATES.put(getStates(blockData), blockData.getSavedBlockStateId());
powerMappings.put(blockData.getSavedBlockStateId(), Integer.parseInt(blockData.getValue("power"))); POWER_MAPPINGS.put(blockData.getSavedBlockStateId(), Integer.parseInt(blockData.getValue("power")));
}; };
} }
@ -72,8 +76,8 @@ public class RedstoneConnectionHandler extends ConnectionHandler {
b |= connects(user, position, BlockFace.NORTH) << 2; b |= connects(user, position, BlockFace.NORTH) << 2;
b |= connects(user, position, BlockFace.SOUTH) << 4; b |= connects(user, position, BlockFace.SOUTH) << 4;
b |= connects(user, position, BlockFace.WEST) << 6; b |= connects(user, position, BlockFace.WEST) << 6;
b |= powerMappings.get(blockState) << 8; b |= POWER_MAPPINGS.get(blockState) << 8;
return connectedBlockStates.getOrDefault(b, blockState); return CONNECTED_BLOCK_STATES.getOrDefault(b, blockState);
} }
private int connects(UserConnection user, Position position, BlockFace side) { private int connects(UserConnection user, Position position, BlockFace side) {
@ -83,11 +87,11 @@ public class RedstoneConnectionHandler extends ConnectionHandler {
return 1; //side return 1; //side
} }
int up = getBlockData(user, relative.getRelative(BlockFace.TOP)); int up = getBlockData(user, relative.getRelative(BlockFace.TOP));
if (redstone.contains(up) && !ConnectionData.occludingStates.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) { if (REDSTONE.contains(up) && !ConnectionData.OCCLUDING_STATES.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) {
return 2; //"up" return 2; //"up"
} }
int down = getBlockData(user, relative.getRelative(BlockFace.BOTTOM)); int down = getBlockData(user, relative.getRelative(BlockFace.BOTTOM));
if (redstone.contains(down) && !ConnectionData.occludingStates.contains(getBlockData(user, relative))) { if (REDSTONE.contains(down) && !ConnectionData.OCCLUDING_STATES.contains(getBlockData(user, relative))) {
return 1; //side return 1; //side
} }
return 0; //none return 0; //none
@ -95,6 +99,6 @@ public class RedstoneConnectionHandler extends ConnectionHandler {
private boolean connects(BlockFace side, int blockState) { private boolean connects(BlockFace side, int blockState) {
final BlockData blockData = ConnectionData.blockConnectionData.get(blockState); final BlockData blockData = ConnectionData.blockConnectionData.get(blockState);
return blockData != null && blockData.connectsTo("redstoneConnections", side.opposite(), false); return blockData != null && blockData.connectsTo(BLOCK_CONNECTION_TYPE_ID, side.opposite(), false);
} }
} }

View File

@ -20,15 +20,16 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.util.Pair; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class SnowyGrassConnectionHandler extends ConnectionHandler { public class SnowyGrassConnectionHandler extends ConnectionHandler {
private static final Map<Pair<Integer, Boolean>, Integer> grassBlocks = new HashMap<>(); private static final Object2IntMap<GrassBlock> GRASS_BLOCKS = new Object2IntOpenHashMap<>();
private static final Set<Integer> snows = new HashSet<>(); private static final IntSet SNOWY_GRASS_BLOCKS = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final Set<String> snowyGrassBlocks = new HashSet<>(); final Set<String> snowyGrassBlocks = new HashSet<>();
@ -36,18 +37,19 @@ public class SnowyGrassConnectionHandler extends ConnectionHandler {
snowyGrassBlocks.add("minecraft:podzol"); snowyGrassBlocks.add("minecraft:podzol");
snowyGrassBlocks.add("minecraft:mycelium"); snowyGrassBlocks.add("minecraft:mycelium");
GRASS_BLOCKS.defaultReturnValue(-1);
final SnowyGrassConnectionHandler handler = new SnowyGrassConnectionHandler(); final SnowyGrassConnectionHandler handler = new SnowyGrassConnectionHandler();
return blockData -> { return blockData -> {
if (snowyGrassBlocks.contains(blockData.getMinecraftKey())) { if (snowyGrassBlocks.contains(blockData.getMinecraftKey())) {
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler);
blockData.set("snowy", "true"); blockData.set("snowy", "true");
grassBlocks.put(new Pair<>(blockData.getSavedBlockStateId(), true), blockData.getBlockStateId()); GRASS_BLOCKS.put(new GrassBlock(blockData.getSavedBlockStateId(), true), blockData.getBlockStateId());
blockData.set("snowy", "false"); blockData.set("snowy", "false");
grassBlocks.put(new Pair<>(blockData.getSavedBlockStateId(), false), blockData.getBlockStateId()); GRASS_BLOCKS.put(new GrassBlock(blockData.getSavedBlockStateId(), false), blockData.getBlockStateId());
} }
if (blockData.getMinecraftKey().equals("minecraft:snow") || blockData.getMinecraftKey().equals("minecraft:snow_block")) { if (blockData.getMinecraftKey().equals("minecraft:snow") || blockData.getMinecraftKey().equals("minecraft:snow_block")) {
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler);
snows.add(blockData.getSavedBlockStateId()); SnowyGrassConnectionHandler.SNOWY_GRASS_BLOCKS.add(blockData.getSavedBlockStateId());
} }
}; };
} }
@ -55,10 +57,33 @@ public class SnowyGrassConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
int blockUpId = getBlockData(user, position.getRelative(BlockFace.TOP)); int blockUpId = getBlockData(user, position.getRelative(BlockFace.TOP));
Integer newId = grassBlocks.get(new Pair<>(blockState, snows.contains(blockUpId))); int newId = GRASS_BLOCKS.getInt(new GrassBlock(blockState, SNOWY_GRASS_BLOCKS.contains(blockUpId)));
if (newId != null) { return newId != -1 ? newId : blockState;
return newId; }
private static final class GrassBlock {
private final int blockStateId;
private final boolean snowy;
private GrassBlock(final int blockStateId, final boolean snowy) {
this.blockStateId = blockStateId;
this.snowy = snowy;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GrassBlock that = (GrassBlock) o;
if (blockStateId != that.blockStateId) return false;
return snowy == that.snowy;
}
@Override
public int hashCode() {
int result = blockStateId;
result = 31 * result + (snowy ? 1 : 0);
return result;
} }
return blockState;
} }
} }

View File

@ -20,6 +20,8 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -27,8 +29,8 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
public class StairConnectionHandler extends ConnectionHandler { public class StairConnectionHandler extends ConnectionHandler {
private static final Map<Integer, StairData> stairDataMap = new HashMap<>(); private static final Int2ObjectMap<StairData> STAIR_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<Short, Integer> connectedBlocks = new HashMap<>(); private static final Map<Short, Integer> CONNECTED_BLOCKS = new HashMap<>();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final List<String> baseStairs = new LinkedList<>(); final List<String> baseStairs = new LinkedList<>();
@ -84,8 +86,8 @@ public class StairConnectionHandler extends ConnectionHandler {
BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT)) BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT))
); );
stairDataMap.put(blockData.getSavedBlockStateId(), stairData); STAIR_DATA_MAP.put(blockData.getSavedBlockStateId(), stairData);
connectedBlocks.put(getStates(stairData), blockData.getSavedBlockStateId()); CONNECTED_BLOCKS.put(getStates(stairData), blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
}; };
@ -102,7 +104,7 @@ public class StairConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
StairData stairData = stairDataMap.get(blockState); StairData stairData = STAIR_DATA_MAP.get(blockState);
if (stairData == null) return blockState; if (stairData == null) return blockState;
short s = 0; short s = 0;
@ -111,14 +113,14 @@ public class StairConnectionHandler extends ConnectionHandler {
s |= stairData.getType() << 4; s |= stairData.getType() << 4;
s |= stairData.getFacing().ordinal() << 9; s |= stairData.getFacing().ordinal() << 9;
Integer newBlockState = connectedBlocks.get(s); Integer newBlockState = CONNECTED_BLOCKS.get(s);
return newBlockState == null ? blockState : newBlockState; return newBlockState == null ? blockState : newBlockState;
} }
private int getShape(UserConnection user, Position position, StairData stair) { private int getShape(UserConnection user, Position position, StairData stair) {
BlockFace facing = stair.getFacing(); BlockFace facing = stair.getFacing();
StairData relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(facing))); StairData relativeStair = STAIR_DATA_MAP.get(getBlockData(user, position.getRelative(facing)));
if (relativeStair != null && relativeStair.isBottom() == stair.isBottom()) { if (relativeStair != null && relativeStair.isBottom() == stair.isBottom()) {
BlockFace facing2 = relativeStair.getFacing(); BlockFace facing2 = relativeStair.getFacing();
if (facing.axis() != facing2.axis() && checkOpposite(user, stair, position, facing2.opposite())) { if (facing.axis() != facing2.axis() && checkOpposite(user, stair, position, facing2.opposite())) {
@ -126,7 +128,7 @@ public class StairConnectionHandler extends ConnectionHandler {
} }
} }
relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(facing.opposite()))); relativeStair = STAIR_DATA_MAP.get(getBlockData(user, position.getRelative(facing.opposite())));
if (relativeStair != null && relativeStair.isBottom() == stair.isBottom()) { if (relativeStair != null && relativeStair.isBottom() == stair.isBottom()) {
BlockFace facing2 = relativeStair.getFacing(); BlockFace facing2 = relativeStair.getFacing();
if (facing.axis() != facing2.axis() && checkOpposite(user, stair, position, facing2)) { if (facing.axis() != facing2.axis() && checkOpposite(user, stair, position, facing2)) {
@ -138,7 +140,7 @@ public class StairConnectionHandler extends ConnectionHandler {
} }
private boolean checkOpposite(UserConnection user, StairData stair, Position position, BlockFace face) { private boolean checkOpposite(UserConnection user, StairData stair, Position position, BlockFace face) {
StairData relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(face))); StairData relativeStair = STAIR_DATA_MAP.get(getBlockData(user, position.getRelative(face)));
return relativeStair == null || relativeStair.getFacing() != stair.getFacing() || relativeStair.isBottom() != stair.isBottom(); return relativeStair == null || relativeStair.getFacing() != stair.getFacing() || relativeStair.isBottom() != stair.isBottom();
} }

View File

@ -20,20 +20,23 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
public class TripwireConnectionHandler extends ConnectionHandler { public class TripwireConnectionHandler extends ConnectionHandler {
private static final Map<Integer, TripwireData> tripwireDataMap = new HashMap<>(); private static final Int2ObjectMap<TripwireData> TRIPWIRE_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<Byte, Integer> connectedBlocks = new HashMap<>(); private static final Int2ObjectMap<BlockFace> TRIPWIRE_HOOKS = new Int2ObjectArrayMap<>();
private static final Map<Integer, BlockFace> tripwireHooks = new HashMap<>(); private static final int[] CONNECTED_BLOCKS = new int[128];
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
Arrays.fill(CONNECTED_BLOCKS, -1);
final TripwireConnectionHandler connectionHandler = new TripwireConnectionHandler(); final TripwireConnectionHandler connectionHandler = new TripwireConnectionHandler();
return blockData -> { return blockData -> {
if (blockData.getMinecraftKey().equals("minecraft:tripwire_hook")) { if (blockData.getMinecraftKey().equals("minecraft:tripwire_hook")) {
tripwireHooks.put(blockData.getSavedBlockStateId(), BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT))); TRIPWIRE_HOOKS.put(blockData.getSavedBlockStateId(), BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT)));
} else if (blockData.getMinecraftKey().equals("minecraft:tripwire")) { } else if (blockData.getMinecraftKey().equals("minecraft:tripwire")) {
TripwireData tripwireData = new TripwireData( TripwireData tripwireData = new TripwireData(
blockData.getValue("attached").equals("true"), blockData.getValue("attached").equals("true"),
@ -41,8 +44,8 @@ public class TripwireConnectionHandler extends ConnectionHandler {
blockData.getValue("powered").equals("true") blockData.getValue("powered").equals("true")
); );
tripwireDataMap.put(blockData.getSavedBlockStateId(), tripwireData); TRIPWIRE_DATA_MAP.put(blockData.getSavedBlockStateId(), tripwireData);
connectedBlocks.put(getStates(blockData), blockData.getSavedBlockStateId()); CONNECTED_BLOCKS[getStates(blockData)] = blockData.getSavedBlockStateId();
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
} }
@ -63,7 +66,7 @@ public class TripwireConnectionHandler extends ConnectionHandler {
@Override @Override
public int connect(UserConnection user, Position position, int blockState) { public int connect(UserConnection user, Position position, int blockState) {
TripwireData tripwireData = tripwireDataMap.get(blockState); TripwireData tripwireData = TRIPWIRE_DATA_MAP.get(blockState);
if (tripwireData == null) return blockState; if (tripwireData == null) return blockState;
byte b = 0; byte b = 0;
if (tripwireData.isAttached()) b |= 1; if (tripwireData.isAttached()) b |= 1;
@ -75,21 +78,21 @@ public class TripwireConnectionHandler extends ConnectionHandler {
int south = getBlockData(user, position.getRelative(BlockFace.SOUTH)); int south = getBlockData(user, position.getRelative(BlockFace.SOUTH));
int west = getBlockData(user, position.getRelative(BlockFace.WEST)); int west = getBlockData(user, position.getRelative(BlockFace.WEST));
if (tripwireDataMap.containsKey(east) || tripwireHooks.get(east) == BlockFace.WEST) { if (TRIPWIRE_DATA_MAP.containsKey(east) || TRIPWIRE_HOOKS.get(east) == BlockFace.WEST) {
b |= 8; b |= 8;
} }
if (tripwireDataMap.containsKey(north) || tripwireHooks.get(north) == BlockFace.SOUTH) { if (TRIPWIRE_DATA_MAP.containsKey(north) || TRIPWIRE_HOOKS.get(north) == BlockFace.SOUTH) {
b |= 16; b |= 16;
} }
if (tripwireDataMap.containsKey(south) || tripwireHooks.get(south) == BlockFace.NORTH) { if (TRIPWIRE_DATA_MAP.containsKey(south) || TRIPWIRE_HOOKS.get(south) == BlockFace.NORTH) {
b |= 32; b |= 32;
} }
if (tripwireDataMap.containsKey(west) || tripwireHooks.get(west) == BlockFace.EAST) { if (TRIPWIRE_DATA_MAP.containsKey(west) || TRIPWIRE_HOOKS.get(west) == BlockFace.EAST) {
b |= 64; b |= 64;
} }
Integer newBlockState = connectedBlocks.get(b); int newBlockState = CONNECTED_BLOCKS[b];
return newBlockState == null ? blockState : newBlockState; return newBlockState == -1 ? blockState : newBlockState;
} }
private static final class TripwireData { private static final class TripwireData {

View File

@ -20,18 +20,18 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockFace; import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position; import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.Set; import it.unimi.dsi.fastutil.ints.IntSet;
class VineConnectionHandler extends ConnectionHandler { class VineConnectionHandler extends ConnectionHandler {
private static final Set<Integer> vines = new HashSet<>(); private static final IntSet VINES = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() { static ConnectionData.ConnectorInitAction init() {
final VineConnectionHandler connectionHandler = new VineConnectionHandler(); final VineConnectionHandler connectionHandler = new VineConnectionHandler();
return blockData -> { return blockData -> {
if (!blockData.getMinecraftKey().equals("minecraft:vine")) return; if (!blockData.getMinecraftKey().equals("minecraft:vine")) return;
vines.add(blockData.getSavedBlockStateId()); VINES.add(blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler); ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
}; };
} }
@ -42,7 +42,7 @@ class VineConnectionHandler extends ConnectionHandler {
Position upperPos = position.getRelative(BlockFace.TOP); Position upperPos = position.getRelative(BlockFace.TOP);
int upperBlock = getBlockData(user, upperPos); int upperBlock = getBlockData(user, upperPos);
if (vines.contains(upperBlock) && isAttachedToBlock(user, upperPos)) return blockState; if (VINES.contains(upperBlock) && isAttachedToBlock(user, upperPos)) return blockState;
// Map to air if not attached to block, and upper block is also not a vine attached to a block // Map to air if not attached to block, and upper block is also not a vine attached to a block
return 0; return 0;
@ -56,6 +56,6 @@ class VineConnectionHandler extends ConnectionHandler {
} }
private boolean isAttachedToBlock(UserConnection user, Position position, BlockFace blockFace) { private boolean isAttachedToBlock(UserConnection user, Position position, BlockFace blockFace) {
return ConnectionData.occludingStates.contains(getBlockData(user, position.getRelative(blockFace))); return ConnectionData.OCCLUDING_STATES.contains(getBlockData(user, position.getRelative(blockFace)));
} }
} }

View File

@ -29,8 +29,8 @@ public class WallConnectionHandler extends AbstractFenceConnectionHandler {
static List<ConnectionData.ConnectorInitAction> init() { static List<ConnectionData.ConnectorInitAction> init() {
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(2); List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(2);
actions.add(new WallConnectionHandler("cobbleWallConnections").getInitAction("minecraft:cobblestone_wall")); actions.add(new WallConnectionHandler("cobbleWall").getInitAction("minecraft:cobblestone_wall"));
actions.add(new WallConnectionHandler("cobbleWallConnections").getInitAction("minecraft:mossy_cobblestone_wall")); actions.add(new WallConnectionHandler("cobbleWall").getInitAction("minecraft:mossy_cobblestone_wall"));
return actions; return actions;
} }
@ -46,12 +46,18 @@ public class WallConnectionHandler extends AbstractFenceConnectionHandler {
return states; return states;
} }
@Override
protected byte getStates(UserConnection user, Position position, int blockState) { protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState); byte states = super.getStates(user, position, blockState);
if (up(user, position)) states |= 16; if (up(user, position)) states |= 16;
return states; return states;
} }
@Override
protected byte statesSize() {
return 32;
}
public boolean up(UserConnection user, Position position) { public boolean up(UserConnection user, Position position) {
if (isWall(getBlockData(user, position.getRelative(BlockFace.BOTTOM))) || isWall(getBlockData(user, position.getRelative(BlockFace.TOP)))) if (isWall(getBlockData(user, position.getRelative(BlockFace.BOTTOM))) || isWall(getBlockData(user, position.getRelative(BlockFace.TOP))))
return true; return true;

View File

@ -17,15 +17,14 @@
*/ */
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections; package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.util.Key; import com.viaversion.viaversion.util.Key;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map.Entry; import java.util.Map.Entry;
public class WrappedBlockData { public final class WrappedBlockData {
private final LinkedHashMap<String, String> blockData = new LinkedHashMap<>();
private final String minecraftKey; private final String minecraftKey;
private final int savedBlockStateId; private final int savedBlockStateId;
private final LinkedHashMap<String, String> blockData = new LinkedHashMap<>();
public static WrappedBlockData fromString(String s) { public static WrappedBlockData fromString(String s) {
String[] array = s.split("\\["); String[] array = s.split("\\[");
@ -43,15 +42,6 @@ public class WrappedBlockData {
return wrappedBlockdata; return wrappedBlockdata;
} }
public static WrappedBlockData fromStateId(int id) {
String blockData = ConnectionData.getKey(id);
if (blockData != null) {
return fromString(blockData);
}
Via.getPlatform().getLogger().info("Unable to get blockdata from " + id);
return fromString("minecraft:air");
}
private WrappedBlockData(String minecraftKey, int savedBlockStateId) { private WrappedBlockData(String minecraftKey, int savedBlockStateId) {
this.minecraftKey = Key.namespaced(minecraftKey); this.minecraftKey = Key.namespaced(minecraftKey);
this.savedBlockStateId = savedBlockStateId; this.savedBlockStateId = savedBlockStateId;

View File

@ -59,6 +59,7 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
@Override @Override
public UserBlockData forUser(UserConnection connection) { public UserBlockData forUser(UserConnection connection) {
return connection.get(BlockConnectionStorage.class)::get; final BlockConnectionStorage storage = connection.get(BlockConnectionStorage.class);
return (x, y, z) -> storage.get(x, y, z);
} }
} }

View File

@ -17,8 +17,8 @@
*/ */
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers; package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers;
@FunctionalInterface
public interface UserBlockData { public interface UserBlockData {
int getBlockData(int x, int y, int z); int getBlockData(int x, int y, int z);
} }

View File

@ -183,18 +183,18 @@ public class WorldPackets {
UserConnection userConnection = wrapper.user(); UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) { if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
newId = ConnectionData.connect(userConnection, position, newId); newId = ConnectionData.connect(userConnection, position, newId);
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
} }
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId)); wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
if (Via.getConfig().isServersideBlockConnections()) { if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue // Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class); wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel(); wrapper.cancel();
ConnectionData.update(userConnection, position); ConnectionData.update(userConnection, position);
} }
}); });
} }
}); });
@ -214,14 +214,14 @@ public class WorldPackets {
for (BlockChangeRecord record : records) { for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId()); int newBlock = toNewId(record.getBlockId());
Position position = new Position( Position position = new Position(
record.getSectionX() + (chunkX * 16), record.getSectionX() + (chunkX << 4),
record.getY(), record.getY(),
record.getSectionZ() + (chunkZ * 16)); record.getSectionZ() + (chunkZ << 4));
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
if (Via.getConfig().isServersideBlockConnections()) { if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock); ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
} }
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
} }
if (Via.getConfig().isServersideBlockConnections()) { if (Via.getConfig().isServersideBlockConnections()) {
@ -237,8 +237,10 @@ public class WorldPackets {
if (handler != null) { if (handler != null) {
blockState = handler.connect(userConnection, position, blockState); blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState); record.setBlockId(blockState);
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), blockState);
} }
} }
// Workaround for packet order issue // Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class); wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel(); wrapper.cancel();
@ -251,7 +253,6 @@ public class WorldPackets {
ConnectionData.update(userConnection, position); ConnectionData.update(userConnection, position);
} }
} }
}); });
} }
}); });
@ -358,22 +359,25 @@ public class WorldPackets {
} }
for (int idx = 0; idx < ChunkSection.SIZE; idx++) { for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx); int id = blocks.idAt(idx);
Position position = new Position(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), ChunkSection.yFromIndex(idx) + (s << 4), ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4));
if (storage.isWelcome(id)) { if (storage.isWelcome(id)) {
storage.store(new Position(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), ChunkSection.yFromIndex(idx) + (s << 4), ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4)), id); storage.store(position, id);
} else if (!chunk.isFullChunk()) { // Update } else if (!chunk.isFullChunk()) { // Update
storage.remove(new Position(ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), ChunkSection.yFromIndex(idx) + (s << 4), ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4))); storage.remove(position);
} }
} }
} }
save_connections: save_connections:
{ {
if (!Via.getConfig().isServersideBlockConnections() || !ConnectionData.needStoreBlocks()) if (!Via.getConfig().isServersideBlockConnections() || !ConnectionData.needStoreBlocks()) {
break save_connections; break save_connections;
}
if (!chunk.isFullChunk()) { // Update if (!chunk.isFullChunk()) { // Update
ConnectionData.blockConnectionProvider.unloadChunkSection(wrapper.user(), chunk.getX(), s, chunk.getZ()); ConnectionData.blockConnectionProvider.unloadChunkSection(wrapper.user(), chunk.getX(), s, chunk.getZ());
} }
boolean willSave = false; boolean willSave = false;
for (int p = 0; p < blocks.size(); p++) { for (int p = 0; p < blocks.size(); p++) {
if (ConnectionData.isWelcome(blocks.idByIndex(p))) { if (ConnectionData.isWelcome(blocks.idByIndex(p))) {
@ -381,7 +385,9 @@ public class WorldPackets {
break; break;
} }
} }
if (!willSave) break save_connections; if (!willSave) {
break save_connections;
}
for (int idx = 0; idx < ChunkSection.SIZE; idx++) { for (int idx = 0; idx < ChunkSection.SIZE; idx++) {
int id = blocks.idAt(idx); int id = blocks.idAt(idx);

View File

@ -19,18 +19,13 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.storage;
import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.StorableObject; import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.WorldPackets;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockConnectionStorage implements StorableObject { public class BlockConnectionStorage implements StorableObject {
private static final short[] REVERSE_BLOCK_MAPPINGS = new short[8582];
private static Constructor<?> fastUtilLongObjectHashMap; private static Constructor<?> fastUtilLongObjectHashMap;
private final Map<Long, SectionData> blockStorage = createLongObjectMap(); private final Map<Long, SectionData> blockStorage = createLongObjectMap();
@ -47,68 +42,50 @@ public class BlockConnectionStorage implements StorableObject {
Via.getPlatform().getLogger().info("Using FastUtil Long2ObjectOpenHashMap for block connections"); Via.getPlatform().getLogger().info("Using FastUtil Long2ObjectOpenHashMap for block connections");
} catch (ClassNotFoundException | NoSuchMethodException ignored) { } catch (ClassNotFoundException | NoSuchMethodException ignored) {
} }
Arrays.fill(REVERSE_BLOCK_MAPPINGS, (short) -1);
for (int i = 0; i < 4096; i++) {
int newBlock = Protocol1_13To1_12_2.MAPPINGS.getBlockMappings().getNewId(i);
if (newBlock != -1) {
REVERSE_BLOCK_MAPPINGS[newBlock] = (short) i;
}
}
} }
public static void init() { public static void init() {
} }
public void store(int x, int y, int z, int blockState) { public void store(int x, int y, int z, int blockState) {
short mapping = REVERSE_BLOCK_MAPPINGS[blockState]; long index = getChunkSectionIndex(x, y, z);
if (mapping == -1) return; SectionData section = getSection(index);
if (section == null) {
blockState = mapping; if (blockState == 0) {
long pair = getChunkSectionIndex(x, y, z); // No need to store empty sections
SectionData map = getChunkSection(pair, (blockState & 0xF) != 0); return;
int blockIndex = encodeBlockPos(x, y, z);
map.blockIds()[blockIndex] = (byte) (blockState >> 4);
NibbleArray nibbleArray = map.nibbleArray();
if (nibbleArray != null) {
nibbleArray.set(blockIndex, blockState);
} }
blockStorage.put(index, section = new SectionData());
lastSection = section;
lastIndex = index;
}
section.setBlockAt(x, y, z, blockState);
} }
public int get(int x, int y, int z) { public int get(int x, int y, int z) {
long pair = getChunkSectionIndex(x, y, z); long pair = getChunkSectionIndex(x, y, z);
SectionData map = getSection(pair); SectionData section = getSection(pair);
if (map == null) return 0; if (section == null) {
short blockPosition = encodeBlockPos(x, y, z); return 0;
NibbleArray nibbleArray = map.nibbleArray(); }
return WorldPackets.toNewId(
((map.blockIds()[blockPosition] & 0xFF) << 4) return section.blockAt(x, y, z);
| (nibbleArray == null ? 0 : nibbleArray.get(blockPosition))
);
} }
public void remove(int x, int y, int z) { public void remove(int x, int y, int z) {
long pair = getChunkSectionIndex(x, y, z); long index = getChunkSectionIndex(x, y, z);
SectionData map = getSection(pair); SectionData section = getSection(index);
if (map == null) return; if (section == null) {
int blockIndex = encodeBlockPos(x, y, z); return;
NibbleArray nibbleArray = map.nibbleArray();
if (nibbleArray != null) {
nibbleArray.set(blockIndex, 0);
boolean allZero = true;
for (int i = 0; i < 4096; i++) {
if (nibbleArray.get(i) != 0) {
allZero = false;
break;
} }
section.setBlockAt(x, y, z, 0);
if (section.nonEmptyBlocks() == 0) {
removeSection(index);
} }
if (allZero) map.setNibbleArray(null);
}
map.blockIds()[blockIndex] = 0;
for (short entry : map.blockIds()) {
if (entry != 0) return;
}
removeSection(pair);
} }
public void clear() { public void clear() {
@ -127,21 +104,7 @@ public class BlockConnectionStorage implements StorableObject {
removeSection(getChunkSectionIndex(x << 4, y << 4, z << 4)); removeSection(getChunkSectionIndex(x << 4, y << 4, z << 4));
} }
private SectionData getChunkSection(long index, boolean requireNibbleArray) { private @Nullable SectionData getSection(long 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));
}
return map;
}
private SectionData getSection(long index) {
if (lastIndex != null && lastIndex == index) { if (lastIndex != null && lastIndex == index) {
return lastSection; return lastSection;
} }
@ -157,17 +120,14 @@ public class BlockConnectionStorage implements StorableObject {
} }
} }
private long getChunkSectionIndex(int x, int y, int z) { private static long getChunkSectionIndex(int x, int y, int z) {
return (((x >> 4) & 0x3FFFFFFL) << 38) | (((y >> 4) & 0xFFFL) << 26) | ((z >> 4) & 0x3FFFFFFL); return (((x >> 4) & 0x3FFFFFFL) << 38) | (((y >> 4) & 0xFFFL) << 26) | ((z >> 4) & 0x3FFFFFFL);
} }
private short encodeBlockPos(int x, int y, int z) {
return (short) (((y & 0xF) << 8) | ((x & 0xF) << 4) | (z & 0xF));
}
private <T> Map<Long, T> createLongObjectMap() { private <T> Map<Long, T> createLongObjectMap() {
if (fastUtilLongObjectHashMap != null) { if (fastUtilLongObjectHashMap != null) {
try { try {
//noinspection unchecked
return (Map<Long, T>) fastUtilLongObjectHashMap.newInstance(); return (Map<Long, T>) fastUtilLongObjectHashMap.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace(); e.printStackTrace();
@ -177,23 +137,33 @@ public class BlockConnectionStorage implements StorableObject {
} }
private static final class SectionData { private static final class SectionData {
private final byte[] blockIds; private final short[] blockStates = new short[4096];
private NibbleArray nibbleArray; private short nonEmptyBlocks;
private SectionData(byte[] blockIds) { public int blockAt(int x, int y, int z) {
this.blockIds = blockIds; return blockStates[encodeBlockPos(x, y, z)];
} }
public byte[] blockIds() { public void setBlockAt(int x, int y, int z, int blockState) {
return blockIds; int index = encodeBlockPos(x, y, z);
if (blockState == blockStates[index]) {
return;
} }
public @Nullable NibbleArray nibbleArray() { blockStates[index] = (short) blockState;
return nibbleArray; if (blockState == 0) {
nonEmptyBlocks--;
} else {
nonEmptyBlocks++;
}
} }
public void setNibbleArray(@Nullable NibbleArray nibbleArray) { public short nonEmptyBlocks() {
this.nibbleArray = nibbleArray; return nonEmptyBlocks;
}
private static int encodeBlockPos(int x, int y, int z) {
return ((y & 0xF) << 8) | ((x & 0xF) << 4) | (z & 0xF);
} }
} }
} }