Implement command block and jigsaw support (#1291)

* Implement command block and jigsaw support

- Command block UI is now fully implemented to match Java Edition.
- Command block minecarts are now supported.
- Command blocks now show the correct type of command block.
- Jigsaw blocks are translated.

Structure blocks can be implemented, but these will be trickier as there are significant GUI differences between Java and Bedrock.

* Add more detail about command block minecart color

* Set PlayerPermission.OPERATOR to allow command blocks to be destroyed
This commit is contained in:
Camotoy 2020-09-14 20:54:19 -04:00 committed by GitHub
parent f5a9254fae
commit 6638c53029
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 359 additions and 155 deletions

View file

@ -39,7 +39,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
- [ ] Beacon - [ ] Beacon
- [ ] Cartography Table - [ ] Cartography Table
- [ ] Stonecutter - [ ] Stonecutter
- [ ] Command Block
- [ ] Structure Block - [ ] Structure Block
- [ ] Horse Inventory - [ ] Horse Inventory
- [ ] Loom - [ ] Loom

View file

@ -0,0 +1,67 @@
/*
* 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.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.message.Message;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.MessageUtils;
public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity {
public CommandBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
// Required, or else the GUI will not open
metadata.put(EntityData.CONTAINER_TYPE, (byte) 16);
metadata.put(EntityData.CONTAINER_BASE_SIZE, 1);
// Required, or else the client does not bother to send a packet back with the new information
metadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1);
}
@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
if (entityMetadata.getId() == 13) {
metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue());
}
if (entityMetadata.getId() == 14) {
metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue()));
}
super.updateBedrockMetadata(entityMetadata, session);
}
/**
* By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange.
*/
@Override
public void updateDefaultBlockMetadata() {
metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID);
metadata.put(EntityData.DISPLAY_OFFSET, 6);
}
}

View file

@ -135,7 +135,7 @@ public enum EntityType {
MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"), MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"),
MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"),
MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"),
LINGERING_POTION(ThrowableEntity.class, 101, 0f), LINGERING_POTION(ThrowableEntity.class, 101, 0f),
LLAMA_SPIT(Entity.class, 102, 0.25f), LLAMA_SPIT(Entity.class, 102, 0.25f),
EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"),

View file

@ -714,7 +714,8 @@ public class GeyserSession implements CommandSender {
// This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR // This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR
// and all commands there are accessible with OP permission level 2 // and all commands there are accessible with OP permission level 2
adventureSettingsPacket.setCommandPermission(opPermissionLevel >= 2 ? CommandPermission.OPERATOR : CommandPermission.NORMAL); adventureSettingsPacket.setCommandPermission(opPermissionLevel >= 2 ? CommandPermission.OPERATOR : CommandPermission.NORMAL);
adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); // Required to make command blocks destroyable
adventureSettingsPacket.setPlayerPermission(opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER);
Set<AdventureSetting> flags = new HashSet<>(); Set<AdventureSetting> flags = new HashSet<>();
if (canFly) { if (canFly) {

View file

@ -26,6 +26,7 @@
package org.geysermc.connector.network.translators.bedrock; package org.geysermc.connector.network.translators.bedrock;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateJigsawBlockPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket;
import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMap;
import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
@ -109,6 +110,18 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator<BlockEnti
// We set the sign text cached in the session to null to indicate there is no work-in-progress sign // We set the sign text cached in the session to null to indicate there is no work-in-progress sign
session.setLastSignMessage(null); session.setLastSignMessage(null);
} else if (tag.getString("id").equals("JigsawBlock")) {
// Client has just sent a jigsaw block update
Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
String name = tag.getString("name");
String target = tag.getString("target");
String pool = tag.getString("target_pool");
String finalState = tag.getString("final_state");
String joint = tag.getString("joint");
ClientUpdateJigsawBlockPacket jigsawPacket = new ClientUpdateJigsawBlockPacket(pos, name, target, pool,
finalState, joint);
session.sendDownstreamPacket(jigsawPacket);
} }
} }

View file

@ -0,0 +1,71 @@
/*
* 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.bedrock;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.CommandBlockMode;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateCommandBlockMinecartPacket;
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateCommandBlockPacket;
import com.nukkitx.protocol.bedrock.packet.CommandBlockUpdatePacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@Translator(packet = CommandBlockUpdatePacket.class)
public class BedrockCommandBlockUpdateTranslator extends PacketTranslator<CommandBlockUpdatePacket> {
@Override
public void translate(CommandBlockUpdatePacket packet, GeyserSession session) {
String command = packet.getCommand();
boolean outputTracked = packet.isOutputTracked();
if (packet.isBlock()) {
CommandBlockMode mode;
switch (packet.getMode()) {
case CHAIN: // The green one
mode = CommandBlockMode.SEQUENCE;
break;
case REPEATING: // The purple one
mode = CommandBlockMode.AUTO;
break;
default: // NORMAL, the orange one
mode = CommandBlockMode.REDSTONE;
break;
}
boolean isConditional = packet.isConditional();
boolean automatic = !packet.isRedstoneMode(); // Automatic = Always Active option in Java
ClientUpdateCommandBlockPacket commandBlockPacket = new ClientUpdateCommandBlockPacket(
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
command, mode, outputTracked, isConditional, automatic);
session.sendDownstreamPacket(commandBlockPacket);
} else {
ClientUpdateCommandBlockMinecartPacket commandMinecartPacket = new ClientUpdateCommandBlockMinecartPacket(
(int) session.getEntityCache().getEntityByGeyserId(packet.getMinecartRuntimeEntityId()).getEntityId(),
command, outputTracked
);
session.sendDownstreamPacket(commandMinecartPacket);
}
}
}

View file

@ -39,8 +39,11 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.entity.CommandBlockMinecartEntity;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.ItemFrameEntity;
import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity;
@ -105,6 +108,24 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendDownstreamPacket(itemPacket); session.sendDownstreamPacket(itemPacket);
} }
if (packet.getActions().isEmpty()) {
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
// Otherwise insufficient permissions
int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId());
String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, "");
// In the future this can be used for structure blocks too, however not all elements
// are available in each GUI
if (blockName.contains("jigsaw")) {
ContainerOpenPacket openPacket = new ContainerOpenPacket();
openPacket.setBlockPosition(packet.getBlockPosition());
openPacket.setId((byte) 1);
openPacket.setType(ContainerType.JIGSAW_EDITOR);
openPacket.setUniqueEntityId(-1);
session.sendUpstreamPacket(openPacket);
}
}
}
Vector3i blockPos = packet.getBlockPosition(); Vector3i blockPos = packet.getBlockPosition();
// TODO: Find a better way to do this? // TODO: Find a better way to do this?
switch (packet.getBlockFace()) { switch (packet.getBlockFace()) {
@ -197,6 +218,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
//https://wiki.vg/Protocol#Interact_Entity //https://wiki.vg/Protocol#Interact_Entity
switch (packet.getActionType()) { switch (packet.getActionType()) {
case 0: //Interact case 0: //Interact
if (entity instanceof CommandBlockMinecartEntity) {
// The UI is handled client-side on Java Edition
// Ensure OP permission level and gamemode is appropriate
if (session.getOpPermissionLevel() < 2 || session.getGameMode() != GameMode.CREATIVE) return;
ContainerOpenPacket openPacket = new ContainerOpenPacket();
openPacket.setBlockPosition(Vector3i.ZERO);
openPacket.setId((byte) 1);
openPacket.setType(ContainerType.COMMAND_BLOCK);
openPacket.setUniqueEntityId(entity.getGeyserId());
session.sendUpstreamPacket(openPacket);
break;
}
Vector3f vector = packet.getClickPosition(); Vector3f vector = packet.getClickPosition();
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
InteractAction.INTERACT, Hand.MAIN_HAND, session.isSneaking()); InteractAction.INTERACT, Hand.MAIN_HAND, session.isSneaking());

View file

@ -25,8 +25,12 @@
package org.geysermc.connector.network.translators.java.world; package org.geysermc.connector.network.translators.java.world;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.world.block.UpdatedTileType;
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTileEntityPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTileEntityPacket;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.Translator;
@ -54,5 +58,15 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdat
} else { } else {
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), 0), packet.getPosition()); BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), 0), packet.getPosition());
} }
// If block entity is command block, OP permission level is appropriate, player is in creative mode and the NBT is not empty
if (packet.getType() == UpdatedTileType.COMMAND_BLOCK && session.getOpPermissionLevel() >= 2 &&
session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) {
ContainerOpenPacket openPacket = new ContainerOpenPacket();
openPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()));
openPacket.setId((byte) 1);
openPacket.setType(ContainerType.COMMAND_BLOCK);
openPacket.setUniqueEntityId(-1);
session.sendUpstreamPacket(openPacket);
}
} }
} }

View file

@ -39,6 +39,7 @@ public class BlockStateValues {
private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap(); private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap();
private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap(); private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap();
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
private static final Map<String, NbtMap> FLOWER_POT_BLOCKS = new HashMap<>(); private static final Map<String, NbtMap> FLOWER_POT_BLOCKS = new HashMap<>();
@ -67,6 +68,11 @@ public class BlockStateValues {
return; return;
} }
if (entry.getKey().contains("command_block")) {
COMMAND_BLOCK_VALUES.put(javaBlockState, entry.getKey().contains("conditional=true") ? (byte) 1 : (byte) 0);
return;
}
if (entry.getValue().get("double_chest_position") != null) { if (entry.getValue().get("double_chest_position") != null) {
boolean isX = (entry.getValue().get("x") != null); boolean isX = (entry.getValue().get("x") != null);
boolean isDirectionPositive = ((entry.getValue().get("x") != null && entry.getValue().get("x").asBoolean()) || boolean isDirectionPositive = ((entry.getValue().get("x") != null && entry.getValue().get("x").asBoolean()) ||
@ -138,6 +144,16 @@ public class BlockStateValues {
return -1; return -1;
} }
/**
* The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags
* in Bedrock need the conditional information.
*
* @return the list of all command blocks and if they are conditional (1 or 0)
*/
public static Int2ByteMap getCommandBlockValues() {
return COMMAND_BLOCK_VALUES;
}
/** /**
* All double chest values are part of the block state in Java and part of the block entity tag in Bedrock. * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
* This gives the DoubleChestValue that can be calculated into the final tag. * This gives the DoubleChestValue that can be calculated into the final tag.

View file

@ -67,6 +67,11 @@ public class BlockTranslator {
public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap(); public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
public static final Int2ObjectMap<String> JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>(); public static final Int2ObjectMap<String> JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>();
/**
* Runtime command block ID, used for fixing command block minecart appearances
*/
public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID;
// For block breaking animation math // For block breaking animation math
public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet();
public static final int JAVA_RUNTIME_COBWEB_ID; public static final int JAVA_RUNTIME_COBWEB_ID;
@ -115,6 +120,7 @@ public class BlockTranslator {
int javaRuntimeId = -1; int javaRuntimeId = -1;
int bedrockRuntimeId = 0; int bedrockRuntimeId = 0;
int cobwebRuntimeId = -1; int cobwebRuntimeId = -1;
int commandBlockRuntimeId = -1;
int furnaceRuntimeId = -1; int furnaceRuntimeId = -1;
int furnaceLitRuntimeId = -1; int furnaceLitRuntimeId = -1;
int spawnerRuntimeId = -1; int spawnerRuntimeId = -1;
@ -142,14 +148,6 @@ public class BlockTranslator {
JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue()); JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue());
} }
if (javaId.contains("wool")) {
JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
}
if (javaId.contains("cobweb")) {
cobwebRuntimeId = javaRuntimeId;
}
JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId);
// Used for adding all "special" Java block states to block state map // Used for adding all "special" Java block states to block state map
@ -205,15 +203,23 @@ public class BlockTranslator {
} }
JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId);
if (javaId.startsWith("minecraft:furnace[facing=north")) { if (javaId.contains("wool")) {
JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
} else if (javaId.contains("cobweb")) {
cobwebRuntimeId = javaRuntimeId;
} else if (javaId.equals("minecraft:command_block[conditional=false,facing=north]")) {
commandBlockRuntimeId = bedrockRuntimeId;
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
if (javaId.contains("lit=true")) { if (javaId.contains("lit=true")) {
furnaceLitRuntimeId = javaRuntimeId; furnaceLitRuntimeId = javaRuntimeId;
} else { } else {
furnaceRuntimeId = javaRuntimeId; furnaceRuntimeId = javaRuntimeId;
} }
}
if (javaId.startsWith("minecraft:spawner")) { } else if (javaId.startsWith("minecraft:spawner")) {
spawnerRuntimeId = javaRuntimeId; spawnerRuntimeId = javaRuntimeId;
} }
@ -225,6 +231,11 @@ public class BlockTranslator {
} }
JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
if (commandBlockRuntimeId == -1) {
throw new AssertionError("Unable to find command block in palette");
}
BEDROCK_RUNTIME_COMMAND_BLOCK_ID = commandBlockRuntimeId;
if (furnaceRuntimeId == -1) { if (furnaceRuntimeId == -1) {
throw new AssertionError("Unable to find furnace in palette"); throw new AssertionError("Unable to find furnace in palette");
} }

View file

@ -27,12 +27,9 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtType;
import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.item.translators.BannerTranslator;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -65,17 +62,4 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
tag.put(new ListTag("Patterns"));
return tag;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putList("Patterns", NbtType.COMPOUND, new ArrayList<>())
.build();
}
} }

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMap;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap; import java.util.HashMap;
@ -50,15 +49,4 @@ public class BedBlockEntityTranslator extends BlockEntityTranslator implements R
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return null;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putByte("color", (byte) 0)
.build();
}
} }

View file

@ -28,9 +28,9 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtMapBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.BlockEntityUtils;
@ -53,6 +53,7 @@ public abstract class BlockEntityTranslator {
{ {
// Bedrock/Java differences // Bedrock/Java differences
put("minecraft:enchanting_table", "EnchantTable"); put("minecraft:enchanting_table", "EnchantTable");
put("minecraft:jigsaw", "JigsawBlock");
put("minecraft:piston_head", "PistonArm"); put("minecraft:piston_head", "PistonArm");
put("minecraft:trapped_chest", "Chest"); put("minecraft:trapped_chest", "Chest");
// There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly // There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly
@ -90,10 +91,6 @@ public abstract class BlockEntityTranslator {
public abstract Map<String, Object> translateTag(CompoundTag tag, int blockState); public abstract Map<String, Object> translateTag(CompoundTag tag, int blockState);
public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z);
public abstract NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z);
public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) {
int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue()));
int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue()));
@ -124,7 +121,7 @@ public abstract class BlockEntityTranslator {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T getOrDefault(com.github.steveice10.opennbt.tag.builtin.Tag tag, T defaultValue) { protected <T> T getOrDefault(Tag tag, T defaultValue) {
return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue; return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue;
} }
} }

View file

@ -50,18 +50,6 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator {
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
tag.put(new ListTag("Items"));
return tag;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z);
}
protected NbtMap getItem(CompoundTag tag) { protected NbtMap getItem(CompoundTag tag) {
ItemEntry entry = ItemRegistry.getItemEntry((String) tag.get("id").getValue()); ItemEntry entry = ItemRegistry.getItemEntry((String) tag.get("id").getValue());
NbtMapBuilder tagBuilder = NbtMap.builder() NbtMapBuilder tagBuilder = NbtMap.builder()

View file

@ -0,0 +1,67 @@
/*
* 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.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.*;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.MessageUtils;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "CommandBlock", regex = "command_block")
public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) {
Map<String, Object> map = new HashMap<>();
if (tag.size() < 5) {
return map; // These values aren't here
}
// Java infers from the block state, but Bedrock needs it in the tag
map.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0));
// Java and Bedrock values
map.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue());
map.put("auto", ((ByteTag) tag.get("auto")).getValue());
map.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue()));
map.put("powered", ((ByteTag) tag.get("powered")).getValue());
map.put("Command", ((StringTag) tag.get("Command")).getValue());
map.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue());
map.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue());
map.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue());
if (tag.get("LastExecution") != null) {
map.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue());
} else {
map.put("LastExecution", (long) 0);
}
return map;
}
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getCommandBlockValues().containsKey(blockState);
}
}

View file

@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
@ -92,13 +91,4 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return null;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return null;
}
} }

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -39,13 +38,4 @@ public class EmptyBlockEntityTranslator extends BlockEntityTranslator {
return new HashMap<>(); return new HashMap<>();
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return getConstantJavaTag(javaId, x, y, z);
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z);
}
} }

View file

@ -26,14 +26,12 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag; import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtType; import com.nukkitx.nbt.NbtType;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -56,25 +54,11 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator {
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
tag.put(new LongTag("Age"));
return tag;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putList("ExitPortal", NbtType.INT, Arrays.asList(0, 0, 0))
.build();
}
private int getExitPortalCoordinate(CompoundTag tag, String axis) { private int getExitPortalCoordinate(CompoundTag tag, String axis) {
// Return 0 if it doesn't exist, otherwise give proper value // Return 0 if it doesn't exist, otherwise give proper value
if (tag.get("ExitPortal") != null) { if (tag.get("ExitPortal") != null) {
LinkedHashMap<?, ?> compoundTag = (LinkedHashMap<?, ?>) tag.get("ExitPortal").getValue(); LinkedHashMap<?, ?> compoundTag = (LinkedHashMap<?, ?>) tag.get("ExitPortal").getValue();
com.github.steveice10.opennbt.tag.builtin.IntTag intTag = (com.github.steveice10.opennbt.tag.builtin.IntTag) compoundTag.get(axis); IntTag intTag = (IntTag) compoundTag.get(axis);
return intTag.getValue(); return intTag.getValue();
} return 0; } return 0;
} }

View file

@ -0,0 +1,47 @@
/*
* 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.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import java.util.HashMap;
import java.util.Map;
@BlockEntity(name = "JigsawBlock", regex = "jigsaw")
public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator {
@Override
public Map<String, Object> translateTag(CompoundTag tag, int blockState) {
Map<String, Object> map = new HashMap<>();
map.put("joint", ((StringTag) tag.get("joint")).getValue());
map.put("name", ((StringTag) tag.get("name")).getValue());
map.put("target_pool", ((StringTag) tag.get("pool")).getValue());
map.put("final_state", ((StringTag) tag.get("final_state")).getValue());
map.put("target", ((StringTag) tag.get("target")).getValue());
return map;
}
}

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMap;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap; import java.util.HashMap;
@ -46,15 +45,4 @@ public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return null;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putByte("facing", (byte) 1)
.build();
}
} }

View file

@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.data.message.MessageSerializer;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMap;
import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.MessageUtils;
import org.geysermc.connector.utils.SignUtils; import org.geysermc.connector.utils.SignUtils;
@ -73,23 +72,6 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator {
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
CompoundTag tag = getConstantJavaTag(javaId, x, y, z);
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "{\"text\":\"\"}"));
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "{\"text\":\"\"}"));
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "{\"text\":\"\"}"));
tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text4", "{\"text\":\"\"}"));
return tag;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putString("Text", "")
.build();
}
/** /**
* Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code.
* <br> * <br>

View file

@ -25,7 +25,6 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.nukkitx.nbt.NbtMap;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import java.util.HashMap; import java.util.HashMap;
@ -51,16 +50,4 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
return tags; return tags;
} }
@Override
public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return null;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putFloat("Rotation", 0f)
.putByte("SkullType", (byte) 0)
.build();
}
} }

View file

@ -26,7 +26,6 @@
package org.geysermc.connector.network.translators.world.block.entity; package org.geysermc.connector.network.translators.world.block.entity;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.nbt.NbtMap;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import java.util.HashMap; import java.util.HashMap;
@ -86,16 +85,4 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator {
return tags; return tags;
} }
@Override
public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) {
return null;
}
@Override
public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) {
return getConstantBedrockTag(bedrockId, x, y, z).toBuilder()
.putByte("isMovable", (byte) 1)
.putString("id", "MobSpawner")
.build();
}
} }