From b96ef99beb1bb401e1ab67a8688200160a984a24 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Mon, 30 Dec 2019 23:33:27 +0000 Subject: [PATCH 01/12] Start working on direct block mapping. This will not work --- .../org/geysermc/connector/utils/Toolbox.java | 202 +++++++++++------- 1 file changed, 124 insertions(+), 78 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 9522e391a..6eeaf0430 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -1,16 +1,21 @@ package org.geysermc.connector.utils; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.nbt.stream.NBTInputStream; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.ListTag; -import com.nukkitx.nbt.tag.Tag; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; - +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.network.translators.block.BlockEntry; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -20,105 +25,146 @@ import java.util.*; public class Toolbox { + public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); + public static final Collection ITEMS = new ArrayList<>(); - public static ListTag BLOCKS; + public static final ListTag BLOCKS; public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); public static final Int2ObjectMap BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>(); + public static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); + public static final Int2IntMap JAVA_TO_BEDROCK_LIQUID_MAP = new Int2IntOpenHashMap(); - public static void init() { - InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/runtime_block_states.dat"); - if (stream == null) { - throw new AssertionError("Unable to find bedrock/runtime_block_states.dat"); - } + static { + + /* Load block palette */ + InputStream stream = getResource("bedrock/runtime_block_states.dat"); ListTag blocksTag; - - NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream); - try { + try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { blocksTag = (ListTag) nbtInputStream.readTag(); - nbtInputStream.close(); - } catch (Exception ex) { - GeyserLogger.DEFAULT.warning("Failed to get blocks from runtime block states, please report this error!"); - throw new AssertionError(ex); - } - - BLOCKS = blocksTag; - InputStream stream2 = Toolbox.class.getClassLoader().getResourceAsStream("bedrock/items.json"); - if (stream2 == null) { - throw new AssertionError("Items Table not found"); - } - - ObjectMapper startGameItemMapper = new ObjectMapper(); - List startGameItems = new ArrayList<>(); - try { - startGameItems = startGameItemMapper.readValue(stream2, ArrayList.class); } catch (Exception e) { - e.printStackTrace(); + throw new AssertionError("Unable to get blocks from runtime block states", e); } - for (Map entry : startGameItems) { - ITEMS.add(new StartGamePacket.ItemEntry((String) entry.get("name"), (short) ((int) entry.get("id")))); + Map blockStateMap = new HashMap<>(); + + for (CompoundTag tag : blocksTag.getValue()) { + if (blockStateMap.putIfAbsent(tag.getAsCompound("block"), tag) != null) { + throw new AssertionError("Duplicate block states in Bedrock palette"); + } } - InputStream itemStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/items.json"); - ObjectMapper itemMapper = new ObjectMapper(); - Map> items = new HashMap<>(); - + stream = getResource("mappings/blocks.json"); + JsonNode blocks; try { - items = itemMapper.readValue(itemStream, LinkedHashMap.class); - } catch (Exception ex) { - ex.printStackTrace(); + blocks = JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java block mappings", e); + } + TObjectIntMap stateRuntimeMap = new TObjectIntHashMap<>(512, 0.5f, -1); + List paletteList = new ArrayList<>(); + + int javaRuntimeId = -1; + int bedrockRuntimeId = 0; + Iterator> blocksIterator = blocks.fields(); + while (blocksIterator.hasNext()) { + javaRuntimeId++; + Map.Entry entry = blocksIterator.next(); + String javaId = entry.getKey(); + CompoundTag blockTag = buildBedrockState(entry.getValue()); + + CompoundTag runtimeTag = blockStateMap.remove(blockTag); + if (runtimeTag != null) { + stateRuntimeMap.put(blockTag, javaRuntimeId); + paletteList.add(runtimeTag); + } else { + int duplicateRuntimeId = stateRuntimeMap.get(blockTag); + if (duplicateRuntimeId == -1) { + GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!"); + } else { + JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); + } + continue; + } + JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); + + bedrockRuntimeId++; + } + + BLOCKS = new ListTag<>("", CompoundTag.class, paletteList); + + /* Load item palette */ + stream = getResource("bedrock/items.json"); + + TypeReference> itemEntriesType = new TypeReference>() { + }; + + List itemEntries; + try { + itemEntries = JSON_MAPPER.readValue(stream, itemEntriesType); + } catch (Exception e) { + throw new AssertionError("Unable to load Bedrock runtime item IDs", e); + } + + for (JsonNode entry : itemEntries) { + ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue())); + } + + stream = getResource("mappings/items.json"); + + JsonNode items; + try { + items = JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java runtime item IDs", e); } int itemIndex = 0; - for (Map.Entry> itemEntry : items.entrySet()) { - ITEM_ENTRIES.put(itemIndex, new ItemEntry(itemEntry.getKey(), itemIndex, (int) itemEntry.getValue().get("bedrock_id"), (int) itemEntry.getValue().get("bedrock_data"))); + Iterator> iterator = items.fields(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex, + entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue())); itemIndex++; } + } - InputStream blockStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/blocks.json"); - ObjectMapper blockMapper = new ObjectMapper(); - Map> blocks = new HashMap<>(); + private static CompoundTag buildBedrockState(JsonNode node) { + CompoundTagBuilder tagBuilder = CompoundTag.builder(); + tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue()); - try { - blocks = blockMapper.readValue(blockStream, LinkedHashMap.class); - } catch (Exception ex) { - ex.printStackTrace(); - } + // check for states + if (node.has("bedrock_states")) { + Iterator> statesIterator = node.get("bedrock_states").fields(); - int javaIndex = -1; - javaLoop: - for (Map.Entry> javaEntry : blocks.entrySet()) { - javaIndex++; - String wantedIdentifier = (String) javaEntry.getValue().get("bedrock_identifier"); - Map wantedStates = (Map) javaEntry.getValue().get("bedrock_states"); - - int bedrockIndex = -1; - bedrockLoop: - for (CompoundTag bedrockEntry : BLOCKS.getValue()) { - bedrockIndex++; - CompoundTag blockTag = bedrockEntry.getAsCompound("block"); - if (blockTag.getAsString("name").equals(wantedIdentifier)) { - if (wantedStates != null) { - Map> bedrockStates = blockTag.getAsCompound("states").getValue(); - for (Map.Entry stateEntry : wantedStates.entrySet()) { - Tag bedrockStateTag = bedrockStates.get(stateEntry.getKey()); - if (bedrockStateTag == null) - continue bedrockLoop; - Object bedrockStateValue = bedrockStateTag.getValue(); - if (bedrockStateValue instanceof Byte) - bedrockStateValue = ((Byte) bedrockStateValue).intValue(); - if (!stateEntry.getValue().equals(bedrockStateValue)) - continue bedrockLoop; - } - } - BlockEntry blockEntry = new BlockEntry(javaEntry.getKey(), javaIndex, bedrockIndex); - BLOCK_ENTRIES.put(javaIndex, blockEntry); - continue javaLoop; + while (statesIterator.hasNext()) { + Map.Entry stateEntry = statesIterator.next(); + JsonNode stateValue = stateEntry.getValue(); + switch (stateValue.getNodeType()) { + case BOOLEAN: + tagBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue()); + continue; + case STRING: + tagBuilder.stringTag(stateEntry.getKey(), stateValue.textValue()); + continue; + case NUMBER: + tagBuilder.intTag(stateEntry.getKey(), stateValue.intValue()); } } - GeyserLogger.DEFAULT.debug("Mapping " + javaEntry.getKey() + " was not found for bedrock edition!"); } + return tagBuilder.build("block"); + } + + private static InputStream getResource(String resource) { + InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource); + if (stream == null) { + throw new AssertionError("Unable to find resource: " + resource); + } + return stream; + } + + public static void init() { + // no-op } } \ No newline at end of file From 6599aefc43d4aae69f30c63429de7bbaf8048a4f Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Mon, 30 Dec 2019 23:35:28 +0000 Subject: [PATCH 02/12] Use correct ID for duplicates --- .../src/main/java/org/geysermc/connector/utils/Toolbox.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 6eeaf0430..b90f4d46d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -83,7 +83,7 @@ public class Toolbox { if (duplicateRuntimeId == -1) { GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!"); } else { - JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); + JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, duplicateRuntimeId); } continue; } From 2e46ecf28fe64404b9d5d86a933146bdbbba7eec Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Mon, 30 Dec 2019 23:37:30 +0000 Subject: [PATCH 03/12] Rename map and use correct ID when entering. --- .../src/main/java/org/geysermc/connector/utils/Toolbox.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index b90f4d46d..6c5775d32 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -62,7 +62,7 @@ public class Toolbox { } catch (Exception e) { throw new AssertionError("Unable to load Java block mappings", e); } - TObjectIntMap stateRuntimeMap = new TObjectIntHashMap<>(512, 0.5f, -1); + TObjectIntMap addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1); List paletteList = new ArrayList<>(); int javaRuntimeId = -1; @@ -76,10 +76,10 @@ public class Toolbox { CompoundTag runtimeTag = blockStateMap.remove(blockTag); if (runtimeTag != null) { - stateRuntimeMap.put(blockTag, javaRuntimeId); + addedStatesMap.put(blockTag, bedrockRuntimeId); paletteList.add(runtimeTag); } else { - int duplicateRuntimeId = stateRuntimeMap.get(blockTag); + int duplicateRuntimeId = addedStatesMap.get(blockTag); if (duplicateRuntimeId == -1) { GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!"); } else { From 94851ef4b80d1ad39de59cd30e78a77b9bb82f82 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Tue, 31 Dec 2019 00:14:38 +0000 Subject: [PATCH 04/12] Move all block related code into BlockTranslator It makes more sense. --- .../network/session/GeyserSession.java | 3 +- .../network/session/cache/ChunkCache.java | 11 +- .../network/translators/TranslatorsInit.java | 10 +- .../network/translators/block/BlockEntry.java | 54 ------- .../translators/block/BlockTranslator.java | 137 +++++++++++++++++- .../geysermc/connector/utils/ChunkUtils.java | 23 ++- .../org/geysermc/connector/utils/Toolbox.java | 103 +------------ 7 files changed, 154 insertions(+), 187 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index db13eff95..3de469053 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -57,6 +57,7 @@ import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.Registry; +import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.Toolbox; @@ -319,7 +320,7 @@ public class GeyserSession implements Player { // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.setBlockPalette(Toolbox.BLOCKS); + startGamePacket.setBlockPalette(BlockTranslator.BLOCKS); startGamePacket.setItemEntries(Toolbox.ITEMS); startGamePacket.setVanillaVersion("*"); // startGamePacket.setMovementServerAuthoritative(true); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 3ff1b7f98..7180ede65 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import lombok.Getter; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.TranslatorsInit; -import org.geysermc.connector.network.translators.block.BlockEntry; +import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.world.chunk.ChunkPosition; import java.util.HashMap; @@ -69,20 +69,19 @@ public class ChunkCache { } } - public BlockEntry getBlockAt(Position position) { + public BlockState getBlockAt(Position position) { ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4); if (!chunks.containsKey(chunkPosition)) - return BlockEntry.AIR; + return BlockTranslator.AIR; Column column = chunks.get(chunkPosition); Chunk chunk = column.getChunks()[position.getY() >> 4]; Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ()); if (chunk != null) { - BlockState blockState = chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); - return TranslatorsInit.getBlockTranslator().getBlockEntry(blockState); + return chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); } - return BlockEntry.AIR; + return BlockTranslator.AIR; } public void removeChunk(ChunkPosition position) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index aa7692830..32900db50 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -27,7 +27,10 @@ package org.geysermc.connector.network.translators; import com.github.steveice10.mc.protocol.packet.ingame.server.*; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.*; -import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.*; +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerHealthPacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerSetExperiencePacket; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.*; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket; @@ -70,9 +73,6 @@ public class TranslatorsInit { @Getter private static ItemTranslator itemTranslator; - @Getter - private static BlockTranslator blockTranslator; - @Getter private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator(); @@ -159,7 +159,7 @@ public class TranslatorsInit { Registry.registerBedrock(RespawnPacket.class, new BedrockRespawnTranslator()); itemTranslator = new ItemTranslator(); - blockTranslator = new BlockTranslator(); + BlockTranslator.init(); registerInventoryTranslators(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java deleted file mode 100644 index ab126eeb2..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2019 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.connector.network.translators.block; - -import lombok.Getter; - -@Getter -public class BlockEntry { - - public static BlockEntry AIR = new BlockEntry("minecraft:air", 0, 0); - - private final String javaIdentifier; - private final int javaId; - private final int bedrockRuntimeId; - private final boolean waterlogged; - - public BlockEntry(String javaIdentifier, int javaId, int bedrockRuntimeId) { - this.javaIdentifier = javaIdentifier; - this.javaId = javaId; - this.bedrockRuntimeId = bedrockRuntimeId; - this.waterlogged = (javaIdentifier.contains("waterlogged=true") - || javaIdentifier.startsWith("minecraft:kelp") - || javaIdentifier.contains("seagrass") - || javaIdentifier.startsWith("minecraft:bubble_column")); - } - - @Override - public boolean equals(Object obj) { - return obj == this || (obj instanceof BlockEntry && ((BlockEntry) obj).getBedrockRuntimeId() == this.getBedrockRuntimeId() && ((BlockEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier())); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index 7ff630c30..c4f31abe9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -1,20 +1,141 @@ package org.geysermc.connector.network.translators.block; +import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.NbtUtils; +import com.nukkitx.nbt.stream.NBTInputStream; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.nbt.tag.ListTag; +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.utils.Toolbox; -import java.util.HashMap; -import java.util.Map; +import java.io.InputStream; +import java.util.*; public class BlockTranslator { - private final Map javaIdentifierMap = new HashMap<>(); + public static final ListTag BLOCKS; + public static final BlockState AIR = new BlockState(0); - public BlockEntry getBlockEntry(BlockState state) { - return Toolbox.BLOCK_ENTRIES.get(state.getId()); + private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); + private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>(); + private static final Int2IntMap JAVA_TO_BEDROCK_LIQUID_MAP = new Int2IntOpenHashMap(); + private static final Int2ObjectMap BEDROCK_TO_JAVA_LIQUID_MAP = new Int2ObjectOpenHashMap<>(); + + static { + /* Load block palette */ + InputStream stream = Toolbox.getResource("bedrock/runtime_block_states.dat"); + + ListTag blocksTag; + try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { + blocksTag = (ListTag) nbtInputStream.readTag(); + } catch (Exception e) { + throw new AssertionError("Unable to get blocks from runtime block states", e); + } + + Map blockStateMap = new HashMap<>(); + + for (CompoundTag tag : blocksTag.getValue()) { + if (blockStateMap.putIfAbsent(tag.getAsCompound("block"), tag) != null) { + throw new AssertionError("Duplicate block states in Bedrock palette"); + } + } + + stream = Toolbox.getResource("mappings/blocks.json"); + JsonNode blocks; + try { + blocks = Toolbox.JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java block mappings", e); + } + TObjectIntMap addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1); + List paletteList = new ArrayList<>(); + + int javaRuntimeId = -1; + int bedrockRuntimeId = 0; + Iterator> blocksIterator = blocks.fields(); + while (blocksIterator.hasNext()) { + javaRuntimeId++; + Map.Entry entry = blocksIterator.next(); + String javaId = entry.getKey(); + CompoundTag blockTag = buildBedrockState(entry.getValue()); + + CompoundTag runtimeTag = blockStateMap.remove(blockTag); + if (runtimeTag != null) { + addedStatesMap.put(blockTag, bedrockRuntimeId); + paletteList.add(runtimeTag); + } else { + int duplicateRuntimeId = addedStatesMap.get(blockTag); + if (duplicateRuntimeId == -1) { + GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!"); + } else { + JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, duplicateRuntimeId); + } + continue; + } + JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); + BEDROCK_TO_JAVA_BLOCK_MAP.put(bedrockRuntimeId, new BlockState(javaRuntimeId)); + + bedrockRuntimeId++; + } + + paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client + + BLOCKS = new ListTag<>("", CompoundTag.class, paletteList); } - public BlockEntry getBlockEntry(String javaIdentifier) { - return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.BLOCK_ENTRIES.values() - .stream().filter(blockEntry -> blockEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); + private BlockTranslator() { + } + + public static void init() { + // no-op + } + + private static CompoundTag buildBedrockState(JsonNode node) { + CompoundTagBuilder tagBuilder = CompoundTag.builder(); + tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue()); + + // check for states + if (node.has("bedrock_states")) { + Iterator> statesIterator = node.get("bedrock_states").fields(); + + while (statesIterator.hasNext()) { + Map.Entry stateEntry = statesIterator.next(); + JsonNode stateValue = stateEntry.getValue(); + switch (stateValue.getNodeType()) { + case BOOLEAN: + tagBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue()); + continue; + case STRING: + tagBuilder.stringTag(stateEntry.getKey(), stateValue.textValue()); + continue; + case NUMBER: + tagBuilder.intTag(stateEntry.getKey(), stateValue.intValue()); + } + } + } + return tagBuilder.build("block"); + } + + public static int getBedrockBlockId(BlockState state) { + return JAVA_TO_BEDROCK_BLOCK_MAP.get(state.getId()); + } + + public static BlockState getJavaBlockState(int bedrockId) { + return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId); + } + + public static int getBedrockWaterLoggedId(BlockState state) { + return JAVA_TO_BEDROCK_LIQUID_MAP.getOrDefault(state.getId(), -1); + } + + public static BlockState getJavaLiquidState(int bedrockId) { + return BEDROCK_TO_JAVA_LIQUID_MAP.get(bedrockId); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 93681a7d2..3a94a102d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -10,7 +10,7 @@ import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.TranslatorsInit; -import org.geysermc.connector.network.translators.block.BlockEntry; +import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.world.chunk.ChunkSection; public class ChunkUtils { @@ -34,14 +34,13 @@ public class ChunkUtils { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { BlockState blockState = chunk.get(x, y, z); - BlockEntry block = TranslatorsInit.getBlockTranslator().getBlockEntry(blockState); + int id = BlockTranslator.getBedrockBlockId(blockState); - section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), - block.getBedrockRuntimeId()); + section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); - if (block.isWaterlogged()) { - BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]"); - section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), water.getBedrockRuntimeId()); + int waterloggedId = BlockTranslator.getBedrockWaterLoggedId(blockState); + if (waterloggedId != -1) { + section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), waterloggedId); } } } @@ -51,22 +50,22 @@ public class ChunkUtils { } public static void updateBlock(GeyserSession session, BlockState blockState, Position position) { - BlockEntry blockEntry = TranslatorsInit.getBlockTranslator().getBlockEntry(blockState); + int blockId = BlockTranslator.getBedrockBlockId(blockState); Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ()); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(pos); - updateBlockPacket.setRuntimeId(blockEntry.getBedrockRuntimeId()); + updateBlockPacket.setRuntimeId(blockId); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); session.getUpstream().sendPacket(updateBlockPacket); UpdateBlockPacket waterPacket = new UpdateBlockPacket(); waterPacket.setDataLayer(1); waterPacket.setBlockPosition(pos); - if (blockEntry.isWaterlogged()) { - BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]"); - waterPacket.setRuntimeId(water.getBedrockRuntimeId()); + int waterloggedId = BlockTranslator.getBedrockWaterLoggedId(blockState); + if (waterloggedId != -1) { + waterPacket.setRuntimeId(waterloggedId); } else { waterPacket.setRuntimeId(0); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 6c5775d32..58f6a6556 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -4,20 +4,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nukkitx.nbt.CompoundTagBuilder; -import com.nukkitx.nbt.NbtUtils; -import com.nukkitx.nbt.stream.NBTInputStream; -import com.nukkitx.nbt.tag.CompoundTag; -import com.nukkitx.nbt.tag.ListTag; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import gnu.trove.map.TObjectIntMap; -import gnu.trove.map.hash.TObjectIntHashMap; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import org.geysermc.connector.console.GeyserLogger; -import org.geysermc.connector.network.translators.block.BlockEntry; import org.geysermc.connector.network.translators.item.ItemEntry; import java.io.InputStream; @@ -28,74 +17,12 @@ public class Toolbox { public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); public static final Collection ITEMS = new ArrayList<>(); - public static final ListTag BLOCKS; public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); - public static final Int2ObjectMap BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>(); - public static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); - public static final Int2IntMap JAVA_TO_BEDROCK_LIQUID_MAP = new Int2IntOpenHashMap(); static { - - /* Load block palette */ - InputStream stream = getResource("bedrock/runtime_block_states.dat"); - - ListTag blocksTag; - try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { - blocksTag = (ListTag) nbtInputStream.readTag(); - } catch (Exception e) { - throw new AssertionError("Unable to get blocks from runtime block states", e); - } - - Map blockStateMap = new HashMap<>(); - - for (CompoundTag tag : blocksTag.getValue()) { - if (blockStateMap.putIfAbsent(tag.getAsCompound("block"), tag) != null) { - throw new AssertionError("Duplicate block states in Bedrock palette"); - } - } - - stream = getResource("mappings/blocks.json"); - JsonNode blocks; - try { - blocks = JSON_MAPPER.readTree(stream); - } catch (Exception e) { - throw new AssertionError("Unable to load Java block mappings", e); - } - TObjectIntMap addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1); - List paletteList = new ArrayList<>(); - - int javaRuntimeId = -1; - int bedrockRuntimeId = 0; - Iterator> blocksIterator = blocks.fields(); - while (blocksIterator.hasNext()) { - javaRuntimeId++; - Map.Entry entry = blocksIterator.next(); - String javaId = entry.getKey(); - CompoundTag blockTag = buildBedrockState(entry.getValue()); - - CompoundTag runtimeTag = blockStateMap.remove(blockTag); - if (runtimeTag != null) { - addedStatesMap.put(blockTag, bedrockRuntimeId); - paletteList.add(runtimeTag); - } else { - int duplicateRuntimeId = addedStatesMap.get(blockTag); - if (duplicateRuntimeId == -1) { - GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!"); - } else { - JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, duplicateRuntimeId); - } - continue; - } - JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); - - bedrockRuntimeId++; - } - - BLOCKS = new ListTag<>("", CompoundTag.class, paletteList); - /* Load item palette */ - stream = getResource("bedrock/items.json"); + InputStream stream = getResource("bedrock/items.json"); TypeReference> itemEntriesType = new TypeReference>() { }; @@ -130,33 +57,7 @@ public class Toolbox { } } - private static CompoundTag buildBedrockState(JsonNode node) { - CompoundTagBuilder tagBuilder = CompoundTag.builder(); - tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue()); - - // check for states - if (node.has("bedrock_states")) { - Iterator> statesIterator = node.get("bedrock_states").fields(); - - while (statesIterator.hasNext()) { - Map.Entry stateEntry = statesIterator.next(); - JsonNode stateValue = stateEntry.getValue(); - switch (stateValue.getNodeType()) { - case BOOLEAN: - tagBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue()); - continue; - case STRING: - tagBuilder.stringTag(stateEntry.getKey(), stateValue.textValue()); - continue; - case NUMBER: - tagBuilder.intTag(stateEntry.getKey(), stateValue.intValue()); - } - } - } - return tagBuilder.build("block"); - } - - private static InputStream getResource(String resource) { + public static InputStream getResource(String resource) { InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource); if (stream == null) { throw new AssertionError("Unable to find resource: " + resource); From d686a009d1832a8cce4ffdf5bc4c48869f108ad6 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Tue, 31 Dec 2019 23:24:54 +0000 Subject: [PATCH 05/12] Fix most block palette issues. Others are due to the mappings --- .../translators/block/BlockTranslator.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index c4f31abe9..5a6e06acd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -28,6 +28,8 @@ public class BlockTranslator { private static final Int2IntMap JAVA_TO_BEDROCK_LIQUID_MAP = new Int2IntOpenHashMap(); private static final Int2ObjectMap BEDROCK_TO_JAVA_LIQUID_MAP = new Int2ObjectOpenHashMap<>(); + private static final int BLOCK_STATE_VERSION = 17760256; + static { /* Load block palette */ InputStream stream = Toolbox.getResource("bedrock/runtime_block_states.dat"); @@ -99,7 +101,10 @@ public class BlockTranslator { private static CompoundTag buildBedrockState(JsonNode node) { CompoundTagBuilder tagBuilder = CompoundTag.builder(); - tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue()); + tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue()) + .intTag("version", BlockTranslator.BLOCK_STATE_VERSION); + + CompoundTagBuilder statesBuilder = CompoundTag.builder(); // check for states if (node.has("bedrock_states")) { @@ -110,17 +115,17 @@ public class BlockTranslator { JsonNode stateValue = stateEntry.getValue(); switch (stateValue.getNodeType()) { case BOOLEAN: - tagBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue()); + statesBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue()); continue; case STRING: - tagBuilder.stringTag(stateEntry.getKey(), stateValue.textValue()); + statesBuilder.stringTag(stateEntry.getKey(), stateValue.textValue()); continue; case NUMBER: - tagBuilder.intTag(stateEntry.getKey(), stateValue.intValue()); + statesBuilder.intTag(stateEntry.getKey(), stateValue.intValue()); } } } - return tagBuilder.build("block"); + return tagBuilder.tag(statesBuilder.build("states")).build("block"); } public static int getBedrockBlockId(BlockState state) { From 8a19e353423c3df3237343a9beffddf6d52c815c Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 31 Dec 2019 16:49:30 -0900 Subject: [PATCH 06/12] Update mappings submodule --- connector/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 582fb1609..1ad8a1941 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 582fb1609ffde735e514b99550362a9a533fc88a +Subproject commit 1ad8a19417391710d34d72214e3aa430f84740cd From 4dff067faad244c82c7299e21f46a7224b435b92 Mon Sep 17 00:00:00 2001 From: OnlyBMan <27742182+OnlyBMan@users.noreply.github.com> Date: Wed, 5 Feb 2020 21:10:34 -0500 Subject: [PATCH 07/12] Add biomes --- .../network/session/GeyserSession.java | 21 ++++++++++- .../network/translators/BiomeTranslator.java | 35 ++++++++++++++++++ .../java/world/JavaChunkDataTranslator.java | 5 ++- .../geysermc/connector/utils/ChunkUtils.java | 1 - .../resources/bedrock/biome_definitions.dat | Bin 3544 -> 27359 bytes 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 53fecf79c..b873db90f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -41,6 +41,8 @@ import com.nukkitx.math.vector.Vector2f; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.NbtUtils; +import com.nukkitx.nbt.stream.NBTInputStream; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.GamePublishSetting; @@ -53,6 +55,7 @@ import org.geysermc.api.RemoteServer; import org.geysermc.api.session.AuthData; import org.geysermc.api.window.FormWindow; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.cache.*; @@ -60,6 +63,7 @@ import org.geysermc.connector.network.translators.Registry; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.Toolbox; +import java.io.InputStream; import java.net.InetSocketAddress; import java.util.UUID; @@ -131,7 +135,22 @@ public class GeyserSession implements Player { ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); BiomeDefinitionListPacket biomePacket = new BiomeDefinitionListPacket(); - biomePacket.setTag(CompoundTag.EMPTY); + InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat"); + if (stream == null) { + throw new AssertionError("Unable to find bedrock/biome_definitions.dat"); + } + + CompoundTag biomesTag; + + NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream); + try { + biomesTag = (CompoundTag) nbtInputStream.readTag(); + biomePacket.setTag(biomesTag); + nbtInputStream.close(); + } catch (Exception ex) { + GeyserLogger.DEFAULT.warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); + throw new AssertionError(ex); + } upstream.sendPacket(biomePacket); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java new file mode 100644 index 000000000..122788412 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java @@ -0,0 +1,35 @@ +package org.geysermc.connector.network.translators; +import java.util.Arrays; + +//Based off of ProtocolSupport's LegacyBiomeData.java https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java +//Array index formula by https://wiki.vg/Chunk_Format + +public class BiomeTranslator { + + public static byte[] toBedrockBiome(int[] biomeData) { + byte[] bedrockData = new byte[256]; + if(biomeData == null) { + return bedrockData; + } + + for (int z = 0; z < 16; z += 4) { + for (int x = 0; x < 16; x += 4) { + byte biomeId = biomeID(biomeData, x, z); + fillArray(z, x, bedrockData, biomeId); + fillArray(z + 1, x, bedrockData, biomeId); + fillArray(z + 2, x, bedrockData, biomeId); + fillArray(z + 3, x, bedrockData, biomeId); + } + } + return bedrockData; + } + + protected static void fillArray(int z, int x, byte[] legacyBiomeData, int biomeId) { + int offset = (z << 4) | x; + Arrays.fill(legacyBiomeData, offset, offset + 4, (byte) biomeId); + } + + protected static byte biomeID(int[] biomeData, int x, int z) { + return (byte) biomeData[((z >> 2) & 3) << 2 | ((x >> 2) & 3)]; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index 1a9230e6d..d74995993 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -35,6 +35,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.geysermc.api.Geyser; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.world.chunk.ChunkSection; @@ -74,7 +75,9 @@ public class JavaChunkDataTranslator extends PacketTranslator#JTC=TP{R>H)1TY2x@*)_rHV=t_$UZ=T93U_ny)(VLW6yLix@WXe z3>{JasXdIIz=v$BAeA@k?djhPOoGnV?SvnHjBmj`RZeln?%Wzsw~g) zd3sh>dA&|on{~F%=i@u;e07;u*?LpuCnqPLjVJT+syNG*%Xe@8_*wE`wJ7pgm7T3$ z%$AE43<->g4oBhia#YEYXR-grPXGc*N2a*1Ll^ooPukywD z#rj|}K3$Zn+?X>ud|Y3En);1-q85@16{kvDHB4lxSWg>8A?3QOMdeOVonxt^!&aFi zp|~iyUvG-DWqAe5zFcODq9$4t8*6U!+m|6p+z}{AqP7_Y!u{1E#f=1DfU8Y?0Xw`( z7xgkL=Bd;%k?id3%#bXKz8!FX{nLkc!IcTu9^YS;C0UYQ^;j+rEO{}@eR}|V8|gh5 z){>9Ts`9USkxIJ;mTFxX%Q=HV$u`t76=^N!)1Y7KmFRl<_i&;{x$k~3gI^`&!#W_{Pnby(YdUqFFt)btv9D%=Ck$m z#j~f=HH7i$i|IvHr!s)05O=3fr?0Z*CWoIl3z&ZM^!lHW!9{){D_-v^Pm>2Md0-hm zmL$w&rw>R^*y#(a{5;$31C2rL^o`R+HM^L8^9B5$+ z-fI(Hoi2)X>1Mn;%EpC3g0qB@z{!bi_UKQT1mfm{b}sPd`Y1V88LBZ1k_UzaA`?1~ zEu87}iHt|@8FuJ7d&W&ECo^uw7&i~X`z(?XO-;DZlaUY>HE3gxD7n49$WsV%WG~&% zg$&IdfdU-h3{f(=$d^ldqR)xkL5qfotP$-*agoDfswg>{=XGAKQyDsLY`#g{Of#0# zpoUG`d(bY%?0hM<=K(RH^HyM@`7}*z}Nr7qh|xHJh&U zx~=?noxRG6A|s{^#!LfJ#4&9i>b<8X6c~M-XB$55ZAU7I$c)5o0XParVt3)_NfWE= z(%ym^1UGenIngQvKT?74YAt46ER$LT?1S;b6$DqAD4cF!bTn$W&Ww_Wa#ko`&NDA= zhlzgBA@beZaNX;UK+*214UieJ563Z<@9wbibb5!!?uFfFvm0J7Ey3?GS{0fU(ly_Ii`s;tT(NGK-9Tau9IJtPxK zW@UpOYZMFev~&)yEU{LM%0xN`)#(DB`=V?Ieneyj88ykq3b1VE1)#mdtTLA#`$d({ zugY?6&fYqCFgLoQ;i54Csoc{*DzK{a4Ar2m3Uf{iS(KF#EeN(%Ye6LC0k!Z`3uf^( z;EIJBKz!-0_qJM}#zK!yv(;%?ri$@!ZNMcr7^~lNdh#r3r#vn&+fE;ld%4pW_MDzN z@e@=wdKcE}apO5ne6a{V9buy6PFHjV2ezYY;84*W;RvNTL%H{^|6{!S8KD>eY;VLd zNS-^#VTOPr;xb(SI?M?h5q{hyH$QBKNf^q%SS5^?hDmf5y~%>*Po!SC?(v5-M^PQ@ z!jLONVx>anF`ENL_>9973MKmS4h93@RZi05ViDu;HxL zOT(l#`-CLdEz$dtBS1n~3mD8}e0~{8`=L6c@rd*-8_}5j+$bv2$7FE7a^28(P_M26P_LN%pu-(H2DfPUkt{HqjIP9}=^!0g$uM>P*xF%bu`_d>6*Gpf+r5Qde9 zNU4$0A^p!Wp$LtZ25;YVKdjs|;#mg@J)0Zw9GXD(e&0~X8efS1?EzM$=uje1KfQPk zYWl6nU&!?P#=u(99a6*Wx9B_Ajd8}?GLd2Ke?J(+1eywGn1vEM>bHfWUuh>HS{vAf z*|&P%37`-t*8-t|6DxNFW?)1%$c$-7GnR1{ZU|{^k4WJXnjBceqaTeqyEsUl9M%0e zw9J+%#Gf}Pl$|8V+^jrpWQiaaa<$g(#8w&@J}md)gEZW@Q?s?{8_H7ZFs(NY_)!%R z+PF=zN691AwfI4_>zkadU+-X!6^df0 zG(<$o6EN?@G3#7k^Uj_hpEhyVN~Jqvx_%!}xtPF#eqY!(-*}pDux?k)Hx|35F+`Mn zL^uYy9ay$m{%LbBp%XQixxyHR$K-@npm#d9hLb4qG9x!8FEW)xURL0Pd%)`Ymp?4q z*T+IS1$6Q`L6#Bf6fb}=#ggsAL}Biy8E}z*KJhnti{QFlIi9xve$|6K1K+i+M_sl9 z<#W4uq4f!FYn)cEXE_@>$UO>Q@^Lo~Z6cOmK#MTF-mG4T70@)iKc(VbmEUJl`C@>d zpy`*+WjfmZQjTfhJ>1lHiW~Mo*J;oLF%SFBxJ-W}ts7e$l8KA?VR`>nMA!2d9=fuh zCGVlF`8pDH$Y23XUCQh$N0$mVlq-iaHW*F8;j_$_iNx}}^(Hp|6D49*iw zokt=uIZ$n9jwAB(_TKOT{du1QSXX@*=(=f2r2XLiFmjQ}4~U23DX_>;hnA!l7pn^$ zf{~$c53jb^kKhVJh@GlTj6jv4AtOFRp}aj0NcPf4mWXWdZHqJF*!Fw4kFA%C7wyEm zN!}X*ACar9&8rc9L$T|i&^~~2Iw~i6l@fPBn+CQ+M@eHOmeL)Iy>39!4yr@EiM5P52DO8ew}H?clZjPVYFp1mN1? zz-R8sfpI{$zhd9Ktl0I?l&XX|7fSii_kWa;U^cUPESwoD+Bh}ZKN(oUxEOYeo+LO+%NCF9o~69sENEo+B~hwW-2>|2dg}HNvOEd zmZwpnwQp8T7h-5Kq&`WWxT`w;H}-b>KW!>7&D!tE2o&pvtntoiTx?|=8~`N{L=^s(XJ zUgMjee)4SR9!k$snGnK~YiAmIiRAhqO$CR-2#7D$#n%)MA6Fs1>QC7x$@XIA&-kI8S9AY!-;B*VENveN7V z%KzIhm?d+|$urV}yJ}P=rU#2&dsvjI`Q{@$ld5Zqxc!Y760>;s`VSf;4EuPEnMZ@Q z09d~z2(DY1kj#p%9YNKEZ-e=;_q(^>8I}ny$6)O35hw_j1So_DeJfF-WZScC#LC^* zL_+npI|2o~Pk(1h1L>fE(Qmq;$o@kHiS8gIc<)u(Y8(vIs_TzI3lW>AO;!8}*FDk( z-`=Hc`mSyg7X5?5B8`Rmof8j#hh&#~4c*veNCZS4Mdza)tC@}&eOtmZ?nPMs-)%1m pOW3QtsmzNMZf~xBPDamt zwN2JCiauW7-cKCY-O4l;$^)5@)4Sd7u5nubb=PwEm1DRGs!vu8U4`a6*(w9I*TEbrPp$h1;J(EcD7E{dq@~VP+WI-`C$x* zsKV9dNYh>mZQAm{aAd>uK)G73WV}*Jm_F!cK_!?jnA$*L)n6MK45(MsP@Aa81UjrG z3~wv9CV}wFm;bU=KC5ThCJm7Iw`BXk2iS-~1|Ibte{XCAu=sc)#+62d1YVt`QL}7Z zHbE=QsPt&fG`xV6r0H67+59r%vgnjE>iUA>W`}E;kYEBxL@Y+(>JY1(u9n56!|Jomu=>Bp0 z=I!AkurThq+ABvYDadkg+ORn}-y-1A-UDacxc{P(g1=SDH>288D>E05{+pIB4keV zB~iStqZLRmXdk?9%zKP0H>f%J03iX`$J~d9M$caVxpBST5AaogTnIQV5CXFZyr%7} psb%|9r>Gwnysi8&8g<`K)SUSy8dtNClx%UwwCY^8=x$Rv{{YL+hwuOZ From 0bb18d26b053fe96e554f444f26fe467b7adecad Mon Sep 17 00:00:00 2001 From: OnlyBMan <27742182+OnlyBMan@users.noreply.github.com> Date: Wed, 5 Feb 2020 22:02:23 -0500 Subject: [PATCH 08/12] Load biome definitions only once instead of everytime a new player joins --- .../network/session/GeyserSession.java | 21 +++---------------- .../org/geysermc/connector/utils/Toolbox.java | 18 ++++++++++++++++ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index b873db90f..f4835863c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -134,24 +134,9 @@ public class GeyserSession implements Player { ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); - BiomeDefinitionListPacket biomePacket = new BiomeDefinitionListPacket(); - InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat"); - if (stream == null) { - throw new AssertionError("Unable to find bedrock/biome_definitions.dat"); - } - - CompoundTag biomesTag; - - NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream); - try { - biomesTag = (CompoundTag) nbtInputStream.readTag(); - biomePacket.setTag(biomesTag); - nbtInputStream.close(); - } catch (Exception ex) { - GeyserLogger.DEFAULT.warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); - throw new AssertionError(ex); - } - upstream.sendPacket(biomePacket); + BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); + biomeDefinitionListPacket.setTag(Toolbox.BIOMES); + upstream.sendPacket(biomeDefinitionListPacket); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); entityPacket.setTag(CompoundTag.EMPTY); diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index b90aa2352..d2beb8074 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -31,6 +31,7 @@ import com.nukkitx.nbt.stream.NBTInputStream; import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.ListTag; import com.nukkitx.nbt.tag.Tag; +import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -47,11 +48,27 @@ public class Toolbox { public static final Collection ITEMS = new ArrayList<>(); public static ListTag BLOCKS; + public static CompoundTag BIOMES; public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); public static final Int2ObjectMap BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>(); public static void init() { + InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat"); + if (biomestream == null) { + throw new AssertionError("Unable to find bedrock/biome_definitions.dat"); + } + + CompoundTag biomesTag; + + try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(biomestream)){ + biomesTag = (CompoundTag) biomenbtInputStream.readTag(); + BIOMES = biomesTag; + } catch (Exception ex) { + GeyserLogger.DEFAULT.warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); + throw new AssertionError(ex); + } + InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/runtime_block_states.dat"); if (stream == null) { throw new AssertionError("Unable to find bedrock/runtime_block_states.dat"); @@ -69,6 +86,7 @@ public class Toolbox { } BLOCKS = blocksTag; + InputStream stream2 = Toolbox.class.getClassLoader().getResourceAsStream("bedrock/items.json"); if (stream2 == null) { throw new AssertionError("Items Table not found"); From a6aef4ad424664968fc123f55b54a01b5ba69293 Mon Sep 17 00:00:00 2001 From: Redned Date: Sun, 9 Feb 2020 14:45:38 -0600 Subject: [PATCH 09/12] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 005aa4a08..a94914db1 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Please note, Geyser is **not** (currently) a plugin. Watch the video below or ta - Sounds - Block Particles - Block Entities ([`block-entities`](https://github.com/GeyserMC/Geyser/tree/block-entities)) -- Biome Colors - Some Entity Flags - Proper Movement - Support to be Ran as a Plugin ([`plugin`](https://github.com/GeyserMC/Geyser/tree/plugin)) From c9f33f8344bf359625802f6c9f3fc2ed49eae5a3 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Sun, 9 Feb 2020 21:22:39 +0000 Subject: [PATCH 10/12] Pull in new submodule changes --- connector/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 1ad8a1941..278c73449 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 1ad8a19417391710d34d72214e3aa430f84740cd +Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a From 9594b425230bbc9ccf8da37c8c0ea3324f47d930 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Sun, 9 Feb 2020 22:06:22 +0000 Subject: [PATCH 11/12] Add waterlogging support --- .../translators/block/BlockTranslator.java | 36 +++++++++++++------ .../geysermc/connector/utils/ChunkUtils.java | 12 +++---- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index ec651884b..f4753b8dd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -34,10 +34,7 @@ import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.ListTag; import gnu.trove.map.TObjectIntMap; import gnu.trove.map.hash.TObjectIntHashMap; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.*; import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.utils.Toolbox; @@ -47,11 +44,11 @@ import java.util.*; public class BlockTranslator { public static final ListTag BLOCKS; public static final BlockState AIR = new BlockState(0); + public static final int BEDROCK_WATER_ID; private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>(); - private static final Int2IntMap JAVA_TO_BEDROCK_LIQUID_MAP = new Int2IntOpenHashMap(); - private static final Int2ObjectMap BEDROCK_TO_JAVA_LIQUID_MAP = new Int2ObjectOpenHashMap<>(); + private static final IntSet WATERLOGGED = new IntOpenHashSet(); private static final int BLOCK_STATE_VERSION = 17760256; @@ -84,6 +81,7 @@ public class BlockTranslator { TObjectIntMap addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1); List paletteList = new ArrayList<>(); + int waterRuntimeId = -1; int javaRuntimeId = -1; int bedrockRuntimeId = 0; Iterator> blocksIterator = blocks.fields(); @@ -93,6 +91,18 @@ public class BlockTranslator { String javaId = entry.getKey(); CompoundTag blockTag = buildBedrockState(entry.getValue()); + if ("minecraft:water[level=0]".equals(javaId)) { + waterRuntimeId = bedrockRuntimeId; + } + boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue(); + + if (waterlogged) { + BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId)); + WATERLOGGED.add(javaRuntimeId); + } else { + BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, new BlockState(javaRuntimeId)); + } + CompoundTag runtimeTag = blockStateMap.remove(blockTag); if (runtimeTag != null) { addedStatesMap.put(blockTag, bedrockRuntimeId); @@ -107,11 +117,15 @@ public class BlockTranslator { continue; } JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); - BEDROCK_TO_JAVA_BLOCK_MAP.put(bedrockRuntimeId, new BlockState(javaRuntimeId)); bedrockRuntimeId++; } + if (waterRuntimeId == -1) { + throw new AssertionError("Unable to find water in palette"); + } + BEDROCK_WATER_ID = waterRuntimeId; + paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client BLOCKS = new ListTag<>("", CompoundTag.class, paletteList); @@ -161,11 +175,11 @@ public class BlockTranslator { return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId); } - public static int getBedrockWaterLoggedId(BlockState state) { - return JAVA_TO_BEDROCK_LIQUID_MAP.getOrDefault(state.getId(), -1); + public static boolean isWaterlogged(BlockState state) { + return WATERLOGGED.contains(state.getId()); } - public static BlockState getJavaLiquidState(int bedrockId) { - return BEDROCK_TO_JAVA_LIQUID_MAP.get(bedrockId); + public static BlockState getJavaWaterloggedState(int bedrockId) { + return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 8dde34a5b..cb5e0fd8b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -38,6 +38,8 @@ import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.world.chunk.ChunkSection; +import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID; + public class ChunkUtils { public static ChunkData translateToBedrock(Column column) { ChunkData chunkData = new ChunkData(); @@ -63,9 +65,8 @@ public class ChunkUtils { section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); - int waterloggedId = BlockTranslator.getBedrockWaterLoggedId(blockState); - if (waterloggedId != -1) { - section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), waterloggedId); + if (BlockTranslator.isWaterlogged(blockState)) { + section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID); } } } @@ -88,9 +89,8 @@ public class ChunkUtils { UpdateBlockPacket waterPacket = new UpdateBlockPacket(); waterPacket.setDataLayer(1); waterPacket.setBlockPosition(pos); - int waterloggedId = BlockTranslator.getBedrockWaterLoggedId(blockState); - if (waterloggedId != -1) { - waterPacket.setRuntimeId(waterloggedId); + if (BlockTranslator.isWaterlogged(blockState)) { + waterPacket.setRuntimeId(BEDROCK_WATER_ID); } else { waterPacket.setRuntimeId(0); } From c70cf0bb4758dfa956ea718f175875af983ad59e Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Sun, 9 Feb 2020 22:55:07 +0000 Subject: [PATCH 12/12] Make biomes tag final --- .../src/main/java/org/geysermc/connector/utils/Toolbox.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 3dc5cdbbb..5dba02b69 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -45,7 +45,7 @@ import java.util.*; public class Toolbox { public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); - public static CompoundTag BIOMES; + public static final CompoundTag BIOMES; public static final Collection ITEMS = new ArrayList<>();