2020-04-21 05:32:32 +00:00
|
|
|
/*
|
2021-01-01 15:10:36 +00:00
|
|
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
2020-04-21 05:32:32 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2020-04-29 20:01:53 +00:00
|
|
|
package org.geysermc.connector.network.translators.world.block;
|
2020-04-21 05:32:32 +00:00
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
2020-06-19 01:44:50 +00:00
|
|
|
import it.unimi.dsi.fastutil.ints.*;
|
2020-04-21 05:32:32 +00:00
|
|
|
|
|
|
|
import java.util.Map;
|
2021-03-10 18:54:09 +00:00
|
|
|
import java.util.function.BiFunction;
|
2020-04-21 05:32:32 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used for block entities if the Java block state contains Bedrock block information.
|
|
|
|
*/
|
|
|
|
public class BlockStateValues {
|
2020-06-19 01:44:50 +00:00
|
|
|
private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap();
|
|
|
|
private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap();
|
2020-09-15 00:54:19 +00:00
|
|
|
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
|
2020-05-10 19:26:00 +00:00
|
|
|
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
2020-05-05 02:32:02 +00:00
|
|
|
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
2020-12-28 05:29:27 +00:00
|
|
|
private static final Int2BooleanMap LECTERN_BOOK_STATES = new Int2BooleanOpenHashMap();
|
2020-06-19 01:44:50 +00:00
|
|
|
private static final Int2IntMap NOTEBLOCK_PITCHES = new Int2IntOpenHashMap();
|
2020-05-05 02:32:02 +00:00
|
|
|
private static final Int2BooleanMap IS_STICKY_PISTON = new Int2BooleanOpenHashMap();
|
|
|
|
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
|
2020-06-19 01:44:50 +00:00
|
|
|
private static final Int2ByteMap SKULL_VARIANTS = new Int2ByteOpenHashMap();
|
|
|
|
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
|
2020-12-04 21:55:24 +00:00
|
|
|
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
|
2020-06-19 01:44:50 +00:00
|
|
|
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new Int2ByteOpenHashMap();
|
2021-02-19 16:12:36 +00:00
|
|
|
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
|
2020-04-21 05:32:32 +00:00
|
|
|
|
2021-07-13 01:19:40 +00:00
|
|
|
public static final int JAVA_AIR_ID = 0;
|
|
|
|
|
|
|
|
public static int JAVA_BELL_ID;
|
|
|
|
public static int JAVA_COBWEB_ID;
|
|
|
|
public static int JAVA_FURNACE_ID;
|
|
|
|
public static int JAVA_FURNACE_LIT_ID;
|
|
|
|
public static int JAVA_SPAWNER_ID;
|
|
|
|
public static int JAVA_WATER_ID;
|
|
|
|
|
2020-04-21 05:32:32 +00:00
|
|
|
/**
|
|
|
|
* Determines if the block state contains Bedrock block information
|
2020-11-05 21:36:22 +00:00
|
|
|
*
|
2021-02-19 16:12:36 +00:00
|
|
|
* @param javaId The Java Identifier of the block
|
2020-04-21 05:32:32 +00:00
|
|
|
* @param javaBlockState the Java Block State of the block
|
2021-02-19 16:12:36 +00:00
|
|
|
* @param blockData JsonNode of info about the block from blocks.json
|
2020-04-21 05:32:32 +00:00
|
|
|
*/
|
2021-02-19 16:12:36 +00:00
|
|
|
public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) {
|
|
|
|
JsonNode bannerColor = blockData.get("banner_color");
|
2020-04-21 05:32:32 +00:00
|
|
|
if (bannerColor != null) {
|
2020-05-01 05:51:23 +00:00
|
|
|
BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
|
2020-04-21 05:32:32 +00:00
|
|
|
return; // There will never be a banner color and a skull variant
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
JsonNode bedColor = blockData.get("bed_color");
|
2020-04-21 05:32:32 +00:00
|
|
|
if (bedColor != null) {
|
2020-05-01 05:51:23 +00:00
|
|
|
BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
if (javaId.contains("command_block")) {
|
|
|
|
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
|
2020-09-15 00:54:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
if (blockData.get("double_chest_position") != null) {
|
|
|
|
boolean isX = (blockData.get("x") != null);
|
|
|
|
boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) ||
|
|
|
|
(blockData.get("z") != null && blockData.get("z").asBoolean()));
|
|
|
|
boolean isLeft = (blockData.get("double_chest_position").asText().contains("left"));
|
2020-06-19 01:44:50 +00:00
|
|
|
DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft));
|
2020-05-10 19:26:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-14 01:14:22 +00:00
|
|
|
if (javaId.startsWith("minecraft:potted_") || javaId.equals("minecraft:flower_pot")) {
|
|
|
|
String name = javaId.replace("potted_", "");
|
|
|
|
if (name.contains("azalea")) {
|
|
|
|
// Exception to the rule
|
|
|
|
name = name.replace("_bush", "");
|
|
|
|
}
|
|
|
|
FLOWER_POT_VALUES.put(javaBlockState, name);
|
2020-05-05 02:32:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-26 05:46:44 +00:00
|
|
|
if (javaId.startsWith("minecraft:lectern")) {
|
|
|
|
LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true"));
|
2020-12-28 05:29:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
JsonNode notePitch = blockData.get("note_pitch");
|
2020-05-01 05:51:23 +00:00
|
|
|
if (notePitch != null) {
|
2021-02-19 16:12:36 +00:00
|
|
|
NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue());
|
2020-04-21 05:32:32 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
if (javaId.contains("piston")) {
|
2020-05-05 02:32:02 +00:00
|
|
|
// True if extended, false if not
|
2021-02-19 16:12:36 +00:00
|
|
|
PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true"));
|
|
|
|
IS_STICKY_PISTON.put(javaBlockState, javaId.contains("sticky"));
|
2020-05-05 02:32:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
JsonNode skullVariation = blockData.get("variation");
|
2020-11-05 21:36:22 +00:00
|
|
|
if (skullVariation != null) {
|
2020-05-01 05:51:23 +00:00
|
|
|
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
JsonNode skullRotation = blockData.get("skull_rotation");
|
2020-04-21 05:32:32 +00:00
|
|
|
if (skullRotation != null) {
|
2020-05-01 05:51:23 +00:00
|
|
|
SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
2020-04-29 16:01:56 +00:00
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
if (javaId.contains("wall_skull") || javaId.contains("wall_head")) {
|
|
|
|
String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7);
|
2020-12-04 21:55:24 +00:00
|
|
|
int rotation = 0;
|
|
|
|
switch (direction.substring(0, direction.length() - 1)) {
|
|
|
|
case "north":
|
|
|
|
rotation = 180;
|
|
|
|
break;
|
|
|
|
case "south":
|
|
|
|
rotation = 0;
|
|
|
|
break;
|
|
|
|
case "west":
|
|
|
|
rotation = 90;
|
|
|
|
break;
|
|
|
|
case "east":
|
|
|
|
rotation = 270;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation);
|
|
|
|
}
|
|
|
|
|
2021-02-19 16:12:36 +00:00
|
|
|
JsonNode shulkerDirection = blockData.get("shulker_direction");
|
2020-04-29 16:01:56 +00:00
|
|
|
if (shulkerDirection != null) {
|
|
|
|
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
|
|
|
|
}
|
2021-02-19 16:12:36 +00:00
|
|
|
|
|
|
|
if (javaId.startsWith("minecraft:water")) {
|
|
|
|
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
|
|
|
|
int level = Integer.parseInt(strLevel);
|
|
|
|
WATER_LEVEL.put(javaBlockState, level);
|
|
|
|
}
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
|
|
* This gives an integer color that Bedrock can use.
|
2020-04-29 16:01:56 +00:00
|
|
|
*
|
2020-04-21 05:32:32 +00:00
|
|
|
* @param state BlockState of the block
|
2020-04-29 16:01:56 +00:00
|
|
|
* @return Banner color integer or -1 if no color
|
2020-04-21 05:32:32 +00:00
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static int getBannerColor(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return BANNER_COLORS.getOrDefault(state, -1);
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
|
|
* This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
|
2020-04-29 16:01:56 +00:00
|
|
|
*
|
2020-04-21 05:32:32 +00:00
|
|
|
* @param state BlockState of the block
|
2020-04-29 16:01:56 +00:00
|
|
|
* @return Bed color byte or -1 if no color
|
2020-04-21 05:32:32 +00:00
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static byte getBedColor(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return BED_COLORS.getOrDefault(state, (byte) -1);
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
2020-09-15 00:54:19 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2020-05-10 19:26:00 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
2020-11-05 21:36:22 +00:00
|
|
|
*
|
2020-05-10 19:26:00 +00:00
|
|
|
* @return The map of all DoubleChestValues.
|
|
|
|
*/
|
|
|
|
public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() {
|
|
|
|
return DOUBLE_CHEST_VALUES;
|
|
|
|
}
|
|
|
|
|
2020-05-05 02:32:02 +00:00
|
|
|
/**
|
|
|
|
* Get the Int2ObjectMap of flower pot block states to containing plant
|
2020-11-05 21:36:22 +00:00
|
|
|
*
|
2020-05-05 02:32:02 +00:00
|
|
|
* @return Int2ObjectMap of flower pot values
|
|
|
|
*/
|
|
|
|
public static Int2ObjectMap<String> getFlowerPotValues() {
|
|
|
|
return FLOWER_POT_VALUES;
|
|
|
|
}
|
|
|
|
|
2021-03-10 18:54:09 +00:00
|
|
|
/**
|
|
|
|
* This returns a Map interface so IntelliJ doesn't complain about {@link Int2BooleanMap#compute(int, BiFunction)}
|
|
|
|
* not returning null.
|
|
|
|
*
|
|
|
|
* @return the lectern book state map pointing to book present state
|
|
|
|
*/
|
|
|
|
public static Map<Integer, Boolean> getLecternBookStates() {
|
2020-12-28 05:29:27 +00:00
|
|
|
return LECTERN_BOOK_STATES;
|
|
|
|
}
|
|
|
|
|
2020-05-01 05:51:23 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
2020-11-05 21:36:22 +00:00
|
|
|
*
|
2020-05-01 05:51:23 +00:00
|
|
|
* @param state BlockState of the block
|
|
|
|
* @return note block note integer or -1 if not present
|
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static int getNoteblockPitch(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return NOTEBLOCK_PITCHES.getOrDefault(state, -1);
|
2020-05-01 05:51:23 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 02:32:02 +00:00
|
|
|
/**
|
|
|
|
* Get the Int2BooleanMap showing if a piston block state is extended or not.
|
2020-11-05 21:36:22 +00:00
|
|
|
*
|
2020-05-05 02:32:02 +00:00
|
|
|
* @return the Int2BooleanMap of piston extensions.
|
|
|
|
*/
|
|
|
|
public static Int2BooleanMap getPistonValues() {
|
|
|
|
return PISTON_VALUES;
|
|
|
|
}
|
|
|
|
|
2020-06-19 01:44:50 +00:00
|
|
|
public static boolean isStickyPiston(int blockState) {
|
|
|
|
return IS_STICKY_PISTON.get(blockState);
|
2020-05-05 02:32:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 05:32:32 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
2020-04-29 16:01:56 +00:00
|
|
|
*
|
2020-04-21 05:32:32 +00:00
|
|
|
* @param state BlockState of the block
|
2020-04-29 16:01:56 +00:00
|
|
|
* @return Skull variant byte or -1 if no variant
|
2020-04-21 05:32:32 +00:00
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static byte getSkullVariant(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-29 16:01:56 +00:00
|
|
|
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
|
|
* This gives a byte rotation that Bedrock can use.
|
2020-04-21 05:32:32 +00:00
|
|
|
*
|
|
|
|
* @param state BlockState of the block
|
2020-04-29 16:01:56 +00:00
|
|
|
* @return Skull rotation value or -1 if no value
|
2020-04-21 05:32:32 +00:00
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static byte getSkullRotation(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 21:55:24 +00:00
|
|
|
/**
|
|
|
|
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
|
|
* This gives a integer rotation that Bedrock can use.
|
|
|
|
*
|
|
|
|
* @return Skull wall rotation value with the blockstate
|
|
|
|
*/
|
|
|
|
public static Int2IntMap getSkullWallDirections() {
|
|
|
|
return SKULL_WALL_DIRECTIONS;
|
|
|
|
}
|
2020-04-29 16:01:56 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
|
|
* This gives a byte direction that Bedrock can use.
|
|
|
|
*
|
|
|
|
* @param state BlockState of the block
|
|
|
|
* @return Shulker direction value or -1 if no value
|
|
|
|
*/
|
2020-06-19 01:44:50 +00:00
|
|
|
public static byte getShulkerBoxDirection(int state) {
|
2020-11-05 21:36:22 +00:00
|
|
|
return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1);
|
2020-04-29 16:01:56 +00:00
|
|
|
}
|
2021-02-19 16:12:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the level of water from the block state.
|
|
|
|
* This is used in FishingHookEntity to create splash sounds when the hook hits the water.
|
|
|
|
*
|
|
|
|
* @param state BlockState of the block
|
|
|
|
* @return The water level or -1 if the block isn't water
|
|
|
|
*/
|
|
|
|
public static int getWaterLevel(int state) {
|
|
|
|
return WATER_LEVEL.getOrDefault(state, -1);
|
|
|
|
}
|
2020-04-21 05:32:32 +00:00
|
|
|
}
|