//  [java]
    public void translate(GeyserSession session, ServerBlockValuePacket packet) {
        BlockEventPacket blockEventPacket = new BlockEventPacket();
        blockEventPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(),
                packet.getPosition().getY(), packet.getPosition().getZ()));
        if (packet.getValue() instanceof ChestValue) {
            ChestValue value = (ChestValue) packet.getValue() ;
            blockEventPacket.setEventType(1);
            blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
            session.sendUpstreamPacket(blockEventPacket);
        } else if (packet.getValue() instanceof EndGatewayValue) {
            blockEventPacket.setEventType(1);
            session.sendUpstreamPacket(blockEventPacket);
        } else if (packet.getValue() instanceof NoteBlockValue) {
            NoteblockBlockEntityTranslator.translate(session, packet.getPosition());
        } else if (packet.getValue() instanceof PistonValue) {
            PistonValueType type = (PistonValueType) packet.getType();

            // Unlike everything else, pistons need a block entity packet to convey motion
            // TODO: Doesn't register on chunk load; needs to be interacted with first
            Vector3i position = Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
            if (type == PistonValueType.PUSHING) {
                extendPiston(session, position, 0.0f, 0.0f);
            } else {
                retractPiston(session, position, 1.0f, 1.0f);
            }
        } else if (packet.getValue() instanceof MobSpawnerValue) {
            blockEventPacket.setEventType(1);
            session.sendUpstreamPacket(blockEventPacket);
        } else if (packet.getValue() instanceof EndGatewayValue) {
            blockEventPacket.setEventType(1);
            session.sendUpstreamPacket(blockEventPacket);
        } else if (packet.getValue() instanceof GenericBlockValue && packet.getBlockId() == BlockStateValues.JAVA_BELL_ID) {
            // Bells - needed to show ring from other players
            GenericBlockValue bellValue = (GenericBlockValue) packet.getValue();
            Position position = packet.getPosition();

            BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
            blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));

            NbtMapBuilder builder = NbtMap.builder();
            builder.putInt("x", position.getX());
            builder.putInt("y", position.getY());
            builder.putInt("z", position.getZ());
            builder.putString("id", "Bell");
            int bedrockRingDirection;
            switch (bellValue.getValue()) {
                case 3: // north
                    bedrockRingDirection = 0;
                    break;
                case 4: // east
                    bedrockRingDirection = 1;
                    break;
                case 5: // west
                    bedrockRingDirection = 3;
                    break;
                default: // south (2) is identical
                    bedrockRingDirection = bellValue.getValue();
            }
            builder.putInt("Direction", bedrockRingDirection);
            builder.putByte("Ringing", (byte) 1);
            builder.putInt("Ticks", 0);
            
            blockEntityPacket.setData(builder.build());
            session.sendUpstreamPacket(blockEntityPacket);
        }
    }