mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Pistons now use the new block stuff
This commit is contained in:
parent
06dc0d1ca8
commit
beef01f3fc
29 changed files with 978 additions and 829 deletions
|
@ -26,11 +26,12 @@
|
||||||
package org.geysermc.geyser.erosion;
|
package org.geysermc.geyser.erosion;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
@ -44,6 +45,7 @@ import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
|
||||||
import org.geysermc.erosion.packet.geyserbound.*;
|
import org.geysermc.erosion.packet.geyserbound.*;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
import org.geysermc.geyser.level.physics.Direction;
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
@ -153,9 +155,10 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
||||||
var stream = packet.getAttachedBlocks()
|
var stream = packet.getAttachedBlocks()
|
||||||
.object2IntEntrySet()
|
.object2IntEntrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend));
|
.map(entry -> Pair.of(entry.getKey(), BlockState.of(entry.getIntValue())))
|
||||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
.filter(pair -> BlockStateValues.canPistonMoveBlock(pair.value(), isExtend));
|
||||||
stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue()));
|
Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectOpenHashMap<>();
|
||||||
|
stream.forEach(pair -> attachedBlocks.put(pair.key(), pair.value()));
|
||||||
|
|
||||||
session.executeInEventLoop(() -> {
|
session.executeInEventLoop(() -> {
|
||||||
PistonCache pistonCache = session.getPistonCache();
|
PistonCache pistonCache = session.getPistonCache();
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
|
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
|
||||||
|
@ -58,6 +59,14 @@ import java.util.function.Function;
|
||||||
*/
|
*/
|
||||||
public abstract class WorldManager {
|
public abstract class WorldManager {
|
||||||
|
|
||||||
|
public final BlockState blockAt(GeyserSession session, Vector3i vector3i) {
|
||||||
|
return BlockState.of(this.getBlockAt(session, vector3i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState blockAt(GeyserSession session, int x, int y, int z) {
|
||||||
|
return BlockState.of(this.getBlockAt(session, x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Java block state at the specified location
|
* Gets the Java block state at the specified location
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,12 +29,14 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import it.unimi.dsi.fastutil.ints.*;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
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.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.Direction;
|
||||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntityTranslator;
|
|
||||||
import org.geysermc.geyser.util.collection.FixedInt2ByteMap;
|
import org.geysermc.geyser.util.collection.FixedInt2ByteMap;
|
||||||
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
|
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
|
||||||
import org.geysermc.geyser.util.collection.LecternHasBookMap;
|
import org.geysermc.geyser.util.collection.LecternHasBookMap;
|
||||||
|
@ -229,28 +231,6 @@ public final class BlockStateValues {
|
||||||
return BANNER_COLORS.getOrDefault(state, -1);
|
return BANNER_COLORS.getOrDefault(state, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
||||||
* This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag.
|
|
||||||
*
|
|
||||||
* @param state BlockState of the block
|
|
||||||
* @return Bed color byte or -1 if no color
|
|
||||||
*/
|
|
||||||
public static byte getBedColor(int state) {
|
|
||||||
return BED_COLORS.getOrDefault(state, (byte) -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The brush progress of suspicious sand/gravel is not sent by the java server when it updates the block entity.
|
|
||||||
* Although brush progress is part of the bedrock block state, it must be included in the block entity update.
|
|
||||||
*
|
|
||||||
* @param state BlockState of the block
|
|
||||||
* @return brush progress or 0 if the lookup failed
|
|
||||||
*/
|
|
||||||
public static int getBrushProgress(int state) {
|
|
||||||
return BRUSH_PROGRESS.getOrDefault(state, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return if this Java block state is a non-empty non-water cauldron
|
* @return if this Java block state is a non-empty non-water cauldron
|
||||||
*/
|
*/
|
||||||
|
@ -341,18 +321,6 @@ public final class BlockStateValues {
|
||||||
return PISTON_HEADS.getOrDefault(direction, Block.JAVA_AIR_ID);
|
return PISTON_HEADS.getOrDefault(direction, Block.JAVA_AIR_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a block is a minecraft:moving_piston
|
|
||||||
* This is used in ChunkUtils to prevent them from being placed as it causes
|
|
||||||
* pistons to flicker and it is not needed
|
|
||||||
*
|
|
||||||
* @param state Block state of the block
|
|
||||||
* @return True if the block is a moving_piston
|
|
||||||
*/
|
|
||||||
public static boolean isMovingPiston(int state) {
|
|
||||||
return MOVING_PISTONS.contains(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used in GeyserPistonEvents.java and accepts minecraft:piston,
|
* This is used in GeyserPistonEvents.java and accepts minecraft:piston,
|
||||||
* minecraft:sticky_piston, and minecraft:moving_piston.
|
* minecraft:sticky_piston, and minecraft:moving_piston.
|
||||||
|
@ -371,8 +339,9 @@ public final class BlockStateValues {
|
||||||
* @param state The block state
|
* @param state The block state
|
||||||
* @return True if the block sticks to adjacent blocks
|
* @return True if the block sticks to adjacent blocks
|
||||||
*/
|
*/
|
||||||
public static boolean isBlockSticky(int state) {
|
public static boolean isBlockSticky(BlockState state) {
|
||||||
return state == JAVA_SLIME_BLOCK_ID || state == JAVA_HONEY_BLOCK_ID;
|
Block block = state.block();
|
||||||
|
return block == Blocks.SLIME_BLOCK || block == Blocks.HONEY_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -382,13 +351,13 @@ public final class BlockStateValues {
|
||||||
* @param stateB The block state of block b
|
* @param stateB The block state of block b
|
||||||
* @return True if the blocks are attached to each other
|
* @return True if the blocks are attached to each other
|
||||||
*/
|
*/
|
||||||
public static boolean isBlockAttached(int stateA, int stateB) {
|
public static boolean isBlockAttached(BlockState stateA, BlockState stateB) {
|
||||||
boolean aSticky = isBlockSticky(stateA);
|
boolean aSticky = isBlockSticky(stateA);
|
||||||
boolean bSticky = isBlockSticky(stateB);
|
boolean bSticky = isBlockSticky(stateB);
|
||||||
if (aSticky && bSticky) {
|
if (aSticky && bSticky) {
|
||||||
// Only matching sticky blocks are attached together
|
// Only matching sticky blocks are attached together
|
||||||
// Honey + Honey & Slime + Slime
|
// Honey + Honey & Slime + Slime
|
||||||
return stateA == stateB;
|
return stateA.block() == stateB.block();
|
||||||
}
|
}
|
||||||
return aSticky || bSticky;
|
return aSticky || bSticky;
|
||||||
}
|
}
|
||||||
|
@ -397,27 +366,30 @@ public final class BlockStateValues {
|
||||||
* @param state The block state of the block
|
* @param state The block state of the block
|
||||||
* @return true if a piston can break the block
|
* @return true if a piston can break the block
|
||||||
*/
|
*/
|
||||||
public static boolean canPistonDestroyBlock(int state) {
|
public static boolean canPistonDestroyBlock(BlockState state) {
|
||||||
return BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getPistonBehavior() == PistonBehavior.DESTROY;
|
return state.block().pushReaction() == PistonBehavior.DESTROY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canPistonMoveBlock(int javaId, boolean isPushing) {
|
public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) {
|
||||||
if (javaId == Block.JAVA_AIR_ID) {
|
Block block = state.block();
|
||||||
|
if (block == Blocks.AIR) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Pistons can only be moved if they aren't extended
|
if (block == Blocks.OBSIDIAN || block == Blocks.CRYING_OBSIDIAN || block == Blocks.RESPAWN_ANCHOR || block == Blocks.REINFORCED_DEEPSLATE) { // Hardcoded as of 1.20.5
|
||||||
if (PistonBlockEntityTranslator.isBlock(javaId)) {
|
|
||||||
return !PISTON_VALUES.get(javaId);
|
|
||||||
}
|
|
||||||
BlockMapping block = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaId, BlockMapping.DEFAULT);
|
|
||||||
// Bedrock, End portal frames, etc. can't be moved
|
|
||||||
if (block.getHardness() == -1.0d) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return switch (block.getPistonBehavior()) {
|
// Pistons can only be moved if they aren't extended
|
||||||
|
if (block instanceof PistonBlock) {
|
||||||
|
return !state.getValue(Properties.EXTENDED);
|
||||||
|
}
|
||||||
|
// Bedrock, End portal frames, etc. can't be moved
|
||||||
|
if (block.destroyTime() == -1.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return switch (block.pushReaction()) {
|
||||||
case BLOCK, DESTROY -> false;
|
case BLOCK, DESTROY -> false;
|
||||||
case PUSH_ONLY -> isPushing; // Glazed terracotta can only be pushed
|
case PUSH_ONLY -> isPushing; // Glazed terracotta can only be pushed
|
||||||
default -> !block.isBlockEntity(); // Pistons can't move block entities
|
default -> !block.hasBlockEntity(); // Pistons can't move block entities
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,17 +415,6 @@ public final class BlockStateValues {
|
||||||
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* As of Java 1.20.2:
|
|
||||||
* Skull powered states are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
||||||
*
|
|
||||||
* @param state BlockState of the block
|
|
||||||
* @return true if this skull is currently being powered.
|
|
||||||
*/
|
|
||||||
public static boolean isSkullPowered(int state) {
|
|
||||||
return SKULL_POWERED.contains(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
* 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.
|
* This gives a integer rotation that Bedrock can use.
|
||||||
|
@ -464,17 +425,6 @@ public final class BlockStateValues {
|
||||||
return SKULL_WALL_DIRECTIONS;
|
return SKULL_WALL_DIRECTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shulker box directions are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
|
||||||
* This gives a byte direction that Bedrock can use.
|
|
||||||
*
|
|
||||||
* @param state BlockState of the block
|
|
||||||
* @return Shulker direction value or -1 if no value
|
|
||||||
*/
|
|
||||||
public static byte getShulkerBoxDirection(int state) {
|
|
||||||
return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the level of water from the block state.
|
* Get the level of water from the block state.
|
||||||
*
|
*
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -118,7 +118,7 @@ public final class Properties {
|
||||||
public static final Property<Integer> RESPAWN_ANCHOR_CHARGES = Property.create("charges");
|
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<Integer> ROTATION_16 = Property.create("rotation");
|
||||||
public static final Property<String> BED_PART = Property.create("part");
|
public static final Property<String> BED_PART = Property.create("part");
|
||||||
public static final Property<String> CHEST_TYPE = Property.create("type");
|
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> MODE_COMPARATOR = Property.create("mode");
|
||||||
public static final Property<String> DOOR_HINGE = Property.create("hinge");
|
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> NOTEBLOCK_INSTRUMENT = Property.create("instrument");
|
||||||
|
|
|
@ -30,8 +30,15 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
|
import org.geysermc.geyser.level.block.Blocks;
|
||||||
import org.geysermc.geyser.level.block.property.Property;
|
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.registry.BlockRegistries;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
|
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -44,6 +51,7 @@ public class Block {
|
||||||
private final boolean requiresCorrectToolForDrops;
|
private final boolean requiresCorrectToolForDrops;
|
||||||
private final boolean hasBlockEntity;
|
private final boolean hasBlockEntity;
|
||||||
private final float destroyTime;
|
private final float destroyTime;
|
||||||
|
private final @NonNull PistonBehavior pushReaction;
|
||||||
private int javaId = -1;
|
private int javaId = -1;
|
||||||
|
|
||||||
public Block(String javaIdentifier, Builder builder) {
|
public Block(String javaIdentifier, Builder builder) {
|
||||||
|
@ -51,9 +59,76 @@ public class Block {
|
||||||
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
|
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
|
||||||
this.hasBlockEntity = builder.hasBlockEntity;
|
this.hasBlockEntity = builder.hasBlockEntity;
|
||||||
this.destroyTime = builder.destroyTime;
|
this.destroyTime = builder.destroyTime;
|
||||||
|
this.pushReaction = builder.pushReaction;
|
||||||
builder.build(this);
|
builder.build(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
|
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state);
|
||||||
|
sendBlockUpdatePacket(session, state, definition, position);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Extended collision boxes for custom blocks
|
||||||
|
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
||||||
|
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
||||||
|
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(state.javaId());
|
||||||
|
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
||||||
|
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
||||||
|
if (belowBedrockExtendedCollisionDefinition != null && state.is(Blocks.AIR)) {
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
updateBlockPacket.setBlockPosition(position);
|
||||||
|
updateBlockPacket.setDefinition(belowBedrockExtendedCollisionDefinition);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == Block.JAVA_AIR_ID) {
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||||
|
updateBlockPacket.setDefinition(aboveBedrockExtendedCollisionDefinition);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
} else if (aboveBlock == Block.JAVA_AIR_ID) {
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
||||||
|
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLecternBlockUpdate(session, state, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
updateBlockPacket.setBlockPosition(position);
|
||||||
|
updateBlockPacket.setDefinition(definition);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
|
||||||
|
UpdateBlockPacket waterPacket = new UpdateBlockPacket();
|
||||||
|
waterPacket.setDataLayer(1);
|
||||||
|
waterPacket.setBlockPosition(position);
|
||||||
|
if (BlockRegistries.WATERLOGGED.get().get(state.javaId())) {
|
||||||
|
waterPacket.setDefinition(session.getBlockMappings().getBedrockWater());
|
||||||
|
} else {
|
||||||
|
waterPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
||||||
|
}
|
||||||
|
session.sendUpstreamPacket(waterPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
session.getLecternCache().remove(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String javaIdentifier() {
|
public String javaIdentifier() {
|
||||||
return javaIdentifier;
|
return javaIdentifier;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +145,11 @@ public class Block {
|
||||||
return destroyTime;
|
return destroyTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public PistonBehavior pushReaction() {
|
||||||
|
return this.pushReaction;
|
||||||
|
}
|
||||||
|
|
||||||
public int javaId() {
|
public int javaId() {
|
||||||
return javaId;
|
return javaId;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +177,7 @@ public class Block {
|
||||||
private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>();
|
private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>();
|
||||||
private boolean requiresCorrectToolForDrops = false;
|
private boolean requiresCorrectToolForDrops = false;
|
||||||
private boolean hasBlockEntity = false;
|
private boolean hasBlockEntity = false;
|
||||||
|
private PistonBehavior pushReaction = PistonBehavior.NORMAL;
|
||||||
private float destroyTime;
|
private float destroyTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,6 +224,11 @@ public class Block {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder pushReaction(PistonBehavior pushReaction) {
|
||||||
|
this.pushReaction = pushReaction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void build(Block block) {
|
private void build(Block block) {
|
||||||
if (states.isEmpty()) {
|
if (states.isEmpty()) {
|
||||||
BlockRegistries.BLOCK_STATES.get().add(new BlockState(block, BlockRegistries.BLOCK_STATES.get().size()));
|
BlockRegistries.BLOCK_STATES.get().add(new BlockState(block, BlockRegistries.BLOCK_STATES.get().size()));
|
||||||
|
|
|
@ -58,6 +58,10 @@ public final class BlockState {
|
||||||
return javaId;
|
return javaId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean is(Block block) {
|
||||||
|
return this.block == block;
|
||||||
|
}
|
||||||
|
|
||||||
public static BlockState of(int javaId) {
|
public static BlockState of(int javaId) {
|
||||||
return BlockRegistries.BLOCK_STATES.get(javaId);
|
return BlockRegistries.BLOCK_STATES.get(javaId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.cloudburstmc.nbt.NbtList;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.cloudburstmc.nbt.NbtType;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
|
|
||||||
|
public class CauldronBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||||
|
public CauldronBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||||
|
// As of 1.18.30: this is required to make rendering not look weird on chunk load (lava and snow cauldrons look dim)
|
||||||
|
return BlockEntityTranslator.getConstantBedrockTag("Cauldron", position.getX(), position.getY(), position.getZ())
|
||||||
|
.putByte("isMovable", (byte) 0)
|
||||||
|
.putShort("PotionId", (short) -1)
|
||||||
|
.putShort("PotionType", (short) -1)
|
||||||
|
.putList("Items", NbtType.END, NbtList.EMPTY)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
|
import org.geysermc.geyser.level.block.property.ChestType;
|
||||||
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
|
||||||
|
public class ChestBlock extends Block {
|
||||||
|
public ChestBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
|
super.updateBlock(session, state, position);
|
||||||
|
|
||||||
|
if (state.getValue(Properties.CHEST_TYPE) != ChestType.SINGLE) {
|
||||||
|
NbtMapBuilder tagBuilder = BlockEntityTranslator.getConstantBedrockTag(BlockEntityType.CHEST, position.getX(), position.getY(), position.getZ());
|
||||||
|
BlockEntityUtils.getBlockEntityTranslator(BlockEntityType.CHEST).translateTag(session, tagBuilder, null, state); //TODO
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.ChunkUtils;
|
||||||
|
|
||||||
|
public class DoorBlock extends Block {
|
||||||
|
public DoorBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
|
super.updateBlock(session, state, position);
|
||||||
|
|
||||||
|
if (state.getValue(Properties.DOUBLE_BLOCK_HALF).equals("upper")) {
|
||||||
|
// Update the lower door block as Bedrock client doesn't like door to be closed from the top
|
||||||
|
// See https://github.com/GeyserMC/Geyser/issues/4358
|
||||||
|
Vector3i belowDoorPosition = position.sub(0, 1, 0);
|
||||||
|
BlockState belowDoorBlockState = session.getGeyser().getWorldManager().blockAt(session, belowDoorPosition.getX(), belowDoorPosition.getY(), belowDoorPosition.getZ());
|
||||||
|
ChunkUtils.updateBlock(session, belowDoorBlockState, belowDoorPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,65 +23,35 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.level.block.entity;
|
package org.geysermc.geyser.level.block.type;
|
||||||
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.Blocks;
|
||||||
import org.geysermc.geyser.level.block.type.BlockState;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
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.geyser.util.BlockEntityUtils;
|
||||||
|
|
||||||
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||||
/**
|
private final Block flower;
|
||||||
* @param blockState the Java block state of a potential flower pot block
|
|
||||||
* @return true if the block is a flower pot
|
|
||||||
*/
|
|
||||||
public static boolean isFlowerBlock(int blockState) {
|
|
||||||
return BlockStateValues.getFlowerPotValues().containsKey(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public FlowerPotBlock(String javaIdentifier, Block flower, Builder builder) {
|
||||||
* Get the Nukkit CompoundTag of the flower pot.
|
super(javaIdentifier, builder);
|
||||||
*
|
this.flower = flower;
|
||||||
* @param blockState Java block state of flower pot.
|
|
||||||
* @param position Bedrock position of flower pot.
|
|
||||||
* @return Bedrock tag of flower pot.
|
|
||||||
*/
|
|
||||||
public static NbtMap getTag(GeyserSession session, int blockState, Vector3i position) {
|
|
||||||
NbtMapBuilder tagBuilder = NbtMap.builder()
|
|
||||||
.putInt("x", position.getX())
|
|
||||||
.putInt("y", position.getY())
|
|
||||||
.putInt("z", position.getZ())
|
|
||||||
.putByte("isMovable", (byte) 1)
|
|
||||||
.putString("id", "FlowerPot");
|
|
||||||
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
|
|
||||||
String name = BlockStateValues.getFlowerPotValues().get(blockState);
|
|
||||||
if (name != null) {
|
|
||||||
// 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(name);
|
|
||||||
if (plant != null) {
|
|
||||||
tagBuilder.put("PlantBlock", plant.toBuilder().build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tagBuilder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBlock(int blockState) {
|
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
return isFlowerBlock(blockState);
|
super.updateBlock(session, state, position);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
NbtMap tag = createTag(session, position, state);
|
||||||
public void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
|
|
||||||
NbtMap tag = getTag(session, blockState.javaId(), position);
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState.javaId()));
|
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
|
||||||
updateBlockPacket.setBlockPosition(position);
|
updateBlockPacket.setBlockPosition(position);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
@ -89,4 +59,20 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||||
|
NbtMapBuilder tagBuilder = BlockEntityTranslator.getConstantBedrockTag("FlowerPot", position.getX(), position.getY(), position.getZ())
|
||||||
|
.putByte("isMovable", (byte) 1);
|
||||||
|
// Get the Java name of the plant inside. e.g. minecraft:oak_sapling
|
||||||
|
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());
|
||||||
|
if (plant != null) {
|
||||||
|
tagBuilder.putCompound("PlantBlock", plant.toBuilder().build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tagBuilder.build();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
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.level.block.property.Properties;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
|
||||||
|
public class LecternBlock extends Block {
|
||||||
|
public LecternBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) {
|
||||||
|
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||||
|
if (worldManager.shouldExpectLecternHandled(session)) {
|
||||||
|
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean currentHasBook = state.getValue(Properties.HAS_BOOK);
|
||||||
|
Boolean previousHasBook = worldManager.blockAt(session, position).getValue(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||||
|
if (currentHasBook != previousHasBook) {
|
||||||
|
if (currentHasBook) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
|
public class MovingPistonBlock extends Block {
|
||||||
|
public MovingPistonBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||||
|
// Prevent moving_piston from being placed
|
||||||
|
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.type;
|
||||||
|
|
||||||
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.geysermc.geyser.level.block.Blocks;
|
||||||
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||||
|
|
||||||
|
public class PistonBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||||
|
public PistonBlock(String javaIdentifier, Builder builder) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||||
|
boolean extended = blockState.getValue(Properties.EXTENDED);
|
||||||
|
boolean sticky = blockState.is(Blocks.STICKY_PISTON);
|
||||||
|
return PistonBlockEntity.buildStaticPistonTag(position, extended, sticky);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.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) {
|
||||||
|
super(javaIdentifier, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
// 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());
|
||||||
|
if (skull != null && skull.getBlockDefinition() != null) {
|
||||||
|
definition = skull.getBlockDefinition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.sendBlockUpdatePacket(session, state, definition, position);
|
||||||
|
}
|
||||||
|
}
|
|
@ -424,39 +424,12 @@ public final class BlockRegistryPopulator {
|
||||||
|
|
||||||
// TODO fix this, (no block should have a null hardness)
|
// TODO fix this, (no block should have a null hardness)
|
||||||
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
|
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
|
||||||
JsonNode hardnessNode = entry.getValue().get("block_hardness");
|
|
||||||
if (hardnessNode != null) {
|
|
||||||
builder.hardness(hardnessNode.floatValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand");
|
|
||||||
if (canBreakWithHandNode != null) {
|
|
||||||
builder.canBreakWithHand(canBreakWithHandNode.booleanValue());
|
|
||||||
} else {
|
|
||||||
builder.canBreakWithHand(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode collisionIndexNode = entry.getValue().get("collision_index");
|
|
||||||
if (hardnessNode != null) {
|
|
||||||
builder.collisionIndex(collisionIndexNode.intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode pickItemNode = entry.getValue().get("pick_item");
|
JsonNode pickItemNode = entry.getValue().get("pick_item");
|
||||||
if (pickItemNode != null) {
|
if (pickItemNode != null) {
|
||||||
builder.pickItem(pickItemNode.textValue().intern());
|
builder.pickItem(pickItemNode.textValue().intern());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (javaId.equals("minecraft:obsidian") || javaId.equals("minecraft:crying_obsidian") || javaId.startsWith("minecraft:respawn_anchor") || javaId.startsWith("minecraft:reinforced_deepslate")) {
|
|
||||||
builder.pistonBehavior(PistonBehavior.BLOCK);
|
|
||||||
} else {
|
|
||||||
JsonNode pistonBehaviorNode = entry.getValue().get("piston_behavior");
|
|
||||||
if (pistonBehaviorNode != null) {
|
|
||||||
builder.pistonBehavior(PistonBehavior.getByName(pistonBehaviorNode.textValue()));
|
|
||||||
} else {
|
|
||||||
builder.pistonBehavior(PistonBehavior.NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity");
|
JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity");
|
||||||
if (hasBlockEntityNode != null) {
|
if (hasBlockEntityNode != null) {
|
||||||
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
|
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
|
||||||
|
@ -475,7 +448,6 @@ public final class BlockRegistryPopulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.javaIdentifier(javaId);
|
builder.javaIdentifier(javaId);
|
||||||
builder.javaBlockId(uniqueJavaId);
|
|
||||||
|
|
||||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
||||||
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
||||||
|
@ -547,26 +519,23 @@ public final class BlockRegistryPopulator {
|
||||||
int stateRuntimeId = javaBlockState.javaId();
|
int stateRuntimeId = javaBlockState.javaId();
|
||||||
String pistonBehavior = javaBlockState.pistonBehavior();
|
String pistonBehavior = javaBlockState.pistonBehavior();
|
||||||
BlockMapping blockMapping = BlockMapping.builder()
|
BlockMapping blockMapping = BlockMapping.builder()
|
||||||
.canBreakWithHand(javaBlockState.canBreakWithHand())
|
|
||||||
.pickItem(javaBlockState.pickItem())
|
.pickItem(javaBlockState.pickItem())
|
||||||
.isNonVanilla(true)
|
.isNonVanilla(true)
|
||||||
.javaIdentifier(javaId)
|
.javaIdentifier(javaId)
|
||||||
.javaBlockId(javaBlockState.stateGroupId())
|
|
||||||
.hardness(javaBlockState.blockHardness())
|
|
||||||
.pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior))
|
|
||||||
.isBlockEntity(javaBlockState.hasBlockEntity())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Block.Builder builder = Block.builder()
|
Block.Builder builder = Block.builder()
|
||||||
.destroyTime(javaBlockState.blockHardness());
|
.destroyTime(javaBlockState.blockHardness())
|
||||||
|
.pushReaction(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior));
|
||||||
if (!javaBlockState.canBreakWithHand()) {
|
if (!javaBlockState.canBreakWithHand()) {
|
||||||
builder.requiresCorrectToolForDrops();
|
builder.requiresCorrectToolForDrops();
|
||||||
}
|
}
|
||||||
if (javaBlockState.hasBlockEntity()) {
|
if (javaBlockState.hasBlockEntity()) {
|
||||||
builder.setBlockEntity();
|
builder.setBlockEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
||||||
|
Block block = new Block(cleanJavaIdentifier, builder);
|
||||||
|
|
||||||
String bedrockIdentifier = customBlockState.block().identifier();
|
String bedrockIdentifier = customBlockState.block().identifier();
|
||||||
|
|
||||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||||
|
@ -574,6 +543,7 @@ public final class BlockRegistryPopulator {
|
||||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockRegistries.JAVA_BLOCKS_TO_RENAME.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks
|
||||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
|
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
|
||||||
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
|
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
|
||||||
|
|
||||||
|
|
|
@ -27,32 +27,19 @@ package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
|
||||||
import org.geysermc.geyser.util.BlockUtils;
|
import org.geysermc.geyser.util.BlockUtils;
|
||||||
|
|
||||||
@Builder
|
@Builder
|
||||||
@Value
|
@Value
|
||||||
public class BlockMapping {
|
public class BlockMapping {
|
||||||
public static BlockMapping DEFAULT = BlockMapping.builder().javaIdentifier("minecraft:air").pistonBehavior(PistonBehavior.NORMAL).build();
|
public static BlockMapping DEFAULT = BlockMapping.builder().javaIdentifier("minecraft:air").build();
|
||||||
|
|
||||||
String javaIdentifier;
|
String javaIdentifier;
|
||||||
/**
|
|
||||||
* The block ID shared between all different block states of this block.
|
|
||||||
* NOT the runtime ID!
|
|
||||||
*/
|
|
||||||
int javaBlockId;
|
|
||||||
|
|
||||||
float hardness;
|
|
||||||
boolean canBreakWithHand;
|
boolean canBreakWithHand;
|
||||||
/**
|
|
||||||
* The index of this collision in collision.json
|
|
||||||
*/
|
|
||||||
int collisionIndex;
|
|
||||||
@Nullable String pickItem;
|
@Nullable String pickItem;
|
||||||
|
|
||||||
@NonNull PistonBehavior pistonBehavior;
|
|
||||||
boolean isBlockEntity;
|
boolean isBlockEntity;
|
||||||
boolean isNonVanilla;
|
boolean isNonVanilla;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -78,6 +79,10 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
||||||
return this.javaToBedrockBlocks[javaState];
|
return this.javaToBedrockBlocks[javaState];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GeyserBedrockBlock getBedrockBlock(BlockState javaState) {
|
||||||
|
return this.getBedrockBlock(javaState.javaId());
|
||||||
|
}
|
||||||
|
|
||||||
public GeyserBedrockBlock getVanillaBedrockBlock(int javaState) {
|
public GeyserBedrockBlock getVanillaBedrockBlock(int javaState) {
|
||||||
if (javaState < 0 || javaState >= this.javaToVanillaBedrockBlocks.length) {
|
if (javaState < 0 || javaState >= this.javaToVanillaBedrockBlocks.length) {
|
||||||
return bedrockAir;
|
return bedrockAir;
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.item.type.Item;
|
import org.geysermc.geyser.item.type.Item;
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.tags.BlockTag;
|
import org.geysermc.geyser.session.cache.tags.BlockTag;
|
||||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||||
|
@ -107,17 +106,6 @@ public final class TagCache {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the block tag is present and contains this block mapping's Java ID.
|
|
||||||
*/
|
|
||||||
public boolean is(BlockTag tag, BlockMapping mapping) {
|
|
||||||
IntList values = this.blocks.get(tag);
|
|
||||||
if (values != null) {
|
|
||||||
return values.contains(mapping.getJavaBlockId());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the item tag is present and contains this item stack's Java ID.
|
* @return true if the item tag is present and contains this item stack's Java ID.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -180,7 +180,7 @@ public final class WorldCache {
|
||||||
// This block may be out of sync with the server
|
// This block may be out of sync with the server
|
||||||
// In 1.19.0 Java, you can verify this by trying to mine in spawn protection
|
// In 1.19.0 Java, you can verify this by trying to mine in spawn protection
|
||||||
Vector3i position = entry.getKey();
|
Vector3i position = entry.getKey();
|
||||||
ChunkUtils.updateBlockClientSide(session, session.getGeyser().getWorldManager().getBlockAt(session, position), position);
|
ChunkUtils.updateBlockClientSide(session, session.getGeyser().getWorldManager().blockAt(session, position), position);
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,32 +27,18 @@ package org.geysermc.geyser.translator.level.block.entity;
|
||||||
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pistons are a special case where they are only a block entity on Bedrock.
|
* Implemented only if a block is a block entity in Bedrock and not Java Edition.
|
||||||
*/
|
*/
|
||||||
public class PistonBlockEntityTranslator {
|
public interface BedrockChunkWantsBlockEntityTag extends RequiresBlockState {
|
||||||
/**
|
/**
|
||||||
* Used in ChunkUtils to determine if the block is a piston.
|
* Get the tag of the Bedrock-only block entity. Used during chunk loading.
|
||||||
*
|
* @param position Bedrock position of block.
|
||||||
* @param blockState Java BlockState of block.
|
* @param blockState Java BlockState of block.
|
||||||
* @return if block is a piston or not.
|
* @return Bedrock tag
|
||||||
*/
|
*/
|
||||||
public static boolean isBlock(int blockState) {
|
NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState);
|
||||||
return BlockStateValues.getPistonValues().containsKey(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the Nukkit CompoundTag to send to the client on chunk
|
|
||||||
*
|
|
||||||
* @param blockState Java block state of block.
|
|
||||||
* @param position Bedrock position of piston.
|
|
||||||
* @return Bedrock tag of piston.
|
|
||||||
*/
|
|
||||||
public static NbtMap getTag(int blockState, Vector3i position) {
|
|
||||||
boolean extended = BlockStateValues.getPistonValues().get(blockState);
|
|
||||||
boolean sticky = BlockStateValues.isStickyPiston(blockState);
|
|
||||||
return PistonBlockEntity.buildStaticPistonTag(position, extended, sticky);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,82 +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.translator.level.block.entity;
|
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
|
||||||
import org.cloudburstmc.nbt.NbtList;
|
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
|
||||||
import org.cloudburstmc.nbt.NbtType;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
|
||||||
import org.geysermc.geyser.level.block.type.BlockState;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implemented only if a block is a block entity in Bedrock and not Java Edition.
|
|
||||||
*/
|
|
||||||
public interface BedrockOnlyBlockEntity extends RequiresBlockState {
|
|
||||||
/**
|
|
||||||
* Determines if block is part of class
|
|
||||||
* @param blockState BlockState to be compared
|
|
||||||
* @return true if part of the class
|
|
||||||
*/
|
|
||||||
boolean isBlock(int blockState);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the block on Bedrock Edition.
|
|
||||||
* @param session GeyserConnection.
|
|
||||||
* @param blockState The Java block state.
|
|
||||||
* @param position The Bedrock block position.
|
|
||||||
*/
|
|
||||||
void updateBlock(GeyserSession session, BlockState blockState, Vector3i position);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the tag of the Bedrock-only block entity
|
|
||||||
* @param position Bedrock position of block.
|
|
||||||
* @param blockState Java BlockState of block.
|
|
||||||
* @return Bedrock tag, or null if not a Bedrock-only Block Entity
|
|
||||||
*/
|
|
||||||
static @Nullable NbtMap getTag(GeyserSession session, Vector3i position, int blockState) {
|
|
||||||
if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) {
|
|
||||||
return FlowerPotBlockEntityTranslator.getTag(session, blockState, position);
|
|
||||||
} else if (PistonBlockEntityTranslator.isBlock(blockState)) {
|
|
||||||
return PistonBlockEntityTranslator.getTag(blockState, position);
|
|
||||||
} else if (BlockStateValues.isCauldron(blockState)) {
|
|
||||||
// As of 1.18.30: this is required to make rendering not look weird on chunk load (lava and snow cauldrons look dim)
|
|
||||||
return NbtMap.builder()
|
|
||||||
.putString("id", "Cauldron")
|
|
||||||
.putByte("isMovable", (byte) 0)
|
|
||||||
.putShort("PotionId", (short) -1)
|
|
||||||
.putShort("PotionType", (short) -1)
|
|
||||||
.putList("Items", NbtType.END, NbtList.EMPTY)
|
|
||||||
.putInt("x", position.getX())
|
|
||||||
.putInt("y", position.getY())
|
|
||||||
.putInt("z", position.getZ())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,33 +25,20 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.level.block.entity;
|
package org.geysermc.geyser.translator.level.block.entity;
|
||||||
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.property.ChestType;
|
||||||
import org.geysermc.geyser.level.block.property.Properties;
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
import org.geysermc.geyser.level.block.type.BlockState;
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
import org.geysermc.geyser.level.physics.Direction;
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity
|
* Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockChunkWantsBlockEntityTag
|
||||||
*/
|
*/
|
||||||
@BlockEntity(type = { BlockEntityType.CHEST, BlockEntityType.TRAPPED_CHEST })
|
@BlockEntity(type = { BlockEntityType.CHEST, BlockEntityType.TRAPPED_CHEST })
|
||||||
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity {
|
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator {
|
||||||
@Override
|
|
||||||
public boolean isBlock(int blockState) {
|
|
||||||
return BlockStateValues.getDoubleChestValues().containsKey(blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
|
|
||||||
NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(BlockEntityType.CHEST), position.getX(), position.getY(), position.getZ());
|
|
||||||
translateTag(session, tagBuilder, null, blockState);
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
|
public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) {
|
||||||
|
@ -71,7 +58,7 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl
|
||||||
public static void translateChestValue(NbtMapBuilder builder, BlockState state, int x, int z) {
|
public static void translateChestValue(NbtMapBuilder builder, BlockState state, int x, int z) {
|
||||||
// Calculate the position of the other chest based on the Java block state
|
// Calculate the position of the other chest based on the Java block state
|
||||||
Direction facing = state.getValue(Properties.HORIZONTAL_FACING);
|
Direction facing = state.getValue(Properties.HORIZONTAL_FACING);
|
||||||
boolean isLeft = state.getValue(Properties.CHEST_TYPE).equals("left"); //TODO enum
|
boolean isLeft = state.getValue(Properties.CHEST_TYPE) == ChestType.LEFT;
|
||||||
switch (facing) {
|
switch (facing) {
|
||||||
case EAST -> z = z + (isLeft ? 1 : -1);
|
case EAST -> z = z + (isLeft ? 1 : -1);
|
||||||
case WEST -> z = z + (isLeft ? -1 : 1);
|
case WEST -> z = z + (isLeft ? -1 : 1);
|
||||||
|
|
|
@ -25,7 +25,11 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.level.block.entity;
|
package org.geysermc.geyser.translator.level.block.entity;
|
||||||
|
|
||||||
|
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.Block;
|
||||||
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
|
import org.geysermc.geyser.level.block.type.PistonBlock;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||||
import org.cloudburstmc.math.vector.Vector3d;
|
import org.cloudburstmc.math.vector.Vector3d;
|
||||||
import org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
|
@ -33,9 +37,6 @@ import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
@ -69,7 +70,7 @@ public class PistonBlockEntity {
|
||||||
/**
|
/**
|
||||||
* A map of attached block positions to Java ids.
|
* A map of attached block positions to Java ids.
|
||||||
*/
|
*/
|
||||||
private final Object2IntMap<Vector3i> attachedBlocks = new Object2IntOpenHashMap<>();
|
private final Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectOpenHashMap<>();
|
||||||
/**
|
/**
|
||||||
* A flattened array of the positions of attached blocks, stored in XYZ order.
|
* A flattened array of the positions of attached blocks, stored in XYZ order.
|
||||||
*/
|
*/
|
||||||
|
@ -158,7 +159,7 @@ public class PistonBlockEntity {
|
||||||
BlockEntityUtils.updateBlockEntity(session, buildPistonTag(), position);
|
BlockEntityUtils.updateBlockEntity(session, buildPistonTag(), position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAction(PistonValueType action, Object2IntMap<Vector3i> attachedBlocks) {
|
public void setAction(PistonValueType action, Map<Vector3i, BlockState> attachedBlocks) {
|
||||||
// Don't check if this.action == action, since on some Paper versions BlockPistonRetractEvent is called multiple times
|
// Don't check if this.action == action, since on some Paper versions BlockPistonRetractEvent is called multiple times
|
||||||
// with the first 1-2 events being empty.
|
// with the first 1-2 events being empty.
|
||||||
placeFinalBlocks();
|
placeFinalBlocks();
|
||||||
|
@ -255,13 +256,13 @@ public class PistonBlockEntity {
|
||||||
if (!blocksChecked.add(blockPos)) {
|
if (!blocksChecked.add(blockPos)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockPos);
|
BlockState state = session.getGeyser().getWorldManager().blockAt(session, blockPos);
|
||||||
if (blockId == Block.JAVA_AIR_ID) {
|
if (state.block() == Blocks.AIR) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (BlockStateValues.canPistonMoveBlock(blockId, action == PistonValueType.PUSHING)) {
|
if (BlockStateValues.canPistonMoveBlock(state, action == PistonValueType.PUSHING)) {
|
||||||
attachedBlocks.put(blockPos, blockId);
|
attachedBlocks.put(blockPos, state);
|
||||||
if (BlockStateValues.isBlockSticky(blockId)) {
|
if (BlockStateValues.isBlockSticky(state)) {
|
||||||
// For honey blocks and slime blocks check the blocks adjacent to it
|
// For honey blocks and slime blocks check the blocks adjacent to it
|
||||||
for (Direction direction : Direction.VALUES) {
|
for (Direction direction : Direction.VALUES) {
|
||||||
Vector3i offset = direction.getUnitVector();
|
Vector3i offset = direction.getUnitVector();
|
||||||
|
@ -278,13 +279,13 @@ public class PistonBlockEntity {
|
||||||
if (action == PistonValueType.PULLING && position.add(directionOffset).equals(adjacentPos)) {
|
if (action == PistonValueType.PULLING && position.add(directionOffset).equals(adjacentPos)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int adjacentBlockId = session.getGeyser().getWorldManager().getBlockAt(session, adjacentPos);
|
BlockState adjacentBlockState = session.getGeyser().getWorldManager().blockAt(session, adjacentPos);
|
||||||
if (adjacentBlockId != Block.JAVA_AIR_ID && BlockStateValues.isBlockAttached(blockId, adjacentBlockId) && BlockStateValues.canPistonMoveBlock(adjacentBlockId, false)) {
|
if (adjacentBlockState.block() != Blocks.AIR && BlockStateValues.isBlockAttached(state, adjacentBlockState) && BlockStateValues.canPistonMoveBlock(adjacentBlockState, false)) {
|
||||||
// If it is another slime/honey block we need to check its adjacent blocks
|
// If it is another slime/honey block we need to check its adjacent blocks
|
||||||
if (BlockStateValues.isBlockSticky(adjacentBlockId)) {
|
if (BlockStateValues.isBlockSticky(adjacentBlockState)) {
|
||||||
blocksToCheck.add(adjacentPos);
|
blocksToCheck.add(adjacentPos);
|
||||||
} else {
|
} else {
|
||||||
attachedBlocks.put(adjacentPos, adjacentBlockId);
|
attachedBlocks.put(adjacentPos, adjacentBlockState);
|
||||||
blocksChecked.add(adjacentPos);
|
blocksChecked.add(adjacentPos);
|
||||||
blocksToCheck.add(adjacentPos.add(movement));
|
blocksToCheck.add(adjacentPos.add(movement));
|
||||||
}
|
}
|
||||||
|
@ -293,7 +294,7 @@ public class PistonBlockEntity {
|
||||||
}
|
}
|
||||||
// Check next block in line
|
// Check next block in line
|
||||||
blocksToCheck.add(blockPos.add(movement));
|
blocksToCheck.add(blockPos.add(movement));
|
||||||
} else if (!BlockStateValues.canPistonDestroyBlock(blockId)) {
|
} else if (!BlockStateValues.canPistonDestroyBlock(state)) {
|
||||||
// Block can't be moved or destroyed, so it blocks all block movement
|
// Block can't be moved or destroyed, so it blocks all block movement
|
||||||
moveBlocks = false;
|
moveBlocks = false;
|
||||||
break;
|
break;
|
||||||
|
@ -350,24 +351,24 @@ public class PistonBlockEntity {
|
||||||
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() - shrink.getZ());
|
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() - shrink.getZ());
|
||||||
|
|
||||||
// Resolve collision with the piston head
|
// Resolve collision with the piston head
|
||||||
int pistonHeadId = BlockStateValues.getPistonHead(orientation);
|
BlockState pistonHeadId = BlockState.of(BlockStateValues.getPistonHead(orientation));
|
||||||
pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox);
|
pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox);
|
||||||
|
|
||||||
// Resolve collision with any attached moving blocks, but skip slime blocks
|
// Resolve collision with any attached moving blocks, but skip slime blocks
|
||||||
// This prevents players from being launched by slime blocks covered by other blocks
|
// This prevents players from being launched by slime blocks covered by other blocks
|
||||||
for (Object2IntMap.Entry<Vector3i> entry : attachedBlocks.object2IntEntrySet()) {
|
for (Map.Entry<Vector3i, BlockState> entry : Object2ObjectMaps.fastIterable(attachedBlocks)) {
|
||||||
int blockId = entry.getIntValue();
|
BlockState state = entry.getValue();
|
||||||
if (blockId != BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
if (!state.is(Blocks.SLIME_BLOCK)) {
|
||||||
Vector3d blockPos = entry.getKey().toDouble();
|
Vector3d blockPos = entry.getKey().toDouble();
|
||||||
pushPlayerBlock(blockId, blockPos, blockMovement, playerBoundingBox);
|
pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Resolve collision with slime blocks
|
// Resolve collision with slime blocks
|
||||||
for (Object2IntMap.Entry<Vector3i> entry : attachedBlocks.object2IntEntrySet()) {
|
for (Map.Entry<Vector3i, BlockState> entry : Object2ObjectMaps.fastIterable(attachedBlocks)) {
|
||||||
int blockId = entry.getIntValue();
|
BlockState state = entry.getValue();
|
||||||
if (blockId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||||
Vector3d blockPos = entry.getKey().toDouble();
|
Vector3d blockPos = entry.getKey().toDouble();
|
||||||
pushPlayerBlock(blockId, blockPos, blockMovement, playerBoundingBox);
|
pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +464,7 @@ public class PistonBlockEntity {
|
||||||
return maxIntersection;
|
return maxIntersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushPlayerBlock(int javaId, Vector3d startingPos, double blockMovement, BoundingBox playerBoundingBox) {
|
private void pushPlayerBlock(BlockState state, Vector3d startingPos, double blockMovement, BoundingBox playerBoundingBox) {
|
||||||
PistonCache pistonCache = session.getPistonCache();
|
PistonCache pistonCache = session.getPistonCache();
|
||||||
Vector3d movement = getMovement().toDouble();
|
Vector3d movement = getMovement().toDouble();
|
||||||
// Check if the player collides with the movingBlock block entity
|
// Check if the player collides with the movingBlock block entity
|
||||||
|
@ -471,14 +472,14 @@ public class PistonBlockEntity {
|
||||||
if (SOLID_BOUNDING_BOX.checkIntersection(finalBlockPos, playerBoundingBox)) {
|
if (SOLID_BOUNDING_BOX.checkIntersection(finalBlockPos, playerBoundingBox)) {
|
||||||
pistonCache.setPlayerCollided(true);
|
pistonCache.setPlayerCollided(true);
|
||||||
|
|
||||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||||
pistonCache.setPlayerSlimeCollision(true);
|
pistonCache.setPlayerSlimeCollision(true);
|
||||||
applySlimeBlockMotion(finalBlockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
applySlimeBlockMotion(finalBlockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3d blockPos = startingPos.add(movement.mul(blockMovement));
|
Vector3d blockPos = startingPos.add(movement.mul(blockMovement));
|
||||||
if (javaId == BlockStateValues.JAVA_HONEY_BLOCK_ID && isPlayerAttached(blockPos, playerBoundingBox)) {
|
if (state.is(Blocks.HONEY_BLOCK) && isPlayerAttached(blockPos, playerBoundingBox)) {
|
||||||
pistonCache.setPlayerCollided(true);
|
pistonCache.setPlayerCollided(true);
|
||||||
pistonCache.setPlayerAttachedToHoney(true);
|
pistonCache.setPlayerAttachedToHoney(true);
|
||||||
|
|
||||||
|
@ -486,7 +487,7 @@ public class PistonBlockEntity {
|
||||||
pistonCache.displacePlayer(movement.mul(delta));
|
pistonCache.displacePlayer(movement.mul(delta));
|
||||||
} else {
|
} else {
|
||||||
// Move the player out of collision
|
// Move the player out of collision
|
||||||
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(javaId);
|
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(state.javaId());
|
||||||
if (blockCollision != null) {
|
if (blockCollision != null) {
|
||||||
Vector3d extend = movement.mul(Math.min(1 - blockMovement, 0.5));
|
Vector3d extend = movement.mul(Math.min(1 - blockMovement, 0.5));
|
||||||
Direction movementDirection = orientation;
|
Direction movementDirection = orientation;
|
||||||
|
@ -499,7 +500,7 @@ public class PistonBlockEntity {
|
||||||
pistonCache.setPlayerCollided(true);
|
pistonCache.setPlayerCollided(true);
|
||||||
pistonCache.displacePlayer(movement.mul(intersection + 0.01d));
|
pistonCache.displacePlayer(movement.mul(intersection + 0.01d));
|
||||||
|
|
||||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||||
pistonCache.setPlayerSlimeCollision(true);
|
pistonCache.setPlayerSlimeCollision(true);
|
||||||
applySlimeBlockMotion(blockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
applySlimeBlockMotion(blockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
||||||
}
|
}
|
||||||
|
@ -509,7 +510,7 @@ public class PistonBlockEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockCollision getCollision(Vector3i blockPos) {
|
private BlockCollision getCollision(Vector3i blockPos) {
|
||||||
return BlockUtils.getCollision(getAttachedBlockId(blockPos));
|
return BlockUtils.getCollision(getAttachedBlockId(blockPos).javaId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -533,7 +534,7 @@ public class PistonBlockEntity {
|
||||||
double y = blockPos.getY() + movementVec.getY() * movementProgress;
|
double y = blockPos.getY() + movementVec.getY() * movementProgress;
|
||||||
double z = blockPos.getZ() + movementVec.getZ() * movementProgress;
|
double z = blockPos.getZ() + movementVec.getZ() * movementProgress;
|
||||||
double adjustedMovement = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, movement);
|
double adjustedMovement = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, movement);
|
||||||
if (getAttachedBlockId(blockPos) == BlockStateValues.JAVA_SLIME_BLOCK_ID && adjustedMovement != movement) {
|
if (getAttachedBlockId(blockPos).is(Blocks.SLIME_BLOCK) && adjustedMovement != movement) {
|
||||||
session.getPistonCache().setPlayerSlimeCollision(true);
|
session.getPistonCache().setPlayerSlimeCollision(true);
|
||||||
}
|
}
|
||||||
return adjustedMovement;
|
return adjustedMovement;
|
||||||
|
@ -557,11 +558,11 @@ public class PistonBlockEntity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getAttachedBlockId(Vector3i blockPos) {
|
private BlockState getAttachedBlockId(Vector3i blockPos) {
|
||||||
if (blockPos.equals(getPistonHeadPos())) {
|
if (blockPos.equals(getPistonHeadPos())) {
|
||||||
return BlockStateValues.getPistonHead(orientation);
|
return BlockState.of(BlockStateValues.getPistonHead(orientation));
|
||||||
} else {
|
} else {
|
||||||
return attachedBlocks.getOrDefault(blockPos, Block.JAVA_AIR_ID);
|
return attachedBlocks.getOrDefault(blockPos, BlockState.of(Block.JAVA_AIR_ID)); //FIXME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,12 +583,12 @@ public class PistonBlockEntity {
|
||||||
playerBoundingBox.setSizeX(playerBoundingBox.getSizeX() + 0.5);
|
playerBoundingBox.setSizeX(playerBoundingBox.getSizeX() + 0.5);
|
||||||
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() + 0.5);
|
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() + 0.5);
|
||||||
}
|
}
|
||||||
attachedBlocks.forEach((blockPos, javaId) -> {
|
attachedBlocks.forEach((blockPos, state) -> {
|
||||||
Vector3i newPos = blockPos.add(movement);
|
Vector3i newPos = blockPos.add(movement);
|
||||||
if (SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), playerBoundingBox) ||
|
if (SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), playerBoundingBox) ||
|
||||||
SOLID_BOUNDING_BOX.checkIntersection(newPos.toDouble(), playerBoundingBox)) {
|
SOLID_BOUNDING_BOX.checkIntersection(newPos.toDouble(), playerBoundingBox)) {
|
||||||
session.getPistonCache().setPlayerCollided(true);
|
session.getPistonCache().setPlayerCollided(true);
|
||||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||||
session.getPistonCache().setPlayerSlimeCollision(true);
|
session.getPistonCache().setPlayerSlimeCollision(true);
|
||||||
}
|
}
|
||||||
// Don't place moving blocks that collide with the player
|
// Don't place moving blocks that collide with the player
|
||||||
|
@ -603,7 +604,7 @@ public class PistonBlockEntity {
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
// Update moving block with correct details
|
// Update moving block with correct details
|
||||||
BlockEntityUtils.updateBlockEntity(session, buildMovingBlockTag(newPos, javaId, position), newPos);
|
BlockEntityUtils.updateBlockEntity(session, buildMovingBlockTag(newPos, state, position), newPos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,13 +774,13 @@ public class PistonBlockEntity {
|
||||||
* Create a moving block tag of a block that will be moved by a piston
|
* Create a moving block tag of a block that will be moved by a piston
|
||||||
*
|
*
|
||||||
* @param position The ending position of the block (The location of the movingBlock block entity)
|
* @param position The ending position of the block (The location of the movingBlock block entity)
|
||||||
* @param javaId The Java Id of the block that is moving
|
* @param state The Java BlockState of the block that is moving
|
||||||
* @param pistonPosition The position for the base of the piston that's moving the block
|
* @param pistonPosition The position for the base of the piston that's moving the block
|
||||||
* @return A moving block data tag
|
* @return A moving block data tag
|
||||||
*/
|
*/
|
||||||
private NbtMap buildMovingBlockTag(Vector3i position, int javaId, Vector3i pistonPosition) {
|
private NbtMap buildMovingBlockTag(Vector3i position, BlockState state, Vector3i pistonPosition) {
|
||||||
// Get Bedrock block state data
|
// Get Bedrock block state data
|
||||||
NbtMap movingBlock = session.getBlockMappings().getBedrockBlock(javaId).getState();
|
NbtMap movingBlock = session.getBlockMappings().getBedrockBlock(state).getState();
|
||||||
NbtMapBuilder builder = NbtMap.builder()
|
NbtMapBuilder builder = NbtMap.builder()
|
||||||
.putString("id", "MovingBlock")
|
.putString("id", "MovingBlock")
|
||||||
.putBoolean("expanding", action == PistonValueType.PUSHING)
|
.putBoolean("expanding", action == PistonValueType.PUSHING)
|
||||||
|
@ -791,8 +792,8 @@ public class PistonBlockEntity {
|
||||||
.putInt("x", position.getX())
|
.putInt("x", position.getX())
|
||||||
.putInt("y", position.getY())
|
.putInt("y", position.getY())
|
||||||
.putInt("z", position.getZ());
|
.putInt("z", position.getZ());
|
||||||
if (PistonBlockEntityTranslator.isBlock(javaId)) {
|
if (state.block() instanceof PistonBlock piston) {
|
||||||
builder.putCompound("movingEntity", PistonBlockEntityTranslator.getTag(javaId, position));
|
builder.putCompound("movingEntity", piston.createTag(session, position, state));
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,12 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.protocol.bedrock;
|
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||||
|
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
@ -39,6 +38,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.geyser.util.InventoryUtils;
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||||
|
|
||||||
@Translator(packet = BlockPickRequestPacket.class)
|
@Translator(packet = BlockPickRequestPacket.class)
|
||||||
public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPickRequestPacket> {
|
public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPickRequestPacket> {
|
||||||
|
|
|
@ -25,23 +25,25 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.protocol.java.level;
|
package org.geysermc.geyser.translator.protocol.java.level;
|
||||||
|
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.*;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockEventPacket;
|
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEventPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BlockEventPacket;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
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.physics.Direction;
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.PistonCache;
|
import org.geysermc.geyser.session.cache.PistonCache;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.*;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockEventPacket;
|
||||||
|
|
||||||
@Translator(packet = ClientboundBlockEventPacket.class)
|
@Translator(packet = ClientboundBlockEventPacket.class)
|
||||||
public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> {
|
public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> {
|
||||||
|
@ -63,7 +65,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
||||||
session.sendUpstreamPacket(blockEventPacket);
|
session.sendUpstreamPacket(blockEventPacket);
|
||||||
} else if (value instanceof NoteBlockValue) {
|
} else if (value instanceof NoteBlockValue) {
|
||||||
session.getGeyser().getWorldManager().getBlockAtAsync(session, position).thenAccept(blockState -> {
|
session.getGeyser().getWorldManager().getBlockAtAsync(session, position).thenAccept(blockState -> {
|
||||||
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
|
blockEventPacket.setEventData(BlockState.of(blockState).getValue(Properties.NOTE));
|
||||||
session.sendUpstreamPacket(blockEventPacket);
|
session.sendUpstreamPacket(blockEventPacket);
|
||||||
});
|
});
|
||||||
} else if (value instanceof PistonValue pistonValue) {
|
} else if (value instanceof PistonValue pistonValue) {
|
||||||
|
@ -90,7 +92,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
||||||
}
|
}
|
||||||
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> new PistonBlockEntity(session, pos, direction, true, true));
|
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> new PistonBlockEntity(session, pos, direction, true, true));
|
||||||
if (blockEntity.getAction() != action) {
|
if (blockEntity.getAction() != action) {
|
||||||
blockEntity.setAction(action, Object2IntMaps.emptyMap());
|
blockEntity.setAction(action, Object2ObjectMaps.emptyMap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,11 +112,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
||||||
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
|
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
|
||||||
blockEntityPacket.setBlockPosition(position);
|
blockEntityPacket.setBlockPosition(position);
|
||||||
|
|
||||||
NbtMapBuilder builder = NbtMap.builder();
|
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Bell", position.getX(), position.getY(), position.getZ());
|
||||||
builder.putInt("x", position.getX());
|
|
||||||
builder.putInt("y", position.getY());
|
|
||||||
builder.putInt("z", position.getZ());
|
|
||||||
builder.putString("id", "Bell");
|
|
||||||
int bedrockRingDirection = switch (bellValue.getDirection()) {
|
int bedrockRingDirection = switch (bellValue.getDirection()) {
|
||||||
case SOUTH -> 0;
|
case SOUTH -> 0;
|
||||||
case WEST -> 1;
|
case WEST -> 1;
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import org.geysermc.erosion.util.LecternUtils;
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
import org.geysermc.geyser.level.BedrockDimension;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
|
||||||
import org.geysermc.geyser.level.block.property.Properties;
|
import org.geysermc.geyser.level.block.property.Properties;
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
import org.geysermc.geyser.level.block.type.BlockState;
|
import org.geysermc.geyser.level.block.type.BlockState;
|
||||||
|
@ -53,7 +52,7 @@ import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.SkullBlockEntityTranslator;
|
import org.geysermc.geyser.translator.level.block.entity.SkullBlockEntityTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
|
@ -193,11 +192,12 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockState state = BlockState.of(javaId);
|
||||||
// Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock
|
// Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock
|
||||||
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId) || BlockStateValues.isCauldron(javaId)) {
|
if (state.block() instanceof BedrockChunkWantsBlockEntityTag blockEntity) {
|
||||||
bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
|
bedrockBlockEntities.add(blockEntity.createTag(session,
|
||||||
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
|
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
|
||||||
javaId
|
state
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if block is piston, flower or cauldron to see if we'll need to create additional block entities, as they're only block entities in Bedrock
|
// Check if block is piston, flower or cauldron to see if we'll need to create additional block entities, as they're only block entities in Bedrock
|
||||||
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId) || BlockStateValues.isCauldron(javaId)) {
|
// TODO this needs a performance check when my head is clearer
|
||||||
|
BlockState state = BlockState.of(javaId);
|
||||||
|
if (state.block() instanceof BedrockChunkWantsBlockEntityTag) {
|
||||||
bedrockOnlyBlockEntityIds.set(i);
|
bedrockOnlyBlockEntityIds.set(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,9 +267,10 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
|
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
|
||||||
int paletteId = javaData.get(yzx);
|
int paletteId = javaData.get(yzx);
|
||||||
if (bedrockOnlyBlockEntityIds.get(paletteId)) {
|
if (bedrockOnlyBlockEntityIds.get(paletteId)) {
|
||||||
bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
|
BlockState state = BlockState.of(javaPalette.idToState(paletteId));
|
||||||
|
bedrockBlockEntities.add(((BedrockChunkWantsBlockEntityTag) state.block()).createTag(session,
|
||||||
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
|
Vector3i.from((packet.getX() << 4) + (yzx & 0xF), ((sectionY + yOffset) << 4) + ((yzx >> 8) & 0xF), (packet.getZ() << 4) + ((yzx >> 4) & 0xF)),
|
||||||
javaPalette.idToState(paletteId)
|
state
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,25 +31,13 @@ import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.FlowerPotBlockEntityTranslator;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class BlockEntityUtils {
|
public class BlockEntityUtils {
|
||||||
/**
|
|
||||||
* A list of all block entities that require the Java block state in order to fill out their block entity information.
|
|
||||||
* This list will be smaller with cache sections on as we don't need to double-cache data
|
|
||||||
*/
|
|
||||||
public static final List<BedrockOnlyBlockEntity> BEDROCK_ONLY_BLOCK_ENTITIES = List.of(
|
|
||||||
(BedrockOnlyBlockEntity) Registries.BLOCK_ENTITIES.get().get(BlockEntityType.CHEST),
|
|
||||||
new FlowerPotBlockEntityTranslator()
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains a list of irregular block entity name translations that can't be fit into the regex
|
* Contains a list of irregular block entity name translations that can't be fit into the regex
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,24 +33,19 @@ import lombok.experimental.UtilityClass;
|
||||||
import org.cloudburstmc.math.GenericMath;
|
import org.cloudburstmc.math.GenericMath;
|
||||||
import org.cloudburstmc.math.vector.Vector2i;
|
import org.cloudburstmc.math.vector.Vector2i;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
import org.geysermc.geyser.level.BedrockDimension;
|
||||||
import org.geysermc.geyser.level.JavaDimension;
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
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.BlockState;
|
||||||
import org.geysermc.geyser.level.chunk.BlockStorage;
|
import org.geysermc.geyser.level.chunk.BlockStorage;
|
||||||
import org.geysermc.geyser.level.chunk.GeyserChunkSection;
|
import org.geysermc.geyser.level.chunk.GeyserChunkSection;
|
||||||
import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.SkullCache;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class ChunkUtils {
|
public class ChunkUtils {
|
||||||
|
@ -119,18 +114,30 @@ public class ChunkUtils {
|
||||||
* @param position the position of the block
|
* @param position the position of the block
|
||||||
*/
|
*/
|
||||||
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
|
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
|
||||||
updateBlockClientSide(session, blockState, position);
|
updateBlockClientSide(session, BlockState.of(blockState), position);
|
||||||
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a block update to the Bedrock client. If the platform does not have an integrated world manager, this also
|
||||||
|
* adds that block to the cache.
|
||||||
|
* @param session the Bedrock session to send/register the block to
|
||||||
|
* @param blockState the Java block state of the block
|
||||||
|
* @param position the position of the block
|
||||||
|
*/
|
||||||
|
public static void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
|
||||||
|
updateBlockClientSide(session, blockState, position);
|
||||||
|
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState.javaId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a block, but client-side only.
|
* Updates a block, but client-side only.
|
||||||
*/
|
*/
|
||||||
public static void updateBlockClientSide(GeyserSession session, int blockState, Vector3i position) {
|
public static void updateBlockClientSide(GeyserSession session, BlockState blockState, Vector3i position) {
|
||||||
// Checks for item frames so they aren't tripped up and removed
|
// Checks for item frames so they aren't tripped up and removed
|
||||||
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
||||||
if (itemFrameEntity != null) {
|
if (itemFrameEntity != null) {
|
||||||
if (blockState == Block.JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it
|
if (blockState.is(Blocks.AIR)) { // Item frame is still present and no block overrides that; refresh it
|
||||||
itemFrameEntity.updateBlock(true);
|
itemFrameEntity.updateBlock(true);
|
||||||
// Still update the chunk cache with the new block if updateBlock is called
|
// Still update the chunk cache with the new block if updateBlock is called
|
||||||
return;
|
return;
|
||||||
|
@ -138,91 +145,7 @@ public class ChunkUtils {
|
||||||
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(blockState);
|
blockState.block().updateBlock(session, blockState, position);
|
||||||
|
|
||||||
int skullVariant = BlockStateValues.getSkullVariant(blockState);
|
|
||||||
if (skullVariant == -1) {
|
|
||||||
// Skull is gone
|
|
||||||
session.getSkullCache().removeSkull(position);
|
|
||||||
} else if (skullVariant == 3) {
|
|
||||||
// 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, blockState);
|
|
||||||
if (skull != null && skull.getBlockDefinition() != null) {
|
|
||||||
definition = skull.getBlockDefinition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent moving_piston from being placed
|
|
||||||
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
|
||||||
if (!BlockStateValues.isMovingPiston(blockState)) {
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
|
||||||
updateBlockPacket.setDataLayer(0);
|
|
||||||
updateBlockPacket.setBlockPosition(position);
|
|
||||||
updateBlockPacket.setDefinition(definition);
|
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
|
|
||||||
UpdateBlockPacket waterPacket = new UpdateBlockPacket();
|
|
||||||
waterPacket.setDataLayer(1);
|
|
||||||
waterPacket.setBlockPosition(position);
|
|
||||||
if (BlockRegistries.WATERLOGGED.get().get(blockState)) {
|
|
||||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockWater());
|
|
||||||
} else {
|
|
||||||
waterPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
|
||||||
}
|
|
||||||
session.sendUpstreamPacket(waterPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extended collision boxes for custom blocks
|
|
||||||
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
|
||||||
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
|
||||||
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(blockState);
|
|
||||||
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
|
||||||
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
|
||||||
if (belowBedrockExtendedCollisionDefinition != null && blockState == Block.JAVA_AIR_ID) {
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
|
||||||
updateBlockPacket.setDataLayer(0);
|
|
||||||
updateBlockPacket.setBlockPosition(position);
|
|
||||||
updateBlockPacket.setDefinition(belowBedrockExtendedCollisionDefinition);
|
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == Block.JAVA_AIR_ID) {
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
|
||||||
updateBlockPacket.setDataLayer(0);
|
|
||||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
|
||||||
updateBlockPacket.setDefinition(aboveBedrockExtendedCollisionDefinition);
|
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
} else if (aboveBlock == Block.JAVA_AIR_ID) {
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
|
||||||
updateBlockPacket.setDataLayer(0);
|
|
||||||
updateBlockPacket.setBlockPosition(position.add(0, 1, 0));
|
|
||||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir());
|
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStateValues.getLecternBookStates().handleBlockChange(session, blockState, position);
|
|
||||||
|
|
||||||
// Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet
|
|
||||||
// needs to be sent
|
|
||||||
for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityUtils.BEDROCK_ONLY_BLOCK_ENTITIES) {
|
|
||||||
if (bedrockOnlyBlockEntity.isBlock(blockState)) {
|
|
||||||
// Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks
|
|
||||||
bedrockOnlyBlockEntity.updateBlock(session, BlockState.of(blockState), position); //TODO blockState
|
|
||||||
break; //No block will be a part of two classes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BlockStateValues.isUpperDoor(blockState)) {
|
|
||||||
// Update the lower door block as Bedrock client doesn't like door to be closed from the top
|
|
||||||
// See https://github.com/GeyserMC/Geyser/issues/4358
|
|
||||||
Vector3i belowDoorPosition = position.sub(0, 1, 0);
|
|
||||||
int belowDoorBlockState = session.getGeyser().getWorldManager().getBlockAt(session, belowDoorPosition.getX(), belowDoorPosition.getY(), belowDoorPosition.getZ());
|
|
||||||
updateBlock(session, belowDoorBlockState, belowDoorPosition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendEmptyChunk(GeyserSession session, int chunkX, int chunkZ, boolean forceUpdate) {
|
public static void sendEmptyChunk(GeyserSession session, int chunkX, int chunkZ, boolean forceUpdate) {
|
||||||
|
|
Loading…
Reference in a new issue