Merge branch 'master' into feature/new-protocol-lib

This commit is contained in:
RednedEpic 2020-02-14 17:57:28 -06:00
commit 4bb34cdcce
12 changed files with 255 additions and 168 deletions

View file

@ -34,7 +34,6 @@ Please note, Geyser is **not** (currently) a plugin. Watch the video below or ta
- Sounds - Sounds
- Block Particles - Block Particles
- Block Entities ([`block-entities`](https://github.com/GeyserMC/Geyser/tree/block-entities)) - Block Entities ([`block-entities`](https://github.com/GeyserMC/Geyser/tree/block-entities))
- Biome Colors
- Some Entity Flags - Some Entity Flags
- Proper Movement - Proper Movement
- Support to be Ran as a Plugin ([`plugin`](https://github.com/GeyserMC/Geyser/tree/plugin)) - Support to be Ran as a Plugin ([`plugin`](https://github.com/GeyserMC/Geyser/tree/plugin))

View file

@ -58,6 +58,7 @@ import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.session.cache.*;
import org.geysermc.connector.network.translators.Registry; 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.ChunkUtils;
import org.geysermc.connector.utils.Toolbox; import org.geysermc.connector.utils.Toolbox;
@ -131,9 +132,9 @@ public class GeyserSession implements Player {
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
BiomeDefinitionListPacket biomePacket = new BiomeDefinitionListPacket(); BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
biomePacket.setTag(CompoundTag.EMPTY); biomeDefinitionListPacket.setTag(Toolbox.BIOMES);
upstream.sendPacket(biomePacket); upstream.sendPacket(biomeDefinitionListPacket);
AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
entityPacket.setTag(CompoundTag.EMPTY); entityPacket.setTag(CompoundTag.EMPTY);
@ -320,7 +321,7 @@ public class GeyserSession implements Player {
// startGamePacket.setCurrentTick(0); // startGamePacket.setCurrentTick(0);
startGamePacket.setEnchantmentSeed(0); startGamePacket.setEnchantmentSeed(0);
startGamePacket.setMultiplayerCorrelationId(""); startGamePacket.setMultiplayerCorrelationId("");
startGamePacket.setBlockPalette(Toolbox.BLOCKS); startGamePacket.setBlockPalette(BlockTranslator.BLOCKS);
startGamePacket.setItemEntries(Toolbox.ITEMS); startGamePacket.setItemEntries(Toolbox.ITEMS);
startGamePacket.setVanillaVersion("*"); startGamePacket.setVanillaVersion("*");
// startGamePacket.setMovementServerAuthoritative(true); // startGamePacket.setMovementServerAuthoritative(true);

View file

@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit; 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 org.geysermc.connector.world.chunk.ChunkPosition;
import java.util.HashMap; 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); ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4);
if (!chunks.containsKey(chunkPosition)) if (!chunks.containsKey(chunkPosition))
return BlockEntry.AIR; return BlockTranslator.AIR;
Column column = chunks.get(chunkPosition); Column column = chunks.get(chunkPosition);
Chunk chunk = column.getChunks()[position.getY() >> 4]; Chunk chunk = column.getChunks()[position.getY() >> 4];
Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ()); Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ());
if (chunk != null) { if (chunk != null) {
BlockState blockState = chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); return chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
return TranslatorsInit.getBlockTranslator().getBlockEntry(blockState);
} }
return BlockEntry.AIR; return BlockTranslator.AIR;
} }
public void removeChunk(ChunkPosition position) { public void removeChunk(ChunkPosition position) {

View file

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

View file

@ -70,9 +70,6 @@ public class TranslatorsInit {
@Getter @Getter
private static ItemTranslator itemTranslator; private static ItemTranslator itemTranslator;
@Getter
private static BlockTranslator blockTranslator;
@Getter @Getter
private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator(); private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
@ -160,7 +157,7 @@ public class TranslatorsInit {
Registry.registerBedrock(ShowCreditsPacket.class, new BedrockShowCreditsTranslator()); Registry.registerBedrock(ShowCreditsPacket.class, new BedrockShowCreditsTranslator());
itemTranslator = new ItemTranslator(); itemTranslator = new ItemTranslator();
blockTranslator = new BlockTranslator(); BlockTranslator.init();
registerInventoryTranslators(); registerInventoryTranslators();
} }

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2019-2020 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()));
}
}

View file

@ -25,21 +25,161 @@
package org.geysermc.connector.network.translators.block; 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.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.*;
import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.utils.Toolbox; import org.geysermc.connector.utils.Toolbox;
import java.util.HashMap; import java.io.InputStream;
import java.util.Map; import java.util.*;
public class BlockTranslator { public class BlockTranslator {
private final Map<String, BlockEntry> javaIdentifierMap = new HashMap<>(); public static final ListTag<CompoundTag> BLOCKS;
public static final BlockState AIR = new BlockState(0);
public static final int BEDROCK_WATER_ID;
public BlockEntry getBlockEntry(BlockState state) { private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
return Toolbox.BLOCK_ENTRIES.get(state.getId()); private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
private static final IntSet WATERLOGGED = new IntOpenHashSet();
private static final int BLOCK_STATE_VERSION = 17760256;
static {
/* Load block palette */
InputStream stream = Toolbox.getResource("bedrock/runtime_block_states.dat");
ListTag<CompoundTag> blocksTag;
try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
blocksTag = (ListTag<CompoundTag>) nbtInputStream.readTag();
} catch (Exception e) {
throw new AssertionError("Unable to get blocks from runtime block states", e);
}
Map<CompoundTag, CompoundTag> blockStateMap = new HashMap<>();
for (CompoundTag tag : blocksTag.getValue()) {
if (blockStateMap.putIfAbsent(tag.getCompound("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<CompoundTag> addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1);
List<CompoundTag> paletteList = new ArrayList<>();
int waterRuntimeId = -1;
int javaRuntimeId = -1;
int bedrockRuntimeId = 0;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
Map.Entry<String, JsonNode> entry = blocksIterator.next();
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);
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++;
}
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);
} }
public BlockEntry getBlockEntry(String javaIdentifier) { private BlockTranslator() {
return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.BLOCK_ENTRIES.values() }
.stream().filter(blockEntry -> blockEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
public static void init() {
// no-op
}
private static CompoundTag buildBedrockState(JsonNode node) {
CompoundTagBuilder tagBuilder = CompoundTag.builder();
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")) {
Iterator<Map.Entry<String, JsonNode>> statesIterator = node.get("bedrock_states").fields();
while (statesIterator.hasNext()) {
Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
JsonNode stateValue = stateEntry.getValue();
switch (stateValue.getNodeType()) {
case BOOLEAN:
statesBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue());
continue;
case STRING:
statesBuilder.stringTag(stateEntry.getKey(), stateValue.textValue());
continue;
case NUMBER:
statesBuilder.intTag(stateEntry.getKey(), stateValue.intValue());
}
}
}
return tagBuilder.tag(statesBuilder.build("states")).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 boolean isWaterlogged(BlockState state) {
return WATERLOGGED.contains(state.getId());
}
public static BlockState getJavaWaterloggedState(int bedrockId) {
return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
} }
} }

View file

@ -35,6 +35,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.geysermc.api.Geyser; import org.geysermc.api.Geyser;
import org.geysermc.connector.network.session.GeyserSession; 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.network.translators.PacketTranslator;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
import org.geysermc.connector.world.chunk.ChunkSection; import org.geysermc.connector.world.chunk.ChunkSection;
@ -74,7 +75,9 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
section.writeToNetwork(byteBuf); section.writeToNetwork(byteBuf);
} }
byteBuf.writeBytes(chunkData.biomes); // Biomes - 256 bytes byte[] bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData());
byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes
byteBuf.writeByte(0); // Border blocks - Edu edition only byteBuf.writeByte(0); // Border blocks - Edu edition only
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now

View file

@ -35,9 +35,11 @@ import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.TranslatorsInit; 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; import org.geysermc.connector.world.chunk.ChunkSection;
import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
public class ChunkUtils { public class ChunkUtils {
public static ChunkData translateToBedrock(Column column) { public static ChunkData translateToBedrock(Column column) {
ChunkData chunkData = new ChunkData(); ChunkData chunkData = new ChunkData();
@ -59,14 +61,12 @@ public class ChunkUtils {
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
BlockState blockState = chunk.get(x, y, 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), section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
block.getBedrockRuntimeId());
if (block.isWaterlogged()) { if (BlockTranslator.isWaterlogged(blockState)) {
BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]"); section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), water.getBedrockRuntimeId());
} }
} }
} }
@ -76,22 +76,21 @@ public class ChunkUtils {
} }
public static void updateBlock(GeyserSession session, BlockState blockState, Position position) { 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()); Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ());
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0); updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(pos); updateBlockPacket.setBlockPosition(pos);
updateBlockPacket.setRuntimeId(blockEntry.getBedrockRuntimeId()); updateBlockPacket.setRuntimeId(blockId);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
session.getUpstream().sendPacket(updateBlockPacket); session.getUpstream().sendPacket(updateBlockPacket);
UpdateBlockPacket waterPacket = new UpdateBlockPacket(); UpdateBlockPacket waterPacket = new UpdateBlockPacket();
waterPacket.setDataLayer(1); waterPacket.setDataLayer(1);
waterPacket.setBlockPosition(pos); waterPacket.setBlockPosition(pos);
if (blockEntry.isWaterlogged()) { if (BlockTranslator.isWaterlogged(blockState)) {
BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]"); waterPacket.setRuntimeId(BEDROCK_WATER_ID);
waterPacket.setRuntimeId(water.getBedrockRuntimeId());
} else { } else {
waterPacket.setRuntimeId(0); waterPacket.setRuntimeId(0);
} }
@ -131,7 +130,6 @@ public class ChunkUtils {
public static final class ChunkData { public static final class ChunkData {
public ChunkSection[] sections; public ChunkSection[] sections;
public byte[] biomes = new byte[256];
public byte[] blockEntities = new byte[0]; public byte[] blockEntities = new byte[0];
} }
} }

View file

@ -25,19 +25,18 @@
package org.geysermc.connector.utils; 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.fasterxml.jackson.databind.ObjectMapper;
import com.nukkitx.nbt.NbtUtils; import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.nbt.stream.NBTInputStream; import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.tag.CompoundTag; 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 com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.console.GeyserLogger;
import org.geysermc.connector.network.translators.block.BlockEntry;
import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemEntry;
import java.io.InputStream; import java.io.InputStream;
@ -45,105 +44,75 @@ import java.util.*;
public class Toolbox { public class Toolbox {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
public static final CompoundTag BIOMES;
public static final Collection<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>(); public static final Collection<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>();
public static ListTag<CompoundTag> BLOCKS;
public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
public static final Int2ObjectMap<BlockEntry> BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>();
public static void init() { static {
InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/runtime_block_states.dat"); /* Load biomes */
if (stream == null) { InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
throw new AssertionError("Unable to find bedrock/runtime_block_states.dat"); if (biomestream == null) {
throw new AssertionError("Unable to find bedrock/biome_definitions.dat");
} }
ListTag<CompoundTag> blocksTag; CompoundTag biomesTag;
NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream); try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(biomestream)){
try { biomesTag = (CompoundTag) biomenbtInputStream.readTag();
blocksTag = (ListTag<CompoundTag>) nbtInputStream.readTag(); BIOMES = biomesTag;
nbtInputStream.close();
} catch (Exception ex) { } catch (Exception ex) {
GeyserLogger.DEFAULT.warning("Failed to get blocks from runtime block states, please report this error!"); GeyserLogger.DEFAULT.warning("Failed to get biomes from biome definitions, is there something wrong with the file?");
throw new AssertionError(ex); throw new AssertionError(ex);
} }
BLOCKS = blocksTag; /* Load item palette */
InputStream stream2 = Toolbox.class.getClassLoader().getResourceAsStream("bedrock/items.json"); InputStream stream = getResource("bedrock/items.json");
if (stream2 == null) {
throw new AssertionError("Items Table not found");
}
ObjectMapper startGameItemMapper = new ObjectMapper(); TypeReference<List<JsonNode>> itemEntriesType = new TypeReference<List<JsonNode>>() {
List<Map> startGameItems = new ArrayList<>(); };
List<JsonNode> itemEntries;
try { try {
startGameItems = startGameItemMapper.readValue(stream2, ArrayList.class); itemEntries = JSON_MAPPER.readValue(stream, itemEntriesType);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
} }
for (Map entry : startGameItems) { for (JsonNode entry : itemEntries) {
ITEMS.add(new StartGamePacket.ItemEntry((String) entry.get("name"), (short) ((int) entry.get("id")))); ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
} }
InputStream itemStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/items.json"); stream = getResource("mappings/items.json");
ObjectMapper itemMapper = new ObjectMapper();
Map<String, Map<String, Object>> items = new HashMap<>();
JsonNode items;
try { try {
items = itemMapper.readValue(itemStream, LinkedHashMap.class); items = JSON_MAPPER.readTree(stream);
} catch (Exception ex) { } catch (Exception e) {
ex.printStackTrace(); throw new AssertionError("Unable to load Java runtime item IDs", e);
} }
int itemIndex = 0; int itemIndex = 0;
for (Map.Entry<String, Map<String, Object>> itemEntry : items.entrySet()) { Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
ITEM_ENTRIES.put(itemIndex, new ItemEntry(itemEntry.getKey(), itemIndex, (int) itemEntry.getValue().get("bedrock_id"), (int) itemEntry.getValue().get("bedrock_data"))); while (iterator.hasNext()) {
Map.Entry<String, JsonNode> 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++; itemIndex++;
} }
}
InputStream blockStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/blocks.json"); public static InputStream getResource(String resource) {
ObjectMapper blockMapper = new ObjectMapper(); InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource);
Map<String, Map<String, Object>> blocks = new HashMap<>(); if (stream == null) {
throw new AssertionError("Unable to find resource: " + resource);
try {
blocks = blockMapper.readValue(blockStream, LinkedHashMap.class);
} catch (Exception ex) {
ex.printStackTrace();
} }
return stream;
}
int javaIndex = -1; public static void init() {
javaLoop: // no-op
for (Map.Entry<String, Map<String, Object>> javaEntry : blocks.entrySet()) {
javaIndex++;
String wantedIdentifier = (String) javaEntry.getValue().get("bedrock_identifier");
Map<String, Object> wantedStates = (Map<String, Object>) javaEntry.getValue().get("bedrock_states");
int bedrockIndex = -1;
bedrockLoop:
for (CompoundTag bedrockEntry : BLOCKS.getValue()) {
bedrockIndex++;
CompoundTag blockTag = bedrockEntry.getCompound("block");
if (blockTag.getString("name").equals(wantedIdentifier)) {
if (wantedStates != null) {
Map<String, Tag<?>> bedrockStates = blockTag.getCompound("states").getValue();
for (Map.Entry<String, Object> 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) != 0;
if (!stateEntry.getValue().equals(bedrockStateValue))
continue bedrockLoop;
}
}
BlockEntry blockEntry = new BlockEntry(javaEntry.getKey(), javaIndex, bedrockIndex);
BLOCK_ENTRIES.put(javaIndex, blockEntry);
continue javaLoop;
}
}
GeyserLogger.DEFAULT.debug("Mapping " + javaEntry.getKey() + " was not found for bedrock edition!");
}
} }
} }

@ -1 +1 @@
Subproject commit 1ad8a19417391710d34d72214e3aa430f84740cd Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a