Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-25 15:50:14 +01:00
BlockMapping is removed
Dieser Commit ist enthalten in:
Ursprung
1e9dbaf38b
Commit
d85549c38d
@ -256,17 +256,6 @@ public class GeyserImpl implements GeyserApi {
|
||||
}
|
||||
|
||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||
|
||||
for (int i = 0; i < BlockRegistries.JAVA_BLOCKS.get().length; i++) {
|
||||
String oldIdentifier = BlockRegistries.JAVA_BLOCKS.get(i).getJavaIdentifier();
|
||||
String newIdentifier = BlockRegistries.BLOCK_STATES.get(i).toString();
|
||||
if (!oldIdentifier.equals(newIdentifier)) {
|
||||
System.out.println("Check block " + oldIdentifier + " " + newIdentifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.out.println(BlockRegistries.JAVA_BLOCKS.get().length);
|
||||
System.out.println(BlockRegistries.BLOCK_STATES.get().size());
|
||||
}
|
||||
|
||||
private void startInstance() {
|
||||
|
@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AddItemEntityPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
@ -137,7 +138,7 @@ public class ItemEntity extends ThrowableEntity {
|
||||
protected float getDrag() {
|
||||
if (isOnGround()) {
|
||||
Vector3i groundBlockPos = position.toInt().down(1);
|
||||
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos);
|
||||
BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, groundBlockPos);
|
||||
return BlockStateValues.getSlipperiness(blockState) * 0.98f;
|
||||
}
|
||||
return 0.98f;
|
||||
|
@ -33,8 +33,8 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData;
|
||||
@ -119,28 +119,29 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
||||
}
|
||||
|
||||
// The bed block
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition);
|
||||
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, bedPosition);
|
||||
|
||||
// Set the correct position offset and rotation when sleeping
|
||||
int bedRotation = 0;
|
||||
float xOffset = 0;
|
||||
float zOffset = 0;
|
||||
if (fullIdentifier.contains("facing=south")) {
|
||||
// bed is facing south
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=east")) {
|
||||
// bed is facing east
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=west")) {
|
||||
// bed is facing west
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
} else if (fullIdentifier.contains("facing=north")) {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
switch (state.getValue(Properties.HORIZONTAL_FACING)) {
|
||||
case SOUTH -> {
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
}
|
||||
case EAST -> {
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
}
|
||||
case WEST -> {
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
}
|
||||
case NORTH -> {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
}
|
||||
}
|
||||
|
||||
setYaw(yaw);
|
||||
|
@ -34,7 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.WallSkullBlock;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.geyser.skin.SkullSkinManager;
|
||||
@ -137,20 +140,19 @@ public class SkullPlayerEntity extends PlayerEntity {
|
||||
float z = skull.getPosition().getZ() + .5f;
|
||||
float rotation;
|
||||
|
||||
int blockState = skull.getBlockState();
|
||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||
if (floorRotation == -1) {
|
||||
// Wall skull
|
||||
BlockState blockState = skull.getBlockState();
|
||||
if (blockState.block() instanceof WallSkullBlock) {
|
||||
y += 0.25f;
|
||||
rotation = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||
switch ((int) rotation) {
|
||||
case 180 -> z += 0.24f; // North
|
||||
case 0 -> z -= 0.24f; // South
|
||||
case 90 -> x += 0.24f; // West
|
||||
case 270 -> x -= 0.24f; // East
|
||||
Direction direction = blockState.getValue(Properties.HORIZONTAL_FACING);
|
||||
rotation = WallSkullBlock.getDegrees(direction);
|
||||
switch (direction) {
|
||||
case NORTH -> z += 0.24f;
|
||||
case SOUTH -> z -= 0.24f;
|
||||
case WEST -> x += 0.24f;
|
||||
case EAST -> x -= 0.24f;
|
||||
}
|
||||
} else {
|
||||
rotation = (180f + (floorRotation * 22.5f)) % 360;
|
||||
rotation = (180f + (blockState.getValue(Properties.ROTATION_16) * 22.5f)) % 360;
|
||||
}
|
||||
|
||||
moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@ -83,9 +84,9 @@ public class Container extends Inventory {
|
||||
* Will be overwritten for droppers.
|
||||
*
|
||||
* @param usingRealBlock whether this container is using a real container or not
|
||||
* @param javaBlockId the Java block string of the block, if real
|
||||
* @param block the Java block, if real
|
||||
*/
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
isUsingRealBlock = usingRealBlock;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
@ -44,10 +46,10 @@ public class Generic3X3Container extends Container {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
super.setUsingRealBlock(usingRealBlock, javaBlockId);
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
super.setUsingRealBlock(usingRealBlock, block);
|
||||
if (usingRealBlock) {
|
||||
isDropper = javaBlockId.startsWith("minecraft:dropper");
|
||||
isDropper = block == Blocks.DROPPER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
||||
if (isValidBlock(javaBlockString)) {
|
||||
// We can safely use this block
|
||||
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||
((Container) inventory).setUsingRealBlock(true, state.block());
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, state);
|
||||
|
||||
return true;
|
||||
|
@ -59,10 +59,12 @@ import java.util.function.Function;
|
||||
*/
|
||||
public abstract class WorldManager {
|
||||
|
||||
public final BlockState blockAt(GeyserSession session, Vector3i vector3i) {
|
||||
return BlockState.of(this.getBlockAt(session, vector3i));
|
||||
@NonNull
|
||||
public final BlockState blockAt(GeyserSession session, Vector3i vector) {
|
||||
return this.blockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BlockState blockAt(GeyserSession session, int x, int y, int z) {
|
||||
return BlockState.of(this.getBlockAt(session, x, y, z));
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
@ -36,10 +35,6 @@ import org.geysermc.geyser.level.block.type.PistonBlock;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.util.collection.FixedInt2ByteMap;
|
||||
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
|
||||
import org.geysermc.geyser.util.collection.LecternHasBookMap;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@ -47,35 +42,13 @@ import java.util.Locale;
|
||||
* Used for block entities if the Java block state contains Bedrock block information.
|
||||
*/
|
||||
public final class BlockStateValues {
|
||||
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
|
||||
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap();
|
||||
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet HORIZONTAL_FACING_JIGSAWS = new IntOpenHashSet();
|
||||
private static final LecternHasBookMap LECTERN_BOOK_STATES = new LecternHasBookMap();
|
||||
private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap NOTEBLOCK_PITCHES = new FixedInt2IntMap();
|
||||
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
|
||||
private static final IntSet STICKY_PISTONS = new IntOpenHashSet();
|
||||
private static final Object2IntMap<Direction> PISTON_HEADS = new Object2IntOpenHashMap<>();
|
||||
private static final Int2ObjectMap<Direction> PISTON_ORIENTATION = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet();
|
||||
private static final IntSet MOVING_PISTONS = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_VARIANTS = new FixedInt2ByteMap();
|
||||
private static final IntSet SKULL_POWERED = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
|
||||
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
|
||||
private static final IntSet UPPER_DOORS = new IntOpenHashSet();
|
||||
|
||||
public static int JAVA_FURNACE_ID;
|
||||
public static int JAVA_FURNACE_LIT_ID;
|
||||
public static int JAVA_HONEY_BLOCK_ID;
|
||||
public static int JAVA_SLIME_BLOCK_ID;
|
||||
public static int JAVA_SPAWNER_ID;
|
||||
public static int JAVA_WATER_ID;
|
||||
|
||||
public static final int NUM_WATER_LEVELS = 9;
|
||||
@ -85,66 +58,9 @@ public final class BlockStateValues {
|
||||
*
|
||||
* @param javaId The Java Identifier of the block
|
||||
* @param javaBlockState the Java Block State of the block
|
||||
* @param blockData JsonNode of info about the block from blocks.json
|
||||
*/
|
||||
public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) {
|
||||
JsonNode bannerColor = blockData.get("banner_color");
|
||||
if (bannerColor != null) {
|
||||
BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
|
||||
return; // There will never be a banner color and a skull variant
|
||||
}
|
||||
|
||||
JsonNode bedColor = blockData.get("bed_color");
|
||||
if (bedColor != null) {
|
||||
BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode bedrockStates = blockData.get("bedrock_states");
|
||||
if (bedrockStates != null) {
|
||||
JsonNode brushedProgress = bedrockStates.get("brushed_progress");
|
||||
if (brushedProgress != null) {
|
||||
BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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"));
|
||||
DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft));
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:lectern")) {
|
||||
LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true"));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode notePitch = blockData.get("note_pitch");
|
||||
if (notePitch != null) {
|
||||
NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
public static void storeBlockStateValues(String javaId, int javaBlockState) {
|
||||
if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston
|
||||
if (javaId.startsWith("minecraft:moving_piston")) {
|
||||
MOVING_PISTONS.add(javaBlockState);
|
||||
} else {
|
||||
PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true"));
|
||||
}
|
||||
if (javaId.contains("sticky")) {
|
||||
STICKY_PISTONS.add(javaBlockState);
|
||||
}
|
||||
@ -158,40 +74,6 @@ public final class BlockStateValues {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode skullVariation = blockData.get("variation");
|
||||
if (skullVariation != null) {
|
||||
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
|
||||
}
|
||||
|
||||
JsonNode skullRotation = blockData.get("skull_rotation");
|
||||
if (skullRotation != null) {
|
||||
SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:dragon_head[") || javaId.startsWith("minecraft:piglin_head[")
|
||||
|| javaId.startsWith("minecraft:dragon_wall_head[") || javaId.startsWith("minecraft:piglin_wall_head[")) {
|
||||
if (javaId.contains("powered=true")) {
|
||||
SKULL_POWERED.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
if (javaId.contains("wall_skull") || javaId.contains("wall_head")) {
|
||||
String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7, javaId.lastIndexOf("powered=") - 1);
|
||||
int rotation = switch (direction) {
|
||||
case "north" -> 180;
|
||||
case "west" -> 90;
|
||||
case "east" -> 270;
|
||||
default -> 0; // Also south
|
||||
};
|
||||
SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation);
|
||||
}
|
||||
|
||||
JsonNode shulkerDirection = blockData.get("shulker_direction");
|
||||
if (shulkerDirection != null) {
|
||||
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) {
|
||||
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
|
||||
int level = Integer.parseInt(strLevel);
|
||||
@ -205,48 +87,7 @@ public final class BlockStateValues {
|
||||
if (direction.isHorizontal()) {
|
||||
HORIZONTAL_FACING_JIGSAWS.add(javaBlockState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.contains("cauldron")) {
|
||||
ALL_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
if (javaId.contains("_cauldron") && !javaId.contains("water_")) {
|
||||
NON_WATER_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
|
||||
if (javaId.contains("_door[") && javaId.contains("half=upper")) {
|
||||
UPPER_DOORS.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Banner color integer or -1 if no color
|
||||
*/
|
||||
public static int getBannerColor(int state) {
|
||||
return BANNER_COLORS.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if this Java block state is a non-empty non-water cauldron
|
||||
*/
|
||||
public static boolean isNonWaterCauldron(int state) {
|
||||
return NON_WATER_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
|
||||
* <p>
|
||||
* When using a bucket on a cauldron sending a ServerboundUseItemPacket can result in the liquid being placed.
|
||||
*
|
||||
* @return if this Java block state is a cauldron
|
||||
*/
|
||||
public static boolean isCauldron(int state) {
|
||||
return ALL_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,13 +97,6 @@ public final class BlockStateValues {
|
||||
return HORIZONTAL_FACING_JIGSAWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lectern book state map pointing to book present state
|
||||
*/
|
||||
public static LecternHasBookMap getLecternBookStates() {
|
||||
return LECTERN_BOOK_STATES;
|
||||
}
|
||||
|
||||
public static boolean isStickyPiston(int blockState) {
|
||||
return STICKY_PISTONS.contains(blockState);
|
||||
}
|
||||
@ -354,38 +188,6 @@ public final class BlockStateValues {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull variant byte or -1 if no variant
|
||||
*/
|
||||
public static byte getSkullVariant(int state) {
|
||||
return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull rotation value or -1 if no value
|
||||
*/
|
||||
public static byte getSkullRotation(int state) {
|
||||
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level of water from the block state.
|
||||
*
|
||||
@ -427,14 +229,18 @@ public final class BlockStateValues {
|
||||
* @param state BlockState of the block
|
||||
* @return The block's slipperiness
|
||||
*/
|
||||
public static float getSlipperiness(int state) {
|
||||
String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
return switch (blockIdentifier) {
|
||||
case "minecraft:slime_block" -> 0.8f;
|
||||
case "minecraft:ice", "minecraft:packed_ice" -> 0.98f;
|
||||
case "minecraft:blue_ice" -> 0.989f;
|
||||
default -> 0.6f;
|
||||
};
|
||||
public static float getSlipperiness(BlockState state) {
|
||||
Block block = state.block();
|
||||
if (block == Blocks.SLIME_BLOCK) {
|
||||
return 0.8f;
|
||||
}
|
||||
if (block == Blocks.ICE || block == Blocks.PACKED_ICE) {
|
||||
return 0.98f;
|
||||
}
|
||||
if (block == Blocks.BLUE_ICE) {
|
||||
return 0.989f;
|
||||
}
|
||||
return 0.6f;
|
||||
}
|
||||
|
||||
private static Direction getBlockDirection(String javaId) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.property.ChestType;
|
||||
import org.geysermc.geyser.level.block.type.*;
|
||||
import org.geysermc.geyser.level.physics.Axis;
|
||||
@ -308,7 +309,7 @@ public final class Blocks {
|
||||
public static final Block FERN = register(new Block("fern", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block DEAD_BUSH = register(new Block("dead_bush", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block SEAGRASS = register(new Block("seagrass", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block TALL_SEAGRASS = register(new Block("tall_seagrass", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block TALL_SEAGRASS = register(new Block("tall_seagrass", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.SEAGRASS)
|
||||
.enumState(DOUBLE_BLOCK_HALF, "upper", "lower")));
|
||||
public static final Block PISTON = register(new PistonBlock("piston", builder().destroyTime(1.5f).pushReaction(PistonBehavior.BLOCK)
|
||||
.booleanState(EXTENDED)
|
||||
@ -758,9 +759,9 @@ public final class Blocks {
|
||||
.booleanState(WEST)));
|
||||
public static final Block PUMPKIN = register(new Block("pumpkin", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block MELON = register(new Block("melon", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block ATTACHED_PUMPKIN_STEM = register(new Block("attached_pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block ATTACHED_PUMPKIN_STEM = register(new Block("attached_pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.PUMPKIN_SEEDS)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)));
|
||||
public static final Block ATTACHED_MELON_STEM = register(new Block("attached_melon_stem", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block ATTACHED_MELON_STEM = register(new Block("attached_melon_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.MELON_SEEDS)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)));
|
||||
public static final Block PUMPKIN_STEM = register(new Block("pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_7, 0, 7)));
|
||||
@ -963,46 +964,46 @@ public final class Blocks {
|
||||
.enumState(ATTACH_FACE, "floor", "wall", "ceiling")
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block SKELETON_WALL_SKULL = register(new SkullBlock("skeleton_wall_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block SKELETON_WALL_SKULL = register(new WallSkullBlock("skeleton_wall_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block WITHER_SKELETON_WALL_SKULL = register(new SkullBlock("wither_skeleton_wall_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block WITHER_SKELETON_WALL_SKULL = register(new WallSkullBlock("wither_skeleton_wall_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block ZOMBIE_WALL_HEAD = register(new SkullBlock("zombie_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block ZOMBIE_WALL_HEAD = register(new WallSkullBlock("zombie_wall_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", SkullBlock.Type.PLAYER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block PLAYER_WALL_HEAD = register(new SkullBlock("player_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PLAYER_WALL_HEAD = register(new WallSkullBlock("player_wall_head", SkullBlock.Type.PLAYER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", SkullBlock.Type.CREEPER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block CREEPER_WALL_HEAD = register(new SkullBlock("creeper_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block CREEPER_WALL_HEAD = register(new WallSkullBlock("creeper_wall_head", SkullBlock.Type.CREEPER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", SkullBlock.Type.DRAGON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block DRAGON_WALL_HEAD = register(new SkullBlock("dragon_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block DRAGON_WALL_HEAD = register(new WallSkullBlock("dragon_wall_head", SkullBlock.Type.DRAGON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.booleanState(POWERED)
|
||||
.intState(ROTATION_16, 0, 15)));
|
||||
public static final Block PIGLIN_WALL_HEAD = register(new SkullBlock("piglin_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PIGLIN_WALL_HEAD = register(new WallSkullBlock("piglin_wall_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)
|
||||
.booleanState(POWERED)));
|
||||
public static final Block ANVIL = register(new Block("anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK)
|
||||
@ -1694,7 +1695,7 @@ public final class Blocks {
|
||||
public static final Block BLACK_CONCRETE_POWDER = register(new Block("black_concrete_powder", builder().destroyTime(0.5f)));
|
||||
public static final Block KELP = register(new Block("kelp", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_25, 0, 25)));
|
||||
public static final Block KELP_PLANT = register(new Block("kelp_plant", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block KELP_PLANT = register(new Block("kelp_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.KELP)));
|
||||
public static final Block DRIED_KELP_BLOCK = register(new Block("dried_kelp_block", builder().destroyTime(0.5f)));
|
||||
public static final Block TURTLE_EGG = register(new Block("turtle_egg", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(EGGS, 1, 4)
|
||||
@ -1787,7 +1788,7 @@ public final class Blocks {
|
||||
public static final Block BLUE_ICE = register(new Block("blue_ice", builder().destroyTime(2.8f)));
|
||||
public static final Block CONDUIT = register(new Block("conduit", builder().setBlockEntity().destroyTime(3.0f)
|
||||
.booleanState(WATERLOGGED)));
|
||||
public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.BAMBOO)));
|
||||
public static final Block BAMBOO = register(new Block("bamboo", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_1, 0, 1)
|
||||
.enumState(BAMBOO_LEAVES, "none", "small", "large")
|
||||
@ -2072,10 +2073,10 @@ public final class Blocks {
|
||||
public static final Block SHROOMLIGHT = register(new Block("shroomlight", builder().destroyTime(1.0f)));
|
||||
public static final Block WEEPING_VINES = register(new Block("weeping_vines", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_25, 0, 25)));
|
||||
public static final Block WEEPING_VINES_PLANT = register(new Block("weeping_vines_plant", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block WEEPING_VINES_PLANT = register(new Block("weeping_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.WEEPING_VINES)));
|
||||
public static final Block TWISTING_VINES = register(new Block("twisting_vines", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_25, 0, 25)));
|
||||
public static final Block TWISTING_VINES_PLANT = register(new Block("twisting_vines_plant", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block TWISTING_VINES_PLANT = register(new Block("twisting_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.TWISTING_VINES)));
|
||||
public static final Block CRIMSON_ROOTS = register(new Block("crimson_roots", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block CRIMSON_PLANKS = register(new Block("crimson_planks", builder().destroyTime(2.0f)));
|
||||
public static final Block WARPED_PLANKS = register(new Block("warped_planks", builder().destroyTime(2.0f)));
|
||||
@ -2319,39 +2320,39 @@ public final class Blocks {
|
||||
.intState(CANDLES, 1, 4)
|
||||
.booleanState(LIT)
|
||||
.booleanState(WATERLOGGED)));
|
||||
public static final Block CANDLE_CAKE = register(new Block("candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block CANDLE_CAKE = register(new Block("candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block WHITE_CANDLE_CAKE = register(new Block("white_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block WHITE_CANDLE_CAKE = register(new Block("white_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block ORANGE_CANDLE_CAKE = register(new Block("orange_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block ORANGE_CANDLE_CAKE = register(new Block("orange_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block MAGENTA_CANDLE_CAKE = register(new Block("magenta_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block MAGENTA_CANDLE_CAKE = register(new Block("magenta_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block LIGHT_BLUE_CANDLE_CAKE = register(new Block("light_blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block LIGHT_BLUE_CANDLE_CAKE = register(new Block("light_blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block YELLOW_CANDLE_CAKE = register(new Block("yellow_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block YELLOW_CANDLE_CAKE = register(new Block("yellow_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block LIME_CANDLE_CAKE = register(new Block("lime_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block LIME_CANDLE_CAKE = register(new Block("lime_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block PINK_CANDLE_CAKE = register(new Block("pink_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PINK_CANDLE_CAKE = register(new Block("pink_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block GRAY_CANDLE_CAKE = register(new Block("gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block GRAY_CANDLE_CAKE = register(new Block("gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block LIGHT_GRAY_CANDLE_CAKE = register(new Block("light_gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block LIGHT_GRAY_CANDLE_CAKE = register(new Block("light_gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block CYAN_CANDLE_CAKE = register(new Block("cyan_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block CYAN_CANDLE_CAKE = register(new Block("cyan_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block PURPLE_CANDLE_CAKE = register(new Block("purple_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block PURPLE_CANDLE_CAKE = register(new Block("purple_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block BLUE_CANDLE_CAKE = register(new Block("blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block BLUE_CANDLE_CAKE = register(new Block("blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block BROWN_CANDLE_CAKE = register(new Block("brown_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block BROWN_CANDLE_CAKE = register(new Block("brown_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block GREEN_CANDLE_CAKE = register(new Block("green_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block GREEN_CANDLE_CAKE = register(new Block("green_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block RED_CANDLE_CAKE = register(new Block("red_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block RED_CANDLE_CAKE = register(new Block("red_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block BLACK_CANDLE_CAKE = register(new Block("black_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block BLACK_CANDLE_CAKE = register(new Block("black_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE)
|
||||
.booleanState(LIT)));
|
||||
public static final Block AMETHYST_BLOCK = register(new Block("amethyst_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f)));
|
||||
public static final Block BUDDING_AMETHYST = register(new Block("budding_amethyst", builder().requiresCorrectToolForDrops().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY)));
|
||||
@ -2682,7 +2683,7 @@ public final class Blocks {
|
||||
public static final Block CAVE_VINES = register(new Block("cave_vines", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
.intState(AGE_25, 0, 25)
|
||||
.booleanState(BERRIES)));
|
||||
public static final Block CAVE_VINES_PLANT = register(new Block("cave_vines_plant", builder().pushReaction(PistonBehavior.DESTROY)
|
||||
public static final Block CAVE_VINES_PLANT = register(new Block("cave_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.GLOW_BERRIES)
|
||||
.booleanState(BERRIES)));
|
||||
public static final Block SPORE_BLOSSOM = register(new Block("spore_blossom", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
public static final Block AZALEA = register(new Block("azalea", builder().pushReaction(PistonBehavior.DESTROY)));
|
||||
@ -2811,7 +2812,8 @@ public final class Blocks {
|
||||
.booleanState(WATERLOGGED)));
|
||||
|
||||
private static <T extends Block> T register(T block) {
|
||||
BlockRegistries.JAVA_BLOCKS_TO_RENAME.get().add(block);
|
||||
block.setJavaId(BlockRegistries.JAVA_BLOCKS.get().size());
|
||||
BlockRegistries.JAVA_BLOCKS.get().add(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.level.block;
|
||||
|
||||
/**
|
||||
* This stores all values of double chests that are part of the Java block state.
|
||||
*
|
||||
* @param isFacingEast If true, then chest is facing east/west; if false, south/north
|
||||
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
|
||||
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
|
||||
*/
|
||||
public record DoubleChestValue(
|
||||
boolean isFacingEast,
|
||||
boolean isDirectionPositive,
|
||||
boolean isLeft) {
|
||||
|
||||
}
|
@ -37,24 +37,33 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.property.Property;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Block {
|
||||
public static final int JAVA_AIR_ID = 0;
|
||||
|
||||
private final Key javaIdentifier;
|
||||
/**
|
||||
* Can you harvest this with your hand.
|
||||
*/
|
||||
private final boolean requiresCorrectToolForDrops;
|
||||
private final boolean hasBlockEntity;
|
||||
private final float destroyTime;
|
||||
private final @NonNull PistonBehavior pushReaction;
|
||||
/**
|
||||
* Used for classes we don't have implemented yet that override Mojmap getCloneItemStack with their own item.
|
||||
* A supplier prevents any issues arising where the Items class finishes before the Blocks class.
|
||||
*/
|
||||
private final Supplier<Item> pickItem;
|
||||
protected Item item = null;
|
||||
private int javaId = -1;
|
||||
|
||||
@ -64,10 +73,13 @@ public class Block {
|
||||
this.hasBlockEntity = builder.hasBlockEntity;
|
||||
this.destroyTime = builder.destroyTime;
|
||||
this.pushReaction = builder.pushReaction;
|
||||
this.pickItem = builder.pickItem;
|
||||
processStates(builder.build(this));
|
||||
}
|
||||
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
checkForEmptySkull(session, state, position);
|
||||
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
||||
sendBlockUpdatePacket(session, state, definition, position);
|
||||
|
||||
@ -118,8 +130,7 @@ public class Block {
|
||||
UpdateBlockPacket waterPacket = new UpdateBlockPacket();
|
||||
waterPacket.setDataLayer(1);
|
||||
waterPacket.setBlockPosition(position);
|
||||
Boolean waterlogged = state.getValue(Properties.WATERLOGGED);
|
||||
if (waterlogged == Boolean.TRUE) {
|
||||
if (BlockRegistries.WATERLOGGED.get().get(state.javaId())) {
|
||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockWater());
|
||||
} else {
|
||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||
@ -127,6 +138,13 @@ public class Block {
|
||||
session.sendUpstreamPacket(waterPacket);
|
||||
}
|
||||
|
||||
protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
if (!(state.block() instanceof SkullBlock)) {
|
||||
// Skull is gone
|
||||
session.getSkullCache().removeSkull(position);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
|
||||
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) {
|
||||
@ -141,6 +159,13 @@ public class Block {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.pickItem != null) {
|
||||
return new ItemStack(this.pickItem.get().javaId());
|
||||
}
|
||||
return new ItemStack(this.asItem().javaId());
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of BlockStates is created pertaining to this block. Do we need any of them? If so, override this method.
|
||||
*/
|
||||
@ -198,6 +223,7 @@ public class Block {
|
||||
private boolean hasBlockEntity = false;
|
||||
private PistonBehavior pushReaction = PistonBehavior.NORMAL;
|
||||
private float destroyTime;
|
||||
private Supplier<Item> pickItem;
|
||||
|
||||
/**
|
||||
* For states that we're just tracking for mirroring Java states.
|
||||
@ -248,6 +274,11 @@ public class Block {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pickItem(Supplier<Item> pickItem) {
|
||||
this.pickItem = pickItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<BlockState> build(Block block) {
|
||||
if (states.isEmpty()) {
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size());
|
||||
|
@ -37,7 +37,7 @@ public final class BlockState {
|
||||
private final int javaId;
|
||||
private final Reference2ObjectMap<Property<?>, Comparable<?>> states;
|
||||
|
||||
BlockState(Block block, int javaId) {
|
||||
public BlockState(Block block, int javaId) {
|
||||
this(block, javaId, Reference2ObjectMaps.emptyMap());
|
||||
}
|
||||
|
||||
@ -52,6 +52,14 @@ public final class BlockState {
|
||||
return (T) states.get(property);
|
||||
}
|
||||
|
||||
public boolean getValue(Property<Boolean> property, boolean def) {
|
||||
var value = states.get(property);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
public Block block() {
|
||||
return block;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
private final Block flower;
|
||||
@ -68,11 +69,24 @@ public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntit
|
||||
if (this.flower != Blocks.AIR) {
|
||||
// Get the Bedrock CompoundTag of the block.
|
||||
// This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states.
|
||||
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower.javaIdentifier().asString());
|
||||
// TODO flattening might make this nicer in the future!
|
||||
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower);
|
||||
if (plant != null) {
|
||||
tagBuilder.putCompound("PlantBlock", plant.toBuilder().build());
|
||||
}
|
||||
}
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.flower != Blocks.AIR) {
|
||||
return new ItemStack(this.flower.asItem().javaId());
|
||||
}
|
||||
return super.pickItem(state);
|
||||
}
|
||||
|
||||
public Block flower() {
|
||||
return flower;
|
||||
}
|
||||
}
|
||||
|
@ -27,28 +27,58 @@ package org.geysermc.geyser.level.block.type;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
|
||||
public class SkullBlock extends Block {
|
||||
public SkullBlock(String javaIdentifier, Builder builder) {
|
||||
private final Type type;
|
||||
|
||||
public SkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
int skullVariant = BlockStateValues.getSkullVariant(state.javaId()); // TODO
|
||||
if (skullVariant == -1) {
|
||||
// Skull is gone
|
||||
session.getSkullCache().removeSkull(position);
|
||||
} else if (skullVariant == 3) {
|
||||
if (this.type == Type.PLAYER) {
|
||||
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state.javaId());
|
||||
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state);
|
||||
if (skull != null && skull.getBlockDefinition() != null) {
|
||||
definition = skull.getBlockDefinition();
|
||||
}
|
||||
}
|
||||
super.sendBlockUpdatePacket(session, state, definition, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// It's not an empty skull.
|
||||
}
|
||||
|
||||
public Type skullType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum order matches Java.
|
||||
*/
|
||||
public enum Type {
|
||||
SKELETON(0),
|
||||
WITHER_SKELETON(1),
|
||||
PLAYER(3),
|
||||
ZOMBIE(2),
|
||||
CREEPER(4),
|
||||
PIGLIN(6),
|
||||
DRAGON(5);
|
||||
|
||||
private final int bedrockId;
|
||||
|
||||
Type(int bedrockId) {
|
||||
this.bedrockId = bedrockId;
|
||||
}
|
||||
|
||||
public int bedrockId() {
|
||||
return bedrockId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2024 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
|
||||
@ -23,21 +23,27 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry.type;
|
||||
package org.geysermc.geyser.level.block.type;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
@Builder
|
||||
@Value
|
||||
public class BlockMapping {
|
||||
public static BlockMapping DEFAULT = BlockMapping.builder().javaIdentifier("minecraft:air").build();
|
||||
public class WallSkullBlock extends SkullBlock {
|
||||
public WallSkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, type, builder);
|
||||
}
|
||||
|
||||
String javaIdentifier;
|
||||
public static int getDegrees(BlockState state) {
|
||||
return getDegrees(state.getValue(Properties.HORIZONTAL_FACING));
|
||||
}
|
||||
|
||||
@Nullable String pickItem;
|
||||
|
||||
boolean isBlockEntity;
|
||||
boolean isNonVanilla;
|
||||
public static int getDegrees(Direction direction) {
|
||||
return switch (direction) {
|
||||
case NORTH -> 180;
|
||||
case WEST -> 90;
|
||||
case EAST -> 270;
|
||||
case SOUTH -> 0;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
}
|
@ -42,7 +42,6 @@ import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
@ -67,24 +66,22 @@ public class BlockRegistries {
|
||||
*/
|
||||
public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
public static final ListRegistry<Block> JAVA_BLOCKS_TO_RENAME = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry which stores Java to Bedrock block identifiers.
|
||||
*/
|
||||
public static final SimpleMappedRegistry<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link BlockMapping}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ArrayRegistry<BlockMapping> JAVA_BLOCKS = ArrayRegistry.create(RegistryLoaders.uninitialized());
|
||||
|
||||
/**
|
||||
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
|
||||
*/
|
||||
public static final ListRegistry<BlockCollision> COLLISIONS;
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link Block}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ListRegistry<Block> JAVA_BLOCKS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry containing the Java identifiers to IDs.
|
||||
*/
|
||||
|
@ -32,13 +32,12 @@ import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import org.cloudburstmc.blockstateupdater.BlockStateUpdater;
|
||||
import org.cloudburstmc.blockstateupdater.util.tagupdater.CompoundTagUpdaterContext;
|
||||
import org.cloudburstmc.nbt.NBTInputStream;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.nbt.*;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
@ -50,21 +49,26 @@ import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.FlowerPotBlock;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
@ -102,7 +106,7 @@ public final class BlockRegistryPopulator {
|
||||
|
||||
public static void populate(Stage stage) {
|
||||
switch (stage) {
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNode();
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNbt();
|
||||
case INIT_JAVA -> registerJavaBlocks();
|
||||
case INIT_BEDROCK -> registerBedrockBlocks();
|
||||
default -> throw new IllegalArgumentException("Unknown stage: " + stage);
|
||||
@ -110,14 +114,14 @@ public final class BlockRegistryPopulator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the raw blocks JSON until it is no longer needed.
|
||||
* Stores the raw blocks NBT until it is no longer needed.
|
||||
*/
|
||||
private static JsonNode BLOCKS_JSON;
|
||||
private static List<NbtMap> BLOCKS_NBT;
|
||||
private static int MIN_CUSTOM_RUNTIME_ID = -1;
|
||||
private static int JAVA_BLOCKS_SIZE = -1;
|
||||
|
||||
private static void nullifyBlocksNode() {
|
||||
BLOCKS_JSON = null;
|
||||
private static void nullifyBlocksNbt() {
|
||||
BLOCKS_NBT = null;
|
||||
}
|
||||
|
||||
private static void registerBedrockBlocks() {
|
||||
@ -218,19 +222,31 @@ public final class BlockRegistryPopulator {
|
||||
|
||||
int javaRuntimeId = -1;
|
||||
|
||||
List<BlockState> javaBlockStates = BlockRegistries.BLOCK_STATES.get();
|
||||
|
||||
GeyserBedrockBlock airDefinition = null;
|
||||
BlockDefinition commandBlockDefinition = null;
|
||||
BlockDefinition mobSpawnerBlockDefinition = null;
|
||||
BlockDefinition waterDefinition = null;
|
||||
BlockDefinition movingBlockDefinition = null;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields();
|
||||
Iterator<NbtMap> blocksIterator = BLOCKS_NBT.iterator();
|
||||
|
||||
Remapper stateMapper = blockMappers.get(palette);
|
||||
|
||||
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
|
||||
Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
// Stream isn't ideal.
|
||||
List<Block> javaPottable = BlockRegistries.JAVA_BLOCKS.get()
|
||||
.parallelStream()
|
||||
.flatMap(block -> {
|
||||
if (block instanceof FlowerPotBlock flowerPot && flowerPot.flower() != Blocks.AIR) {
|
||||
return Stream.of(flowerPot.flower());
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.toList();
|
||||
Map<Block, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
|
||||
@ -239,10 +255,11 @@ public final class BlockRegistryPopulator {
|
||||
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
|
||||
while (blocksIterator.hasNext()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
NbtMap entry = blocksIterator.next();
|
||||
BlockState blockState = javaBlockStates.get(javaRuntimeId);
|
||||
String javaId = blockState.toString();
|
||||
|
||||
NbtMap originalBedrockTag = buildBedrockState(entry.getValue());
|
||||
NbtMap originalBedrockTag = buildBedrockState(blockState, entry);
|
||||
NbtMap bedrockTag = stateMapper.remap(originalBedrockTag);
|
||||
|
||||
GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag);
|
||||
@ -274,35 +291,27 @@ public final class BlockRegistryPopulator {
|
||||
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
|
||||
}
|
||||
|
||||
if (javaId.contains("jigsaw")) {
|
||||
if (blockState.block() == Blocks.JIGSAW) {
|
||||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
if (javaId.contains("structure_block")) {
|
||||
int modeIndex = javaId.indexOf("mode=");
|
||||
if (modeIndex != -1) {
|
||||
int startIndex = modeIndex + 5; // Length of "mode=" is 5
|
||||
int endIndex = javaId.indexOf("]", startIndex);
|
||||
if (endIndex != -1) {
|
||||
String modeValue = javaId.substring(startIndex, endIndex);
|
||||
structureBlockDefinitions.put(modeValue.toUpperCase(), bedrockDefinition);
|
||||
}
|
||||
}
|
||||
if (blockState.block() == Blocks.STRUCTURE_BLOCK) {
|
||||
String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
|
||||
structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
|
||||
}
|
||||
|
||||
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|
||||
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
|
||||
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|
||||
|| blockState.block() == Blocks.BUBBLE_COLUMN || blockState.block() == Blocks.KELP || blockState.block() == Blocks.SEAGRASS;
|
||||
|
||||
if (waterlogged) {
|
||||
int finalJavaRuntimeId = javaRuntimeId;
|
||||
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
|
||||
}
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
|
||||
// Get the tag needed for non-empty flower pots
|
||||
if (entry.getValue().get("pottable") != null) {
|
||||
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
if (javaPottable.contains(blockState.block())) {
|
||||
// Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
|
||||
flowerPotBlocks.put(blockState.block(), blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
}
|
||||
|
||||
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
|
||||
@ -386,9 +395,10 @@ public final class BlockRegistryPopulator {
|
||||
}
|
||||
|
||||
private static void registerJavaBlocks() {
|
||||
JsonNode blocksJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) {
|
||||
blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream);
|
||||
List<NbtMap> blocksNbt;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) {
|
||||
blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag())
|
||||
.getList("bedrock_mappings", NbtType.COMPOUND);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Java block mappings", e);
|
||||
}
|
||||
@ -399,56 +409,29 @@ public final class BlockRegistryPopulator {
|
||||
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) {
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) {
|
||||
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
|
||||
}
|
||||
|
||||
JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
|
||||
}
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates
|
||||
|
||||
Deque<String> cleanIdentifiers = new ArrayDeque<>();
|
||||
|
||||
int javaRuntimeId = -1;
|
||||
int waterRuntimeId = -1;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocksJson.fields();
|
||||
while (blocksIterator.hasNext()) {
|
||||
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
String javaId = javaBlockState.toString().intern();
|
||||
|
||||
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
|
||||
BlockStateValues.storeBlockStateValues(javaId, javaRuntimeId);
|
||||
|
||||
JsonNode pickItemNode = entry.getValue().get("pick_item");
|
||||
if (pickItemNode != null) {
|
||||
builder.pickItem(pickItemNode.textValue().intern());
|
||||
}
|
||||
|
||||
JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity");
|
||||
if (hasBlockEntityNode != null) {
|
||||
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
|
||||
} else {
|
||||
builder.isBlockEntity(false);
|
||||
}
|
||||
|
||||
BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue());
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
}
|
||||
|
||||
builder.javaIdentifier(javaId);
|
||||
//String cleanJavaIdentifier = javaBlockState.block().javaIdentifier().toString();
|
||||
//String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
|
||||
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
//BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
|
||||
if ("minecraft:water[level=0]".equals(javaId)) {
|
||||
waterRuntimeId = javaRuntimeId;
|
||||
@ -461,7 +444,7 @@ public final class BlockRegistryPopulator {
|
||||
BlockStateValues.JAVA_WATER_ID = waterRuntimeId;
|
||||
|
||||
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
|
||||
Set<Integer> usedNonVanillaRuntimeIDs = new HashSet<>();
|
||||
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();
|
||||
|
||||
for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
|
||||
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
|
||||
@ -473,11 +456,6 @@ public final class BlockRegistryPopulator {
|
||||
String javaId = javaBlockState.identifier();
|
||||
int stateRuntimeId = javaBlockState.javaId();
|
||||
String pistonBehavior = javaBlockState.pistonBehavior();
|
||||
BlockMapping blockMapping = BlockMapping.builder()
|
||||
.pickItem(javaBlockState.pickItem())
|
||||
.isNonVanilla(true)
|
||||
.javaIdentifier(javaId)
|
||||
.build();
|
||||
|
||||
Block.Builder builder = Block.builder()
|
||||
.destroyTime(javaBlockState.blockHardness())
|
||||
@ -492,23 +470,25 @@ public final class BlockRegistryPopulator {
|
||||
String pickItem = javaBlockState.pickItem();
|
||||
Block block = new Block(cleanJavaIdentifier, builder) {
|
||||
@Override
|
||||
public Item asItem() {
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.item == null) {
|
||||
return Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
|
||||
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
|
||||
if (this.item == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
|
||||
+ " for getting the item for block " + javaBlockState.identifier());
|
||||
this.item = Items.AIR;
|
||||
}
|
||||
}
|
||||
return this.item;
|
||||
return new ItemStack(this.item.javaId());
|
||||
}
|
||||
};
|
||||
block.setJavaId(javaBlockState.stateGroupId());
|
||||
|
||||
String bedrockIdentifier = customBlockState.block().identifier();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
}
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS_TO_RENAME.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks
|
||||
BlockRegistries.JAVA_BLOCKS.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
|
||||
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
@ -516,7 +496,7 @@ public final class BlockRegistryPopulator {
|
||||
}
|
||||
}
|
||||
|
||||
BLOCKS_JSON = blocksJson;
|
||||
BLOCKS_NBT = blocksNbt;
|
||||
|
||||
JsonNode blockInteractionsJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
|
||||
@ -537,29 +517,11 @@ public final class BlockRegistryPopulator {
|
||||
return blockStateSet;
|
||||
}
|
||||
|
||||
private static NbtMap buildBedrockState(JsonNode node) {
|
||||
private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) {
|
||||
NbtMapBuilder tagBuilder = NbtMap.builder();
|
||||
String bedrockIdentifier = node.get("bedrock_identifier").textValue();
|
||||
String bedrockIdentifier = "minecraft:" + nbt.getString("bedrock_identifier", state.block().javaIdentifier().value());
|
||||
tagBuilder.putString("name", bedrockIdentifier);
|
||||
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder();
|
||||
|
||||
// check for states
|
||||
JsonNode states = node.get("bedrock_states");
|
||||
if (states != null) {
|
||||
Iterator<Map.Entry<String, JsonNode>> statesIterator = states.fields();
|
||||
|
||||
while (statesIterator.hasNext()) {
|
||||
Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
|
||||
JsonNode stateValue = stateEntry.getValue();
|
||||
switch (stateValue.getNodeType()) {
|
||||
case BOOLEAN -> statesBuilder.putBoolean(stateEntry.getKey(), stateValue.booleanValue());
|
||||
case STRING -> statesBuilder.putString(stateEntry.getKey(), stateValue.textValue());
|
||||
case NUMBER -> statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
tagBuilder.put("states", statesBuilder.build());
|
||||
tagBuilder.put("states", nbt.getCompound("state"));
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
@ -59,7 +60,7 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||
BlockDefinition mobSpawnerBlock;
|
||||
|
||||
Map<NbtMap, BlockDefinition> itemFrames;
|
||||
Map<String, NbtMap> flowerPotBlocks;
|
||||
Map<Block, NbtMap> flowerPotBlocks;
|
||||
|
||||
Set<BlockDefinition> jigsawStates;
|
||||
Map<String, BlockDefinition> structureBlockStates;
|
||||
|
@ -25,18 +25,20 @@
|
||||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.WallSkullBlock;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -80,7 +82,7 @@ public class SkullCache {
|
||||
this.skullRenderDistanceSquared = distance * distance;
|
||||
}
|
||||
|
||||
public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, int blockState) {
|
||||
public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, BlockState blockState) {
|
||||
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
||||
skull.uuid = uuid;
|
||||
if (!texturesProperty.equals(skull.texturesProperty)) {
|
||||
@ -147,7 +149,7 @@ public class SkullCache {
|
||||
}
|
||||
}
|
||||
|
||||
public Skull updateSkull(Vector3i position, int blockState) {
|
||||
public Skull updateSkull(Vector3i position, BlockState blockState) {
|
||||
Skull skull = skulls.get(position);
|
||||
if (skull != null) {
|
||||
putSkull(position, skull.uuid, skull.texturesProperty, blockState);
|
||||
@ -248,17 +250,14 @@ public class SkullCache {
|
||||
lastPlayerPosition = null;
|
||||
}
|
||||
|
||||
private @Nullable BlockDefinition translateCustomSkull(String skinHash, int blockState) {
|
||||
private @Nullable BlockDefinition translateCustomSkull(String skinHash, BlockState blockState) {
|
||||
CustomSkull customSkull = BlockRegistries.CUSTOM_SKULLS.get(skinHash);
|
||||
if (customSkull != null) {
|
||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||
CustomBlockState customBlockState;
|
||||
if (floorRotation == -1) {
|
||||
// Wall skull
|
||||
int wallDirection = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||
customBlockState = customSkull.getWallBlockState(wallDirection);
|
||||
if (blockState.block() instanceof WallSkullBlock) {
|
||||
customBlockState = customSkull.getWallBlockState(WallSkullBlock.getDegrees(blockState));
|
||||
} else {
|
||||
customBlockState = customSkull.getFloorBlockState(floorRotation);
|
||||
customBlockState = customSkull.getFloorBlockState(blockState.getValue(Properties.ROTATION_16));
|
||||
}
|
||||
|
||||
return session.getBlockMappings().getCustomBlockStateDefinitions().get(customBlockState);
|
||||
@ -273,7 +272,7 @@ public class SkullCache {
|
||||
private String texturesProperty;
|
||||
private String skinHash;
|
||||
|
||||
private int blockState;
|
||||
private BlockState blockState;
|
||||
private BlockDefinition blockDefinition;
|
||||
private SkullPlayerEntity entity;
|
||||
|
||||
|
@ -36,10 +36,13 @@ import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.ChestType;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
|
||||
@ -55,24 +58,17 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
|
||||
public boolean prepareInventory(GeyserSession session, Inventory inventory) {
|
||||
// See BlockInventoryHolder - same concept there except we're also dealing with a specific block state
|
||||
if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) {
|
||||
int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(javaBlockId)) {
|
||||
String[] javaBlockString = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaBlockId, BlockMapping.DEFAULT).getJavaIdentifier().split("\\[");
|
||||
if (javaBlockString.length > 1 && (javaBlockString[0].equals("minecraft:chest") || javaBlockString[0].equals("minecraft:trapped_chest"))
|
||||
&& !javaBlockString[1].contains("type=single")) {
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) {
|
||||
if (state.block() == Blocks.CHEST || state.block() == Blocks.TRAPPED_CHEST
|
||||
&& state.getValue(Properties.CHEST_TYPE) != ChestType.SINGLE) {
|
||||
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||
((Container) inventory).setUsingRealBlock(true, state.block());
|
||||
|
||||
NbtMapBuilder tag = NbtMap.builder()
|
||||
.putString("id", "Chest")
|
||||
.putInt("x", session.getLastInteractionBlockPosition().getX())
|
||||
.putInt("y", session.getLastInteractionBlockPosition().getY())
|
||||
.putInt("z", session.getLastInteractionBlockPosition().getZ())
|
||||
.putString("CustomName", inventory.getTitle())
|
||||
.putString("id", "Chest");
|
||||
NbtMapBuilder tag = BlockEntityTranslator.getConstantBedrockTag("Chest", session.getLastInteractionBlockPosition())
|
||||
.putString("CustomName", inventory.getTitle());
|
||||
|
||||
BlockState blockState = BlockState.of(javaBlockId);
|
||||
DoubleChestBlockEntityTranslator.translateChestValue(tag, blockState,
|
||||
DoubleChestBlockEntityTranslator.translateChestValue(tag, state,
|
||||
session.getLastInteractionBlockPosition().getX(), session.getLastInteractionBlockPosition().getZ());
|
||||
|
||||
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
|
||||
|
@ -33,9 +33,9 @@ import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.SkullBlock;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.geyser.skin.SkinProvider;
|
||||
@ -53,17 +53,12 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
||||
|
||||
@Override
|
||||
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
|
||||
byte skullVariant = BlockStateValues.getSkullVariant(blockState.javaId()); // TODO
|
||||
// Just in case...
|
||||
if (skullVariant == -1) {
|
||||
skullVariant = 0;
|
||||
}
|
||||
Integer rotation = blockState.getValue(Properties.ROTATION_16);
|
||||
if (rotation != null) {
|
||||
// Could be a wall skull block
|
||||
// Could be a wall skull block otherwise, which has rotation in its Bedrock state
|
||||
bedrockNbt.putFloat("Rotation", rotation * 22.5f);
|
||||
}
|
||||
bedrockNbt.putByte("SkullType", skullVariant);
|
||||
bedrockNbt.putByte("SkullType", (byte) (blockState.block() instanceof SkullBlock skull ? skull.skullType().bedrockId() : 0));
|
||||
if (blockState.getValue(Properties.POWERED)) {
|
||||
bedrockNbt.putBoolean("MouthMoving", true);
|
||||
}
|
||||
@ -106,7 +101,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
||||
return CompletableFuture.completedFuture(texture);
|
||||
}
|
||||
|
||||
public static @Nullable BlockDefinition translateSkull(GeyserSession session, NbtMap javaNbt, Vector3i blockPosition, int blockState) {
|
||||
public static @Nullable BlockDefinition translateSkull(GeyserSession session, NbtMap javaNbt, Vector3i blockPosition, BlockState blockState) {
|
||||
NbtMap profile = javaNbt.getCompound("profile");
|
||||
if (profile.isEmpty()) {
|
||||
session.getSkullCache().removeSkull(blockPosition);
|
||||
@ -150,7 +145,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void putSkull(GeyserSession session, Vector3i blockPosition, UUID uuid, String texturesProperty, int blockState) {
|
||||
private static void putSkull(GeyserSession session, Vector3i blockPosition, UUID uuid, String texturesProperty, BlockState blockState) {
|
||||
SkullCache.Skull skull = session.getSkullCache().putSkull(blockPosition, uuid, texturesProperty, blockState);
|
||||
if (skull.getBlockDefinition() != null) {
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
|
@ -32,7 +32,7 @@ import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.BannerBlock;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
@ -45,10 +45,10 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
||||
@Override
|
||||
public void translate(GeyserSession session, BlockPickRequestPacket packet) {
|
||||
Vector3i vector = packet.getBlockPosition();
|
||||
Block blockToPick = session.getGeyser().getWorldManager().blockAt(session, vector.getX(), vector.getY(), vector.getZ()).block();
|
||||
BlockState blockToPick = session.getGeyser().getWorldManager().blockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
|
||||
// Block is air - chunk caching is probably off
|
||||
if (blockToPick == Blocks.AIR) {
|
||||
if (blockToPick.is(Blocks.AIR)) {
|
||||
// Check for an item frame since the client thinks that's a block when it's an entity in Java
|
||||
ItemFrameEntity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||
if (entity != null) {
|
||||
@ -64,8 +64,8 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
||||
return;
|
||||
}
|
||||
|
||||
boolean addExtraData = packet.isAddUserData() && blockToPick.hasBlockEntity(); // Holding down CTRL
|
||||
if (blockToPick instanceof BannerBlock || addExtraData) { //TODO
|
||||
boolean addExtraData = packet.isAddUserData() && blockToPick.block().hasBlockEntity(); // Holding down CTRL
|
||||
if (blockToPick.block() instanceof BannerBlock || addExtraData) {
|
||||
session.getGeyser().getWorldManager().getPickItemComponents(session, vector.getX(), vector.getY(), vector.getZ(), addExtraData)
|
||||
.whenComplete((components, ex) -> session.ensureInEventLoop(() -> {
|
||||
if (components == null) {
|
||||
@ -73,7 +73,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack itemStack = new ItemStack(blockToPick.asItem().javaId(), 1, components);
|
||||
ItemStack itemStack = new ItemStack(blockToPick.block().asItem().javaId(), 1, components);
|
||||
InventoryUtils.findOrCreateItem(session, itemStack);
|
||||
}));
|
||||
return;
|
||||
@ -82,7 +82,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
||||
pickItem(session, blockToPick);
|
||||
}
|
||||
|
||||
private void pickItem(GeyserSession session, Block block) {
|
||||
InventoryUtils.findOrCreateItem(session, block.asItem());
|
||||
private void pickItem(GeyserSession session, BlockState state) {
|
||||
InventoryUtils.findOrCreateItem(session, state.block().pickItem(state));
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,11 @@ import org.geysermc.geyser.item.type.BoatItem;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.item.type.SpawnEggItem;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.CauldronBlock;
|
||||
import org.geysermc.geyser.level.block.type.SkullBlock;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
@ -311,24 +315,25 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
Item item = session.getPlayerInventory().getItemInHand().asItem();
|
||||
if (packet.getItemInHand() != null) {
|
||||
ItemDefinition definition = packet.getItemInHand().getDefinition();
|
||||
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition());
|
||||
BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, packet.getBlockPosition());
|
||||
// Otherwise boats will not be able to be placed in survival and buckets, lily pads, frogspawn, and glass bottles won't work on mobile
|
||||
if (item instanceof BoatItem || item == Items.LILY_PAD || item == Items.FROGSPAWN) {
|
||||
useItem(session, packet, blockState);
|
||||
useItem(session, packet, blockState.javaId());
|
||||
} else if (item == Items.GLASS_BOTTLE) {
|
||||
if (!session.isSneaking() && BlockStateValues.isCauldron(blockState) && !BlockStateValues.isNonWaterCauldron(blockState)) {
|
||||
Block block = blockState.block();
|
||||
if (!session.isSneaking() && block instanceof CauldronBlock && block != Blocks.WATER_CAULDRON) {
|
||||
// ServerboundUseItemPacket is not sent for water cauldrons and glass bottles
|
||||
return;
|
||||
}
|
||||
useItem(session, packet, blockState);
|
||||
useItem(session, packet, blockState.javaId());
|
||||
} else if (session.getItemMappings().getBuckets().contains(definition)) {
|
||||
// Don't send ServerboundUseItemPacket for powder snow buckets
|
||||
if (definition != session.getItemMappings().getStoredItems().powderSnowBucket().getBedrockDefinition()) {
|
||||
if (!session.isSneaking() && BlockStateValues.isCauldron(blockState)) {
|
||||
if (!session.isSneaking() && blockState.block() instanceof CauldronBlock) {
|
||||
// ServerboundUseItemPacket is not sent for cauldrons and buckets
|
||||
return;
|
||||
}
|
||||
session.setPlacedBucket(useItem(session, packet, blockState));
|
||||
session.setPlacedBucket(useItem(session, packet, blockState.javaId()));
|
||||
} else {
|
||||
session.setPlacedBucket(true);
|
||||
}
|
||||
@ -553,10 +558,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
* @param blockPos the block position to restore
|
||||
*/
|
||||
private void restoreCorrectBlock(GeyserSession session, Vector3i blockPos, InventoryTransactionPacket packet) {
|
||||
int javaBlockState = session.getGeyser().getWorldManager().getBlockAt(session, blockPos);
|
||||
BlockState javaBlockState = session.getGeyser().getWorldManager().blockAt(session, blockPos);
|
||||
BlockDefinition bedrockBlock = session.getBlockMappings().getBedrockBlock(javaBlockState);
|
||||
|
||||
if (BlockStateValues.getSkullVariant(javaBlockState) == 3) {
|
||||
if (javaBlockState.block() instanceof SkullBlock skullBlock && skullBlock.skullType() == SkullBlock.Type.PLAYER) {
|
||||
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(blockPos);
|
||||
if (skull != null && skull.getBlockDefinition() != null) {
|
||||
@ -574,7 +579,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
|
||||
updateWaterPacket.setDataLayer(1);
|
||||
updateWaterPacket.setBlockPosition(blockPos);
|
||||
updateWaterPacket.setDefinition(BlockRegistries.WATERLOGGED.get().get(javaBlockState) ? session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir());
|
||||
updateWaterPacket.setDefinition(BlockRegistries.WATERLOGGED.get().get(javaBlockState.javaId()) ? session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir());
|
||||
updateWaterPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||
session.sendUpstreamPacket(updateWaterPacket);
|
||||
|
||||
|
@ -38,12 +38,12 @@ import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
@ -180,9 +180,8 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
|
||||
// Account for fire - the client likes to hit the block behind.
|
||||
Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, packet.getFace());
|
||||
int blockUp = session.getGeyser().getWorldManager().getBlockAt(session, fireBlockPos);
|
||||
String identifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockUp, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
|
||||
Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block();
|
||||
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
|
||||
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos,
|
||||
Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
|
||||
session.sendDownstreamGamePacket(startBreakingPacket);
|
||||
@ -336,9 +335,9 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
break;
|
||||
}
|
||||
|
||||
int interactedBlock = session.getGeyser().getWorldManager().getBlockAt(session, vector);
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, vector);
|
||||
|
||||
if (BlockStateValues.getLecternBookStates().getOrDefault(interactedBlock, false)) {
|
||||
if (state.getValue(Properties.HAS_BOOK, false)) {
|
||||
session.setDroppingLecternBook(true);
|
||||
|
||||
ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket(
|
||||
@ -352,16 +351,13 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
break;
|
||||
}
|
||||
|
||||
if (session.getItemFrameCache().containsKey(vector)) {
|
||||
Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||
|
||||
if (itemFrame != null) {
|
||||
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(),
|
||||
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
||||
session.sendDownstreamGamePacket(interactPacket);
|
||||
}
|
||||
break;
|
||||
Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||
if (itemFrame != null) {
|
||||
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(),
|
||||
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
|
||||
session.sendDownstreamGamePacket(interactPacket);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,21 @@
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock.world;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.util.CooldownUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
||||
|
||||
@Translator(packet = LevelSoundEventPacket.class)
|
||||
public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoundEventPacket> {
|
||||
@ -77,9 +78,9 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoun
|
||||
Vector3f position = packet.getPosition();
|
||||
Vector3i blockPosition = Vector3i.from(position.getX(), position.getY(), position.getZ());
|
||||
|
||||
int potentialLectern = session.getGeyser().getWorldManager().getBlockAt(session, blockPosition);
|
||||
BlockState potentialLectern = session.getGeyser().getWorldManager().blockAt(session, blockPosition);
|
||||
|
||||
if (BlockStateValues.getLecternBookStates().getOrDefault(potentialLectern, false)) {
|
||||
if (potentialLectern.getValue(Properties.HAS_BOOK, false)) {
|
||||
session.setDroppingLecternBook(true);
|
||||
|
||||
ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket(
|
||||
|
@ -72,7 +72,7 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
|
||||
// Check for custom skulls.
|
||||
boolean hasCustomHeadBlock = false;
|
||||
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().containsKey("profile")) {
|
||||
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position, blockState.javaId());
|
||||
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position, blockState);
|
||||
if (blockDefinition != null) {
|
||||
hasCustomHeadBlock = true;
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
|
@ -25,19 +25,17 @@
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.java.level;
|
||||
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockUpdatePacket;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockUpdatePacket;
|
||||
|
||||
@Translator(packet = ClientboundBlockUpdatePacket.class)
|
||||
public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlockUpdatePacket> {
|
||||
@ -102,7 +100,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
||||
|| lastInteractPos.getZ() != packet.getEntry().getPosition().getZ())) {
|
||||
return;
|
||||
}
|
||||
String identifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(packet.getEntry().getBlock(), BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
String identifier = BlockState.of(packet.getEntry().getBlock()).toString(); // This will be yeeted soon. Thanks Chris.
|
||||
session.setInteracting(false);
|
||||
BlockSoundInteractionTranslator.handleBlockInteraction(session, lastInteractPos.toFloat(), identifier);
|
||||
}
|
||||
|
@ -171,8 +171,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
int xzy = indexYZXtoXZY(yzx);
|
||||
section.getBlockStorageArray()[0].setFullBlock(xzy, bedrockId);
|
||||
|
||||
Boolean waterlogged = state.getValue(Properties.WATERLOGGED); // TODO performance check
|
||||
if (waterlogged == Boolean.TRUE) {
|
||||
if (BlockRegistries.WATERLOGGED.get().get(javaId)) {
|
||||
section.getBlockStorageArray()[1].setFullBlock(xzy, session.getBlockMappings().getBedrockWater().getRuntimeId());
|
||||
}
|
||||
|
||||
@ -426,7 +425,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||
|
||||
// Check for custom skulls
|
||||
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.containsKey("profile")) {
|
||||
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, tag, Vector3i.from(x + chunkBlockX, y, z + chunkBlockZ), blockState.javaId());
|
||||
BlockDefinition blockDefinition = SkullBlockEntityTranslator.translateSkull(session, tag, Vector3i.from(x + chunkBlockX, y, z + chunkBlockZ), blockState);
|
||||
if (blockDefinition != null) {
|
||||
int bedrockSectionY = (y >> 4) - (bedrockDimension.minY() >> 4);
|
||||
int subChunkIndex = (y >> 4) + (bedrockDimension.minY() >> 4);
|
||||
|
@ -94,7 +94,7 @@ public class StatisticsUtils {
|
||||
|
||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||
if (entry.getKey() instanceof BreakBlockStatistic statistic) {
|
||||
Block block = BlockRegistries.JAVA_BLOCKS_TO_RENAME.get(statistic.getId());
|
||||
Block block = BlockRegistries.JAVA_BLOCKS.get(statistic.getId());
|
||||
if (block != null) {
|
||||
String identifier = "block.minecraft." + block.javaIdentifier().value();
|
||||
content.add(identifier + ": " + entry.getIntValue());
|
||||
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.util.collection;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.AbstractInt2BooleanMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class FixedInt2BooleanMap extends AbstractInt2BooleanMap {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected boolean[] value;
|
||||
protected int start = -1;
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<Entry> int2BooleanEntrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean get(int i) {
|
||||
return getOrDefault(i, defRetValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOrDefault(int key, boolean defaultValue) {
|
||||
int offset = key - start;
|
||||
if (offset < 0 || offset >= value.length) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value[offset];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(int key, boolean value) {
|
||||
if (start == -1) {
|
||||
start = key;
|
||||
this.value = new boolean[] {value};
|
||||
} else {
|
||||
int offset = key - start;
|
||||
if (offset >= 0 && offset < this.value.length) {
|
||||
boolean curr = this.value[offset];
|
||||
this.value[offset] = value;
|
||||
return curr;
|
||||
} else if (offset != this.value.length) {
|
||||
throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key);
|
||||
}
|
||||
|
||||
boolean[] newValue = new boolean[offset + 1];
|
||||
System.arraycopy(this.value, 0, newValue, 0, this.value.length);
|
||||
this.value = newValue;
|
||||
this.value[offset] = value;
|
||||
}
|
||||
|
||||
return this.defRetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(int k) {
|
||||
int offset = k - start;
|
||||
return offset >= 0 && offset < value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(boolean v) {
|
||||
for (boolean b : value) {
|
||||
if (b == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
int index = start;
|
||||
for (boolean b : value) {
|
||||
builder.append(index++).append("=>").append(b);
|
||||
if (index < value.length + start) {
|
||||
// Add commas while there are still more entries in the list
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
return builder.append('}').toString();
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.util.collection;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.AbstractInt2ByteMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ByteMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class FixedInt2ByteMap extends AbstractInt2ByteMap {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected byte[] value;
|
||||
protected int start = -1;
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<Int2ByteMap.Entry> int2ByteEntrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte get(int i) {
|
||||
return getOrDefault(i, defRetValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getOrDefault(int key, byte defaultValue) {
|
||||
int offset = key - start;
|
||||
if (offset < 0 || offset >= value.length) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value[offset];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte put(int key, byte value) {
|
||||
if (start == -1) {
|
||||
start = key;
|
||||
this.value = new byte[] {value};
|
||||
} else {
|
||||
int offset = key - start;
|
||||
if (offset >= 0 && offset < this.value.length) {
|
||||
byte curr = this.value[offset];
|
||||
this.value[offset] = value;
|
||||
return curr;
|
||||
} else if (offset != this.value.length) {
|
||||
throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key);
|
||||
}
|
||||
|
||||
byte[] newValue = new byte[offset + 1];
|
||||
System.arraycopy(this.value, 0, newValue, 0, this.value.length);
|
||||
this.value = newValue;
|
||||
this.value[offset] = value;
|
||||
}
|
||||
|
||||
return this.defRetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(int k) {
|
||||
int offset = k - start;
|
||||
return offset >= 0 && offset < value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(byte v) {
|
||||
for (byte i : value) {
|
||||
if (i == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
int index = start;
|
||||
for (byte b : value) {
|
||||
builder.append(index++).append("=>").append(b);
|
||||
if (index < value.length + start) {
|
||||
// Add commas while there are still more entries in the list
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
return builder.append('}').toString();
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.util.collection;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.AbstractInt2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class FixedInt2IntMap extends AbstractInt2IntMap {
|
||||
protected int[] value;
|
||||
protected int start = -1;
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<Int2IntMap.Entry> int2IntEntrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get(int i) {
|
||||
return getOrDefault(i, defRetValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrDefault(int key, int defaultValue) {
|
||||
int offset = key - start;
|
||||
if (offset < 0 || offset >= value.length) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value[offset];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int put(int key, int value) {
|
||||
if (start == -1) {
|
||||
start = key;
|
||||
this.value = new int[] {value};
|
||||
} else {
|
||||
int offset = key - start;
|
||||
if (offset >= 0 && offset < this.value.length) {
|
||||
int curr = this.value[offset];
|
||||
this.value[offset] = value;
|
||||
return curr;
|
||||
} else if (offset != this.value.length) {
|
||||
throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key);
|
||||
}
|
||||
|
||||
int[] newValue = new int[offset + 1];
|
||||
System.arraycopy(this.value, 0, newValue, 0, this.value.length);
|
||||
this.value = newValue;
|
||||
this.value[offset] = value;
|
||||
}
|
||||
|
||||
return this.defRetValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(int k) {
|
||||
int offset = k - start;
|
||||
return offset >= 0 && offset < value.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(int v) {
|
||||
for (int i : value) {
|
||||
if (i == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
int index = start;
|
||||
for (int i : value) {
|
||||
builder.append(index++).append("=>").append(i);
|
||||
if (index < value.length + start) {
|
||||
// Add commas while there are still more entries in the list
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
return builder.append('}').toString();
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.util.collection;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Map that takes advantage of its internals for fast operations on block states to determine if they are lecterns.
|
||||
*/
|
||||
public class LecternHasBookMap extends FixedInt2BooleanMap {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Update a potential lectern within the world. This is a map method so it can use the internal fields to
|
||||
* optimize lectern determining.
|
||||
*/
|
||||
public void handleBlockChange(GeyserSession session, int blockState, Vector3i position) {
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
|
||||
int offset = blockState - this.start;
|
||||
if (offset < 0 || offset >= this.value.length) {
|
||||
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
|
||||
if (!worldManager.shouldExpectLecternHandled(session)) {
|
||||
session.getLecternCache().remove(position);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean newLecternHasBook;
|
||||
if (worldManager.shouldExpectLecternHandled(session)) {
|
||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||
} else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) {
|
||||
// newLecternHasBook = the new lectern block state's "has book" toggle.
|
||||
if (newLecternHasBook) {
|
||||
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||
} else {
|
||||
session.getLecternCache().remove(position);
|
||||
NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
|
||||
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains useful collections for use in Geyser.
|
||||
* <p>
|
||||
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
|
||||
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
|
||||
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
|
||||
* size and its "start" integer.
|
||||
*/
|
||||
package org.geysermc.geyser.util.collection;
|
@ -1 +1 @@
|
||||
Subproject commit 6b661f0d517d895aebc1f55a25d2c86f033beb1d
|
||||
Subproject commit 968a22bbab02d7d003c5b451a40d8bb2439b0d97
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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.geyser.util.collection;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class GeyserCollectionsTest {
|
||||
private final byte[] bytes = new byte[] {(byte) 5, (byte) 4, (byte) 3, (byte) 2, (byte) 2, (byte) 1};
|
||||
private final boolean[] booleans = new boolean[] {true, false, false, true};
|
||||
private final int[] ints = new int[] {76, 3006, 999, 2323, 888, 0, 111, 999};
|
||||
|
||||
private final int[] startBlockRanges = new int[] {0, 70, 600, 450, 787, 1980};
|
||||
|
||||
@Test
|
||||
public void testBytes() {
|
||||
for (int startRange : startBlockRanges) {
|
||||
testBytes(startRange, new FixedInt2ByteMap());
|
||||
}
|
||||
}
|
||||
|
||||
private void testBytes(final int start, final FixedInt2ByteMap map) {
|
||||
int index = start;
|
||||
for (byte b : bytes) {
|
||||
map.put(index++, b);
|
||||
}
|
||||
|
||||
int lastKey = index;
|
||||
|
||||
// Easy, understandable out-of-bounds checks
|
||||
Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!");
|
||||
Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!");
|
||||
|
||||
// Ensure the first and last values do not throw an exception on get, and test getOrDefault
|
||||
map.get(start - 1);
|
||||
map.get(lastKey);
|
||||
Assertions.assertEquals(map.getOrDefault(start - 1, Byte.MAX_VALUE), Byte.MAX_VALUE);
|
||||
Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MAX_VALUE), Byte.MAX_VALUE);
|
||||
Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MIN_VALUE), Byte.MIN_VALUE);
|
||||
|
||||
Assertions.assertEquals(map.size(), bytes.length);
|
||||
|
||||
for (int i = start; i < bytes.length; i++) {
|
||||
Assertions.assertTrue(map.containsKey(i));
|
||||
Assertions.assertEquals(map.get(i), bytes[i - start]);
|
||||
}
|
||||
|
||||
for (int i = start - 1; i >= (start - 6); i--) {
|
||||
// Lower than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start);
|
||||
}
|
||||
|
||||
for (int i = bytes.length + start; i < bytes.length + 5 + start; i++) {
|
||||
// Higher than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + bytes.length));
|
||||
}
|
||||
|
||||
for (byte b : bytes) {
|
||||
Assertions.assertTrue(map.containsValue(b));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBooleans() {
|
||||
for (int startRange : startBlockRanges) {
|
||||
testBooleans(startRange, new FixedInt2BooleanMap());
|
||||
}
|
||||
}
|
||||
|
||||
private void testBooleans(final int start, final FixedInt2BooleanMap map) {
|
||||
int index = start;
|
||||
for (boolean b : booleans) {
|
||||
map.put(index++, b);
|
||||
}
|
||||
|
||||
int lastKey = index;
|
||||
|
||||
// Easy, understandable out-of-bounds checks
|
||||
Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!");
|
||||
Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!");
|
||||
|
||||
// Ensure the first and last values do not throw an exception on get
|
||||
map.get(start - 1);
|
||||
map.get(lastKey);
|
||||
Assertions.assertTrue(map.getOrDefault(lastKey, true));
|
||||
|
||||
Assertions.assertEquals(map.size(), booleans.length);
|
||||
|
||||
for (int i = start; i < booleans.length; i++) {
|
||||
Assertions.assertTrue(map.containsKey(i));
|
||||
Assertions.assertEquals(map.get(i), booleans[i - start]);
|
||||
}
|
||||
|
||||
for (int i = start - 1; i >= (start - 6); i--) {
|
||||
// Lower than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start);
|
||||
}
|
||||
|
||||
for (int i = booleans.length + start; i < booleans.length + start + 5; i++) {
|
||||
// Higher than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + booleans.length));
|
||||
}
|
||||
|
||||
for (boolean b : booleans) {
|
||||
Assertions.assertTrue(map.containsValue(b));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInts() {
|
||||
for (int startRange : startBlockRanges) {
|
||||
testInts(startRange, new FixedInt2IntMap());
|
||||
}
|
||||
}
|
||||
|
||||
private void testInts(final int start, final FixedInt2IntMap map) {
|
||||
int index = start;
|
||||
for (int i : ints) {
|
||||
map.put(index++, i);
|
||||
}
|
||||
|
||||
int lastKey = index;
|
||||
|
||||
// Easy, understandable out-of-bounds checks
|
||||
Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!");
|
||||
Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!");
|
||||
|
||||
// Ensure the first and last values do not throw an exception on get, and test getOrDefault
|
||||
map.get(start - 1);
|
||||
map.get(lastKey);
|
||||
Assertions.assertEquals(map.getOrDefault(start - 1, Integer.MAX_VALUE), Integer.MAX_VALUE);
|
||||
Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MAX_VALUE), Integer.MAX_VALUE);
|
||||
Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MIN_VALUE), Integer.MIN_VALUE);
|
||||
|
||||
Assertions.assertEquals(map.size(), ints.length);
|
||||
|
||||
for (int i = start; i < ints.length; i++) {
|
||||
Assertions.assertTrue(map.containsKey(i));
|
||||
Assertions.assertEquals(map.get(i), ints[i - start]);
|
||||
}
|
||||
|
||||
for (int i = start - 1; i >= (start - 6); i--) {
|
||||
// Lower than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start);
|
||||
}
|
||||
|
||||
for (int i = ints.length + start; i < ints.length + 5 + start; i++) {
|
||||
// Higher than expected check
|
||||
Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + ints.length));
|
||||
}
|
||||
|
||||
for (int i : ints) {
|
||||
Assertions.assertTrue(map.containsValue(i));
|
||||
}
|
||||
}
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren