From b07161b0a911d6ebb91dd9d5db3522ab8048f527 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 1 May 2020 01:51:23 -0400 Subject: [PATCH] Add block values + note block note graphics (#455) * Add note block visual without note pitch * Add rest of block value code * Add rest of block value code * Fix pistons, somewhat * Remove note block attempt * Re-add whitespace * Simplify sendPacket of BlockEventPacket * Add note block visual without note pitch * Add rest of block value code * Fix pistons, somewhat * Remove note block attempt * Re-add whitespace * Add mappings for noteblock pitch * Change noteblock pitch code * Noteblock Pitch Attempt * Commit with PistonBlockEntityTranslator * Cleanup for PR * Improve pistons Co-authored-by: blazewalker462 --- .../network/translators/Translators.java | 16 +++ .../translators/block/BlockStateValues.java | 28 +++++- .../NoteblockBlockEntityTranslator.java | 58 +++++++++++ .../java/world/JavaBlockValueTranslator.java | 99 ++++++++++++++++++- .../java/world/JavaChunkDataTranslator.java | 2 - .../geysermc/connector/utils/ChunkUtils.java | 15 +-- 6 files changed, 200 insertions(+), 18 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java index f0a3fd28c..f7c7c28aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java @@ -32,6 +32,7 @@ import java.util.Map; import com.github.steveice10.mc.protocol.data.game.window.WindowType; import com.nukkitx.protocol.bedrock.data.ContainerType; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.network.translators.block.entity.*; @@ -61,6 +62,9 @@ public class Translators { @Getter private static Map blockEntityTranslators = new HashMap<>(); + @Getter + private static ObjectArrayList requiresBlockStateMap = new ObjectArrayList<>(); + private static final CompoundTag EMPTY_TAG = CompoundTagBuilder.builder().buildRootTag(); public static final byte[] EMPTY_LEVEL_CHUNK_DATA; @@ -129,6 +133,18 @@ public class Translators { GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity " + clazz.getCanonicalName() + "."); } } + + for (Class clazz : ref.getSubTypesOf(RequiresBlockState.class)) { + + GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName()); + + try { + requiresBlockStateMap.add((RequiresBlockState) clazz.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + clazz.getCanonicalName() + "."); + } + + } } private static void registerInventoryTranslators() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java index 76ee52387..87966fd1d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java @@ -41,6 +41,7 @@ public class BlockStateValues { private static final Object2IntMap BANNER_COLORS = new Object2IntOpenHashMap<>(); private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>(); + private static final Object2IntMap NOTEBLOCK_PITCHES = new Object2IntOpenHashMap<>(); private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>(); private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>(); private static final Object2ByteMap SHULKERBOX_DIRECTIONS = new Object2ByteOpenHashMap<>(); @@ -53,24 +54,30 @@ public class BlockStateValues { public static void storeBlockStateValues(Map.Entry entry, BlockState javaBlockState) { JsonNode bannerColor = entry.getValue().get("banner_color"); if (bannerColor != null) { - BlockStateValues.BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); + BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); return; // There will never be a banner color and a skull variant } JsonNode bedColor = entry.getValue().get("bed_color"); if (bedColor != null) { - BlockStateValues.BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); + BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); + return; + } + + JsonNode notePitch = entry.getValue().get("note_pitch"); + if (notePitch != null) { + NOTEBLOCK_PITCHES.put(javaBlockState, entry.getValue().get("note_pitch").intValue()); return; } JsonNode skullVariation = entry.getValue().get("variation"); if(skullVariation != null) { - BlockStateValues.SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); + SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); } JsonNode skullRotation = entry.getValue().get("skull_rotation"); if (skullRotation != null) { - BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); + SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); } JsonNode shulkerDirection = entry.getValue().get("shulker_direction"); @@ -107,6 +114,19 @@ public class BlockStateValues { return -1; } + /** + * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. + * This gives an integer pitch that Bedrock can use. + * @param state BlockState of the block + * @return note block note integer or -1 if not present + */ + public static int getNoteblockPitch(BlockState state) { + if (NOTEBLOCK_PITCHES.containsKey(state)) { + return NOTEBLOCK_PITCHES.getInt(state); + } + return -1; + } + /** * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. * This gives a byte variant ID that Bedrock can use. diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java new file mode 100644 index 000000000..ba9ac0de8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/NoteblockBlockEntityTranslator.java @@ -0,0 +1,58 @@ +/* + * 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.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.block.BlockStateValues; +import org.geysermc.connector.utils.ChunkUtils; + +/** + * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock + */ +public class NoteblockBlockEntityTranslator implements RequiresBlockState { + + @Override + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getNoteblockPitch(blockState) != -1; + } + + public static void translate(GeyserSession session, Position position) { + BlockState blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.get(position); + BlockEventPacket blockEventPacket = new BlockEventPacket(); + blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); + blockEventPacket.setEventType(0); + blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); + session.getUpstream().sendPacket(blockEventPacket); + + ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position); + } + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java index f4a4d9efa..36ccf6556 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockValueTranslator.java @@ -25,15 +25,21 @@ package org.geysermc.connector.network.translators.java.world; -import com.github.steveice10.mc.protocol.data.game.world.block.value.ChestValue; -import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValue; +import com.github.steveice10.mc.protocol.data.game.world.block.value.*; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockValuePacket; import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; +import org.geysermc.connector.network.translators.block.entity.NoteblockBlockEntityTranslator; + +import java.util.concurrent.TimeUnit; + @Translator(packet = ServerBlockValuePacket.class) public class JavaBlockValueTranslator extends PacketTranslator { @@ -53,5 +59,94 @@ public class JavaBlockValueTranslator extends PacketTranslator + extendPiston(session, position, (progress >= 1.0f) ? 1.0f : progress + 0.5f, progress), + 20, TimeUnit.MILLISECONDS); + } + } + + /** + * Emulate a piston retracting. + * @param session GeyserSession + * @param position Block position + * @param progress Current progress of piston + * @param lastProgress Last progress of piston + */ + private void retractPiston(GeyserSession session, Vector3i position, float progress, float lastProgress) { + BlockEntityDataPacket blockEntityDataPacket = new BlockEntityDataPacket(); + blockEntityDataPacket.setBlockPosition(position); + byte state = (byte) ((progress == 0.0f && lastProgress == 0.0f) ? 0 : 3); + blockEntityDataPacket.setData(buildPistonTag(position, progress, lastProgress, state)); + session.getUpstream().sendPacket(blockEntityDataPacket); + if (lastProgress != 0.0f) { + session.getConnector().getGeneralThreadPool().schedule(() -> + retractPiston(session, position, (progress <= 0.0f) ? 0.0f : progress - 0.5f, progress), + 20, TimeUnit.MILLISECONDS); + } + } + + /** + * Build a piston tag + * @param position Piston position + * @param progress Current progress of piston + * @param lastProgress Last progress of piston + * @param state + * @return Bedrock CompoundTag of piston + */ + private CompoundTag buildPistonTag(Vector3i position, float progress, float lastProgress, byte state) { + CompoundTagBuilder builder = CompoundTag.EMPTY.toBuilder(); + builder.intTag("x", position.getX()) + .intTag("y", position.getY()) + .intTag("z", position.getZ()) + .floatTag("Progress", progress) + .floatTag("LastProgress", lastProgress) + .stringTag("id", "PistonArm") + .byteTag("NewState", state) + .byteTag("State", state); + return builder.buildRootTag(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index e72038c51..4117add77 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -46,8 +46,6 @@ import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.world.chunk.ChunkSection; -import java.util.Map; - @Translator(packet = ServerChunkDataPacket.class) public class JavaChunkDataTranslator extends PacketTranslator { diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index d496215ac..c1ddb60a7 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -32,9 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector2i; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; -import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; -import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -172,13 +170,10 @@ public class ChunkUtils { // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag // This is the only place I could find that interacts with the Java block state and block updates // Iterates through all block entity translators and determines if the block state needs to be saved - for (Map.Entry entry : Translators.getBlockEntityTranslators().entrySet()) { - if (entry.getValue() instanceof RequiresBlockState) { - RequiresBlockState requiresBlockState = (RequiresBlockState) entry.getValue(); - if (requiresBlockState.isBlock(blockState)) { - CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); - break; //No block will be a part of two classes - } + for (RequiresBlockState requiresBlockState : Translators.getRequiresBlockStateMap()) { + if (requiresBlockState.isBlock(blockState)) { + CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + break; //No block will be a part of two classes } } }