BlockState values can now be switched at runtime

E.G. Blocks.PISTON_HEAD.defaultBlockState().withValue(FACING, Direction.SOUTH)

Some of the inspiration may be thanks to FerriteCore, at least with the shared property keys idea, so thank you to them.
This commit is contained in:
Camotoy 2024-05-21 14:25:57 -04:00
parent db166ad8de
commit 0094fa1418
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
47 changed files with 1160 additions and 901 deletions

View File

@ -28,7 +28,9 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.type.FurnaceBlock;
import org.geysermc.geyser.level.block.Blocks;
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.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
@ -51,7 +53,8 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
@Override
public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(FurnaceBlock.state(hasFuel)));
BlockState furnace = Blocks.FURNACE.defaultBlockState().withValue(Properties.LIT, hasFuel);
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(furnace));
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
}

View File

@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.level.block.type.SpawnerBlock;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
@ -41,7 +41,7 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
@Override
public void updateDefaultBlockMetadata() {
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(SpawnerBlock.state()));
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(Blocks.SPAWNER.defaultBlockState()));
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
}
}

View File

@ -41,7 +41,6 @@ import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.InventoryUtils;
import java.util.Collections;
@ -56,22 +55,24 @@ public class BlockInventoryHolder extends InventoryHolder {
/**
* The default Java block ID to translate as a fake block
*/
private final int defaultJavaBlockState;
private final BlockState defaultJavaBlockState;
private final ContainerType containerType;
private final Set<String> validBlocks;
private final Set<Block> validBlocks;
public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, Block... validBlocks) {
this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaBlockIdentifier);
public BlockInventoryHolder(Block defaultJavaBlock, ContainerType containerType, Block... validBlocks) {
this(defaultJavaBlock.defaultBlockState(), containerType, validBlocks);
}
public BlockInventoryHolder(BlockState defaultJavaBlockState, ContainerType containerType, Block... validBlocks) {
this.defaultJavaBlockState = defaultJavaBlockState;
this.containerType = containerType;
if (validBlocks != null) {
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
for (Block block : validBlocks) {
validBlocksTemp.add(block.javaIdentifier().toString());
}
validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
Set<Block> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
Collections.addAll(validBlocksTemp, validBlocks);
validBlocksTemp.add(defaultJavaBlockState.block());
this.validBlocks = Set.copyOf(validBlocksTemp);
} else {
this.validBlocks = Collections.singleton(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
this.validBlocks = Collections.singleton(defaultJavaBlockState.block());
}
}
@ -85,9 +86,7 @@ public class BlockInventoryHolder extends InventoryHolder {
// and the bedrock block is vanilla
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition());
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) {
// TODO TODO TODO
String[] javaBlockString = state.toString().split("\\[");
if (isValidBlock(javaBlockString)) {
if (isValidBlock(state)) {
// We can safely use this block
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
((Container) inventory).setUsingRealBlock(true, state.block());
@ -111,7 +110,7 @@ public class BlockInventoryHolder extends InventoryHolder {
session.sendUpstreamPacket(blockPacket);
inventory.setHolderPosition(position);
setCustomName(session, position, inventory, BlockState.of(defaultJavaBlockState));
setCustomName(session, position, inventory, defaultJavaBlockState);
return true;
}
@ -129,8 +128,8 @@ public class BlockInventoryHolder extends InventoryHolder {
/**
* @return true if this Java block ID can be used for player inventory.
*/
protected boolean isValidBlock(String[] javaBlockString) {
return this.validBlocks.contains(javaBlockString[0]);
protected boolean isValidBlock(BlockState blockState) {
return this.validBlocks.contains(blockState.block());
}
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, BlockState javaBlockState) {

View File

@ -25,17 +25,10 @@
package org.geysermc.geyser.level.block;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
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.PistonBlock;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
@ -43,53 +36,8 @@ import org.geysermc.geyser.registry.BlockRegistries;
* Used for block entities if the Java block state contains Bedrock block information.
*/
public final class BlockStateValues {
private static final Object2IntMap<Direction> PISTON_HEADS = new Object2IntOpenHashMap<>();
private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet();
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
public static int JAVA_WATER_ID;
public static final int NUM_WATER_LEVELS = 9;
/**
* Determines if the block state contains Bedrock block information
*
* @param javaId The Java Identifier of the block
* @param javaBlockState the Java Block State of the block
*/
public static void storeBlockStateValues(String javaId, int javaBlockState) {
if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston
return;
} else if (javaId.startsWith("minecraft:piston_head")) {
ALL_PISTON_HEADS.add(javaBlockState);
if (javaId.contains("short=false")) {
PISTON_HEADS.put(getBlockDirection(javaId), javaBlockState);
}
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);
WATER_LEVEL.put(javaBlockState, level);
}
}
public static boolean isPistonHead(int state) {
return ALL_PISTON_HEADS.contains(state);
}
/**
* Get the Java Block State for a piston head for a specific direction
* This is used in PistonBlockEntity to get the BlockCollision for the piston head.
*
* @param direction Direction the piston head points in
* @return Block state for the piston head
*/
public static int getPistonHead(Direction direction) {
return PISTON_HEADS.getOrDefault(direction, Block.JAVA_AIR_ID);
}
/**
* Checks if a block sticks to other blocks
* (Slime and honey blocks)
@ -158,7 +106,11 @@ public final class BlockStateValues {
* @return The water level or -1 if the block isn't water
*/
public static int getWaterLevel(int state) {
return WATER_LEVEL.getOrDefault(state, -1);
BlockState blockState = BlockState.of(state);
if (!blockState.is(Blocks.WATER)) {
return -1;
}
return blockState.getValue(Properties.LEVEL);
}
/**
@ -206,23 +158,6 @@ public final class BlockStateValues {
return 0.6f;
}
private static Direction getBlockDirection(String javaId) {
if (javaId.contains("down")) {
return Direction.DOWN;
} else if (javaId.contains("up")) {
return Direction.UP;
} else if (javaId.contains("south")) {
return Direction.SOUTH;
} else if (javaId.contains("west")) {
return Direction.WEST;
} else if (javaId.contains("north")) {
return Direction.NORTH;
} else if (javaId.contains("east")) {
return Direction.EAST;
}
throw new IllegalStateException();
}
private BlockStateValues() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
/*
* 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
* 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.property;
import java.util.List;
/**
* Represents enums we don't need classes for in Geyser.
*/
public final class BasicEnumProperty extends Property<String> {
private final List<String> values;
private BasicEnumProperty(String name, List<String> values) {
super(name);
this.values = values;
}
@Override
public int valuesCount() {
return this.values.size();
}
@Override
public int indexOf(String value) {
int index = this.values.indexOf(value);
if (index == -1) {
throw new IllegalStateException("Property " + this + " does not have value " + value);
}
return index;
}
@SuppressWarnings("unchecked")
public <T> T values() {
return (T) this.values;
}
public static BasicEnumProperty create(String name, String... values) {
return new BasicEnumProperty(name, List.of(values));
}
}

View File

@ -0,0 +1,46 @@
/*
* 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
* 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.property;
public final class BooleanProperty extends Property<Boolean> {
private BooleanProperty(String name) {
super(name);
}
@Override
public int valuesCount() {
return 2;
}
@Override
public int indexOf(Boolean value) {
return value ? 0 : 1;
}
public static BooleanProperty create(String name) {
return new BooleanProperty(name);
}
}

View File

@ -0,0 +1,59 @@
/*
* 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
* 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.property;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
public final class EnumProperty<T extends Enum<T>> extends Property<T> {
private final IntList ordinalValues;
/**
* @param values all possible values of this enum.
*/
private EnumProperty(String name, T[] values) {
super(name);
this.ordinalValues = new IntArrayList(values.length);
for (T anEnum : values) {
this.ordinalValues.add(anEnum.ordinal());
}
}
@Override
public int valuesCount() {
return this.ordinalValues.size();
}
@Override
public int indexOf(T value) {
return this.ordinalValues.indexOf(value.ordinal());
}
@SafeVarargs
public static <T extends Enum<T>> EnumProperty<T> create(String name, T... values) {
return new EnumProperty<>(name, values);
}
}

View File

@ -0,0 +1,59 @@
/*
* 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
* 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.property;
public final class IntegerProperty extends Property<Integer> {
private final int offset;
private final int valuesCount;
private IntegerProperty(String name, int low, int high) {
super(name);
this.offset = low;
this.valuesCount = high - low;
}
@Override
public int valuesCount() {
return this.valuesCount;
}
@Override
public int indexOf(Integer value) {
return value - this.offset;
}
public int low() {
return this.offset;
}
public int high() {
return this.offset + this.valuesCount;
}
public static IntegerProperty create(String name, int low, int high) {
return new IntegerProperty(name, low, high);
}
}

View File

@ -29,118 +29,118 @@ import org.geysermc.geyser.level.physics.Axis;
import org.geysermc.geyser.level.physics.Direction;
public final class Properties {
public static final Property<Boolean> ATTACHED = Property.create("attached");
public static final Property<Boolean> BOTTOM = Property.create("bottom");
public static final Property<Boolean> CONDITIONAL = Property.create("conditional");
public static final Property<Boolean> DISARMED = Property.create("disarmed");
public static final Property<Boolean> DRAG = Property.create("drag");
public static final Property<Boolean> ENABLED = Property.create("enabled");
public static final Property<Boolean> EXTENDED = Property.create("extended");
public static final Property<Boolean> EYE = Property.create("eye");
public static final Property<Boolean> FALLING = Property.create("falling");
public static final Property<Boolean> HANGING = Property.create("hanging");
public static final Property<Boolean> HAS_BOTTLE_0 = Property.create("has_bottle_0");
public static final Property<Boolean> HAS_BOTTLE_1 = Property.create("has_bottle_1");
public static final Property<Boolean> HAS_BOTTLE_2 = Property.create("has_bottle_2");
public static final Property<Boolean> HAS_RECORD = Property.create("has_record");
public static final Property<Boolean> HAS_BOOK = Property.create("has_book");
public static final Property<Boolean> INVERTED = Property.create("inverted");
public static final Property<Boolean> IN_WALL = Property.create("in_wall");
public static final Property<Boolean> LIT = Property.create("lit");
public static final Property<Boolean> LOCKED = Property.create("locked");
public static final Property<Boolean> OCCUPIED = Property.create("occupied");
public static final Property<Boolean> OPEN = Property.create("open");
public static final Property<Boolean> PERSISTENT = Property.create("persistent");
public static final Property<Boolean> POWERED = Property.create("powered");
public static final Property<Boolean> SHORT = Property.create("short");
public static final Property<Boolean> SIGNAL_FIRE = Property.create("signal_fire");
public static final Property<Boolean> SNOWY = Property.create("snowy");
public static final Property<Boolean> TRIGGERED = Property.create("triggered");
public static final Property<Boolean> UNSTABLE = Property.create("unstable");
public static final Property<Boolean> WATERLOGGED = Property.create("waterlogged");
public static final Property<Boolean> BERRIES = Property.create("berries");
public static final Property<Boolean> BLOOM = Property.create("bloom");
public static final Property<Boolean> SHRIEKING = Property.create("shrieking");
public static final Property<Boolean> CAN_SUMMON = Property.create("can_summon");
public static final Property<Axis> HORIZONTAL_AXIS = Property.create("axis");
public static final Property<Axis> AXIS = Property.create("axis");
public static final Property<Boolean> UP = Property.create("up");
public static final Property<Boolean> DOWN = Property.create("down");
public static final Property<Boolean> NORTH = Property.create("north");
public static final Property<Boolean> EAST = Property.create("east");
public static final Property<Boolean> SOUTH = Property.create("south");
public static final Property<Boolean> WEST = Property.create("west");
public static final Property<Direction> FACING = Property.create("facing");
public static final Property<Direction> FACING_HOPPER = Property.create("facing");
public static final Property<Direction> HORIZONTAL_FACING = Property.create("facing");
public static final Property<Integer> FLOWER_AMOUNT = Property.create("flower_amount");
public static final Property<FrontAndTop> ORIENTATION = Property.create("orientation");
public static final Property<String> ATTACH_FACE = Property.create("face");
public static final Property<String> BELL_ATTACHMENT = Property.create("attachment");
public static final Property<String> EAST_WALL = Property.create("east");
public static final Property<String> NORTH_WALL = Property.create("north");
public static final Property<String> SOUTH_WALL = Property.create("south");
public static final Property<String> WEST_WALL = Property.create("west");
public static final Property<String> EAST_REDSTONE = Property.create("east");
public static final Property<String> NORTH_REDSTONE = Property.create("north");
public static final Property<String> SOUTH_REDSTONE = Property.create("south");
public static final Property<String> WEST_REDSTONE = Property.create("west");
public static final Property<String> DOUBLE_BLOCK_HALF = Property.create("half");
public static final Property<String> HALF = Property.create("half");
public static final Property<String> RAIL_SHAPE = Property.create("shape");
public static final Property<String> RAIL_SHAPE_STRAIGHT = Property.create("shape");
public static final Property<Integer> AGE_1 = Property.create("age");
public static final Property<Integer> AGE_2 = Property.create("age");
public static final Property<Integer> AGE_3 = Property.create("age");
public static final Property<Integer> AGE_4 = Property.create("age");
public static final Property<Integer> AGE_5 = Property.create("age");
public static final Property<Integer> AGE_7 = Property.create("age");
public static final Property<Integer> AGE_15 = Property.create("age");
public static final Property<Integer> AGE_25 = Property.create("age");
public static final Property<Integer> BITES = Property.create("bites");
public static final Property<Integer> CANDLES = Property.create("candles");
public static final Property<Integer> DELAY = Property.create("delay");
public static final Property<Integer> DISTANCE = Property.create("distance");
public static final Property<Integer> EGGS = Property.create("eggs");
public static final Property<Integer> HATCH = Property.create("hatch");
public static final Property<Integer> LAYERS = Property.create("layers");
public static final Property<Integer> LEVEL_CAULDRON = Property.create("level");
public static final Property<Integer> LEVEL_COMPOSTER = Property.create("level");
public static final Property<Integer> LEVEL_FLOWING = Property.create("level");
public static final Property<Integer> LEVEL_HONEY = Property.create("honey_level");
public static final Property<Integer> LEVEL = Property.create("level");
public static final Property<Integer> MOISTURE = Property.create("moisture");
public static final Property<Integer> NOTE = Property.create("note");
public static final Property<Integer> PICKLES = Property.create("pickles");
public static final Property<Integer> POWER = Property.create("power");
public static final Property<Integer> STAGE = Property.create("stage");
public static final Property<Integer> STABILITY_DISTANCE = Property.create("distance");
public static final Property<Integer> RESPAWN_ANCHOR_CHARGES = Property.create("charges");
public static final Property<Integer> ROTATION_16 = Property.create("rotation");
public static final Property<String> BED_PART = Property.create("part");
public static final Property<ChestType> CHEST_TYPE = Property.create("type");
public static final Property<String> MODE_COMPARATOR = Property.create("mode");
public static final Property<String> DOOR_HINGE = Property.create("hinge");
public static final Property<String> NOTEBLOCK_INSTRUMENT = Property.create("instrument");
public static final Property<String> PISTON_TYPE = Property.create("type");
public static final Property<String> SLAB_TYPE = Property.create("type");
public static final Property<String> STAIRS_SHAPE = Property.create("shape");
public static final Property<String> STRUCTUREBLOCK_MODE = Property.create("mode");
public static final Property<String> BAMBOO_LEAVES = Property.create("leaves");
public static final Property<String> TILT = Property.create("tilt");
public static final Property<Direction> VERTICAL_DIRECTION = Property.create("vertical_direction");
public static final Property<String> DRIPSTONE_THICKNESS = Property.create("thickness");
public static final Property<String> SCULK_SENSOR_PHASE = Property.create("sculk_sensor_phase");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_0_OCCUPIED = Property.create("slot_0_occupied");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_1_OCCUPIED = Property.create("slot_1_occupied");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_2_OCCUPIED = Property.create("slot_2_occupied");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_3_OCCUPIED = Property.create("slot_3_occupied");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_4_OCCUPIED = Property.create("slot_4_occupied");
public static final Property<Boolean> CHISELED_BOOKSHELF_SLOT_5_OCCUPIED = Property.create("slot_5_occupied");
public static final Property<Integer> DUSTED = Property.create("dusted");
public static final Property<Boolean> CRACKED = Property.create("cracked");
public static final Property<Boolean> CRAFTING = Property.create("crafting");
public static final Property<String> TRIAL_SPAWNER_STATE = Property.create("trial_spawner_state");
public static final Property<String> VAULT_STATE = Property.create("vault_state");
public static final Property<Boolean> OMINOUS = Property.create("ominous");
public static final BooleanProperty ATTACHED = BooleanProperty.create("attached");
public static final BooleanProperty BOTTOM = BooleanProperty.create("bottom");
public static final BooleanProperty CONDITIONAL = BooleanProperty.create("conditional");
public static final BooleanProperty DISARMED = BooleanProperty.create("disarmed");
public static final BooleanProperty DRAG = BooleanProperty.create("drag");
public static final BooleanProperty ENABLED = BooleanProperty.create("enabled");
public static final BooleanProperty EXTENDED = BooleanProperty.create("extended");
public static final BooleanProperty EYE = BooleanProperty.create("eye");
public static final BooleanProperty FALLING = BooleanProperty.create("falling");
public static final BooleanProperty HANGING = BooleanProperty.create("hanging");
public static final BooleanProperty HAS_BOTTLE_0 = BooleanProperty.create("has_bottle_0");
public static final BooleanProperty HAS_BOTTLE_1 = BooleanProperty.create("has_bottle_1");
public static final BooleanProperty HAS_BOTTLE_2 = BooleanProperty.create("has_bottle_2");
public static final BooleanProperty HAS_RECORD = BooleanProperty.create("has_record");
public static final BooleanProperty HAS_BOOK = BooleanProperty.create("has_book");
public static final BooleanProperty INVERTED = BooleanProperty.create("inverted");
public static final BooleanProperty IN_WALL = BooleanProperty.create("in_wall");
public static final BooleanProperty LIT = BooleanProperty.create("lit");
public static final BooleanProperty LOCKED = BooleanProperty.create("locked");
public static final BooleanProperty OCCUPIED = BooleanProperty.create("occupied");
public static final BooleanProperty OPEN = BooleanProperty.create("open");
public static final BooleanProperty PERSISTENT = BooleanProperty.create("persistent");
public static final BooleanProperty POWERED = BooleanProperty.create("powered");
public static final BooleanProperty SHORT = BooleanProperty.create("short");
public static final BooleanProperty SIGNAL_FIRE = BooleanProperty.create("signal_fire");
public static final BooleanProperty SNOWY = BooleanProperty.create("snowy");
public static final BooleanProperty TRIGGERED = BooleanProperty.create("triggered");
public static final BooleanProperty UNSTABLE = BooleanProperty.create("unstable");
public static final BooleanProperty WATERLOGGED = BooleanProperty.create("waterlogged");
public static final BooleanProperty BERRIES = BooleanProperty.create("berries");
public static final BooleanProperty BLOOM = BooleanProperty.create("bloom");
public static final BooleanProperty SHRIEKING = BooleanProperty.create("shrieking");
public static final BooleanProperty CAN_SUMMON = BooleanProperty.create("can_summon");
public static final EnumProperty<Axis> HORIZONTAL_AXIS = EnumProperty.create("axis", Axis.X, Axis.Z);
public static final EnumProperty<Axis> AXIS = EnumProperty.create("axis", Axis.VALUES);
public static final BooleanProperty UP = BooleanProperty.create("up");
public static final BooleanProperty DOWN = BooleanProperty.create("down");
public static final BooleanProperty NORTH = BooleanProperty.create("north");
public static final BooleanProperty EAST = BooleanProperty.create("east");
public static final BooleanProperty SOUTH = BooleanProperty.create("south");
public static final BooleanProperty WEST = BooleanProperty.create("west");
public static final EnumProperty<Direction> FACING = EnumProperty.create("facing", Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN);
public static final EnumProperty<Direction> FACING_HOPPER = EnumProperty.create("facing", Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
public static final EnumProperty<Direction> HORIZONTAL_FACING = EnumProperty.create("facing", Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
public static final IntegerProperty FLOWER_AMOUNT = IntegerProperty.create("flower_amount", 1, 4);
public static final EnumProperty<FrontAndTop> ORIENTATION = EnumProperty.create("orientation", FrontAndTop.VALUES);
public static final BasicEnumProperty ATTACH_FACE = BasicEnumProperty.create("face", "floor", "wall", "ceiling");
public static final BasicEnumProperty BELL_ATTACHMENT = BasicEnumProperty.create("attachment", "floor", "ceiling", "single_wall", "double_wall");
public static final BasicEnumProperty EAST_WALL = BasicEnumProperty.create("east", "none", "low", "tall");
public static final BasicEnumProperty NORTH_WALL = BasicEnumProperty.create("north", "none", "low", "tall");
public static final BasicEnumProperty SOUTH_WALL = BasicEnumProperty.create("south", "none", "low", "tall");
public static final BasicEnumProperty WEST_WALL = BasicEnumProperty.create("west", "none", "low", "tall");
public static final BasicEnumProperty EAST_REDSTONE = BasicEnumProperty.create("east", "up", "side", "none");
public static final BasicEnumProperty NORTH_REDSTONE = BasicEnumProperty.create("north", "up", "side", "none");
public static final BasicEnumProperty SOUTH_REDSTONE = BasicEnumProperty.create("south", "up", "side", "none");
public static final BasicEnumProperty WEST_REDSTONE = BasicEnumProperty.create("west", "up", "side", "none");
public static final BasicEnumProperty DOUBLE_BLOCK_HALF = BasicEnumProperty.create("half", "upper", "lower");
public static final BasicEnumProperty HALF = BasicEnumProperty.create("half", "top", "bottom");
public static final BasicEnumProperty RAIL_SHAPE = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south", "south_east", "south_west", "north_west", "north_east");
public static final BasicEnumProperty RAIL_SHAPE_STRAIGHT = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south");
public static final IntegerProperty AGE_1 = IntegerProperty.create("age", 0, 1);
public static final IntegerProperty AGE_2 = IntegerProperty.create("age", 0, 2);
public static final IntegerProperty AGE_3 = IntegerProperty.create("age", 0, 3);
public static final IntegerProperty AGE_4 = IntegerProperty.create("age", 0, 4);
public static final IntegerProperty AGE_5 = IntegerProperty.create("age", 0, 5);
public static final IntegerProperty AGE_7 = IntegerProperty.create("age", 0, 7);
public static final IntegerProperty AGE_15 = IntegerProperty.create("age", 0, 15);
public static final IntegerProperty AGE_25 = IntegerProperty.create("age", 0, 25);
public static final IntegerProperty BITES = IntegerProperty.create("bites", 0, 6);
public static final IntegerProperty CANDLES = IntegerProperty.create("candles", 1, 4);
public static final IntegerProperty DELAY = IntegerProperty.create("delay", 1, 4);
public static final IntegerProperty DISTANCE = IntegerProperty.create("distance", 1, 7);
public static final IntegerProperty EGGS = IntegerProperty.create("eggs", 1, 4);
public static final IntegerProperty HATCH = IntegerProperty.create("hatch", 0, 2);
public static final IntegerProperty LAYERS = IntegerProperty.create("layers", 1, 8);
public static final IntegerProperty LEVEL_CAULDRON = IntegerProperty.create("level", 1, 3);
public static final IntegerProperty LEVEL_COMPOSTER = IntegerProperty.create("level", 0, 8);
public static final IntegerProperty LEVEL_FLOWING = IntegerProperty.create("level", 1, 8);
public static final IntegerProperty LEVEL_HONEY = IntegerProperty.create("honey_level", 0, 5);
public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, 15);
public static final IntegerProperty MOISTURE = IntegerProperty.create("moisture", 0, 7);
public static final IntegerProperty NOTE = IntegerProperty.create("note", 0, 24);
public static final IntegerProperty PICKLES = IntegerProperty.create("pickles", 1, 4);
public static final IntegerProperty POWER = IntegerProperty.create("power", 0, 15);
public static final IntegerProperty STAGE = IntegerProperty.create("stage", 0, 1);
public static final IntegerProperty STABILITY_DISTANCE = IntegerProperty.create("distance", 0, 7);
public static final IntegerProperty RESPAWN_ANCHOR_CHARGES = IntegerProperty.create("charges", 0, 4);
public static final IntegerProperty ROTATION_16 = IntegerProperty.create("rotation", 0, 15);
public static final BasicEnumProperty BED_PART = BasicEnumProperty.create("part", "head", "foot");
public static final EnumProperty<ChestType> CHEST_TYPE = EnumProperty.create("type", ChestType.VALUES);
public static final BasicEnumProperty MODE_COMPARATOR = BasicEnumProperty.create("mode", "compare", "subtract");
public static final BasicEnumProperty DOOR_HINGE = BasicEnumProperty.create("hinge", "left", "right");
public static final BasicEnumProperty NOTEBLOCK_INSTRUMENT = BasicEnumProperty.create("instrument", "harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling", "zombie", "skeleton", "creeper", "dragon", "wither_skeleton", "piglin", "custom_head");
public static final BasicEnumProperty PISTON_TYPE = BasicEnumProperty.create("type", "normal", "sticky");
public static final BasicEnumProperty SLAB_TYPE = BasicEnumProperty.create("type", "top", "bottom", "double");
public static final BasicEnumProperty STAIRS_SHAPE = BasicEnumProperty.create("shape", "straight", "inner_left", "inner_right", "outer_left", "outer_right");
public static final BasicEnumProperty STRUCTUREBLOCK_MODE = BasicEnumProperty.create("mode", "save", "load", "corner", "data");
public static final BasicEnumProperty BAMBOO_LEAVES = BasicEnumProperty.create("leaves", "none", "small", "large");
public static final BasicEnumProperty TILT = BasicEnumProperty.create("tilt", "none", "unstable", "partial", "full");
public static final EnumProperty<Direction> VERTICAL_DIRECTION = EnumProperty.create("vertical_direction", Direction.UP, Direction.DOWN);
public static final BasicEnumProperty DRIPSTONE_THICKNESS = BasicEnumProperty.create("thickness", "tip_merge", "tip", "frustum", "middle", "base");
public static final BasicEnumProperty SCULK_SENSOR_PHASE = BasicEnumProperty.create("sculk_sensor_phase", "inactive", "active", "cooldown");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_0_OCCUPIED = BooleanProperty.create("slot_0_occupied");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_1_OCCUPIED = BooleanProperty.create("slot_1_occupied");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_2_OCCUPIED = BooleanProperty.create("slot_2_occupied");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_3_OCCUPIED = BooleanProperty.create("slot_3_occupied");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_4_OCCUPIED = BooleanProperty.create("slot_4_occupied");
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_5_OCCUPIED = BooleanProperty.create("slot_5_occupied");
public static final IntegerProperty DUSTED = IntegerProperty.create("dusted", 0, 3);
public static final BooleanProperty CRACKED = BooleanProperty.create("cracked");
public static final BooleanProperty CRAFTING = BooleanProperty.create("crafting");
public static final BasicEnumProperty TRIAL_SPAWNER_STATE = BasicEnumProperty.create("trial_spawner_state", "inactive", "waiting_for_players", "active", "waiting_for_reward_ejection", "ejecting_reward", "cooldown");
public static final BasicEnumProperty VAULT_STATE = BasicEnumProperty.create("vault_state", "inactive", "active", "unlocking", "ejecting");
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
}

View File

@ -25,10 +25,10 @@
package org.geysermc.geyser.level.block.property;
public class Property<T extends Comparable<T>> {
public abstract class Property<T extends Comparable<T>> {
private final String name;
public Property(String name) {
protected Property(String name) {
this.name = name;
}
@ -36,12 +36,12 @@ public class Property<T extends Comparable<T>> {
return name;
}
public abstract int valuesCount();
public abstract int indexOf(T value);
@Override
public String toString() {
return getClass().getSimpleName() + "[" + name + "]";
}
public static <T extends Comparable<T>> Property<T> create(String name) {
return new Property<>(name);
}
}

View File

@ -27,9 +27,6 @@ package org.geysermc.geyser.level.block.type;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
@ -37,6 +34,8 @@ 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.BasicEnumProperty;
import org.geysermc.geyser.level.block.property.IntegerProperty;
import org.geysermc.geyser.level.block.property.Property;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
@ -67,6 +66,12 @@ public class Block {
protected Item item = null;
private int javaId = -1;
/**
* Used for switching a given block state to different states.
*/
private final Property<?>[] propertyKeys;
private final BlockState defaultState;
public Block(@Subst("empty") String javaIdentifier, Builder builder) {
this.javaIdentifier = Key.key(javaIdentifier);
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
@ -74,7 +79,10 @@ public class Block {
this.destroyTime = builder.destroyTime;
this.pushReaction = builder.pushReaction;
this.pickItem = builder.pickItem;
processStates(builder.build(this));
BlockState firstState = builder.build(this).get(0);
this.propertyKeys = builder.propertyKeys; // Ensure this is not null before iterating over states
this.defaultState = setDefaultState(firstState);
}
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
@ -167,9 +175,11 @@ public class Block {
}
/**
* A list of BlockStates is created pertaining to this block. Do we need any of them? If so, override this method.
* Should only be ran on block creation. Can be overridden.
* @param firstState the first state created from this block
*/
protected void processStates(List<BlockState> states) {
protected BlockState setDefaultState(BlockState firstState) {
return firstState;
}
@NonNull
@ -194,6 +204,10 @@ public class Block {
return this.pushReaction;
}
public BlockState defaultBlockState() {
return this.defaultState;
}
public int javaId() {
return javaId;
}
@ -213,6 +227,10 @@ public class Block {
'}';
}
Property<?>[] propertyKeys() {
return propertyKeys;
}
public static Builder builder() {
return new Builder();
}
@ -225,11 +243,14 @@ public class Block {
private float destroyTime;
private Supplier<Item> pickItem;
// We'll use this field after building
private Property<?>[] propertyKeys;
/**
* For states that we're just tracking for mirroring Java states.
*/
public Builder enumState(Property<String> property, String... values) {
states.put(property, List.of(values));
public Builder enumState(BasicEnumProperty property) {
states.put(property, property.values());
return this;
}
@ -244,7 +265,9 @@ public class Block {
return this;
}
public Builder intState(Property<Integer> property, int low, int high) {
public Builder intState(IntegerProperty property) {
int low = property.low();
int high = property.high();
IntList list = new IntArrayList();
// There is a state for every number between the low and high.
for (int i = low; i <= high; i++) {
@ -283,17 +306,18 @@ public class Block {
if (states.isEmpty()) {
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size());
BlockRegistries.BLOCK_STATES.get().add(state);
propertyKeys = null;
return List.of(state);
} else if (states.size() == 1) {
// We can optimize because we don't need to worry about combinations
Map.Entry<Property<?>, List<Comparable<?>>> property = this.states.entrySet().stream().findFirst().orElseThrow();
List<BlockState> states = new ArrayList<>(property.getValue().size());
property.getValue().forEach(value -> {
Reference2ObjectMap<Property<?>, Comparable<?>> propertyMap = Reference2ObjectMaps.singleton(property.getKey(), value);
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), propertyMap);
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), new Comparable[] {value});
BlockRegistries.BLOCK_STATES.get().add(state);
states.add(state);
});
this.propertyKeys = new Property[]{property.getKey()};
return states;
} else {
// Think of this stream as another list containing, at the start, one empty list.
@ -327,11 +351,11 @@ public class Block {
Property<?>[] keys = this.states.keySet().toArray(new Property<?>[0]);
result.forEach(properties -> {
Comparable<?>[] values = properties.toArray(new Comparable<?>[0]);
Reference2ObjectMap<Property<?>, Comparable<?>> propertyMap = new Reference2ObjectArrayMap<>(keys, values);
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), propertyMap);
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), values);
BlockRegistries.BLOCK_STATES.get().add(state);
states.add(state);
});
this.propertyKeys = keys;
return states;
}
}

View File

@ -25,8 +25,7 @@
package org.geysermc.geyser.level.block.type;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.level.block.property.Property;
import org.geysermc.geyser.registry.BlockRegistries;
@ -35,13 +34,18 @@ import java.util.Locale;
public final class BlockState {
private final Block block;
private final int javaId;
private final Reference2ObjectMap<Property<?>, Comparable<?>> states;
/**
* The values of each property of this block state. These should be treated as keys to {@link Block#propertyKeys()}
* Of note - the comparable part probably doesn't do anything because we occasionally use strings in place of enums.
* Will be null if there's only one block state for a block.
*/
private final Comparable<?>[] states;
public BlockState(Block block, int javaId) {
this(block, javaId, Reference2ObjectMaps.emptyMap());
this(block, javaId, null);
}
BlockState(Block block, int javaId, Reference2ObjectMap<Property<?>, Comparable<?>> states) {
BlockState(Block block, int javaId, Comparable<?>[] states) {
this.block = block;
this.javaId = javaId;
this.states = states;
@ -49,23 +53,97 @@ public final class BlockState {
public <T extends Comparable<T>> T getValue(Property<T> property) {
//noinspection unchecked
return (T) states.get(property);
return (T) get(property);
}
public boolean getValue(Property<Boolean> property, boolean def) {
var value = states.get(property);
var value = get(property);
if (value == null) {
return def;
}
return (Boolean) value;
}
@Nullable
private Comparable<?> get(Property<?> property) {
Property<?>[] keys = this.block.propertyKeys();
if (keys == null) {
return null;
}
// We're copying the behavior Reference2ObjectArrayMap uses
for (int i = keys.length; i-- != 0;) {
if (keys[i] == property) {
return this.states[i];
}
}
return null;
}
/**
* @return the {@link BlockState} instance with the given value.
*/
public <T extends Comparable<T>> BlockState withValue(Property<T> property, T value) {
Property<?>[] keys = this.block.propertyKeys();
if (keys == null) {
throw new IllegalStateException(this + " does not have any different states!");
}
T currentValue = getValue(property);
if (currentValue == null) {
throw new IllegalArgumentException("This BlockState does not have the property " + property);
}
if (currentValue.equals(value)) {
// No action required. This block state is the state we're looking for.
return this;
}
// Diff is how much we will have to traverse as a sort of offset
// Block states are calculated in a predictable structure:
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=tall]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=none]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=low]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=tall]
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=false,waterlogged=true,west=none]
// The last value goes through all its iterations, then the next state goes through all its iterations.
// West goes none -> low -> tall, then waterlogged is toggled as west cycles again.
// Then when waterlogged goes through all its properties, up is toggled, and west goes through again
// If we want to find the "up" property in order, then we need to find how many iterations each property
// after it goes in. West goes for 3, waterlogged goes for 2. Adding those together, we find that we need to
// add five to get to the next toggle of the up property
int diff = 0;
for (int i = keys.length - 1; i >= 0; i--) {
if (keys[i] != property) {
diff += keys[i].valuesCount();
} else {
break;
}
}
// How many times do we have to jump by diff? This depends on how far away each value is from each other.
// piston_head[facing=north] might be right next to piston_head[facing=south], which just one diff'd hop.
// But piston_head[facing=west] is further away, requiring more hops.
int thatOffset = property.indexOf(value);
int thisOffset = property.indexOf(currentValue);
if (diff == 0) {
// This can happen if the property is at the tail end of the block and there are no other properties to look through
// If we have minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
// And want minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
// The above for loop will always stop at the first break because the last property has already been found
diff = 1;
}
return BlockState.of(this.javaId + ((thatOffset - thisOffset) * diff));
}
public Block block() {
return block;
return this.block;
}
public int javaId() {
return javaId;
return this.javaId;
}
public boolean is(Block block) {
@ -74,7 +152,7 @@ public final class BlockState {
@Override
public String toString() {
if (this.states.isEmpty()) {
if (this.states == null) {
return this.block.javaIdentifier().toString();
}
return this.block.javaIdentifier().toString() + "[" + paramsToString() + "]";
@ -82,14 +160,15 @@ public final class BlockState {
private String paramsToString() {
StringBuilder builder = new StringBuilder();
var it = this.states.entrySet().iterator();
while (it.hasNext()) {
var entry = it.next();
builder.append(entry.getKey().name())
.append("=")
.append(entry.getValue().toString().toLowerCase(Locale.ROOT)); // lowercase covers enums
if (it.hasNext()) {
builder.append(",");
Property<?>[] propertyKeys = this.block.propertyKeys();
if (propertyKeys != null) {
for (int i = 0; i < propertyKeys.length; i++) {
builder.append(propertyKeys[i].name())
.append("=")
.append(this.states[i].toString().toLowerCase(Locale.ROOT)); // lowercase covers enums
if (i < propertyKeys.length - 1) {
builder.append(",");
}
}
}
return builder.toString();

View File

@ -28,29 +28,14 @@ package org.geysermc.geyser.level.block.type;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.physics.Direction;
import java.util.List;
public class FurnaceBlock extends Block {
private static BlockState LIT;
private static BlockState UNLIT;
public FurnaceBlock(String javaIdentifier, Builder builder) {
super(javaIdentifier, builder);
}
@Override
protected void processStates(List<BlockState> states) {
LIT = states.stream()
.filter(state -> state.getValue(Properties.HORIZONTAL_FACING) == Direction.NORTH
&& state.getValue(Properties.LIT))
.findFirst().orElseThrow();
UNLIT = states.stream()
.filter(state -> state.getValue(Properties.HORIZONTAL_FACING) == Direction.NORTH
&& !state.getValue(Properties.LIT))
.findFirst().orElseThrow();
}
public static BlockState state(boolean lit) {
return lit ? LIT : UNLIT;
protected BlockState setDefaultState(BlockState firstState) {
// Both furnace minecart states look north.
return firstState.withValue(Properties.HORIZONTAL_FACING, Direction.NORTH);
}
}

View File

@ -25,21 +25,8 @@
package org.geysermc.geyser.level.block.type;
import java.util.List;
public class HoneyBlock extends Block {
private static BlockState STATE;
public HoneyBlock(String javaIdentifier, Builder builder) {
super(javaIdentifier, builder);
}
@Override
protected void processStates(List<BlockState> states) {
STATE = states.get(0);
}
public static BlockState state() {
return STATE;
}
}

View File

@ -25,21 +25,8 @@
package org.geysermc.geyser.level.block.type;
import java.util.List;
public class SpawnerBlock extends Block {
private static BlockState STATE;
public SpawnerBlock(String javaIdentifier, Builder builder) {
super(javaIdentifier, builder);
}
@Override
protected void processStates(List<BlockState> states) {
STATE = states.get(0);
}
public static BlockState state() {
return STATE;
}
}

View File

@ -25,17 +25,8 @@
package org.geysermc.geyser.level.block.type;
import java.util.List;
public class WaterBlock extends Block {
private static BlockState LEVEL_0;
public WaterBlock(String javaIdentifier, Builder builder) {
super(javaIdentifier, builder);
}
@Override
protected void processStates(List<BlockState> states) {
super.processStates(states);
}
}

View File

@ -39,6 +39,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
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.BlockState;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.collision.BlockCollision;
@ -405,7 +408,8 @@ public class CollisionManager {
* @return if the player is currently in a water block
*/
public boolean isPlayerInWater() {
return session.getGeyser().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID;
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getPlayerEntity().getPosition().toInt());
return state.is(Blocks.WATER) && state.getValue(Properties.LEVEL) == 0;
}
public boolean isWaterInEyes() {

View File

@ -50,7 +50,6 @@ 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.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;
@ -433,25 +432,13 @@ public final class BlockRegistryPopulator {
}
int javaRuntimeId = -1;
int waterRuntimeId = -1;
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
javaRuntimeId++;
String javaId = javaBlockState.toString().intern();
BlockStateValues.storeBlockStateValues(javaId, javaRuntimeId);
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = javaRuntimeId;
}
}
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find Java water in palette");
}
BlockStateValues.JAVA_WATER_ID = waterRuntimeId;
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();

View File

@ -90,6 +90,10 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
return this.getBedrockBlock(javaState.javaId());
}
public GeyserBedrockBlock getVanillaBedrockBlock(BlockState javaState) {
return getVanillaBedrockBlock(javaState.javaId());
}
public GeyserBedrockBlock getVanillaBedrockBlock(int javaState) {
if (javaState < 0 || javaState >= this.javaToVanillaBedrockBlocks.length) {
return bedrockAir;

View File

@ -31,6 +31,7 @@ import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
import org.geysermc.geyser.inventory.holder.InventoryHolder;
import org.geysermc.geyser.inventory.updater.InventoryUpdater;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.session.GeyserSession;
/**
@ -40,17 +41,25 @@ public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTran
private final InventoryHolder holder;
private final InventoryUpdater updater;
/**
* @param javaBlock a Java block that is used as a temporary block
*/
public AbstractBlockInventoryTranslator(int size, Block javaBlock, ContainerType containerType, InventoryUpdater updater,
Block... additionalValidBlocks) {
this(size, javaBlock.defaultBlockState(), containerType, updater, additionalValidBlocks);
}
/**
* @param size the amount of slots that the inventory adds alongside the base inventory slots
* @param javaBlockIdentifier a Java block identifier that is used as a temporary block
* @param javaBlockState a Java block state that is used as a temporary block
* @param containerType the container type of this inventory
* @param updater updater
* @param additionalValidBlocks any other blocks that can safely use this inventory without a fake block
*/
public AbstractBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater,
public AbstractBlockInventoryTranslator(int size, BlockState javaBlockState, ContainerType containerType, InventoryUpdater updater,
Block... additionalValidBlocks) {
super(size);
this.holder = new BlockInventoryHolder(javaBlockIdentifier, containerType, additionalValidBlocks);
this.holder = new BlockInventoryHolder(javaBlockState, containerType, additionalValidBlocks);
this.updater = updater;
}

View File

@ -45,7 +45,7 @@ import java.util.Objects;
public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
public AnvilInventoryTranslator() {
super(3, "minecraft:anvil[facing=north]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE,
super(3, Blocks.ANVIL, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE,
Blocks.CHIPPED_ANVIL, Blocks.DAMAGED_ANVIL);
}

View File

@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.inventory;
import it.unimi.dsi.fastutil.ints.IntSets;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
@ -43,7 +42,9 @@ import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetBeaconPacket;
@ -52,7 +53,7 @@ import java.util.OptionalInt;
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
public BeaconInventoryTranslator() {
super(1, new BlockInventoryHolder("minecraft:beacon", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.BEACON) {
super(1, new BlockInventoryHolder(Blocks.BEACON, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.BEACON) {
@Override
protected boolean checkInteractionPosition(GeyserSession session) {
// Since we can't fall back to a virtual inventory, let's make opening one easier
@ -89,12 +90,8 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator
// Send a block entity data packet update to the fake beacon inventory
Vector3i position = inventory.getHolderPosition();
NbtMapBuilder builder = NbtMap.builder()
.putInt("x", position.getX())
.putInt("y", position.getY())
.putInt("z", position.getZ())
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Beacon", position)
.putString("CustomName", inventory.getTitle())
.putString("id", "Beacon")
.putInt("primary", beaconContainer.getPrimaryId())
.putInt("secondary", beaconContainer.getSecondaryId());

View File

@ -32,11 +32,16 @@ import org.cloudburstmc.protocol.bedrock.packet.ContainerSetDataPacket;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.session.GeyserSession;
public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator {
public BrewingInventoryTranslator() {
super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE);
super(5, Blocks.BREWING_STAND.defaultBlockState()
.withValue(Properties.HAS_BOTTLE_0, false)
.withValue(Properties.HAS_BOTTLE_1, false)
.withValue(Properties.HAS_BOTTLE_2, false), ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE);
}
@Override

View File

@ -30,12 +30,13 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemSt
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
public class CartographyInventoryTranslator extends AbstractBlockInventoryTranslator {
public CartographyInventoryTranslator() {
super(3, "minecraft:cartography_table", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE);
super(3, Blocks.CARTOGRAPHY_TABLE, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.updater.CrafterInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
@ -53,7 +54,7 @@ public class CrafterInventoryTranslator extends AbstractBlockInventoryTranslator
private static final int TRIGGERED = 1; // triggered value
public CrafterInventoryTranslator() {
super(10, "minecraft:crafter", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CRAFTER, CrafterInventoryUpdater.INSTANCE);
super(10, Blocks.CRAFTER, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CRAFTER, CrafterInventoryUpdater.INSTANCE);
}
@Override

View File

@ -31,10 +31,11 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemSt
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
public class CraftingInventoryTranslator extends AbstractBlockInventoryTranslator {
public CraftingInventoryTranslator() {
super(10, "minecraft:crafting_table", ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE);
super(10, Blocks.CRAFTING_TABLE, ContainerType.WORKBENCH, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -38,6 +38,7 @@ import org.cloudburstmc.protocol.bedrock.packet.PlayerEnchantOptionsPacket;
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
@ -47,7 +48,7 @@ import java.util.Locale;
public class EnchantingInventoryTranslator extends AbstractBlockInventoryTranslator {
public EnchantingInventoryTranslator() {
super(2, "minecraft:enchanting_table", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ENCHANTMENT, UIInventoryUpdater.INSTANCE);
super(2, Blocks.ENCHANTING_TABLE, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ENCHANTMENT, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -41,7 +41,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
*/
public class Generic3X3InventoryTranslator extends AbstractBlockInventoryTranslator {
public Generic3X3InventoryTranslator() {
super(9, "minecraft:dispenser[facing=north,triggered=false]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.DISPENSER, ContainerInventoryUpdater.INSTANCE,
super(9, Blocks.DISPENSER, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.DISPENSER, ContainerInventoryUpdater.INSTANCE,
Blocks.DROPPER);
}

View File

@ -30,10 +30,11 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
public class GrindstoneInventoryTranslator extends AbstractBlockInventoryTranslator {
public GrindstoneInventoryTranslator() {
super(3, "minecraft:grindstone[face=floor,facing=north]", ContainerType.GRINDSTONE, UIInventoryUpdater.INSTANCE);
super(3, Blocks.GRINDSTONE, ContainerType.GRINDSTONE, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -29,13 +29,14 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
/**
* Implemented on top of any block that does not have special properties implemented
*/
public class HopperInventoryTranslator extends AbstractBlockInventoryTranslator {
public HopperInventoryTranslator() {
super(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, ContainerInventoryUpdater.INSTANCE);
super(5, Blocks.HOPPER, ContainerType.HOPPER, ContainerInventoryUpdater.INSTANCE);
}
@Override

View File

@ -34,6 +34,7 @@ import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
@ -56,7 +57,7 @@ public class LecternInventoryTranslator extends AbstractBlockInventoryTranslator
private boolean initialized = false;
public LecternInventoryTranslator() {
super(1, "minecraft:lectern[facing=north,has_book=true,powered=true]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.LECTERN , ContainerInventoryUpdater.INSTANCE);
super(1, Blocks.LECTERN, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.LECTERN , ContainerInventoryUpdater.INSTANCE);
}
@Override

View File

@ -45,6 +45,7 @@ import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.item.type.BannerItem;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
@ -101,7 +102,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
}
public LoomInventoryTranslator() {
super(4, "minecraft:loom[facing=north]", ContainerType.LOOM, UIInventoryUpdater.INSTANCE);
super(4, Blocks.LOOM, ContainerType.LOOM, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -37,6 +37,7 @@ import org.cloudburstmc.protocol.bedrock.packet.InventorySlotPacket;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InventoryUtils;
@ -53,7 +54,7 @@ public class OldSmithingTableTranslator extends AbstractBlockInventoryTranslator
private static final IntFunction<ItemData> UPGRADE_TEMPLATE = InventoryUtils.getUpgradeTemplate();
private OldSmithingTableTranslator() {
super(3, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
super(3, Blocks.SMITHING_TABLE, ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -35,7 +35,10 @@ import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
@ -43,12 +46,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType
public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator {
public ShulkerInventoryTranslator() {
super(27, new BlockInventoryHolder("minecraft:shulker_box[facing=north]", ContainerType.CONTAINER) {
// Ensure that the shulker box default state won't be trying to open in a state facing the player
super(27, new BlockInventoryHolder(Blocks.SHULKER_BOX.defaultBlockState().withValue(Properties.FACING, Direction.NORTH), ContainerType.CONTAINER) {
private final BlockEntityTranslator shulkerBoxTranslator = Registries.BLOCK_ENTITIES.get(BlockEntityType.SHULKER_BOX);
@Override
protected boolean isValidBlock(String[] javaBlockString) {
return javaBlockString[0].contains("shulker_box");
protected boolean isValidBlock(BlockState blockState) {
return blockState.block().javaIdentifier().value().contains("shulker_box"); // TODO ew
}
@Override

View File

@ -30,10 +30,11 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
public SmithingInventoryTranslator() {
super(4, "minecraft:smithing_table", ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
super(4, Blocks.SMITHING_TABLE, ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemS
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
@ -42,7 +43,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.S
public class StonecutterInventoryTranslator extends AbstractBlockInventoryTranslator {
public StonecutterInventoryTranslator() {
super(2, "minecraft:stonecutter[facing=north]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.STONECUTTER, UIInventoryUpdater.INSTANCE);
super(2, Blocks.STONECUTTER, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.STONECUTTER, UIInventoryUpdater.INSTANCE);
}
@Override

View File

@ -40,6 +40,7 @@ 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.level.physics.Direction;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
@ -51,7 +52,10 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
public DoubleChestInventoryTranslator(int size) {
super(size, 54);
this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt("minecraft:chest[facing=north,type=single,waterlogged=false]");
this.defaultJavaBlockState = Blocks.CHEST.defaultBlockState()
.withValue(Properties.HORIZONTAL_FACING, Direction.NORTH)
.withValue(Properties.CHEST_TYPE, ChestType.SINGLE)
.javaId();
}
@Override
@ -96,11 +100,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
NbtMap tag = NbtMap.builder()
.putString("id", "Chest")
.putInt("x", position.getX())
.putInt("y", position.getY())
.putInt("z", position.getZ())
NbtMap tag = BlockEntityTranslator.getConstantBedrockTag("Chest", position)
.putInt("pairx", pairPosition.getX())
.putInt("pairz", pairPosition.getZ())
.putString("CustomName", inventory.getTitle()).build();

View File

@ -30,6 +30,9 @@ import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
import org.geysermc.geyser.inventory.holder.InventoryHolder;
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.session.GeyserSession;
public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
@ -38,17 +41,17 @@ public class SingleChestInventoryTranslator extends ChestInventoryTranslator {
// TODO add barrel???
public SingleChestInventoryTranslator(int size) {
super(size, 27);
this.holder = new BlockInventoryHolder("minecraft:chest[facing=north,type=single,waterlogged=false]", ContainerType.CONTAINER,
this.holder = new BlockInventoryHolder(Blocks.CHEST.defaultBlockState().withValue(Properties.CHEST_TYPE, ChestType.SINGLE), ContainerType.CONTAINER,
Blocks.ENDER_CHEST, Blocks.TRAPPED_CHEST) {
@Override
protected boolean isValidBlock(String[] javaBlockString) {
if (javaBlockString[0].equals("minecraft:ender_chest")) {
protected boolean isValidBlock(BlockState blockState) {
if (blockState.is(Blocks.ENDER_CHEST)) {
// Can't have double ender chests
return true;
}
// Add provision to ensure this isn't a double chest
return super.isValidBlock(javaBlockString) && (javaBlockString.length > 1 && javaBlockString[1].contains("type=single"));
return super.isValidBlock(blockState) && blockState.getValue(Properties.CHEST_TYPE) == ChestType.SINGLE;
}
};
}

View File

@ -32,12 +32,14 @@ import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.AbstractBlockInventoryTranslator;
public abstract class AbstractFurnaceInventoryTranslator extends AbstractBlockInventoryTranslator {
AbstractFurnaceInventoryTranslator(String javaBlockIdentifier, ContainerType containerType) {
super(3, javaBlockIdentifier, containerType, ContainerInventoryUpdater.INSTANCE);
AbstractFurnaceInventoryTranslator(Block javaBlock, ContainerType containerType) {
super(3, javaBlock.defaultBlockState().withValue(Properties.LIT, false), containerType, ContainerInventoryUpdater.INSTANCE);
}
@Override

View File

@ -28,10 +28,11 @@ package org.geysermc.geyser.translator.inventory.furnace;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.level.block.Blocks;
public class BlastFurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator {
public BlastFurnaceInventoryTranslator() {
super("minecraft:blast_furnace[facing=north,lit=false]", ContainerType.BLAST_FURNACE);
super(Blocks.BLAST_FURNACE, ContainerType.BLAST_FURNACE);
}
@Override

View File

@ -28,10 +28,11 @@ package org.geysermc.geyser.translator.inventory.furnace;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.level.block.Blocks;
public class FurnaceInventoryTranslator extends AbstractFurnaceInventoryTranslator {
public FurnaceInventoryTranslator() {
super("minecraft:furnace[facing=north,lit=false]", ContainerType.FURNACE);
super(Blocks.FURNACE, ContainerType.FURNACE);
}
@Override

View File

@ -28,10 +28,11 @@ package org.geysermc.geyser.translator.inventory.furnace;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.level.block.Blocks;
public class SmokerInventoryTranslator extends AbstractFurnaceInventoryTranslator {
public SmokerInventoryTranslator() {
super("minecraft:smoker[facing=north,lit=false]", ContainerType.SMOKER);
super(Blocks.SMOKER, ContainerType.SMOKER);
}
@Override

View File

@ -26,22 +26,24 @@
package org.geysermc.geyser.translator.level.block.entity;
import it.unimi.dsi.fastutil.ints.IntArrays;
import it.unimi.dsi.fastutil.objects.*;
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.HoneyBlock;
import org.geysermc.geyser.level.block.type.PistonBlock;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import lombok.Getter;
import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import lombok.Getter;
import org.geysermc.geyser.api.util.PlatformType;
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.PistonBlock;
import org.geysermc.geyser.level.physics.Axis;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.CollisionManager;
@ -53,6 +55,7 @@ import org.geysermc.geyser.translator.collision.BlockCollision;
import org.geysermc.geyser.util.BlockEntityUtils;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
import java.util.LinkedList;
import java.util.Map;
@ -99,7 +102,7 @@ public class PistonBlockEntity {
static {
// Create a ~1 x ~0.5 x ~1 bounding box above the honey block
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(HoneyBlock.state().javaId());
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(Blocks.HONEY_BLOCK.defaultBlockState().javaId());
if (blockCollision == null) {
throw new RuntimeException("Failed to find honey block collision");
}
@ -224,10 +227,10 @@ public class PistonBlockEntity {
private void removePistonHead() {
Vector3i blockInFront = position.add(orientation.getUnitVector());
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockInFront);
if (BlockStateValues.isPistonHead(blockId)) {
BlockState state = session.getGeyser().getWorldManager().blockAt(session, blockInFront);
if (state.is(Blocks.PISTON_HEAD)) {
ChunkUtils.updateBlock(session, Block.JAVA_AIR_ID, blockInFront);
} else if ((session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) && blockId == Block.JAVA_AIR_ID) {
} else if ((session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) && state.is(Blocks.AIR)) {
// Spigot removes the piston head from the cache, but we need to send the block update ourselves
ChunkUtils.updateBlock(session, Block.JAVA_AIR_ID, blockInFront);
}
@ -353,7 +356,9 @@ public class PistonBlockEntity {
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() - shrink.getZ());
// Resolve collision with the piston head
BlockState pistonHeadId = BlockState.of(BlockStateValues.getPistonHead(orientation));
BlockState pistonHeadId = Blocks.PISTON_HEAD.defaultBlockState()
.withValue(Properties.SHORT, false)
.withValue(Properties.FACING, orientation);
pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox);
// Resolve collision with any attached moving blocks, but skip slime blocks
@ -562,9 +567,11 @@ public class PistonBlockEntity {
private BlockState getAttachedBlockId(Vector3i blockPos) {
if (blockPos.equals(getPistonHeadPos())) {
return BlockState.of(BlockStateValues.getPistonHead(orientation));
return Blocks.PISTON_HEAD.defaultBlockState()
.withValue(Properties.SHORT, false)
.withValue(Properties.FACING, orientation);
} else {
return attachedBlocks.getOrDefault(blockPos, BlockState.of(Block.JAVA_AIR_ID)); //FIXME
return attachedBlocks.getOrDefault(blockPos, Blocks.AIR.defaultBlockState());
}
}
@ -633,7 +640,9 @@ public class PistonBlockEntity {
if (action == PistonValueType.PUSHING) {
Vector3i pistonHeadPos = getPistonHeadPos().add(movement);
if (!SOLID_BOUNDING_BOX.checkIntersection(pistonHeadPos.toDouble(), session.getCollisionManager().getPlayerBoundingBox())) {
ChunkUtils.updateBlock(session, BlockStateValues.getPistonHead(orientation), pistonHeadPos);
ChunkUtils.updateBlock(session, Blocks.PISTON_HEAD.defaultBlockState()
.withValue(Properties.SHORT, false)
.withValue(Properties.FACING, orientation), pistonHeadPos);
}
}
}

View File

@ -56,8 +56,8 @@ import org.geysermc.geyser.item.type.BlockItem;
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.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.CauldronBlock;
@ -295,10 +295,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
*/
if (packet.getItemInHand() != null && session.getItemMappings().getMapping(packet.getItemInHand()).getJavaItem() instanceof SpawnEggItem) {
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition());
if (blockState == BlockStateValues.JAVA_WATER_ID) {
BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, packet.getBlockPosition());
if (blockState.is(Blocks.WATER) && blockState.getValue(Properties.LEVEL) == 0) {
// Otherwise causes multiple mobs to spawn - just send a use item packet
useItem(session, packet, blockState);
useItem(session, packet, blockState.javaId());
break;
}
}

View File

@ -25,6 +25,7 @@
package org.geysermc.geyser.translator.protocol.java;
import com.google.common.base.Suppliers;
import org.geysermc.mcprotocollib.protocol.data.game.command.CommandNode;
import org.geysermc.mcprotocollib.protocol.data.game.command.CommandParser;
import org.geysermc.mcprotocollib.protocol.data.game.command.properties.ResourceProperties;
@ -54,12 +55,16 @@ import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.EntityUtils;
import java.util.*;
import java.util.function.Supplier;
@SuppressWarnings("removal") // We know. This is our doing.
@Translator(packet = ClientboundCommandsPacket.class)
public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommandsPacket> {
private static final String[] ALL_BLOCK_NAMES = BlockRegistries.JAVA_BLOCKS.get().stream().map(block -> block.javaIdentifier().toString()).toArray(String[]::new);
/**
* Wait until the registries load before getting all the block names.
*/
private static final Supplier<String[]> ALL_BLOCK_NAMES = Suppliers.memoize(() -> BlockRegistries.JAVA_BLOCKS.get().stream().map(block -> block.javaIdentifier().toString()).toArray(String[]::new));
private static final String[] ALL_EFFECT_IDENTIFIERS = EntityUtils.getAllEffectIdentifiers();
private static final String[] ATTRIBUTES = AttributeType.Builtin.BUILTIN.values().stream().map(AttributeType::getIdentifier).toList().toArray(new String[0]);
private static final String[] ENUM_BOOLEAN = {"true", "false"};
@ -247,7 +252,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
case RESOURCE_LOCATION, FUNCTION -> CommandParam.FILE_PATH;
case BOOL -> ENUM_BOOLEAN;
case OPERATION -> CommandParam.OPERATOR; // ">=", "==", etc
case BLOCK_STATE -> ALL_BLOCK_NAMES;
case BLOCK_STATE -> ALL_BLOCK_NAMES.get();
case ITEM_STACK -> context.getItemNames();
case COLOR -> VALID_COLORS;
case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS;

View File

@ -65,7 +65,6 @@ fastutil-int-byte-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-
fastutil-int-boolean-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-boolean-maps", version.ref = "fastutil" }
fastutil-object-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-int-maps", version.ref = "fastutil" }
fastutil-object-object-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-object-maps", version.ref = "fastutil" }
fastutil-reference-object-maps = { group = "com.nukkitx.fastutil", name = "fastutil-reference-object-maps", version.ref = "fastutil" }
adventure-text-serializer-gson = { group = "net.kyori", name = "adventure-text-serializer-gson", version.ref = "adventure" } # Remove when we remove our Adventure bump
adventure-text-serializer-legacy = { group = "net.kyori", name = "adventure-text-serializer-legacy", version.ref = "adventure" }
@ -143,7 +142,7 @@ blossom = { id = "net.kyori.blossom", version.ref = "blossom" }
[bundles]
jackson = [ "jackson-annotations", "jackson-core", "jackson-dataformat-yaml" ]
fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps", "fastutil-reference-object-maps" ]
fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ]
adventure = [ "adventure-text-serializer-gson", "adventure-text-serializer-legacy", "adventure-text-serializer-plain" ]
log4j = [ "log4j-api", "log4j-core", "log4j-slf4j2-impl" ]
jline = [ "jline-terminal", "jline-terminal-jna", "jline-reader" ]