forked from GeyserMC/Geyser
Merge branch 'master' into plugin
This commit is contained in:
commit
41a24ca6ab
22 changed files with 315 additions and 190 deletions
|
@ -34,7 +34,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
||||||
- 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))
|
||||||
|
|
|
@ -156,6 +156,8 @@ public class Entity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateBedrockAttributes(GeyserSession session) {
|
public void updateBedrockAttributes(GeyserSession session) {
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
List<com.nukkitx.protocol.bedrock.data.Attribute> attributes = new ArrayList<>();
|
List<com.nukkitx.protocol.bedrock.data.Attribute> attributes = new ArrayList<>();
|
||||||
for (Map.Entry<AttributeType, Attribute> entry : this.attributes.entrySet()) {
|
for (Map.Entry<AttributeType, Attribute> entry : this.attributes.entrySet()) {
|
||||||
if (!entry.getValue().getType().isBedrockAttribute())
|
if (!entry.getValue().getType().isBedrockAttribute())
|
||||||
|
@ -182,7 +184,7 @@ public class Entity {
|
||||||
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
|
||||||
// metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20);
|
// metadata.getFlags().setFlag(EntityFlag.INVISIBLE, (xd & 0x20) == 0x20);
|
||||||
if ((xd & 0x20) == 0x20)
|
if ((xd & 0x20) == 0x20)
|
||||||
metadata.put(EntityData.SCALE, 0.01f);
|
metadata.put(EntityData.SCALE, 0.0f);
|
||||||
else
|
else
|
||||||
metadata.put(EntityData.SCALE, scale);
|
metadata.put(EntityData.SCALE, scale);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +195,8 @@ public class Entity {
|
||||||
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name));
|
metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name));
|
||||||
break;
|
break;
|
||||||
case 3: // is custom name visible
|
case 3: // is custom name visible
|
||||||
metadata.getFlags().setFlag(EntityFlag.ALWAYS_SHOW_NAME, (boolean) entityMetadata.getValue());
|
if (!this.is(PlayerEntity.class))
|
||||||
|
metadata.put(EntityData.ALWAYS_SHOW_NAMETAG, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0));
|
||||||
break;
|
break;
|
||||||
case 4: // silent
|
case 4: // silent
|
||||||
metadata.getFlags().setFlag(EntityFlag.SILENT, (boolean) entityMetadata.getValue());
|
metadata.getFlags().setFlag(EntityFlag.SILENT, (boolean) entityMetadata.getValue());
|
||||||
|
@ -203,6 +206,12 @@ public class Entity {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateBedrockMetadata(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBedrockMetadata(GeyserSession session) {
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||||
entityDataPacket.setRuntimeEntityId(geyserId);
|
entityDataPacket.setRuntimeEntityId(geyserId);
|
||||||
entityDataPacket.getMetadata().putAll(metadata);
|
entityDataPacket.getMetadata().putAll(metadata);
|
||||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.connector.entity;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.ContainerId;
|
||||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.ItemData;
|
import com.nukkitx.protocol.bedrock.data.ItemData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
|
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
|
||||||
|
@ -42,11 +43,12 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||||
@Setter
|
@Setter
|
||||||
public class LivingEntity extends Entity {
|
public class LivingEntity extends Entity {
|
||||||
|
|
||||||
protected ItemData helmet;
|
protected ItemData helmet = ItemData.AIR;
|
||||||
protected ItemData chestplate;
|
protected ItemData chestplate = ItemData.AIR;
|
||||||
protected ItemData leggings;
|
protected ItemData leggings = ItemData.AIR;
|
||||||
protected ItemData boots;
|
protected ItemData boots = ItemData.AIR;
|
||||||
protected ItemData hand = ItemData.of(0, (short) 0, 0);
|
protected ItemData hand = ItemData.AIR;
|
||||||
|
protected ItemData offHand = ItemData.AIR;
|
||||||
|
|
||||||
public LivingEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
public LivingEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||||
|
@ -80,11 +82,22 @@ public class LivingEntity extends Entity {
|
||||||
armorEquipmentPacket.setLeggings(leggings);
|
armorEquipmentPacket.setLeggings(leggings);
|
||||||
armorEquipmentPacket.setBoots(boots);
|
armorEquipmentPacket.setBoots(boots);
|
||||||
|
|
||||||
MobEquipmentPacket mobEquipmentPacket = new MobEquipmentPacket();
|
MobEquipmentPacket handPacket = new MobEquipmentPacket();
|
||||||
mobEquipmentPacket.setRuntimeEntityId(geyserId);
|
handPacket.setRuntimeEntityId(geyserId);
|
||||||
mobEquipmentPacket.setItem(hand);
|
handPacket.setItem(hand);
|
||||||
|
handPacket.setHotbarSlot(-1);
|
||||||
|
handPacket.setInventorySlot(0);
|
||||||
|
handPacket.setContainerId(ContainerId.INVENTORY);
|
||||||
|
|
||||||
|
MobEquipmentPacket offHandPacket = new MobEquipmentPacket();
|
||||||
|
offHandPacket.setRuntimeEntityId(geyserId);
|
||||||
|
offHandPacket.setItem(offHand);
|
||||||
|
offHandPacket.setHotbarSlot(-1);
|
||||||
|
offHandPacket.setInventorySlot(0);
|
||||||
|
offHandPacket.setContainerId(ContainerId.OFFHAND);
|
||||||
|
|
||||||
session.getUpstream().sendPacket(armorEquipmentPacket);
|
session.getUpstream().sendPacket(armorEquipmentPacket);
|
||||||
session.getUpstream().sendPacket(mobEquipmentPacket);
|
session.getUpstream().sendPacket(handPacket);
|
||||||
|
session.getUpstream().sendPacket(offHandPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,10 +83,13 @@ public class PlayerEntity extends LivingEntity {
|
||||||
addPlayerPacket.setCustomFlags(0);
|
addPlayerPacket.setCustomFlags(0);
|
||||||
addPlayerPacket.setDeviceId("");
|
addPlayerPacket.setDeviceId("");
|
||||||
addPlayerPacket.setPlatformChatId("");
|
addPlayerPacket.setPlatformChatId("");
|
||||||
addPlayerPacket.getMetadata().putAll(getMetadata());
|
addPlayerPacket.getMetadata().putAll(metadata);
|
||||||
|
|
||||||
valid = true;
|
valid = true;
|
||||||
session.getUpstream().sendPacket(addPlayerPacket);
|
session.getUpstream().sendPacket(addPlayerPacket);
|
||||||
|
|
||||||
|
updateEquipment(session);
|
||||||
|
updateBedrockAttributes(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayer(GeyserSession session) {
|
public void sendPlayer(GeyserSession session) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class ArmorStandEntity extends LivingEntity {
|
||||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||||
if (entityMetadata.getType() == MetadataType.BYTE) {
|
if (entityMetadata.getType() == MetadataType.BYTE) {
|
||||||
byte xd = (byte) entityMetadata.getValue();
|
byte xd = (byte) entityMetadata.getValue();
|
||||||
if((xd & 0x01) == 0x01) {
|
if((xd & 0x01) == 0x01 && !(metadata.get(EntityData.SCALE).equals(0.0f))) {
|
||||||
metadata.put(EntityData.SCALE, .55f);
|
metadata.put(EntityData.SCALE, .55f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ import org.geysermc.connector.network.session.auth.AuthData;
|
||||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||||
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;
|
||||||
import org.geysermc.floodgate.util.BedrockData;
|
import org.geysermc.floodgate.util.BedrockData;
|
||||||
|
@ -148,9 +149,9 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
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);
|
||||||
|
@ -373,7 +374,7 @@ public class GeyserSession implements CommandSender {
|
||||||
// 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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -56,9 +56,13 @@ public class EntityCache {
|
||||||
|
|
||||||
public void spawnEntity(Entity entity) {
|
public void spawnEntity(Entity entity) {
|
||||||
entity.moveAbsolute(entity.getPosition(), entity.getRotation().getX(), entity.getRotation().getY());
|
entity.moveAbsolute(entity.getPosition(), entity.getRotation().getX(), entity.getRotation().getY());
|
||||||
|
cacheEntity(entity);
|
||||||
|
entity.spawnEntity(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cacheEntity(Entity entity) {
|
||||||
entityIdTranslations.put(entity.getEntityId(), entity.getGeyserId());
|
entityIdTranslations.put(entity.getEntityId(), entity.getGeyserId());
|
||||||
entities.put(entity.getGeyserId(), entity);
|
entities.put(entity.getGeyserId(), entity);
|
||||||
entity.spawnEntity(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeEntity(Entity entity, boolean force) {
|
public boolean removeEntity(Entity entity, boolean force) {
|
||||||
|
|
|
@ -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)];
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.*;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.*;
|
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.entity.spawn.*;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
|
||||||
|
@ -70,9 +73,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();
|
||||||
|
|
||||||
|
@ -161,7 +161,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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,14 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
// Don't put anything here as respawn is already handled
|
// Don't put anything here as respawn is already handled
|
||||||
// in BedrockRespawnTranslator
|
// in BedrockRespawnTranslator
|
||||||
break;
|
break;
|
||||||
|
case START_SWIMMING:
|
||||||
|
ClientPlayerStatePacket startSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
|
||||||
|
session.getDownstream().getSession().send(startSwimPacket);
|
||||||
|
break;
|
||||||
|
case STOP_SWIMMING:
|
||||||
|
ClientPlayerStatePacket stopSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
|
||||||
|
session.getDownstream().getSession().send(stopSwimPacket);
|
||||||
|
break;
|
||||||
case START_GLIDE:
|
case START_GLIDE:
|
||||||
case STOP_GLIDE:
|
case STOP_GLIDE:
|
||||||
ClientPlayerStatePacket glidePacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
|
ClientPlayerStatePacket glidePacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
|
||||||
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.GeyserConnector;
|
||||||
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.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<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) {
|
||||||
|
GeyserConnector.getInstance().getLogger().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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class JavaEntityEquipmentTranslator extends PacketTranslator<ServerEntity
|
||||||
livingEntity.setHand(item);
|
livingEntity.setHand(item);
|
||||||
break;
|
break;
|
||||||
case OFF_HAND:
|
case OFF_HAND:
|
||||||
// TODO: livingEntity.setOffHand(item);
|
livingEntity.setOffHand(item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,12 +41,8 @@ public class JavaEntityMetadataTranslator extends PacketTranslator<ServerEntityM
|
||||||
}
|
}
|
||||||
if (entity == null) return;
|
if (entity == null) return;
|
||||||
|
|
||||||
if (entity.isValid()) {
|
for (EntityMetadata metadata : packet.getMetadata()) {
|
||||||
for (EntityMetadata metadata : packet.getMetadata()) {
|
entity.updateBedrockMetadata(metadata, session);
|
||||||
entity.updateBedrockMetadata(metadata, session);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
entity.spawnEntity(session);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class JavaEntityPropertiesTranslator extends PacketTranslator<ServerEntit
|
||||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||||
entity = session.getPlayerEntity();
|
entity = session.getPlayerEntity();
|
||||||
}
|
}
|
||||||
if (entity == null || !entity.isValid()) return;
|
if (entity == null) return;
|
||||||
|
|
||||||
for (Attribute attribute : packet.getAttributes()) {
|
for (Attribute attribute : packet.getAttributes()) {
|
||||||
switch (attribute.getType()) {
|
switch (attribute.getType()) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
|
||||||
entity.setEntityId(packet.getEntityId());
|
entity.setEntityId(packet.getEntityId());
|
||||||
entity.setPosition(position);
|
entity.setPosition(position);
|
||||||
entity.setRotation(rotation);
|
entity.setRotation(rotation);
|
||||||
|
session.getEntityCache().cacheEntity(entity);
|
||||||
|
|
||||||
// async skin loading
|
// async skin loading
|
||||||
if (session.getUpstream().isInitialized()) {
|
if (session.getUpstream().isInitialized()) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ import io.netty.buffer.Unpooled;
|
||||||
|
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
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;
|
||||||
|
@ -75,7 +76,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
|
||||||
|
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,19 +25,19 @@
|
||||||
|
|
||||||
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.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 +45,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) {
|
||||||
GeyserConnector.getInstance().getLogger().warning("Failed to get blocks from runtime block states, please report this error!");
|
GeyserConnector.getInstance().getLogger().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.getAsCompound("block");
|
|
||||||
if (blockTag.getAsString("name").equals(wantedIdentifier)) {
|
|
||||||
if (wantedStates != null) {
|
|
||||||
Map<String, Tag<?>> bedrockStates = blockTag.getAsCompound("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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GeyserConnector.getInstance().getLogger().debug("Mapping " + javaEntry.getKey() + " was not found for bedrock edition!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Binary file not shown.
|
@ -1 +1 @@
|
||||||
Subproject commit 1ad8a19417391710d34d72214e3aa430f84740cd
|
Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a
|
Loading…
Reference in a new issue