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.Position;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
private static final StairConnectionHandler STAIR_CONNECTION_HANDLER = new StairConnectionHandler();
private final String blockConnections;
private final Set<Integer> blockStates = new HashSet<>();
private final Map<Byte, Integer> connectedBlockStates = new HashMap<>();
private final IntSet blockStates = new IntOpenHashSet();
private final int[] connectedBlockStates = new int[statesSize()];
private final int blockConnectionsTypeId;
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) {
final AbstractFenceConnectionHandler handler = this;
return blockData -> {
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());
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;
}
protected byte statesSize() {
return 16;
}
@Override
public int getBlockData(UserConnection user, Position position) {
return STAIR_CONNECTION_HANDLER.connect(user, position, super.getBlockData(user, position));
@ -74,19 +82,19 @@ public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
@Override
public int connect(UserConnection user, Position position, int blockState) {
final Integer newBlockState = connectedBlockStates.get(getStates(user, position, blockState));
return newBlockState == null ? blockState : newBlockState;
final int newBlockState = connectedBlockStates[getStates(user, position, blockState)];
return newBlockState == -1 ? blockState : newBlockState;
}
protected boolean connects(BlockFace side, int blockState, boolean pre1_12) {
if (blockStates.contains(blockState)) return true;
if (blockConnections == null) return false;
if (blockConnectionsTypeId == -1) return false;
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;
}
}

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.minecraft.BlockFace;
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.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
public abstract class AbstractStempConnectionHandler extends ConnectionHandler {
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 Set<Integer> blockId = new HashSet<>();
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() {
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>();
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:oak_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:birch_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:jungle_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:dark_oak_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:acacia_fence"));
actions.add(new BasicFenceConnectionHandler("fenceConnections").getInitAction("minecraft:spruce_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:oak_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:birch_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:jungle_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:dark_oak_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:acacia_fence"));
actions.add(new BasicFenceConnectionHandler("fence").getInitAction("minecraft:spruce_fence"));
return actions;
}

View File

@ -17,25 +17,34 @@
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.minecraft.BlockFace;
import java.util.HashMap;
import java.util.Map;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.Arrays;
import java.util.List;
public class BlockData {
private final Map<String, boolean[]> connectData = new HashMap<>();
public final class BlockData {
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) {
connectData.put(key, booleans);
public void put(final int blockConnectionTypeId, final boolean[] booleans) {
connectData.put(blockConnectionTypeId, booleans);
}
public boolean connectsTo(String blockConnection, BlockFace face, boolean pre1_12AbstractFence) {
boolean[] booleans = null;
if (pre1_12AbstractFence) {
booleans = connectData.get("allFalseIfStairPre1_12"); // https://minecraft.gamepedia.com/Java_Edition_1.12
}
if (booleans == null) {
booleans = connectData.get(blockConnection);
public boolean connectsTo(final int blockConnectionTypeId, final BlockFace face, final boolean pre1_12AbstractFence) {
if (pre1_12AbstractFence && connectData.containsKey(MAGIC_STAIRS_ID)) {
return false;
}
final boolean[] booleans = connectData.get(blockConnectionTypeId);
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.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
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.Map;
import java.util.Set;
class ChestConnectionHandler extends ConnectionHandler {
private static final Map<Integer, BlockFace> chestFacings = new HashMap<>();
private static final Map<Byte, Integer> connectedStates = new HashMap<>();
private static final Set<Integer> trappedChests = new HashSet<>();
private static final Int2ObjectMap<BlockFace> CHEST_FACINGS = new Int2ObjectOpenHashMap<>();
private static final int[] CONNECTED_STATES = new int[32];
private static final IntSet TRAPPED_CHESTS = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() {
Arrays.fill(CONNECTED_STATES, -1);
final ChestConnectionHandler connectionHandler = new ChestConnectionHandler();
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;
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);
};
}
@ -58,22 +63,27 @@ class ChestConnectionHandler extends ConnectionHandler {
@Override
public int connect(UserConnection user, Position position, int blockState) {
BlockFace facing = chestFacings.get(blockState);
BlockFace facing = CHEST_FACINGS.get(blockState);
byte states = 0;
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;
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;
} 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;
} 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;
} 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;
}
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;
}
@Override
protected byte statesSize() {
return 64;
}
@Override
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState);

View File

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

View File

@ -17,16 +17,14 @@
*/
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.minecraft.Position;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
public abstract class ConnectionHandler {
public abstract int connect(UserConnection user, Position position, int blockState);
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.minecraft.BlockFace;
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.LinkedList;
import java.util.List;
@ -27,8 +29,8 @@ import java.util.Locale;
import java.util.Map;
public class DoorConnectionHandler extends ConnectionHandler {
private static final Map<Integer, DoorData> doorDataMap = new HashMap<>();
private static final Map<Short, Integer> connectedStates = new HashMap<>();
private static final Int2ObjectMap<DoorData> DOOR_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<Short, Integer> CONNECTED_STATES = new HashMap<>();
static ConnectionData.ConnectorInitAction init() {
final List<String> baseDoors = new LinkedList<>();
@ -56,9 +58,9 @@ public class DoorConnectionHandler extends ConnectionHandler {
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);
};
@ -77,12 +79,12 @@ public class DoorConnectionHandler extends ConnectionHandler {
@Override
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;
short s = 0;
s |= (doorData.getType() & 0x7) << 6;
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;
s |= 1;
if (doorData.isOpen()) s |= 2;
@ -90,7 +92,7 @@ public class DoorConnectionHandler extends ConnectionHandler {
if (upperHalf.isRightHinge()) s |= 8;
s |= doorData.getFacing().ordinal() << 4;
} 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.isOpen()) s |= 2;
if (doorData.isPowered()) s |= 4;
@ -98,7 +100,7 @@ public class DoorConnectionHandler extends ConnectionHandler {
s |= lowerHalf.getFacing().ordinal() << 4;
}
Integer newBlockState = connectedStates.get(s);
Integer newBlockState = CONNECTED_STATES.get(s);
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.minecraft.BlockFace;
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.Map;
import java.util.Set;
public class FireConnectionHandler extends ConnectionHandler {
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 Set<Integer> flammableBlocks = new HashSet<>();
private static final int[] CONNECTED_BLOCKS = new int[32];
private static final IntSet FLAMMABLE_BLOCKS = new IntOpenHashSet();
private static void addWoodTypes(Set<String> set, String suffix) {
for (String woodType : WOOD_TYPES) {
@ -55,10 +55,10 @@ public class FireConnectionHandler extends ConnectionHandler {
return blockData -> {
String key = blockData.getMinecraftKey();
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")) {
int id = blockData.getSavedBlockStateId();
connectedBlocks.put(getStates(blockData), id);
CONNECTED_BLOCKS[getStates(blockData)] = id;
ConnectionData.connectionHandlerMap.put(id, connectionHandler);
}
};
@ -77,11 +77,11 @@ public class FireConnectionHandler extends ConnectionHandler {
@Override
public int connect(UserConnection user, Position position, int blockState) {
byte states = 0;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.EAST)))) states |= 1;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.NORTH)))) states |= 2;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.SOUTH)))) states |= 4;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) states |= 8;
if (flammableBlocks.contains(getBlockData(user, position.getRelative(BlockFace.WEST)))) states |= 16;
return connectedBlocks.get(states);
if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.EAST)))) states |= 1;
if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.NORTH)))) states |= 2;
if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.SOUTH)))) states |= 4;
if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) states |= 8;
if (FLAMMABLE_BLOCKS.contains(getBlockData(user, position.getRelative(BlockFace.WEST)))) states |= 16;
return CONNECTED_BLOCKS[states];
}
}

View File

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

View File

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

View File

@ -20,7 +20,7 @@ package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnection
public class NetherFenceConnectionHandler extends AbstractFenceConnectionHandler {
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) {

View File

@ -22,23 +22,27 @@ import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import java.util.HashSet;
import java.util.Set;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
public class RedstoneConnectionHandler extends ConnectionHandler {
private static final Set<Integer> redstone = new HashSet<>();
private static final Int2IntMap connectedBlockStates = new Int2IntOpenHashMap(1296);
private static final Int2IntMap powerMappings = new Int2IntOpenHashMap(1296);
private static final IntSet REDSTONE = new IntOpenHashSet();
private static final Int2IntMap CONNECTED_BLOCK_STATES = 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() {
final RedstoneConnectionHandler connectionHandler = new RedstoneConnectionHandler();
final String redstoneKey = "minecraft:redstone_wire";
return blockData -> {
if (!redstoneKey.equals(blockData.getMinecraftKey())) return;
redstone.add(blockData.getSavedBlockStateId());
if (!redstoneKey.equals(blockData.getMinecraftKey())) {
return;
}
REDSTONE.add(blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
connectedBlockStates.put(getStates(blockData), blockData.getSavedBlockStateId());
powerMappings.put(blockData.getSavedBlockStateId(), Integer.parseInt(blockData.getValue("power")));
CONNECTED_BLOCK_STATES.put(getStates(blockData), blockData.getSavedBlockStateId());
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.SOUTH) << 4;
b |= connects(user, position, BlockFace.WEST) << 6;
b |= powerMappings.get(blockState) << 8;
return connectedBlockStates.getOrDefault(b, blockState);
b |= POWER_MAPPINGS.get(blockState) << 8;
return CONNECTED_BLOCK_STATES.getOrDefault(b, blockState);
}
private int connects(UserConnection user, Position position, BlockFace side) {
@ -83,11 +87,11 @@ public class RedstoneConnectionHandler extends ConnectionHandler {
return 1; //side
}
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"
}
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 0; //none
@ -95,6 +99,6 @@ public class RedstoneConnectionHandler extends ConnectionHandler {
private boolean connects(BlockFace side, int 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.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.util.Pair;
import java.util.HashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
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.Map;
import java.util.Set;
public class SnowyGrassConnectionHandler extends ConnectionHandler {
private static final Map<Pair<Integer, Boolean>, Integer> grassBlocks = new HashMap<>();
private static final Set<Integer> snows = new HashSet<>();
private static final Object2IntMap<GrassBlock> GRASS_BLOCKS = new Object2IntOpenHashMap<>();
private static final IntSet SNOWY_GRASS_BLOCKS = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() {
final Set<String> snowyGrassBlocks = new HashSet<>();
@ -36,18 +37,19 @@ public class SnowyGrassConnectionHandler extends ConnectionHandler {
snowyGrassBlocks.add("minecraft:podzol");
snowyGrassBlocks.add("minecraft:mycelium");
GRASS_BLOCKS.defaultReturnValue(-1);
final SnowyGrassConnectionHandler handler = new SnowyGrassConnectionHandler();
return blockData -> {
if (snowyGrassBlocks.contains(blockData.getMinecraftKey())) {
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), handler);
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");
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")) {
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
public int connect(UserConnection user, Position position, int blockState) {
int blockUpId = getBlockData(user, position.getRelative(BlockFace.TOP));
Integer newId = grassBlocks.get(new Pair<>(blockState, snows.contains(blockUpId)));
if (newId != null) {
return newId;
int newId = GRASS_BLOCKS.getInt(new GrassBlock(blockState, SNOWY_GRASS_BLOCKS.contains(blockUpId)));
return newId != -1 ? newId : blockState;
}
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.minecraft.BlockFace;
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.LinkedList;
import java.util.List;
@ -27,8 +29,8 @@ import java.util.Locale;
import java.util.Map;
public class StairConnectionHandler extends ConnectionHandler {
private static final Map<Integer, StairData> stairDataMap = new HashMap<>();
private static final Map<Short, Integer> connectedBlocks = new HashMap<>();
private static final Int2ObjectMap<StairData> STAIR_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Map<Short, Integer> CONNECTED_BLOCKS = new HashMap<>();
static ConnectionData.ConnectorInitAction init() {
final List<String> baseStairs = new LinkedList<>();
@ -84,8 +86,8 @@ public class StairConnectionHandler extends ConnectionHandler {
BlockFace.valueOf(blockData.getValue("facing").toUpperCase(Locale.ROOT))
);
stairDataMap.put(blockData.getSavedBlockStateId(), stairData);
connectedBlocks.put(getStates(stairData), blockData.getSavedBlockStateId());
STAIR_DATA_MAP.put(blockData.getSavedBlockStateId(), stairData);
CONNECTED_BLOCKS.put(getStates(stairData), blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
};
@ -102,7 +104,7 @@ public class StairConnectionHandler extends ConnectionHandler {
@Override
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;
short s = 0;
@ -111,14 +113,14 @@ public class StairConnectionHandler extends ConnectionHandler {
s |= stairData.getType() << 4;
s |= stairData.getFacing().ordinal() << 9;
Integer newBlockState = connectedBlocks.get(s);
Integer newBlockState = CONNECTED_BLOCKS.get(s);
return newBlockState == null ? blockState : newBlockState;
}
private int getShape(UserConnection user, Position position, StairData stair) {
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()) {
BlockFace facing2 = relativeStair.getFacing();
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()) {
BlockFace facing2 = relativeStair.getFacing();
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) {
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();
}

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.minecraft.BlockFace;
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.Map;
public class TripwireConnectionHandler extends ConnectionHandler {
private static final Map<Integer, TripwireData> tripwireDataMap = new HashMap<>();
private static final Map<Byte, Integer> connectedBlocks = new HashMap<>();
private static final Map<Integer, BlockFace> tripwireHooks = new HashMap<>();
private static final Int2ObjectMap<TripwireData> TRIPWIRE_DATA_MAP = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<BlockFace> TRIPWIRE_HOOKS = new Int2ObjectArrayMap<>();
private static final int[] CONNECTED_BLOCKS = new int[128];
static ConnectionData.ConnectorInitAction init() {
Arrays.fill(CONNECTED_BLOCKS, -1);
final TripwireConnectionHandler connectionHandler = new TripwireConnectionHandler();
return blockData -> {
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")) {
TripwireData tripwireData = new TripwireData(
blockData.getValue("attached").equals("true"),
@ -41,8 +44,8 @@ public class TripwireConnectionHandler extends ConnectionHandler {
blockData.getValue("powered").equals("true")
);
tripwireDataMap.put(blockData.getSavedBlockStateId(), tripwireData);
connectedBlocks.put(getStates(blockData), blockData.getSavedBlockStateId());
TRIPWIRE_DATA_MAP.put(blockData.getSavedBlockStateId(), tripwireData);
CONNECTED_BLOCKS[getStates(blockData)] = blockData.getSavedBlockStateId();
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
}
@ -63,7 +66,7 @@ public class TripwireConnectionHandler extends ConnectionHandler {
@Override
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;
byte b = 0;
if (tripwireData.isAttached()) b |= 1;
@ -75,21 +78,21 @@ public class TripwireConnectionHandler extends ConnectionHandler {
int south = getBlockData(user, position.getRelative(BlockFace.SOUTH));
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;
}
if (tripwireDataMap.containsKey(north) || tripwireHooks.get(north) == BlockFace.SOUTH) {
if (TRIPWIRE_DATA_MAP.containsKey(north) || TRIPWIRE_HOOKS.get(north) == BlockFace.SOUTH) {
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;
}
if (tripwireDataMap.containsKey(west) || tripwireHooks.get(west) == BlockFace.EAST) {
if (TRIPWIRE_DATA_MAP.containsKey(west) || TRIPWIRE_HOOKS.get(west) == BlockFace.EAST) {
b |= 64;
}
Integer newBlockState = connectedBlocks.get(b);
return newBlockState == null ? blockState : newBlockState;
int newBlockState = CONNECTED_BLOCKS[b];
return newBlockState == -1 ? blockState : newBlockState;
}
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.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import java.util.HashSet;
import java.util.Set;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
class VineConnectionHandler extends ConnectionHandler {
private static final Set<Integer> vines = new HashSet<>();
private static final IntSet VINES = new IntOpenHashSet();
static ConnectionData.ConnectorInitAction init() {
final VineConnectionHandler connectionHandler = new VineConnectionHandler();
return blockData -> {
if (!blockData.getMinecraftKey().equals("minecraft:vine")) return;
vines.add(blockData.getSavedBlockStateId());
VINES.add(blockData.getSavedBlockStateId());
ConnectionData.connectionHandlerMap.put(blockData.getSavedBlockStateId(), connectionHandler);
};
}
@ -42,7 +42,7 @@ class VineConnectionHandler extends ConnectionHandler {
Position upperPos = position.getRelative(BlockFace.TOP);
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
return 0;
@ -56,6 +56,6 @@ class VineConnectionHandler extends ConnectionHandler {
}
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() {
List<ConnectionData.ConnectorInitAction> actions = new ArrayList<>(2);
actions.add(new WallConnectionHandler("cobbleWallConnections").getInitAction("minecraft:cobblestone_wall"));
actions.add(new WallConnectionHandler("cobbleWallConnections").getInitAction("minecraft:mossy_cobblestone_wall"));
actions.add(new WallConnectionHandler("cobbleWall").getInitAction("minecraft:cobblestone_wall"));
actions.add(new WallConnectionHandler("cobbleWall").getInitAction("minecraft:mossy_cobblestone_wall"));
return actions;
}
@ -46,12 +46,18 @@ public class WallConnectionHandler extends AbstractFenceConnectionHandler {
return states;
}
@Override
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState);
if (up(user, position)) states |= 16;
return states;
}
@Override
protected byte statesSize() {
return 32;
}
public boolean up(UserConnection user, Position position) {
if (isWall(getBlockData(user, position.getRelative(BlockFace.BOTTOM))) || isWall(getBlockData(user, position.getRelative(BlockFace.TOP))))
return true;

View File

@ -17,15 +17,14 @@
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.util.Key;
import java.util.LinkedHashMap;
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 int savedBlockStateId;
private final LinkedHashMap<String, String> blockData = new LinkedHashMap<>();
public static WrappedBlockData fromString(String s) {
String[] array = s.split("\\[");
@ -43,15 +42,6 @@ public class 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) {
this.minecraftKey = Key.namespaced(minecraftKey);
this.savedBlockStateId = savedBlockStateId;

View File

@ -59,6 +59,7 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
@Override
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;
@FunctionalInterface
public interface UserBlockData {
int getBlockData(int x, int y, int z);
}

View File

@ -183,18 +183,18 @@ public class WorldPackets {
UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), 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));
if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
ConnectionData.update(userConnection, position);
}
});
}
});
@ -214,14 +214,14 @@ public class WorldPackets {
for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId());
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getSectionX() + (chunkX << 4),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
record.getSectionZ() + (chunkZ << 4));
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
if (Via.getConfig().isServersideBlockConnections()) {
@ -237,8 +237,10 @@ public class WorldPackets {
if (handler != null) {
blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState);
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), blockState);
}
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
@ -251,7 +253,6 @@ public class WorldPackets {
ConnectionData.update(userConnection, position);
}
}
});
}
});
@ -358,22 +359,25 @@ public class WorldPackets {
}
for (int idx = 0; idx < ChunkSection.SIZE; 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)) {
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
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:
{
if (!Via.getConfig().isServersideBlockConnections() || !ConnectionData.needStoreBlocks())
if (!Via.getConfig().isServersideBlockConnections() || !ConnectionData.needStoreBlocks()) {
break save_connections;
}
if (!chunk.isFullChunk()) { // Update
ConnectionData.blockConnectionProvider.unloadChunkSection(wrapper.user(), chunk.getX(), s, chunk.getZ());
}
boolean willSave = false;
for (int p = 0; p < blocks.size(); p++) {
if (ConnectionData.isWelcome(blocks.idByIndex(p))) {
@ -381,7 +385,9 @@ public class WorldPackets {
break;
}
}
if (!willSave) break save_connections;
if (!willSave) {
break save_connections;
}
for (int idx = 0; idx < ChunkSection.SIZE; 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.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.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
public class BlockConnectionStorage implements StorableObject {
private static final short[] REVERSE_BLOCK_MAPPINGS = new short[8582];
private static Constructor<?> fastUtilLongObjectHashMap;
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");
} 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 void store(int x, int y, int z, int blockState) {
short mapping = REVERSE_BLOCK_MAPPINGS[blockState];
if (mapping == -1) return;
long index = getChunkSectionIndex(x, y, z);
SectionData section = getSection(index);
if (section == null) {
if (blockState == 0) {
// No need to store empty sections
return;
}
blockState = mapping;
long pair = getChunkSectionIndex(x, y, z);
SectionData map = getChunkSection(pair, (blockState & 0xF) != 0);
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) {
long pair = getChunkSectionIndex(x, y, z);
SectionData map = getSection(pair);
if (map == null) return 0;
short blockPosition = encodeBlockPos(x, y, z);
NibbleArray nibbleArray = map.nibbleArray();
return WorldPackets.toNewId(
((map.blockIds()[blockPosition] & 0xFF) << 4)
| (nibbleArray == null ? 0 : nibbleArray.get(blockPosition))
);
SectionData section = getSection(pair);
if (section == null) {
return 0;
}
return section.blockAt(x, y, z);
}
public void remove(int x, int y, int z) {
long pair = getChunkSectionIndex(x, y, z);
SectionData map = getSection(pair);
if (map == null) return;
int blockIndex = encodeBlockPos(x, y, z);
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;
}
}
if (allZero) map.setNibbleArray(null);
long index = getChunkSectionIndex(x, y, z);
SectionData section = getSection(index);
if (section == null) {
return;
}
map.blockIds()[blockIndex] = 0;
for (short entry : map.blockIds()) {
if (entry != 0) return;
section.setBlockAt(x, y, z, 0);
if (section.nonEmptyBlocks() == 0) {
removeSection(index);
}
removeSection(pair);
}
public void clear() {
@ -127,21 +104,7 @@ public class BlockConnectionStorage implements StorableObject {
removeSection(getChunkSectionIndex(x << 4, y << 4, z << 4));
}
private SectionData getChunkSection(long index, boolean requireNibbleArray) {
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) {
private @Nullable SectionData getSection(long index) {
if (lastIndex != null && lastIndex == index) {
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);
}
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() {
if (fastUtilLongObjectHashMap != null) {
try {
//noinspection unchecked
return (Map<Long, T>) fastUtilLongObjectHashMap.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
@ -177,23 +137,33 @@ public class BlockConnectionStorage implements StorableObject {
}
private static final class SectionData {
private final byte[] blockIds;
private NibbleArray nibbleArray;
private final short[] blockStates = new short[4096];
private short nonEmptyBlocks;
private SectionData(byte[] blockIds) {
this.blockIds = blockIds;
public int blockAt(int x, int y, int z) {
return blockStates[encodeBlockPos(x, y, z)];
}
public byte[] blockIds() {
return blockIds;
public void setBlockAt(int x, int y, int z, int blockState) {
int index = encodeBlockPos(x, y, z);
if (blockState == blockStates[index]) {
return;
}
blockStates[index] = (short) blockState;
if (blockState == 0) {
nonEmptyBlocks--;
} else {
nonEmptyBlocks++;
}
}
public @Nullable NibbleArray nibbleArray() {
return nibbleArray;
public short nonEmptyBlocks() {
return nonEmptyBlocks;
}
public void setNibbleArray(@Nullable NibbleArray nibbleArray) {
this.nibbleArray = nibbleArray;
private static int encodeBlockPos(int x, int y, int z) {
return ((y & 0xF) << 8) | ((x & 0xF) << 4) | (z & 0xF);
}
}
}