ViaVersion/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/blockconnections/ConnectionData.java

749 lines
34 KiB
Java

/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2024 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections;
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.NumberTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
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;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord1_8;
import com.viaversion.viaversion.api.minecraft.BlockFace;
import com.viaversion.viaversion.api.minecraft.Position;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.ClientboundPackets1_13;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.PacketBlockConnectionProvider;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.providers.UserBlockData;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
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.ArrayList;
import java.util.List;
import java.util.Map.Entry;
public final class ConnectionData {
public static BlockConnectionProvider blockConnectionProvider;
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 {
Boolean inSync = null;
for (BlockFace face : BlockFace.values()) {
Position pos = position.getRelative(face);
int blockState = blockConnectionProvider.getBlockData(user, pos.x(), pos.y(), pos.z());
ConnectionHandler handler = connectionHandlerMap.get(blockState);
if (handler == null) {
continue;
}
int newBlockState = handler.connect(user, pos, blockState);
if (newBlockState == blockState) {
if (inSync == null) {
inSync = blockConnectionProvider.storesBlocks(user, position);
}
// Blocks-states are the same, and known to be stored and not de-synced, skip update
if (inSync) {
continue;
}
}
updateBlockStorage(user, pos.x(), pos.y(), pos.z(), newBlockState);
PacketWrapper blockUpdatePacket = PacketWrapper.create(ClientboundPackets1_13.BLOCK_CHANGE, null, user);
blockUpdatePacket.write(Type.POSITION1_8, pos);
blockUpdatePacket.write(Type.VAR_INT, newBlockState);
blockUpdatePacket.send(Protocol1_13To1_12_2.class);
}
}
public static void updateBlockStorage(UserConnection userConnection, int x, int y, int z, int blockState) {
if (!needStoreBlocks()) return;
if (ConnectionData.isWelcome(blockState)) {
blockConnectionProvider.storeBlock(userConnection, x, y, z, blockState);
} else {
blockConnectionProvider.removeBlock(userConnection, x, y, z);
}
}
public static void clearBlockStorage(UserConnection connection) {
if (!needStoreBlocks()) return;
blockConnectionProvider.clearStorage(connection);
}
public static void markModified(UserConnection connection, Position pos) {
if (!needStoreBlocks()) return;
blockConnectionProvider.modifiedBlock(connection, pos);
}
public static boolean needStoreBlocks() {
return blockConnectionProvider.storesBlocks(null, null);
}
public static void connectBlocks(UserConnection user, Chunk chunk) {
int xOff = chunk.getX() << 4;
int zOff = chunk.getZ() << 4;
for (int s = 0; s < chunk.getSections().length; s++) {
ChunkSection section = chunk.getSections()[s];
if (section == null) {
continue;
}
DataPalette blocks = section.palette(PaletteType.BLOCKS);
boolean willConnect = false;
for (int p = 0; p < blocks.size(); p++) {
int id = blocks.idByIndex(p);
if (ConnectionData.connects(id)) {
willConnect = true;
break;
}
}
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;
}
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);
}
}
}
}
public static void init() {
if (!Via.getConfig().isServersideBlockConnections()) {
return;
}
Via.getPlatform().getLogger().info("Loading block connection mappings ...");
ListTag<StringTag> blockStates = MappingDataLoader.loadNBT("blockstates-1.13.nbt").getListTag("blockstates", StringTag.class);
for (int id = 0; id < blockStates.size(); id++) {
String key = blockStates.get(id).getValue();
KEY_TO_ID.put(key, id);
}
connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650, .99F);
if (!Via.getConfig().isReduceBlockStorageMemory()) {
blockConnectionData = new Int2ObjectOpenHashMap<>(2048);
ListTag<CompoundTag> blockConnectionMappings = MappingDataLoader.loadNBT("blockConnections.nbt").getListTag("data", CompoundTag.class);
for (CompoundTag blockTag : blockConnectionMappings) {
BlockData blockData = new BlockData();
for (Entry<String, Tag> entry : blockTag.entrySet()) {
String key = entry.getKey();
if (key.equals("id") || key.equals("ids")) {
continue;
}
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);
}
NumberTag idTag = blockTag.getNumberTag("id");
if (idTag != null) {
blockConnectionData.put(idTag.asInt(), blockData);
} else {
IntArrayTag idsTag = blockTag.getIntArrayTag("ids");
for (int id : idsTag.getValue()) {
blockConnectionData.put(id, blockData);
}
}
}
}
for (String state : occludingBlockStates()) {
OCCLUDING_STATES.add(KEY_TO_ID.getInt(state));
}
List<ConnectorInitAction> initActions = new ArrayList<>();
initActions.add(PumpkinConnectionHandler.init());
initActions.addAll(BasicFenceConnectionHandler.init());
initActions.add(NetherFenceConnectionHandler.init());
initActions.addAll(WallConnectionHandler.init());
initActions.add(MelonConnectionHandler.init());
initActions.addAll(GlassConnectionHandler.init());
initActions.add(ChestConnectionHandler.init());
initActions.add(DoorConnectionHandler.init());
initActions.add(RedstoneConnectionHandler.init());
initActions.add(StairConnectionHandler.init());
initActions.add(FlowerConnectionHandler.init());
initActions.addAll(ChorusPlantConnectionHandler.init());
initActions.add(TripwireConnectionHandler.init());
initActions.add(SnowyGrassConnectionHandler.init());
initActions.add(FireConnectionHandler.init());
if (Via.getConfig().isVineClimbFix()) {
initActions.add(VineConnectionHandler.init());
}
for (String key : KEY_TO_ID.keySet()) {
WrappedBlockData wrappedBlockData = WrappedBlockData.fromString(key);
for (ConnectorInitAction action : initActions) {
action.check(wrappedBlockData);
}
}
if (Via.getConfig().getBlockConnectionMethod().equalsIgnoreCase("packet")) {
blockConnectionProvider = new PacketBlockConnectionProvider();
Via.getManager().getProviders().register(BlockConnectionProvider.class, blockConnectionProvider);
}
}
public static boolean isWelcome(int blockState) {
return blockConnectionData.containsKey(blockState) || connectionHandlerMap.containsKey(blockState);
}
public static boolean connects(int blockState) {
return connectionHandlerMap.containsKey(blockState);
}
public static int connect(UserConnection user, Position position, int blockState) {
ConnectionHandler handler = connectionHandlerMap.get(blockState);
return handler != null ? handler.connect(user, position, blockState) : blockState;
}
public static ConnectionHandler getConnectionHandler(int blockstate) {
return connectionHandlerMap.get(blockstate);
}
public static int getId(String key) {
return KEY_TO_ID.getOrDefault(Key.stripMinecraftNamespace(key), -1);
}
private static String[] occludingBlockStates() {
return new String[]{
"stone",
"granite",
"polished_granite",
"diorite",
"polished_diorite",
"andesite",
"polished_andesite",
"grass_block[snowy=false]",
"dirt",
"coarse_dirt",
"podzol[snowy=false]",
"cobblestone",
"oak_planks",
"spruce_planks",
"birch_planks",
"jungle_planks",
"acacia_planks",
"dark_oak_planks",
"bedrock",
"sand",
"red_sand",
"gravel",
"gold_ore",
"iron_ore",
"coal_ore",
"oak_log[axis=x]",
"oak_log[axis=y]",
"oak_log[axis=z]",
"spruce_log[axis=x]",
"spruce_log[axis=y]",
"spruce_log[axis=z]",
"birch_log[axis=x]",
"birch_log[axis=y]",
"birch_log[axis=z]",
"jungle_log[axis=x]",
"jungle_log[axis=y]",
"jungle_log[axis=z]",
"acacia_log[axis=x]",
"acacia_log[axis=y]",
"acacia_log[axis=z]",
"dark_oak_log[axis=x]",
"dark_oak_log[axis=y]",
"dark_oak_log[axis=z]",
"oak_wood[axis=y]",
"spruce_wood[axis=y]",
"birch_wood[axis=y]",
"jungle_wood[axis=y]",
"acacia_wood[axis=y]",
"dark_oak_wood[axis=y]",
"sponge",
"wet_sponge",
"lapis_ore",
"lapis_block",
"dispenser[facing=north,triggered=true]",
"dispenser[facing=north,triggered=false]",
"dispenser[facing=east,triggered=true]",
"dispenser[facing=east,triggered=false]",
"dispenser[facing=south,triggered=true]",
"dispenser[facing=south,triggered=false]",
"dispenser[facing=west,triggered=true]",
"dispenser[facing=west,triggered=false]",
"dispenser[facing=up,triggered=true]",
"dispenser[facing=up,triggered=false]",
"dispenser[facing=down,triggered=true]",
"dispenser[facing=down,triggered=false]",
"sandstone",
"chiseled_sandstone",
"cut_sandstone",
"note_block[instrument=harp,note=0,powered=false]",
"white_wool",
"orange_wool",
"magenta_wool",
"light_blue_wool",
"yellow_wool",
"lime_wool",
"pink_wool",
"gray_wool",
"light_gray_wool",
"cyan_wool",
"purple_wool",
"blue_wool",
"brown_wool",
"green_wool",
"red_wool",
"black_wool",
"gold_block",
"iron_block",
"bricks",
"bookshelf",
"mossy_cobblestone",
"obsidian",
"spawner",
"diamond_ore",
"diamond_block",
"crafting_table",
"furnace[facing=north,lit=true]",
"furnace[facing=north,lit=false]",
"furnace[facing=south,lit=true]",
"furnace[facing=south,lit=false]",
"furnace[facing=west,lit=true]",
"furnace[facing=west,lit=false]",
"furnace[facing=east,lit=true]",
"furnace[facing=east,lit=false]",
"redstone_ore[lit=true]",
"redstone_ore[lit=false]",
"snow_block",
"clay",
"jukebox[has_record=true]",
"jukebox[has_record=false]",
"netherrack",
"soul_sand",
"carved_pumpkin[facing=north]",
"carved_pumpkin[facing=south]",
"carved_pumpkin[facing=west]",
"carved_pumpkin[facing=east]",
"jack_o_lantern[facing=north]",
"jack_o_lantern[facing=south]",
"jack_o_lantern[facing=west]",
"jack_o_lantern[facing=east]",
"infested_stone",
"infested_cobblestone",
"infested_stone_bricks",
"infested_mossy_stone_bricks",
"infested_cracked_stone_bricks",
"infested_chiseled_stone_bricks",
"stone_bricks",
"mossy_stone_bricks",
"cracked_stone_bricks",
"chiseled_stone_bricks",
"brown_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true]",
"brown_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]",
"brown_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]",
"brown_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]",
"brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]",
"brown_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]",
"brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]",
"brown_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]",
"brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]",
"brown_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]",
"brown_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]",
"red_mushroom_block[down=true,east=true,north=true,south=true,up=true,west=true]",
"red_mushroom_block[down=false,east=true,north=true,south=false,up=true,west=false]",
"red_mushroom_block[down=false,east=true,north=false,south=true,up=true,west=false]",
"red_mushroom_block[down=false,east=true,north=false,south=false,up=true,west=false]",
"red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=true]",
"red_mushroom_block[down=false,east=false,north=true,south=false,up=true,west=false]",
"red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=true]",
"red_mushroom_block[down=false,east=false,north=false,south=true,up=true,west=false]",
"red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=true]",
"red_mushroom_block[down=false,east=false,north=false,south=false,up=true,west=false]",
"red_mushroom_block[down=false,east=false,north=false,south=false,up=false,west=false]",
"mushroom_stem[down=true,east=true,north=true,south=true,up=true,west=true]",
"mushroom_stem[down=false,east=true,north=true,south=true,up=false,west=true]",
"melon",
"mycelium[snowy=false]",
"nether_bricks",
"end_stone",
"redstone_lamp[lit=true]",
"redstone_lamp[lit=false]",
"emerald_ore",
"emerald_block",
"command_block[conditional=true,facing=north]",
"command_block[conditional=true,facing=east]",
"command_block[conditional=true,facing=south]",
"command_block[conditional=true,facing=west]",
"command_block[conditional=true,facing=up]",
"command_block[conditional=true,facing=down]",
"command_block[conditional=false,facing=north]",
"command_block[conditional=false,facing=east]",
"command_block[conditional=false,facing=south]",
"command_block[conditional=false,facing=west]",
"command_block[conditional=false,facing=up]",
"command_block[conditional=false,facing=down]",
"nether_quartz_ore",
"quartz_block",
"chiseled_quartz_block",
"quartz_pillar[axis=x]",
"quartz_pillar[axis=y]",
"quartz_pillar[axis=z]",
"dropper[facing=north,triggered=true]",
"dropper[facing=north,triggered=false]",
"dropper[facing=east,triggered=true]",
"dropper[facing=east,triggered=false]",
"dropper[facing=south,triggered=true]",
"dropper[facing=south,triggered=false]",
"dropper[facing=west,triggered=true]",
"dropper[facing=west,triggered=false]",
"dropper[facing=up,triggered=true]",
"dropper[facing=up,triggered=false]",
"dropper[facing=down,triggered=true]",
"dropper[facing=down,triggered=false]",
"white_terracotta",
"orange_terracotta",
"magenta_terracotta",
"light_blue_terracotta",
"yellow_terracotta",
"lime_terracotta",
"pink_terracotta",
"gray_terracotta",
"light_gray_terracotta",
"cyan_terracotta",
"purple_terracotta",
"blue_terracotta",
"brown_terracotta",
"green_terracotta",
"red_terracotta",
"black_terracotta",
"slime_block",
"barrier",
"prismarine",
"prismarine_bricks",
"dark_prismarine",
"hay_block[axis=x]",
"hay_block[axis=y]",
"hay_block[axis=z]",
"terracotta",
"coal_block",
"packed_ice",
"red_sandstone",
"chiseled_red_sandstone",
"cut_red_sandstone",
"oak_slab[type=double,waterlogged=false]",
"spruce_slab[type=double,waterlogged=false]",
"birch_slab[type=double,waterlogged=false]",
"jungle_slab[type=double,waterlogged=false]",
"acacia_slab[type=double,waterlogged=false]",
"dark_oak_slab[type=double,waterlogged=false]",
"stone_slab[type=double,waterlogged=false]",
"sandstone_slab[type=double,waterlogged=false]",
"petrified_oak_slab[type=double,waterlogged=false]",
"cobblestone_slab[type=double,waterlogged=false]",
"brick_slab[type=double,waterlogged=false]",
"stone_brick_slab[type=double,waterlogged=false]",
"nether_brick_slab[type=double,waterlogged=false]",
"quartz_slab[type=double,waterlogged=false]",
"red_sandstone_slab[type=double,waterlogged=false]",
"purpur_slab[type=double,waterlogged=false]",
"smooth_stone",
"smooth_sandstone",
"smooth_quartz",
"smooth_red_sandstone",
"purpur_block",
"purpur_pillar[axis=x]",
"purpur_pillar[axis=y]",
"purpur_pillar[axis=z]",
"end_stone_bricks",
"repeating_command_block[conditional=true,facing=north]",
"repeating_command_block[conditional=true,facing=east]",
"repeating_command_block[conditional=true,facing=south]",
"repeating_command_block[conditional=true,facing=west]",
"repeating_command_block[conditional=true,facing=up]",
"repeating_command_block[conditional=true,facing=down]",
"repeating_command_block[conditional=false,facing=north]",
"repeating_command_block[conditional=false,facing=east]",
"repeating_command_block[conditional=false,facing=south]",
"repeating_command_block[conditional=false,facing=west]",
"repeating_command_block[conditional=false,facing=up]",
"repeating_command_block[conditional=false,facing=down]",
"chain_command_block[conditional=true,facing=north]",
"chain_command_block[conditional=true,facing=east]",
"chain_command_block[conditional=true,facing=south]",
"chain_command_block[conditional=true,facing=west]",
"chain_command_block[conditional=true,facing=up]",
"chain_command_block[conditional=true,facing=down]",
"chain_command_block[conditional=false,facing=north]",
"chain_command_block[conditional=false,facing=east]",
"chain_command_block[conditional=false,facing=south]",
"chain_command_block[conditional=false,facing=west]",
"chain_command_block[conditional=false,facing=up]",
"chain_command_block[conditional=false,facing=down]",
"magma_block",
"nether_wart_block",
"red_nether_bricks",
"bone_block[axis=x]",
"bone_block[axis=y]",
"bone_block[axis=z]",
"white_glazed_terracotta[facing=north]",
"white_glazed_terracotta[facing=south]",
"white_glazed_terracotta[facing=west]",
"white_glazed_terracotta[facing=east]",
"orange_glazed_terracotta[facing=north]",
"orange_glazed_terracotta[facing=south]",
"orange_glazed_terracotta[facing=west]",
"orange_glazed_terracotta[facing=east]",
"magenta_glazed_terracotta[facing=north]",
"magenta_glazed_terracotta[facing=south]",
"magenta_glazed_terracotta[facing=west]",
"magenta_glazed_terracotta[facing=east]",
"light_blue_glazed_terracotta[facing=north]",
"light_blue_glazed_terracotta[facing=south]",
"light_blue_glazed_terracotta[facing=west]",
"light_blue_glazed_terracotta[facing=east]",
"yellow_glazed_terracotta[facing=north]",
"yellow_glazed_terracotta[facing=south]",
"yellow_glazed_terracotta[facing=west]",
"yellow_glazed_terracotta[facing=east]",
"lime_glazed_terracotta[facing=north]",
"lime_glazed_terracotta[facing=south]",
"lime_glazed_terracotta[facing=west]",
"lime_glazed_terracotta[facing=east]",
"pink_glazed_terracotta[facing=north]",
"pink_glazed_terracotta[facing=south]",
"pink_glazed_terracotta[facing=west]",
"pink_glazed_terracotta[facing=east]",
"gray_glazed_terracotta[facing=north]",
"gray_glazed_terracotta[facing=south]",
"gray_glazed_terracotta[facing=west]",
"gray_glazed_terracotta[facing=east]",
"light_gray_glazed_terracotta[facing=north]",
"light_gray_glazed_terracotta[facing=south]",
"light_gray_glazed_terracotta[facing=west]",
"light_gray_glazed_terracotta[facing=east]",
"cyan_glazed_terracotta[facing=north]",
"cyan_glazed_terracotta[facing=south]",
"cyan_glazed_terracotta[facing=west]",
"cyan_glazed_terracotta[facing=east]",
"purple_glazed_terracotta[facing=north]",
"purple_glazed_terracotta[facing=south]",
"purple_glazed_terracotta[facing=west]",
"purple_glazed_terracotta[facing=east]",
"blue_glazed_terracotta[facing=north]",
"blue_glazed_terracotta[facing=south]",
"blue_glazed_terracotta[facing=west]",
"blue_glazed_terracotta[facing=east]",
"brown_glazed_terracotta[facing=north]",
"brown_glazed_terracotta[facing=south]",
"brown_glazed_terracotta[facing=west]",
"brown_glazed_terracotta[facing=east]",
"green_glazed_terracotta[facing=north]",
"green_glazed_terracotta[facing=south]",
"green_glazed_terracotta[facing=west]",
"green_glazed_terracotta[facing=east]",
"red_glazed_terracotta[facing=north]",
"red_glazed_terracotta[facing=south]",
"red_glazed_terracotta[facing=west]",
"red_glazed_terracotta[facing=east]",
"black_glazed_terracotta[facing=north]",
"black_glazed_terracotta[facing=south]",
"black_glazed_terracotta[facing=west]",
"black_glazed_terracotta[facing=east]",
"white_concrete",
"orange_concrete",
"magenta_concrete",
"light_blue_concrete",
"yellow_concrete",
"lime_concrete",
"pink_concrete",
"gray_concrete",
"light_gray_concrete",
"cyan_concrete",
"purple_concrete",
"blue_concrete",
"brown_concrete",
"green_concrete",
"red_concrete",
"black_concrete",
"white_concrete_powder",
"orange_concrete_powder",
"magenta_concrete_powder",
"light_blue_concrete_powder",
"yellow_concrete_powder",
"lime_concrete_powder",
"pink_concrete_powder",
"gray_concrete_powder",
"light_gray_concrete_powder",
"cyan_concrete_powder",
"purple_concrete_powder",
"blue_concrete_powder",
"brown_concrete_powder",
"green_concrete_powder",
"red_concrete_powder",
"black_concrete_powder",
"structure_block[mode=save]",
"structure_block[mode=load]",
"structure_block[mode=corner]",
"structure_block[mode=data]",
"glowstone"
};
}
@FunctionalInterface
interface ConnectorInitAction {
void check(WrappedBlockData blockData);
}
public static Object2IntMap<String> getKeyToId() {
return KEY_TO_ID;
}
public static final class NeighbourUpdater {
private final UserConnection user;
private final UserBlockData userBlockData;
public NeighbourUpdater(UserConnection user) {
this.user = user;
this.userBlockData = blockConnectionProvider.forUser(user);
}
public void updateChunkSectionNeighbours(int chunkX, int chunkZ, int chunkSectionY) throws Exception {
int chunkMinY = chunkSectionY << 4;
List<BlockChangeRecord1_8> updates = new ArrayList<>();
for (int chunkDeltaX = -1; chunkDeltaX <= 1; chunkDeltaX++) {
for (int chunkDeltaZ = -1; chunkDeltaZ <= 1; chunkDeltaZ++) {
int distance = Math.abs(chunkDeltaX) + Math.abs(chunkDeltaZ);
if (distance == 0) continue;
int chunkMinX = (chunkX + chunkDeltaX) << 4;
int chunkMinZ = (chunkZ + chunkDeltaZ) << 4;
if (distance == 2) { // Corner
for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) {
int blockPosX = chunkDeltaX == 1 ? 0 : 15;
int blockPosZ = chunkDeltaZ == 1 ? 0 : 15;
updateBlock(chunkMinX + blockPosX, blockY, chunkMinZ + blockPosZ, updates);
}
} else {
for (int blockY = chunkMinY; blockY < chunkMinY + 16; blockY++) {
int xStart, xEnd;
int zStart, zEnd;
if (chunkDeltaX == 1) {
xStart = 0;
xEnd = 2;
zStart = 0;
zEnd = 16;
} else if (chunkDeltaX == -1) {
xStart = 14;
xEnd = 16;
zStart = 0;
zEnd = 16;
} else if (chunkDeltaZ == 1) {
xStart = 0;
xEnd = 16;
zStart = 0;
zEnd = 2;
} else {
xStart = 0;
xEnd = 16;
zStart = 14;
zEnd = 16;
}
for (int blockX = xStart; blockX < xEnd; blockX++) {
for (int blockZ = zStart; blockZ < zEnd; blockZ++) {
updateBlock(chunkMinX + blockX, blockY, chunkMinZ + blockZ, updates);
}
}
}
}
if (!updates.isEmpty()) {
PacketWrapper wrapper = PacketWrapper.create(ClientboundPackets1_13.MULTI_BLOCK_CHANGE, null, user);
wrapper.write(Type.INT, chunkX + chunkDeltaX);
wrapper.write(Type.INT, chunkZ + chunkDeltaZ);
wrapper.write(Type.BLOCK_CHANGE_RECORD_ARRAY, updates.toArray(EMPTY_RECORDS));
wrapper.send(Protocol1_13To1_12_2.class);
updates.clear();
}
}
}
}
private void updateBlock(int x, int y, int z, List<BlockChangeRecord1_8> records) {
int blockState = userBlockData.getBlockData(x, y, z);
ConnectionHandler handler = getConnectionHandler(blockState);
if (handler == null) {
return;
}
Position pos = new Position(x, y, z);
int newBlockState = handler.connect(user, pos, blockState);
if (blockState != newBlockState || !blockConnectionProvider.storesBlocks(user, null)) {
records.add(new BlockChangeRecord1_8(x & 0xF, y, z & 0xF, newBlockState));
updateBlockStorage(user, x, y, z, newBlockState);
}
}
}
}