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;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
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.geyser.level.block.BlockStateValues;
|
||||
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.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -153,9 +155,10 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
|||
var stream = packet.getAttachedBlocks()
|
||||
.object2IntEntrySet()
|
||||
.stream()
|
||||
.filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend));
|
||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
||||
stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue()));
|
||||
.map(entry -> Pair.of(entry.getKey(), BlockState.of(entry.getIntValue())))
|
||||
.filter(pair -> BlockStateValues.canPistonMoveBlock(pair.value(), isExtend));
|
||||
Object2ObjectMap<Vector3i, BlockState> attachedBlocks = new Object2ObjectOpenHashMap<>();
|
||||
stream.forEach(pair -> attachedBlocks.put(pair.key(), pair.value()));
|
||||
|
||||
session.executeInEventLoop(() -> {
|
||||
PistonCache pistonCache = session.getPistonCache();
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
|
||||
|
@ -58,6 +59,14 @@ import java.util.function.Function;
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
@ -29,12 +29,14 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.PistonBlock;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
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.FixedInt2IntMap;
|
||||
import org.geysermc.geyser.util.collection.LecternHasBookMap;
|
||||
|
@ -229,28 +231,6 @@ public final class BlockStateValues {
|
|||
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
|
||||
*/
|
||||
|
@ -341,18 +321,6 @@ public final class BlockStateValues {
|
|||
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,
|
||||
* minecraft:sticky_piston, and minecraft:moving_piston.
|
||||
|
@ -371,8 +339,9 @@ public final class BlockStateValues {
|
|||
* @param state The block state
|
||||
* @return True if the block sticks to adjacent blocks
|
||||
*/
|
||||
public static boolean isBlockSticky(int state) {
|
||||
return state == JAVA_SLIME_BLOCK_ID || state == JAVA_HONEY_BLOCK_ID;
|
||||
public static boolean isBlockSticky(BlockState state) {
|
||||
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
|
||||
* @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 bSticky = isBlockSticky(stateB);
|
||||
if (aSticky && bSticky) {
|
||||
// Only matching sticky blocks are attached together
|
||||
// Honey + Honey & Slime + Slime
|
||||
return stateA == stateB;
|
||||
return stateA.block() == stateB.block();
|
||||
}
|
||||
return aSticky || bSticky;
|
||||
}
|
||||
|
@ -397,27 +366,30 @@ public final class BlockStateValues {
|
|||
* @param state The block state of the block
|
||||
* @return true if a piston can break the block
|
||||
*/
|
||||
public static boolean canPistonDestroyBlock(int state) {
|
||||
return BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getPistonBehavior() == PistonBehavior.DESTROY;
|
||||
public static boolean canPistonDestroyBlock(BlockState state) {
|
||||
return state.block().pushReaction() == PistonBehavior.DESTROY;
|
||||
}
|
||||
|
||||
public static boolean canPistonMoveBlock(int javaId, boolean isPushing) {
|
||||
if (javaId == Block.JAVA_AIR_ID) {
|
||||
public static boolean canPistonMoveBlock(BlockState state, boolean isPushing) {
|
||||
Block block = state.block();
|
||||
if (block == Blocks.AIR) {
|
||||
return true;
|
||||
}
|
||||
// Pistons can only be moved if they aren't extended
|
||||
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) {
|
||||
if (block == Blocks.OBSIDIAN || block == Blocks.CRYING_OBSIDIAN || block == Blocks.RESPAWN_ANCHOR || block == Blocks.REINFORCED_DEEPSLATE) { // Hardcoded as of 1.20.5
|
||||
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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This gives a integer rotation that Bedrock can use.
|
||||
|
@ -464,17 +425,6 @@ public final class BlockStateValues {
|
|||
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.
|
||||
*
|
||||
|
|
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> ROTATION_16 = Property.create("rotation");
|
||||
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> DOOR_HINGE = Property.create("hinge");
|
||||
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.objects.Reference2ObjectArrayMap;
|
||||
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.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -44,6 +51,7 @@ public class Block {
|
|||
private final boolean requiresCorrectToolForDrops;
|
||||
private final boolean hasBlockEntity;
|
||||
private final float destroyTime;
|
||||
private final @NonNull PistonBehavior pushReaction;
|
||||
private int javaId = -1;
|
||||
|
||||
public Block(String javaIdentifier, Builder builder) {
|
||||
|
@ -51,9 +59,76 @@ public class Block {
|
|||
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
|
||||
this.hasBlockEntity = builder.hasBlockEntity;
|
||||
this.destroyTime = builder.destroyTime;
|
||||
this.pushReaction = builder.pushReaction;
|
||||
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() {
|
||||
return javaIdentifier;
|
||||
}
|
||||
|
@ -70,6 +145,11 @@ public class Block {
|
|||
return destroyTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PistonBehavior pushReaction() {
|
||||
return this.pushReaction;
|
||||
}
|
||||
|
||||
public int javaId() {
|
||||
return javaId;
|
||||
}
|
||||
|
@ -97,6 +177,7 @@ public class Block {
|
|||
private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>();
|
||||
private boolean requiresCorrectToolForDrops = false;
|
||||
private boolean hasBlockEntity = false;
|
||||
private PistonBehavior pushReaction = PistonBehavior.NORMAL;
|
||||
private float destroyTime;
|
||||
|
||||
/**
|
||||
|
@ -143,6 +224,11 @@ public class Block {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder pushReaction(PistonBehavior pushReaction) {
|
||||
this.pushReaction = pushReaction;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void build(Block block) {
|
||||
if (states.isEmpty()) {
|
||||
BlockRegistries.BLOCK_STATES.get().add(new BlockState(block, BlockRegistries.BLOCK_STATES.get().size()));
|
||||
|
|
|
@ -58,6 +58,10 @@ public final class BlockState {
|
|||
return javaId;
|
||||
}
|
||||
|
||||
public boolean is(Block block) {
|
||||
return this.block == block;
|
||||
}
|
||||
|
||||
public static BlockState of(int 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
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,65 +23,35 @@
|
|||
* @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.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
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;
|
||||
|
||||
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
||||
/**
|
||||
* @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 class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
private final Block flower;
|
||||
|
||||
/**
|
||||
* Get the Nukkit CompoundTag of the flower pot.
|
||||
*
|
||||
* @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();
|
||||
public FlowerPotBlock(String javaIdentifier, Block flower, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.flower = flower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlock(int blockState) {
|
||||
return isFlowerBlock(blockState);
|
||||
}
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
super.updateBlock(session, state, position);
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState blockState, Vector3i position) {
|
||||
NbtMap tag = getTag(session, blockState.javaId(), position);
|
||||
NbtMap tag = createTag(session, position, state);
|
||||
BlockEntityUtils.updateBlockEntity(session, tag, position);
|
||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||
updateBlockPacket.setDataLayer(0);
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState.javaId()));
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
|
||||
updateBlockPacket.setBlockPosition(position);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
|
@ -89,4 +59,20 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
|
|||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
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)
|
||||
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");
|
||||
if (pickItemNode != null) {
|
||||
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");
|
||||
if (hasBlockEntityNode != null) {
|
||||
builder.isBlockEntity(hasBlockEntityNode.booleanValue());
|
||||
|
@ -475,7 +448,6 @@ public final class BlockRegistryPopulator {
|
|||
}
|
||||
|
||||
builder.javaIdentifier(javaId);
|
||||
builder.javaBlockId(uniqueJavaId);
|
||||
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
||||
|
@ -547,26 +519,23 @@ public final class BlockRegistryPopulator {
|
|||
int stateRuntimeId = javaBlockState.javaId();
|
||||
String pistonBehavior = javaBlockState.pistonBehavior();
|
||||
BlockMapping blockMapping = BlockMapping.builder()
|
||||
.canBreakWithHand(javaBlockState.canBreakWithHand())
|
||||
.pickItem(javaBlockState.pickItem())
|
||||
.isNonVanilla(true)
|
||||
.javaIdentifier(javaId)
|
||||
.javaBlockId(javaBlockState.stateGroupId())
|
||||
.hardness(javaBlockState.blockHardness())
|
||||
.pistonBehavior(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior))
|
||||
.isBlockEntity(javaBlockState.hasBlockEntity())
|
||||
.build();
|
||||
|
||||
Block.Builder builder = Block.builder()
|
||||
.destroyTime(javaBlockState.blockHardness());
|
||||
.destroyTime(javaBlockState.blockHardness())
|
||||
.pushReaction(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior));
|
||||
if (!javaBlockState.canBreakWithHand()) {
|
||||
builder.requiresCorrectToolForDrops();
|
||||
}
|
||||
if (javaBlockState.hasBlockEntity()) {
|
||||
builder.setBlockEntity();
|
||||
}
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
||||
Block block = new Block(cleanJavaIdentifier, builder);
|
||||
|
||||
String bedrockIdentifier = customBlockState.block().identifier();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
|
@ -574,6 +543,7 @@ public final class BlockRegistryPopulator {
|
|||
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_BLOCKS.register(stateRuntimeId, blockMapping);
|
||||
|
||||
|
|
|
@ -27,32 +27,19 @@ package org.geysermc.geyser.registry.type;
|
|||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
|
||||
@Builder
|
||||
@Value
|
||||
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;
|
||||
/**
|
||||
* The block ID shared between all different block states of this block.
|
||||
* NOT the runtime ID!
|
||||
*/
|
||||
int javaBlockId;
|
||||
|
||||
float hardness;
|
||||
boolean canBreakWithHand;
|
||||
/**
|
||||
* The index of this collision in collision.json
|
||||
*/
|
||||
int collisionIndex;
|
||||
@Nullable String pickItem;
|
||||
|
||||
@NonNull PistonBehavior pistonBehavior;
|
||||
boolean isBlockEntity;
|
||||
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.common.DefinitionRegistry;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -78,6 +79,10 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
|||
return this.javaToBedrockBlocks[javaState];
|
||||
}
|
||||
|
||||
public GeyserBedrockBlock getBedrockBlock(BlockState javaState) {
|
||||
return this.getBedrockBlock(javaState.javaId());
|
||||
}
|
||||
|
||||
public GeyserBedrockBlock getVanillaBedrockBlock(int javaState) {
|
||||
if (javaState < 0 || javaState >= this.javaToVanillaBedrockBlocks.length) {
|
||||
return bedrockAir;
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.geysermc.geyser.GeyserLogger;
|
|||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
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.cache.tags.BlockTag;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
|
@ -107,17 +106,6 @@ public final class TagCache {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -180,7 +180,7 @@ public final class WorldCache {
|
|||
// 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
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,32 +27,18 @@ package org.geysermc.geyser.translator.level.block.entity;
|
|||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
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.
|
||||
* @return if block is a piston or not.
|
||||
* @return Bedrock tag
|
||||
*/
|
||||
public static boolean isBlock(int 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);
|
||||
}
|
||||
NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState);
|
||||
}
|
|
@ -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;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
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.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
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 })
|
||||
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity {
|
||||
@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);
|
||||
}
|
||||
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
// Calculate the position of the other chest based on the Java block state
|
||||
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) {
|
||||
case EAST -> z = z + (isLeft ? 1 : -1);
|
||||
case WEST -> z = z + (isLeft ? -1 : 1);
|
||||
|
|
|
@ -25,7 +25,11 @@
|
|||
|
||||
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.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.PistonBlock;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
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.NbtMapBuilder;
|
||||
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 org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
|
@ -69,7 +70,7 @@ public class PistonBlockEntity {
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -158,7 +159,7 @@ public class PistonBlockEntity {
|
|||
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
|
||||
// with the first 1-2 events being empty.
|
||||
placeFinalBlocks();
|
||||
|
@ -255,13 +256,13 @@ public class PistonBlockEntity {
|
|||
if (!blocksChecked.add(blockPos)) {
|
||||
continue;
|
||||
}
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockPos);
|
||||
if (blockId == Block.JAVA_AIR_ID) {
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, blockPos);
|
||||
if (state.block() == Blocks.AIR) {
|
||||
continue;
|
||||
}
|
||||
if (BlockStateValues.canPistonMoveBlock(blockId, action == PistonValueType.PUSHING)) {
|
||||
attachedBlocks.put(blockPos, blockId);
|
||||
if (BlockStateValues.isBlockSticky(blockId)) {
|
||||
if (BlockStateValues.canPistonMoveBlock(state, action == PistonValueType.PUSHING)) {
|
||||
attachedBlocks.put(blockPos, state);
|
||||
if (BlockStateValues.isBlockSticky(state)) {
|
||||
// For honey blocks and slime blocks check the blocks adjacent to it
|
||||
for (Direction direction : Direction.VALUES) {
|
||||
Vector3i offset = direction.getUnitVector();
|
||||
|
@ -278,13 +279,13 @@ public class PistonBlockEntity {
|
|||
if (action == PistonValueType.PULLING && position.add(directionOffset).equals(adjacentPos)) {
|
||||
continue;
|
||||
}
|
||||
int adjacentBlockId = session.getGeyser().getWorldManager().getBlockAt(session, adjacentPos);
|
||||
if (adjacentBlockId != Block.JAVA_AIR_ID && BlockStateValues.isBlockAttached(blockId, adjacentBlockId) && BlockStateValues.canPistonMoveBlock(adjacentBlockId, false)) {
|
||||
BlockState adjacentBlockState = session.getGeyser().getWorldManager().blockAt(session, adjacentPos);
|
||||
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 (BlockStateValues.isBlockSticky(adjacentBlockId)) {
|
||||
if (BlockStateValues.isBlockSticky(adjacentBlockState)) {
|
||||
blocksToCheck.add(adjacentPos);
|
||||
} else {
|
||||
attachedBlocks.put(adjacentPos, adjacentBlockId);
|
||||
attachedBlocks.put(adjacentPos, adjacentBlockState);
|
||||
blocksChecked.add(adjacentPos);
|
||||
blocksToCheck.add(adjacentPos.add(movement));
|
||||
}
|
||||
|
@ -293,7 +294,7 @@ public class PistonBlockEntity {
|
|||
}
|
||||
// Check next block in line
|
||||
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
|
||||
moveBlocks = false;
|
||||
break;
|
||||
|
@ -350,24 +351,24 @@ public class PistonBlockEntity {
|
|||
playerBoundingBox.setSizeZ(playerBoundingBox.getSizeZ() - shrink.getZ());
|
||||
|
||||
// Resolve collision with the piston head
|
||||
int pistonHeadId = BlockStateValues.getPistonHead(orientation);
|
||||
BlockState pistonHeadId = BlockState.of(BlockStateValues.getPistonHead(orientation));
|
||||
pushPlayerBlock(pistonHeadId, getPistonHeadPos().toDouble(), blockMovement, playerBoundingBox);
|
||||
|
||||
// Resolve collision with any attached moving blocks, but skip slime blocks
|
||||
// This prevents players from being launched by slime blocks covered by other blocks
|
||||
for (Object2IntMap.Entry<Vector3i> entry : attachedBlocks.object2IntEntrySet()) {
|
||||
int blockId = entry.getIntValue();
|
||||
if (blockId != BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
||||
for (Map.Entry<Vector3i, BlockState> entry : Object2ObjectMaps.fastIterable(attachedBlocks)) {
|
||||
BlockState state = entry.getValue();
|
||||
if (!state.is(Blocks.SLIME_BLOCK)) {
|
||||
Vector3d blockPos = entry.getKey().toDouble();
|
||||
pushPlayerBlock(blockId, blockPos, blockMovement, playerBoundingBox);
|
||||
pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox);
|
||||
}
|
||||
}
|
||||
// Resolve collision with slime blocks
|
||||
for (Object2IntMap.Entry<Vector3i> entry : attachedBlocks.object2IntEntrySet()) {
|
||||
int blockId = entry.getIntValue();
|
||||
if (blockId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
||||
for (Map.Entry<Vector3i, BlockState> entry : Object2ObjectMaps.fastIterable(attachedBlocks)) {
|
||||
BlockState state = entry.getValue();
|
||||
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||
Vector3d blockPos = entry.getKey().toDouble();
|
||||
pushPlayerBlock(blockId, blockPos, blockMovement, playerBoundingBox);
|
||||
pushPlayerBlock(state, blockPos, blockMovement, playerBoundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,7 +464,7 @@ public class PistonBlockEntity {
|
|||
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();
|
||||
Vector3d movement = getMovement().toDouble();
|
||||
// Check if the player collides with the movingBlock block entity
|
||||
|
@ -471,14 +472,14 @@ public class PistonBlockEntity {
|
|||
if (SOLID_BOUNDING_BOX.checkIntersection(finalBlockPos, playerBoundingBox)) {
|
||||
pistonCache.setPlayerCollided(true);
|
||||
|
||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
||||
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||
pistonCache.setPlayerSlimeCollision(true);
|
||||
applySlimeBlockMotion(finalBlockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
||||
}
|
||||
}
|
||||
|
||||
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.setPlayerAttachedToHoney(true);
|
||||
|
||||
|
@ -486,7 +487,7 @@ public class PistonBlockEntity {
|
|||
pistonCache.displacePlayer(movement.mul(delta));
|
||||
} else {
|
||||
// Move the player out of collision
|
||||
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(javaId);
|
||||
BlockCollision blockCollision = BlockRegistries.COLLISIONS.get(state.javaId());
|
||||
if (blockCollision != null) {
|
||||
Vector3d extend = movement.mul(Math.min(1 - blockMovement, 0.5));
|
||||
Direction movementDirection = orientation;
|
||||
|
@ -499,7 +500,7 @@ public class PistonBlockEntity {
|
|||
pistonCache.setPlayerCollided(true);
|
||||
pistonCache.displacePlayer(movement.mul(intersection + 0.01d));
|
||||
|
||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
||||
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||
pistonCache.setPlayerSlimeCollision(true);
|
||||
applySlimeBlockMotion(blockPos, Vector3d.from(playerBoundingBox.getMiddleX(), playerBoundingBox.getMiddleY(), playerBoundingBox.getMiddleZ()));
|
||||
}
|
||||
|
@ -509,7 +510,7 @@ public class PistonBlockEntity {
|
|||
}
|
||||
|
||||
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 z = blockPos.getZ() + movementVec.getZ() * movementProgress;
|
||||
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);
|
||||
}
|
||||
return adjustedMovement;
|
||||
|
@ -557,11 +558,11 @@ public class PistonBlockEntity {
|
|||
return false;
|
||||
}
|
||||
|
||||
private int getAttachedBlockId(Vector3i blockPos) {
|
||||
private BlockState getAttachedBlockId(Vector3i blockPos) {
|
||||
if (blockPos.equals(getPistonHeadPos())) {
|
||||
return BlockStateValues.getPistonHead(orientation);
|
||||
return BlockState.of(BlockStateValues.getPistonHead(orientation));
|
||||
} 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.setSizeZ(playerBoundingBox.getSizeZ() + 0.5);
|
||||
}
|
||||
attachedBlocks.forEach((blockPos, javaId) -> {
|
||||
attachedBlocks.forEach((blockPos, state) -> {
|
||||
Vector3i newPos = blockPos.add(movement);
|
||||
if (SOLID_BOUNDING_BOX.checkIntersection(blockPos.toDouble(), playerBoundingBox) ||
|
||||
SOLID_BOUNDING_BOX.checkIntersection(newPos.toDouble(), playerBoundingBox)) {
|
||||
session.getPistonCache().setPlayerCollided(true);
|
||||
if (javaId == BlockStateValues.JAVA_SLIME_BLOCK_ID) {
|
||||
if (state.is(Blocks.SLIME_BLOCK)) {
|
||||
session.getPistonCache().setPlayerSlimeCollision(true);
|
||||
}
|
||||
// Don't place moving blocks that collide with the player
|
||||
|
@ -603,7 +604,7 @@ public class PistonBlockEntity {
|
|||
updateBlockPacket.setDataLayer(0);
|
||||
session.sendUpstreamPacket(updateBlockPacket);
|
||||
// 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
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
NbtMap movingBlock = session.getBlockMappings().getBedrockBlock(javaId).getState();
|
||||
NbtMap movingBlock = session.getBlockMappings().getBedrockBlock(state).getState();
|
||||
NbtMapBuilder builder = NbtMap.builder()
|
||||
.putString("id", "MovingBlock")
|
||||
.putBoolean("expanding", action == PistonValueType.PUSHING)
|
||||
|
@ -791,8 +792,8 @@ public class PistonBlockEntity {
|
|||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ());
|
||||
if (PistonBlockEntityTranslator.isBlock(javaId)) {
|
||||
builder.putCompound("movingEntity", PistonBlockEntityTranslator.getTag(javaId, position));
|
||||
if (state.block() instanceof PistonBlock piston) {
|
||||
builder.putCompound("movingEntity", piston.createTag(session, position, state));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
@ -25,13 +25,12 @@
|
|||
|
||||
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.protocol.bedrock.packet.BlockPickRequestPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
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.type.BlockMapping;
|
||||
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.Translator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
@Translator(packet = BlockPickRequestPacket.class)
|
||||
public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPickRequestPacket> {
|
||||
|
|
|
@ -25,23 +25,25 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java.level;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.*;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockEventPacket;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||
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.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.session.GeyserSession;
|
||||
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.protocol.PacketTranslator;
|
||||
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)
|
||||
public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> {
|
||||
|
@ -63,7 +65,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
session.sendUpstreamPacket(blockEventPacket);
|
||||
} else if (value instanceof NoteBlockValue) {
|
||||
session.getGeyser().getWorldManager().getBlockAtAsync(session, position).thenAccept(blockState -> {
|
||||
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
|
||||
blockEventPacket.setEventData(BlockState.of(blockState).getValue(Properties.NOTE));
|
||||
session.sendUpstreamPacket(blockEventPacket);
|
||||
});
|
||||
} 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));
|
||||
if (blockEntity.getAction() != action) {
|
||||
blockEntity.setAction(action, Object2IntMaps.emptyMap());
|
||||
blockEntity.setAction(action, Object2ObjectMaps.emptyMap());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -110,11 +112,7 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
|
||||
blockEntityPacket.setBlockPosition(position);
|
||||
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putInt("x", position.getX());
|
||||
builder.putInt("y", position.getY());
|
||||
builder.putInt("z", position.getZ());
|
||||
builder.putString("id", "Bell");
|
||||
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Bell", position.getX(), position.getY(), position.getZ());
|
||||
int bedrockRingDirection = switch (bellValue.getDirection()) {
|
||||
case SOUTH -> 0;
|
||||
case WEST -> 1;
|
||||
|
|
|
@ -41,7 +41,6 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
|
|||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
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.type.Block;
|
||||
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.session.GeyserSession;
|
||||
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.SkullBlockEntityTranslator;
|
||||
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
|
||||
if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId) || BlockStateValues.isCauldron(javaId)) {
|
||||
bedrockBlockEntities.add(BedrockOnlyBlockEntity.getTag(session,
|
||||
if (state.block() instanceof BedrockChunkWantsBlockEntityTag blockEntity) {
|
||||
bedrockBlockEntities.add(blockEntity.createTag(session,
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -265,9 +267,10 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
|||
for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) {
|
||||
int paletteId = javaData.get(yzx);
|
||||
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)),
|
||||
javaPalette.idToState(paletteId)
|
||||
state
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,25 +31,13 @@ import org.cloudburstmc.nbt.NbtMap;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
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.FlowerPotBlockEntityTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -33,24 +33,19 @@ import lombok.experimental.UtilityClass;
|
|||
import org.cloudburstmc.math.GenericMath;
|
||||
import org.cloudburstmc.math.vector.Vector2i;
|
||||
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.NetworkChunkPublisherUpdatePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.chunk.BlockStorage;
|
||||
import org.geysermc.geyser.level.chunk.GeyserChunkSection;
|
||||
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.cache.SkullCache;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
||||
|
||||
@UtilityClass
|
||||
public class ChunkUtils {
|
||||
|
@ -119,18 +114,30 @@ public class ChunkUtils {
|
|||
* @param position the position of the block
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
||||
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);
|
||||
// Still update the chunk cache with the new block if updateBlock is called
|
||||
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
|
||||
}
|
||||
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(blockState);
|
||||
|
||||
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);
|
||||
}
|
||||
blockState.block().updateBlock(session, blockState, position);
|
||||
}
|
||||
|
||||
public static void sendEmptyChunk(GeyserSession session, int chunkX, int chunkZ, boolean forceUpdate) {
|
||||
|
|
Loading…
Reference in a new issue