mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/configurate
This commit is contained in:
commit
10bf4eeb4e
220 changed files with 9912 additions and 41664 deletions
|
@ -27,7 +27,6 @@ dependencies {
|
|||
implementation(libs.websocket)
|
||||
|
||||
api(libs.bundles.protocol)
|
||||
implementation(libs.blockstateupdater)
|
||||
|
||||
api(libs.mcauthlib)
|
||||
api(libs.mcprotocollib) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
|||
import org.geysermc.api.Geyser;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.erosion.packet.Packets;
|
||||
import org.geysermc.floodgate.crypto.AesCipher;
|
||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||
import org.geysermc.floodgate.crypto.Base64Topping;
|
||||
|
@ -79,6 +80,7 @@ import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
import org.geysermc.geyser.session.SessionManager;
|
||||
import org.geysermc.geyser.session.cache.RegistryCache;
|
||||
import org.geysermc.geyser.skin.FloodgateSkinUploader;
|
||||
import org.geysermc.geyser.skin.ProvidedSkins;
|
||||
import org.geysermc.geyser.skin.SkinProvider;
|
||||
|
@ -217,6 +219,8 @@ public class GeyserImpl implements GeyserApi {
|
|||
Registries.init();
|
||||
BlockRegistries.init();
|
||||
|
||||
RegistryCache.init();
|
||||
|
||||
/* Initialize translators */
|
||||
EntityDefinitions.init();
|
||||
MessageTranslator.init();
|
||||
|
@ -389,7 +393,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
||||
|
||||
//Packets.initGeyser();
|
||||
Packets.initGeyser();
|
||||
|
||||
if (Epoll.isAvailable()) {
|
||||
this.erosionUnixListener = new UnixSocketClientListener();
|
||||
|
@ -772,6 +776,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
return Integer.parseInt(BUILD_NUMBER);
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ public class Entity implements GeyserEntity {
|
|||
|
||||
this.valid = false;
|
||||
|
||||
this.propertyManager = new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
this.propertyManager = definition.registeredProperties() == null ? null : new GeyserEntityPropertyManager(definition.registeredProperties());
|
||||
|
||||
setPosition(position);
|
||||
setAirSupply(getMaxAir());
|
||||
|
@ -364,7 +364,7 @@ public class Entity implements GeyserEntity {
|
|||
return;
|
||||
}
|
||||
|
||||
if (propertyManager.hasProperties()) {
|
||||
if (propertyManager != null && propertyManager.hasProperties()) {
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(geyserId);
|
||||
propertyManager.applyIntProperties(entityDataPacket.getProperties().getIntProperties());
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.geysermc.erosion.util.BlockPositionIterator;
|
|||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
|
@ -162,7 +163,7 @@ public class FishingHookEntity extends ThrowableEntity {
|
|||
*/
|
||||
protected boolean isInAir() {
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
||||
return block == BlockStateValues.JAVA_AIR_ID;
|
||||
return block == Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,14 +25,16 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -51,7 +53,8 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity {
|
|||
|
||||
@Override
|
||||
public void updateDefaultBlockMetadata() {
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID));
|
||||
BlockState furnace = Blocks.FURNACE.defaultBlockState().withValue(Properties.LIT, hasFuel);
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(furnace));
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AddItemEntityPacket;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
|
@ -137,7 +138,7 @@ public class ItemEntity extends ThrowableEntity {
|
|||
protected float getDrag() {
|
||||
if (isOnGround()) {
|
||||
Vector3i groundBlockPos = position.toInt().down(1);
|
||||
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos);
|
||||
BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, groundBlockPos);
|
||||
return BlockStateValues.getSlipperiness(blockState) * 0.98f;
|
||||
}
|
||||
return 0.98f;
|
||||
|
|
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.entity.type;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -41,7 +41,7 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity {
|
|||
|
||||
@Override
|
||||
public void updateDefaultBlockMetadata() {
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(BlockStateValues.JAVA_SPAWNER_ID));
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_BLOCK_STATE, session.getBlockMappings().getBedrockBlock(Blocks.SPAWNER.defaultBlockState()));
|
||||
dirtyMetadata.put(EntityDataTypes.DISPLAY_OFFSET, 6);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BedBlock;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData;
|
||||
|
@ -119,28 +120,31 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
|||
}
|
||||
|
||||
// The bed block
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition);
|
||||
String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, bedPosition);
|
||||
|
||||
// Set the correct position offset and rotation when sleeping
|
||||
int bedRotation = 0;
|
||||
float xOffset = 0;
|
||||
float zOffset = 0;
|
||||
if (fullIdentifier.contains("facing=south")) {
|
||||
// bed is facing south
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=east")) {
|
||||
// bed is facing east
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
} else if (fullIdentifier.contains("facing=west")) {
|
||||
// bed is facing west
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
} else if (fullIdentifier.contains("facing=north")) {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
if (state.block() instanceof BedBlock) {
|
||||
switch (state.getValue(Properties.HORIZONTAL_FACING)) {
|
||||
case SOUTH -> {
|
||||
bedRotation = 180;
|
||||
zOffset = -.5f;
|
||||
}
|
||||
case EAST -> {
|
||||
bedRotation = 90;
|
||||
xOffset = -.5f;
|
||||
}
|
||||
case WEST -> {
|
||||
bedRotation = 270;
|
||||
xOffset = .5f;
|
||||
}
|
||||
case NORTH -> {
|
||||
// rotation does not change because north is 0
|
||||
zOffset = .5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setYaw(yaw);
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
|||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
|
@ -60,16 +61,14 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
*/
|
||||
@Getter
|
||||
protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>();
|
||||
/**
|
||||
* Whether to check for updated speed after all entity metadata has been processed
|
||||
*/
|
||||
private boolean refreshSpeed = false;
|
||||
/**
|
||||
* Used in PlayerInputTranslator for movement checks.
|
||||
*/
|
||||
@Getter
|
||||
private boolean isRidingInFront;
|
||||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
|
@ -120,9 +119,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
// TODO: proper fix, BDS somehow does it? https://paste.gg/p/anonymous/3adfb7612f1540be80fa03a2281f93dc (BDS 1.20.13)
|
||||
if (!this.session.getGameMode().equals(GameMode.SPECTATOR)) {
|
||||
super.setFlags(entityMetadata);
|
||||
session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING));
|
||||
}
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,7 +147,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
public void setPose(Pose pose) {
|
||||
super.setPose(pose);
|
||||
session.setPose(pose);
|
||||
refreshSpeed = true;
|
||||
}
|
||||
|
||||
public float getMaxHealth() {
|
||||
|
@ -167,7 +163,13 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
|
||||
@Override
|
||||
protected void setAirSupply(int amount) {
|
||||
if (amount == getMaxAir()) {
|
||||
// Seemingly required to be sent as of Bedrock 1.21. Otherwise, bubbles will appear as empty
|
||||
// Also, this changes how the air bubble graphics/sounds are presented. Breathing on means sound effects and
|
||||
// the bubbles visually pop
|
||||
setFlag(EntityFlag.BREATHING, amount >= this.lastAirSupply);
|
||||
this.lastAirSupply = amount;
|
||||
|
||||
if (amount == getMaxAir() && GameProtocol.isPre1_21_0(session)) {
|
||||
super.setAirSupply(0); // Hide the bubble counter from the UI for the player
|
||||
} else {
|
||||
super.setAirSupply(amount);
|
||||
|
@ -199,21 +201,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata() {
|
||||
super.updateBedrockMetadata();
|
||||
if (refreshSpeed) {
|
||||
AttributeData speedAttribute = session.adjustSpeed();
|
||||
if (speedAttribute != null) {
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(geyserId);
|
||||
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
refreshSpeed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
|
||||
|
@ -226,17 +213,6 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
@Override
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
|
||||
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
|
||||
|
||||
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_MOVEMENT_SPEED) {
|
||||
session.setOriginalSpeedAttribute(attributeData.getValue());
|
||||
AttributeData speedAttribute = session.adjustSpeed();
|
||||
if (speedAttribute != null) {
|
||||
// Overwrite the attribute with our own
|
||||
this.attributes.put(type, speedAttribute);
|
||||
return speedAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
this.attributes.put(type, attributeData);
|
||||
return attributeData;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
|
|||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.WallSkullBlock;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.geyser.skin.SkullSkinManager;
|
||||
|
@ -137,20 +140,19 @@ public class SkullPlayerEntity extends PlayerEntity {
|
|||
float z = skull.getPosition().getZ() + .5f;
|
||||
float rotation;
|
||||
|
||||
int blockState = skull.getBlockState();
|
||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||
if (floorRotation == -1) {
|
||||
// Wall skull
|
||||
BlockState blockState = skull.getBlockState();
|
||||
if (blockState.block() instanceof WallSkullBlock) {
|
||||
y += 0.25f;
|
||||
rotation = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||
switch ((int) rotation) {
|
||||
case 180 -> z += 0.24f; // North
|
||||
case 0 -> z -= 0.24f; // South
|
||||
case 90 -> x += 0.24f; // West
|
||||
case 270 -> x -= 0.24f; // East
|
||||
Direction direction = blockState.getValue(Properties.HORIZONTAL_FACING);
|
||||
rotation = WallSkullBlock.getDegrees(direction);
|
||||
switch (direction) {
|
||||
case NORTH -> z += 0.24f;
|
||||
case SOUTH -> z -= 0.24f;
|
||||
case WEST -> x += 0.24f;
|
||||
case EAST -> x -= 0.24f;
|
||||
}
|
||||
} else {
|
||||
rotation = (180f + (floorRotation * 22.5f)) % 360;
|
||||
rotation = (180f + (blockState.getValue(Properties.ROTATION_16) * 22.5f)) % 360;
|
||||
}
|
||||
|
||||
moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true);
|
||||
|
|
|
@ -25,14 +25,13 @@
|
|||
|
||||
package org.geysermc.geyser.erosion;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
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.Object2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
|
@ -43,21 +42,18 @@ import org.geysermc.erosion.packet.ErosionPacketHandler;
|
|||
import org.geysermc.erosion.packet.ErosionPacketSender;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBatchBlockIdPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockEntityPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockIdPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockLookupFailPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockPlacePacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundPickBlockPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.GeyserboundPistonEventPacket;
|
||||
import org.geysermc.erosion.packet.geyserbound.*;
|
||||
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.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.value.PistonValueType;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -71,7 +67,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
|||
@Setter
|
||||
private CompletableFuture<int[]> pendingBatchLookup = null;
|
||||
@Setter
|
||||
private CompletableFuture<DataComponents> pickBlockLookup = null;
|
||||
private CompletableFuture<Int2ObjectMap<byte[]>> pickBlockLookup = null;
|
||||
|
||||
private final AtomicInteger nextTransactionId = new AtomicInteger(1);
|
||||
|
||||
|
@ -127,7 +123,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
|||
}
|
||||
CompletableFuture<Integer> future = this.asyncPendingLookups.remove(transactionId);
|
||||
if (future != null) {
|
||||
future.complete(BlockStateValues.JAVA_AIR_ID);
|
||||
future.complete(Block.JAVA_AIR_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,28 +137,29 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
|
|||
placeBlockSoundPacket.setIdentifier(":");
|
||||
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||
session.setLastBlockPlacePosition(null);
|
||||
session.setLastBlockPlacedId(null);
|
||||
session.setLastBlockPlaced(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePickBlock(GeyserboundPickBlockPacket packet) {
|
||||
if (this.pickBlockLookup != null) {
|
||||
//this.pickBlockLookup.complete(packet.getTag()); // TODO 1.20.5
|
||||
this.pickBlockLookup.complete(packet.getComponents());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
|
||||
Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId());
|
||||
Direction orientation = BlockState.of(packet.getBlockId()).getValue(Properties.FACING);
|
||||
Vector3i position = packet.getPos();
|
||||
boolean isExtend = packet.isExtend();
|
||||
|
||||
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 Object2ObjectArrayMap<>();
|
||||
stream.forEach(pair -> attachedBlocks.put(pair.key(), pair.value()));
|
||||
|
||||
session.executeInEventLoop(() -> {
|
||||
PistonCache pistonCache = session.getPistonCache();
|
||||
|
|
|
@ -32,24 +32,50 @@ import org.cloudburstmc.math.vector.Vector2f;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudElement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudVisibility;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetHudPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserCameraData implements CameraData {
|
||||
private static final HudElement[] HUD_ELEMENT_VALUES = HudElement.values();
|
||||
private static final Set<HudElement> ALL_HUD_ELEMENTS = Set.of(HUD_ELEMENT_VALUES);
|
||||
|
||||
/**
|
||||
* An array of elements to hide when the player is in spectator mode.
|
||||
* Helps with tidying up the GUI; Java-style.
|
||||
*/
|
||||
private static final GuiElement[] SPECTATOR_HIDDEN_ELEMENTS = {
|
||||
GuiElement.AIR_BUBBLES_BAR,
|
||||
GuiElement.ARMOR,
|
||||
GuiElement.HEALTH,
|
||||
GuiElement.FOOD_BAR,
|
||||
GuiElement.PROGRESS_BAR,
|
||||
GuiElement.TOOL_TIPS
|
||||
};
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
/**
|
||||
* All fog effects that are currently applied to the client.
|
||||
*/
|
||||
|
@ -57,6 +83,14 @@ public class GeyserCameraData implements CameraData {
|
|||
|
||||
private final Set<UUID> cameraLockOwners = new HashSet<>();
|
||||
|
||||
/**
|
||||
* All currently hidden HUD elements
|
||||
*/
|
||||
private final Set<GuiElement> hiddenHudElements = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
public GeyserCameraData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
@ -223,4 +257,67 @@ public class GeyserCameraData implements CameraData {
|
|||
public boolean isCameraLocked() {
|
||||
return !this.cameraLockOwners.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideElement(GuiElement... elements) {
|
||||
Objects.requireNonNull(elements);
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.HIDE);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.add(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetElement(GuiElement... elements) {
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.RESET);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
if (elements != null && elements.length != 0) {
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.remove(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
} else {
|
||||
this.hiddenHudElements.clear();
|
||||
elementSet.addAll(ALL_HUD_ELEMENTS);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHudElementHidden(@NonNull GuiElement element) {
|
||||
Objects.requireNonNull(element);
|
||||
return this.hiddenHudElements.contains(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<GuiElement> hiddenElements() {
|
||||
return Collections.unmodifiableSet(hiddenHudElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with hiding hud elements while in spectator.
|
||||
*
|
||||
* @param currentlySpectator whether the player is currently in spectator mode
|
||||
* @param newGameMode the new GameMode to switch to
|
||||
*/
|
||||
public void handleGameModeChange(boolean currentlySpectator, GameMode newGameMode) {
|
||||
if (newGameMode == GameMode.SPECTATOR) {
|
||||
if (!currentlySpectator) {
|
||||
hideElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
} else {
|
||||
if (currentlySpectator) {
|
||||
resetElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
@ -83,9 +84,9 @@ public class Container extends Inventory {
|
|||
* Will be overwritten for droppers.
|
||||
*
|
||||
* @param usingRealBlock whether this container is using a real container or not
|
||||
* @param javaBlockId the Java block string of the block, if real
|
||||
* @param block the Java block, if real
|
||||
*/
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
isUsingRealBlock = usingRealBlock;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
package org.geysermc.geyser.inventory;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
@ -44,10 +46,10 @@ public class Generic3X3Container extends Container {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) {
|
||||
super.setUsingRealBlock(usingRealBlock, javaBlockId);
|
||||
public void setUsingRealBlock(boolean usingRealBlock, Block block) {
|
||||
super.setUsingRealBlock(usingRealBlock, block);
|
||||
if (usingRealBlock) {
|
||||
isDropper = javaBlockId.startsWith("minecraft:dropper");
|
||||
isDropper = block == Blocks.DROPPER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class GeyserItemStack {
|
|||
return of(javaId, amount, null);
|
||||
}
|
||||
|
||||
public static @NonNull GeyserItemStack of(int javaId, int amount, DataComponents components) {
|
||||
public static @NonNull GeyserItemStack of(int javaId, int amount, @Nullable DataComponents components) {
|
||||
return new GeyserItemStack(javaId, amount, components);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,11 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.inventory.Container;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.LecternContainer;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -55,20 +55,24 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
/**
|
||||
* The default Java block ID to translate as a fake block
|
||||
*/
|
||||
private final int defaultJavaBlockState;
|
||||
private final BlockState defaultJavaBlockState;
|
||||
private final ContainerType containerType;
|
||||
private final Set<String> validBlocks;
|
||||
private final Set<Block> validBlocks;
|
||||
|
||||
public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, String... validBlocks) {
|
||||
this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaBlockIdentifier);
|
||||
public BlockInventoryHolder(Block defaultJavaBlock, ContainerType containerType, Block... validBlocks) {
|
||||
this(defaultJavaBlock.defaultBlockState(), containerType, validBlocks);
|
||||
}
|
||||
|
||||
public BlockInventoryHolder(BlockState defaultJavaBlockState, ContainerType containerType, Block... validBlocks) {
|
||||
this.defaultJavaBlockState = defaultJavaBlockState;
|
||||
this.containerType = containerType;
|
||||
if (validBlocks != null) {
|
||||
Set<String> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||
Set<Block> validBlocksTemp = new HashSet<>(validBlocks.length + 1);
|
||||
Collections.addAll(validBlocksTemp, validBlocks);
|
||||
validBlocksTemp.add(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
validBlocksTemp.add(defaultJavaBlockState.block());
|
||||
this.validBlocks = Set.copyOf(validBlocksTemp);
|
||||
} else {
|
||||
this.validBlocks = Collections.singleton(BlockUtils.getCleanIdentifier(javaBlockIdentifier));
|
||||
this.validBlocks = Collections.singleton(defaultJavaBlockState.block());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,14 +84,13 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
if (checkInteractionPosition(session)) {
|
||||
// Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid
|
||||
// and the bedrock block is vanilla
|
||||
int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(javaBlockId)) {
|
||||
String[] javaBlockString = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaBlockId, BlockMapping.DEFAULT).getJavaIdentifier().split("\\[");
|
||||
if (isValidBlock(javaBlockString)) {
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition());
|
||||
if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) {
|
||||
if (isValidBlock(state)) {
|
||||
// We can safely use this block
|
||||
inventory.setHolderPosition(session.getLastInteractionBlockPosition());
|
||||
((Container) inventory).setUsingRealBlock(true, javaBlockString[0]);
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, javaBlockId);
|
||||
((Container) inventory).setUsingRealBlock(true, state.block());
|
||||
setCustomName(session, session.getLastInteractionBlockPosition(), inventory, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -125,11 +128,11 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
/**
|
||||
* @return true if this Java block ID can be used for player inventory.
|
||||
*/
|
||||
protected boolean isValidBlock(String[] javaBlockString) {
|
||||
return this.validBlocks.contains(javaBlockString[0]);
|
||||
protected boolean isValidBlock(BlockState blockState) {
|
||||
return this.validBlocks.contains(blockState.block());
|
||||
}
|
||||
|
||||
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, int javaBlockState) {
|
||||
protected void setCustomName(GeyserSession session, Vector3i position, Inventory inventory, BlockState javaBlockState) {
|
||||
NbtMap tag = NbtMap.builder()
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
|
@ -160,6 +163,7 @@ public class BlockInventoryHolder extends InventoryHolder {
|
|||
ContainerClosePacket packet = new ContainerClosePacket();
|
||||
packet.setId((byte) inventory.getBedrockId());
|
||||
packet.setServerInitiated(true);
|
||||
packet.setType(ContainerType.CONTAINER);
|
||||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -35,6 +35,7 @@ import org.cloudburstmc.nbt.NbtMap;
|
|||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.inventory.item.BannerPattern;
|
||||
import org.geysermc.geyser.inventory.item.DyeColor;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
|
@ -199,8 +200,8 @@ public class BannerItem extends BlockItem {
|
|||
return null;
|
||||
}
|
||||
|
||||
public BannerItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public BannerItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,11 +25,26 @@
|
|||
|
||||
package org.geysermc.geyser.item.type;
|
||||
|
||||
/**
|
||||
* TODO needed?
|
||||
*/
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
|
||||
public class BlockItem extends Item {
|
||||
public BlockItem(String javaIdentifier, Builder builder) {
|
||||
public BlockItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(block.javaIdentifier().value(), builder);
|
||||
|
||||
// Ensure this item can be looked up by its block(s)
|
||||
registerBlock(block, this);
|
||||
for (Block otherBlock : otherBlocks) {
|
||||
registerBlock(otherBlock, this);
|
||||
}
|
||||
}
|
||||
|
||||
// Use this constructor if the item name is not the same as its primary block
|
||||
public BlockItem(String javaIdentifier, Builder builder, Block block, Block... otherBlocks) {
|
||||
super(javaIdentifier, builder);
|
||||
|
||||
registerBlock(block, this);
|
||||
for (Block otherBlock : otherBlocks) {
|
||||
registerBlock(otherBlock, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
|
@ -38,8 +39,8 @@ import java.util.List;
|
|||
|
||||
public class DecoratedPotItem extends BlockItem {
|
||||
|
||||
public DecoratedPotItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public DecoratedPotItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.item.Enchantment;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -49,20 +50,12 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Item {
|
||||
/**
|
||||
* This is a map from Java-only enchantments to their translation keys so that we can
|
||||
* map these enchantments to Bedrock clients, since they don't actually exist there.
|
||||
*/
|
||||
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
|
||||
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
|
||||
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
|
||||
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
|
||||
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
|
||||
|
||||
private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
|
||||
private final String javaIdentifier;
|
||||
private int javaId = -1;
|
||||
private final int stackSize;
|
||||
|
@ -233,6 +226,16 @@ public class Item {
|
|||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a map from Java-only enchantments to their translation keys so that we can
|
||||
* map these enchantments to Bedrock clients, since they don't actually exist there.
|
||||
*/
|
||||
private static final Map<Enchantment.JavaEnchantment, String> ENCHANTMENT_TRANSLATION_KEYS = Map.of(
|
||||
Enchantment.JavaEnchantment.SWEEPING_EDGE, "enchantment.minecraft.sweeping",
|
||||
Enchantment.JavaEnchantment.DENSITY, "enchantment.minecraft.density",
|
||||
Enchantment.JavaEnchantment.BREACH, "enchantment.minecraft.breach",
|
||||
Enchantment.JavaEnchantment.WIND_BURST, "enchantment.minecraft.wind_burst");
|
||||
|
||||
protected final @Nullable NbtMap remapEnchantment(GeyserSession session, int enchantId, int level, BedrockItemBuilder builder) {
|
||||
// TODO verify
|
||||
// TODO streamline Enchantment process
|
||||
|
@ -281,6 +284,18 @@ public class Item {
|
|||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the block associated with this item, or air if nothing
|
||||
*/
|
||||
@NonNull
|
||||
public static Item byBlock(Block block) {
|
||||
return BLOCK_TO_ITEM.getOrDefault(block, Items.AIR);
|
||||
}
|
||||
|
||||
protected static void registerBlock(Block block, Item item) {
|
||||
BLOCK_TO_ITEM.put(block, item);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type;
|
|||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
|
@ -34,9 +35,9 @@ import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
|||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
public class PlayerHeadItem extends Item {
|
||||
public PlayerHeadItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public class PlayerHeadItem extends BlockItem {
|
||||
public PlayerHeadItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.cloudburstmc.nbt.NbtMap;
|
|||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
|
@ -42,8 +43,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
public class ShulkerBoxItem extends BlockItem {
|
||||
public ShulkerBoxItem(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
public ShulkerBoxItem(Builder builder, Block block, Block... otherBlocks) {
|
||||
super(builder, block, otherBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -41,16 +41,18 @@ public class TippedArrowItem extends ArrowItem {
|
|||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId());
|
||||
if (tippedArrowPotion != null) {
|
||||
return ItemData.builder()
|
||||
.definition(mapping.getBedrockDefinition())
|
||||
.damage(tippedArrowPotion.getBedrockId())
|
||||
.count(count);
|
||||
if (components != null) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
TippedArrowPotion tippedArrowPotion = TippedArrowPotion.of(potionContents.getPotionId());
|
||||
if (tippedArrowPotion != null) {
|
||||
return ItemData.builder()
|
||||
.definition(mapping.getBedrockDefinition())
|
||||
.damage(tippedArrowPotion.getBedrockId())
|
||||
.count(count);
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
|
||||
}
|
||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
|
||||
}
|
||||
return super.translateToBedrock(count, components, mapping, mappings);
|
||||
}
|
||||
|
|
|
@ -25,24 +25,20 @@
|
|||
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.erosion.packet.backendbound.*;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
|
||||
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.erosion.util.LecternUtils;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GeyserWorldManager extends WorldManager {
|
||||
|
@ -91,51 +87,6 @@ public class GeyserWorldManager extends WorldManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler == null) {
|
||||
// No-op - don't send any additional information other than what the chunk has already sent
|
||||
return;
|
||||
}
|
||||
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
|
||||
//noinspection ForLoopReplaceableByForEach - avoid constructing iterator
|
||||
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||
vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
|
||||
}
|
||||
erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||
if (erosionHandler != null) {
|
||||
erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Without direct server access, we can't get lectern information on-the-fly.
|
||||
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
|
||||
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
|
||||
lecternTag.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:written_book")
|
||||
.putCompound("tag", NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build())
|
||||
.build());
|
||||
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
|
||||
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||
return session.getErosionHandler().isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameRule(GeyserSession session, String name, Object value) {
|
||||
super.setGameRule(session, name, value);
|
||||
|
@ -179,9 +130,9 @@ public class GeyserWorldManager extends WorldManager {
|
|||
if (erosionHandler == null) {
|
||||
return super.getPickItemComponents(session, x, y, z, addNbtData);
|
||||
}
|
||||
CompletableFuture<DataComponents> future = new CompletableFuture<>();
|
||||
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
|
||||
erosionHandler.setPickBlockLookup(future);
|
||||
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
|
||||
return future;
|
||||
return future.thenApply(RAW_TRANSFORMER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,19 +25,28 @@
|
|||
|
||||
package org.geysermc.geyser.level;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
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;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemCodecHelper;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Class that manages or retrieves various information
|
||||
|
@ -48,6 +57,16 @@ import java.util.concurrent.CompletableFuture;
|
|||
*/
|
||||
public abstract class WorldManager {
|
||||
|
||||
@NonNull
|
||||
public final BlockState blockAt(GeyserSession session, Vector3i vector) {
|
||||
return this.blockAt(session, vector.getX(), vector.getY(), vector.getZ());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BlockState blockAt(GeyserSession session, int x, int y, int z) {
|
||||
return BlockState.of(this.getBlockAt(session, x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Java block state at the specified location
|
||||
*
|
||||
|
@ -97,40 +116,6 @@ public abstract class WorldManager {
|
|||
*/
|
||||
public abstract boolean hasOwnChunkCache();
|
||||
|
||||
/**
|
||||
* Sigh. <br>
|
||||
*
|
||||
* So, on Java Edition, the lectern is an inventory. Java opens it and gets the contents of the book there.
|
||||
* On Bedrock, the lectern contents are part of the block entity tag. Therefore, Bedrock expects to have the contents
|
||||
* of the lectern ready and present in the world. If the contents are not there, it takes at least two clicks for the
|
||||
* lectern to update the tag and then present itself. <br>
|
||||
*
|
||||
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
|
||||
* tag.
|
||||
* <p>
|
||||
* Note that the lectern data may be sent asynchronously.
|
||||
*
|
||||
* @param session the session of the player
|
||||
* @param x the x coordinate of the lectern
|
||||
* @param y the y coordinate of the lectern
|
||||
* @param z the z coordinate of the lectern
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
|
||||
*
|
||||
* @param x chunk x
|
||||
* @param z chunk z
|
||||
* @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
|
||||
*/
|
||||
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
|
||||
|
||||
/**
|
||||
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
||||
*/
|
||||
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
|
||||
|
||||
/**
|
||||
* Updates a gamerule value on the Java server
|
||||
*
|
||||
|
@ -223,4 +208,20 @@ public abstract class WorldManager {
|
|||
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
protected static final Function<Int2ObjectMap<byte[]>, DataComponents> RAW_TRANSFORMER = map -> {
|
||||
try {
|
||||
Map<DataComponentType<?>, DataComponent<?, ?>> components = new HashMap<>();
|
||||
Int2ObjectMaps.fastForEach(map, entry -> {
|
||||
DataComponentType type = DataComponentType.from(entry.getIntKey());
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(entry.getValue());
|
||||
DataComponent value = type.readDataComponent(ItemCodecHelper.INSTANCE, buf);
|
||||
components.put(type, value);
|
||||
});
|
||||
return new DataComponents(components);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,363 +25,19 @@
|
|||
|
||||
package org.geysermc.geyser.level.block;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
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.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;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Used for block entities if the Java block state contains Bedrock block information.
|
||||
*/
|
||||
public final class BlockStateValues {
|
||||
private static final IntSet ALL_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap();
|
||||
private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap();
|
||||
private static final Int2ObjectMap<DoubleChestValue> DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<String> FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet HORIZONTAL_FACING_JIGSAWS = new IntOpenHashSet();
|
||||
private static final LecternHasBookMap LECTERN_BOOK_STATES = new LecternHasBookMap();
|
||||
private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet();
|
||||
private static final Int2IntMap NOTEBLOCK_PITCHES = new FixedInt2IntMap();
|
||||
private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap();
|
||||
private static final IntSet STICKY_PISTONS = new IntOpenHashSet();
|
||||
private static final Object2IntMap<Direction> PISTON_HEADS = new Object2IntOpenHashMap<>();
|
||||
private static final Int2ObjectMap<Direction> PISTON_ORIENTATION = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet();
|
||||
private static final IntSet MOVING_PISTONS = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_VARIANTS = new FixedInt2ByteMap();
|
||||
private static final IntSet SKULL_POWERED = new IntOpenHashSet();
|
||||
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
|
||||
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap();
|
||||
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
|
||||
private static final IntSet UPPER_DOORS = new IntOpenHashSet();
|
||||
|
||||
public static final int JAVA_AIR_ID = 0;
|
||||
|
||||
public static int JAVA_COBWEB_ID;
|
||||
public static int JAVA_FURNACE_ID;
|
||||
public static int JAVA_FURNACE_LIT_ID;
|
||||
public static int JAVA_HONEY_BLOCK_ID;
|
||||
public static int JAVA_SLIME_BLOCK_ID;
|
||||
public static int JAVA_SPAWNER_ID;
|
||||
public static int JAVA_WATER_ID;
|
||||
|
||||
public static final int NUM_WATER_LEVELS = 9;
|
||||
|
||||
/**
|
||||
* Determines if the block state contains Bedrock block information
|
||||
*
|
||||
* @param javaId The Java Identifier of the block
|
||||
* @param javaBlockState the Java Block State of the block
|
||||
* @param blockData JsonNode of info about the block from blocks.json
|
||||
*/
|
||||
public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) {
|
||||
JsonNode bannerColor = blockData.get("banner_color");
|
||||
if (bannerColor != null) {
|
||||
BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue());
|
||||
return; // There will never be a banner color and a skull variant
|
||||
}
|
||||
|
||||
JsonNode bedColor = blockData.get("bed_color");
|
||||
if (bedColor != null) {
|
||||
BED_COLORS.put(javaBlockState, (byte) bedColor.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode bedrockStates = blockData.get("bedrock_states");
|
||||
if (bedrockStates != null) {
|
||||
JsonNode brushedProgress = bedrockStates.get("brushed_progress");
|
||||
if (brushedProgress != null) {
|
||||
BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (javaId.contains("command_block")) {
|
||||
COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockData.get("double_chest_position") != null) {
|
||||
boolean isX = (blockData.get("x") != null);
|
||||
boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) ||
|
||||
(blockData.get("z") != null && blockData.get("z").asBoolean()));
|
||||
boolean isLeft = (blockData.get("double_chest_position").asText().contains("left"));
|
||||
DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft));
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:potted_") || javaId.equals("minecraft:flower_pot")) {
|
||||
String name = javaId.replace("potted_", "");
|
||||
if (name.contains("azalea")) {
|
||||
// Exception to the rule
|
||||
name = name.replace("_bush", "");
|
||||
}
|
||||
FLOWER_POT_VALUES.put(javaBlockState, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:lectern")) {
|
||||
LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true"));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode notePitch = blockData.get("note_pitch");
|
||||
if (notePitch != null) {
|
||||
NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston
|
||||
if (javaId.startsWith("minecraft:moving_piston")) {
|
||||
MOVING_PISTONS.add(javaBlockState);
|
||||
} else {
|
||||
PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true"));
|
||||
}
|
||||
if (javaId.contains("sticky")) {
|
||||
STICKY_PISTONS.add(javaBlockState);
|
||||
}
|
||||
PISTON_ORIENTATION.put(javaBlockState, getBlockDirection(javaId));
|
||||
return;
|
||||
} else if (javaId.startsWith("minecraft:piston_head")) {
|
||||
ALL_PISTON_HEADS.add(javaBlockState);
|
||||
if (javaId.contains("short=false")) {
|
||||
PISTON_HEADS.put(getBlockDirection(javaId), javaBlockState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode skullVariation = blockData.get("variation");
|
||||
if (skullVariation != null) {
|
||||
SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue());
|
||||
}
|
||||
|
||||
JsonNode skullRotation = blockData.get("skull_rotation");
|
||||
if (skullRotation != null) {
|
||||
SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue());
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:dragon_head[") || javaId.startsWith("minecraft:piglin_head[")
|
||||
|| javaId.startsWith("minecraft:dragon_wall_head[") || javaId.startsWith("minecraft:piglin_wall_head[")) {
|
||||
if (javaId.contains("powered=true")) {
|
||||
SKULL_POWERED.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
if (javaId.contains("wall_skull") || javaId.contains("wall_head")) {
|
||||
String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7, javaId.lastIndexOf("powered=") - 1);
|
||||
int rotation = switch (direction) {
|
||||
case "north" -> 180;
|
||||
case "west" -> 90;
|
||||
case "east" -> 270;
|
||||
default -> 0; // Also south
|
||||
};
|
||||
SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation);
|
||||
}
|
||||
|
||||
JsonNode shulkerDirection = blockData.get("shulker_direction");
|
||||
if (shulkerDirection != null) {
|
||||
BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) {
|
||||
String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1);
|
||||
int level = Integer.parseInt(strLevel);
|
||||
WATER_LEVEL.put(javaBlockState, level);
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.startsWith("minecraft:jigsaw[orientation=")) {
|
||||
String blockStateData = javaId.substring(javaId.indexOf("orientation=") + "orientation=".length(), javaId.lastIndexOf('_'));
|
||||
Direction direction = Direction.valueOf(blockStateData.toUpperCase(Locale.ROOT));
|
||||
if (direction.isHorizontal()) {
|
||||
HORIZONTAL_FACING_JIGSAWS.add(javaBlockState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (javaId.contains("cauldron")) {
|
||||
ALL_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
if (javaId.contains("_cauldron") && !javaId.contains("water_")) {
|
||||
NON_WATER_CAULDRONS.add(javaBlockState);
|
||||
}
|
||||
|
||||
if (javaId.contains("_door[") && javaId.contains("half=upper")) {
|
||||
UPPER_DOORS.add(javaBlockState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives an integer color that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Banner color integer or -1 if no color
|
||||
*/
|
||||
public static int getBannerColor(int state) {
|
||||
return BANNER_COLORS.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static boolean isNonWaterCauldron(int state) {
|
||||
return NON_WATER_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues.
|
||||
* <p>
|
||||
* When using a bucket on a cauldron sending a ServerboundUseItemPacket can result in the liquid being placed.
|
||||
*
|
||||
* @return if this Java block state is a cauldron
|
||||
*/
|
||||
public static boolean isCauldron(int state) {
|
||||
return ALL_CAULDRONS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags
|
||||
* in Bedrock need the conditional information.
|
||||
*
|
||||
* @return the list of all command blocks and if they are conditional (1 or 0)
|
||||
*/
|
||||
public static Int2ByteMap getCommandBlockValues() {
|
||||
return COMMAND_BLOCK_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* All double chest values are part of the block state in Java and part of the block entity tag in Bedrock.
|
||||
* This gives the DoubleChestValue that can be calculated into the final tag.
|
||||
*
|
||||
* @return The map of all DoubleChestValues.
|
||||
*/
|
||||
public static Int2ObjectMap<DoubleChestValue> getDoubleChestValues() {
|
||||
return DOUBLE_CHEST_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Int2ObjectMap of flower pot block states to containing plant
|
||||
*
|
||||
* @return Int2ObjectMap of flower pot values
|
||||
*/
|
||||
public static Int2ObjectMap<String> getFlowerPotValues() {
|
||||
return FLOWER_POT_VALUES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set of all forward-facing jigsaws, to use as a fallback if NBT is missing.
|
||||
*/
|
||||
public static IntSet getHorizontalFacingJigsaws() {
|
||||
return HORIZONTAL_FACING_JIGSAWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lectern book state map pointing to book present state
|
||||
*/
|
||||
public static LecternHasBookMap getLecternBookStates() {
|
||||
return LECTERN_BOOK_STATES;
|
||||
}
|
||||
|
||||
/**
|
||||
* The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock.
|
||||
* This gives an integer pitch that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return note block note integer or -1 if not present
|
||||
*/
|
||||
public static int getNoteblockPitch(int state) {
|
||||
return NOTEBLOCK_PITCHES.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Int2BooleanMap showing if a piston block state is extended or not.
|
||||
*
|
||||
* @return the Int2BooleanMap of piston extensions.
|
||||
*/
|
||||
public static Int2BooleanMap getPistonValues() {
|
||||
return PISTON_VALUES;
|
||||
}
|
||||
|
||||
public static boolean isStickyPiston(int blockState) {
|
||||
return STICKY_PISTONS.contains(blockState);
|
||||
}
|
||||
|
||||
public static boolean isPistonHead(int state) {
|
||||
return ALL_PISTON_HEADS.contains(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Java Block State for a piston head for a specific direction
|
||||
* This is used in PistonBlockEntity to get the BlockCollision for the piston head.
|
||||
*
|
||||
* @param direction Direction the piston head points in
|
||||
* @return Block state for the piston head
|
||||
*/
|
||||
public static int getPistonHead(Direction direction) {
|
||||
return PISTON_HEADS.getOrDefault(direction, BlockStateValues.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.
|
||||
*
|
||||
* @param state The block state of the piston base
|
||||
* @return The direction in which the piston faces
|
||||
*/
|
||||
public static Direction getPistonOrientation(int state) {
|
||||
return PISTON_ORIENTATION.get(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block sticks to other blocks
|
||||
* (Slime and honey blocks)
|
||||
|
@ -389,8 +45,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,13 +57,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;
|
||||
}
|
||||
|
@ -415,84 +72,33 @@ 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 == 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
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte variant ID that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull variant byte or -1 if no variant
|
||||
*/
|
||||
public static byte getSkullVariant(int state) {
|
||||
return SKULL_VARIANTS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock.
|
||||
* This gives a byte rotation that Bedrock can use.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return Skull rotation value or -1 if no value
|
||||
*/
|
||||
public static byte getSkullRotation(int state) {
|
||||
return SKULL_ROTATIONS.getOrDefault(state, (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return Skull wall rotation value with the blockstate
|
||||
*/
|
||||
public static Int2IntMap getSkullWallDirections() {
|
||||
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.
|
||||
*
|
||||
|
@ -500,17 +106,11 @@ public final class BlockStateValues {
|
|||
* @return The water level or -1 if the block isn't water
|
||||
*/
|
||||
public static int getWaterLevel(int state) {
|
||||
return WATER_LEVEL.getOrDefault(state, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a block is the upper half of a door.
|
||||
*
|
||||
* @param state BlockState of the block
|
||||
* @return True if the block is the upper half of a door
|
||||
*/
|
||||
public static boolean isUpperDoor(int state) {
|
||||
return UPPER_DOORS.contains(state);
|
||||
BlockState blockState = BlockState.of(state);
|
||||
if (!blockState.is(Blocks.WATER)) {
|
||||
return -1;
|
||||
}
|
||||
return blockState.getValue(Properties.LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -544,31 +144,18 @@ public final class BlockStateValues {
|
|||
* @param state BlockState of the block
|
||||
* @return The block's slipperiness
|
||||
*/
|
||||
public static float getSlipperiness(int state) {
|
||||
String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier();
|
||||
return switch (blockIdentifier) {
|
||||
case "minecraft:slime_block" -> 0.8f;
|
||||
case "minecraft:ice", "minecraft:packed_ice" -> 0.98f;
|
||||
case "minecraft:blue_ice" -> 0.989f;
|
||||
default -> 0.6f;
|
||||
};
|
||||
}
|
||||
|
||||
private static Direction getBlockDirection(String javaId) {
|
||||
if (javaId.contains("down")) {
|
||||
return Direction.DOWN;
|
||||
} else if (javaId.contains("up")) {
|
||||
return Direction.UP;
|
||||
} else if (javaId.contains("south")) {
|
||||
return Direction.SOUTH;
|
||||
} else if (javaId.contains("west")) {
|
||||
return Direction.WEST;
|
||||
} else if (javaId.contains("north")) {
|
||||
return Direction.NORTH;
|
||||
} else if (javaId.contains("east")) {
|
||||
return Direction.EAST;
|
||||
public static float getSlipperiness(BlockState state) {
|
||||
Block block = state.block();
|
||||
if (block == Blocks.SLIME_BLOCK) {
|
||||
return 0.8f;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
if (block == Blocks.ICE || block == Blocks.PACKED_ICE) {
|
||||
return 0.98f;
|
||||
}
|
||||
if (block == Blocks.BLUE_ICE) {
|
||||
return 0.989f;
|
||||
}
|
||||
return 0.6f;
|
||||
}
|
||||
|
||||
private BlockStateValues() {
|
||||
|
|
2823
core/src/main/java/org/geysermc/geyser/level/block/Blocks.java
Normal file
2823
core/src/main/java/org/geysermc/geyser/level/block/Blocks.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents enums we don't need classes for in Geyser.
|
||||
*/
|
||||
public final class BasicEnumProperty extends Property<String> {
|
||||
private final List<String> values;
|
||||
|
||||
private BasicEnumProperty(String name, List<String> values) {
|
||||
super(name);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.values.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(String value) {
|
||||
int index = this.values.indexOf(value);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Property " + this + " does not have value " + value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T values() {
|
||||
return (T) this.values;
|
||||
}
|
||||
|
||||
public static BasicEnumProperty create(String name, String... values) {
|
||||
return new BasicEnumProperty(name, List.of(values));
|
||||
}
|
||||
}
|
|
@ -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,12 +23,24 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains useful collections for use in Geyser.
|
||||
* <p>
|
||||
* Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be
|
||||
* added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)}
|
||||
* and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map
|
||||
* size and its "start" integer.
|
||||
*/
|
||||
package org.geysermc.geyser.util.collection;
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
public final class BooleanProperty extends Property<Boolean> {
|
||||
private BooleanProperty(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Boolean value) {
|
||||
return value ? 0 : 1;
|
||||
}
|
||||
|
||||
public static BooleanProperty create(String name) {
|
||||
return new BooleanProperty(name);
|
||||
}
|
||||
}
|
|
@ -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,18 +23,12 @@
|
|||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block;
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
/**
|
||||
* This stores all values of double chests that are part of the Java block state.
|
||||
*
|
||||
* @param isFacingEast If true, then chest is facing east/west; if false, south/north
|
||||
* @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north)
|
||||
* @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair.
|
||||
*/
|
||||
public record DoubleChestValue(
|
||||
boolean isFacingEast,
|
||||
boolean isDirectionPositive,
|
||||
boolean isLeft) {
|
||||
public enum ChestType {
|
||||
SINGLE,
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
public static final ChestType[] VALUES = values();
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public final class EnumProperty<T extends Enum<T>> extends Property<T> {
|
||||
private final IntList ordinalValues;
|
||||
|
||||
/**
|
||||
* @param values all possible values of this enum.
|
||||
*/
|
||||
private EnumProperty(String name, T[] values) {
|
||||
super(name);
|
||||
this.ordinalValues = new IntArrayList(values.length);
|
||||
for (T anEnum : values) {
|
||||
this.ordinalValues.add(anEnum.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.ordinalValues.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(T value) {
|
||||
return this.ordinalValues.indexOf(value.ordinal());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T extends Enum<T>> EnumProperty<T> create(String name, T... values) {
|
||||
return new EnumProperty<>(name, values);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public enum FrontAndTop {
|
||||
DOWN_EAST(Direction.DOWN),
|
||||
DOWN_NORTH(Direction.DOWN),
|
||||
DOWN_SOUTH(Direction.DOWN),
|
||||
DOWN_WEST(Direction.DOWN),
|
||||
UP_EAST(Direction.UP),
|
||||
UP_NORTH(Direction.UP),
|
||||
UP_SOUTH(Direction.UP),
|
||||
UP_WEST(Direction.UP),
|
||||
WEST_UP(Direction.WEST),
|
||||
EAST_UP(Direction.EAST),
|
||||
NORTH_UP(Direction.NORTH),
|
||||
SOUTH_UP(Direction.SOUTH);
|
||||
|
||||
private final boolean horizontal;
|
||||
|
||||
FrontAndTop(Direction front) {
|
||||
this.horizontal = front.isHorizontal();
|
||||
}
|
||||
|
||||
public boolean isHorizontal() {
|
||||
return horizontal;
|
||||
}
|
||||
|
||||
public static final FrontAndTop[] VALUES = values();
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
public final class IntegerProperty extends Property<Integer> {
|
||||
private final int offset;
|
||||
private final int valuesCount;
|
||||
|
||||
private IntegerProperty(String name, int low, int high) {
|
||||
super(name);
|
||||
this.offset = low;
|
||||
this.valuesCount = high - low;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int valuesCount() {
|
||||
return this.valuesCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Integer value) {
|
||||
return value - this.offset;
|
||||
}
|
||||
|
||||
public int low() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public int high() {
|
||||
return this.offset + this.valuesCount;
|
||||
}
|
||||
|
||||
public static IntegerProperty create(String name, int low, int high) {
|
||||
return new IntegerProperty(name, low, high);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.level.block.property;
|
||||
|
||||
import org.geysermc.geyser.level.physics.Axis;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public final class Properties {
|
||||
public static final BooleanProperty ATTACHED = BooleanProperty.create("attached");
|
||||
public static final BooleanProperty BOTTOM = BooleanProperty.create("bottom");
|
||||
public static final BooleanProperty CONDITIONAL = BooleanProperty.create("conditional");
|
||||
public static final BooleanProperty DISARMED = BooleanProperty.create("disarmed");
|
||||
public static final BooleanProperty DRAG = BooleanProperty.create("drag");
|
||||
public static final BooleanProperty ENABLED = BooleanProperty.create("enabled");
|
||||
public static final BooleanProperty EXTENDED = BooleanProperty.create("extended");
|
||||
public static final BooleanProperty EYE = BooleanProperty.create("eye");
|
||||
public static final BooleanProperty FALLING = BooleanProperty.create("falling");
|
||||
public static final BooleanProperty HANGING = BooleanProperty.create("hanging");
|
||||
public static final BooleanProperty HAS_BOTTLE_0 = BooleanProperty.create("has_bottle_0");
|
||||
public static final BooleanProperty HAS_BOTTLE_1 = BooleanProperty.create("has_bottle_1");
|
||||
public static final BooleanProperty HAS_BOTTLE_2 = BooleanProperty.create("has_bottle_2");
|
||||
public static final BooleanProperty HAS_RECORD = BooleanProperty.create("has_record");
|
||||
public static final BooleanProperty HAS_BOOK = BooleanProperty.create("has_book");
|
||||
public static final BooleanProperty INVERTED = BooleanProperty.create("inverted");
|
||||
public static final BooleanProperty IN_WALL = BooleanProperty.create("in_wall");
|
||||
public static final BooleanProperty LIT = BooleanProperty.create("lit");
|
||||
public static final BooleanProperty LOCKED = BooleanProperty.create("locked");
|
||||
public static final BooleanProperty OCCUPIED = BooleanProperty.create("occupied");
|
||||
public static final BooleanProperty OPEN = BooleanProperty.create("open");
|
||||
public static final BooleanProperty PERSISTENT = BooleanProperty.create("persistent");
|
||||
public static final BooleanProperty POWERED = BooleanProperty.create("powered");
|
||||
public static final BooleanProperty SHORT = BooleanProperty.create("short");
|
||||
public static final BooleanProperty SIGNAL_FIRE = BooleanProperty.create("signal_fire");
|
||||
public static final BooleanProperty SNOWY = BooleanProperty.create("snowy");
|
||||
public static final BooleanProperty TRIGGERED = BooleanProperty.create("triggered");
|
||||
public static final BooleanProperty UNSTABLE = BooleanProperty.create("unstable");
|
||||
public static final BooleanProperty WATERLOGGED = BooleanProperty.create("waterlogged");
|
||||
public static final BooleanProperty BERRIES = BooleanProperty.create("berries");
|
||||
public static final BooleanProperty BLOOM = BooleanProperty.create("bloom");
|
||||
public static final BooleanProperty SHRIEKING = BooleanProperty.create("shrieking");
|
||||
public static final BooleanProperty CAN_SUMMON = BooleanProperty.create("can_summon");
|
||||
public static final EnumProperty<Axis> HORIZONTAL_AXIS = EnumProperty.create("axis", Axis.X, Axis.Z);
|
||||
public static final EnumProperty<Axis> AXIS = EnumProperty.create("axis", Axis.VALUES);
|
||||
public static final BooleanProperty UP = BooleanProperty.create("up");
|
||||
public static final BooleanProperty DOWN = BooleanProperty.create("down");
|
||||
public static final BooleanProperty NORTH = BooleanProperty.create("north");
|
||||
public static final BooleanProperty EAST = BooleanProperty.create("east");
|
||||
public static final BooleanProperty SOUTH = BooleanProperty.create("south");
|
||||
public static final BooleanProperty WEST = BooleanProperty.create("west");
|
||||
public static final EnumProperty<Direction> FACING = EnumProperty.create("facing", Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN);
|
||||
public static final EnumProperty<Direction> FACING_HOPPER = EnumProperty.create("facing", Direction.DOWN, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
|
||||
public static final EnumProperty<Direction> HORIZONTAL_FACING = EnumProperty.create("facing", Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST);
|
||||
public static final IntegerProperty FLOWER_AMOUNT = IntegerProperty.create("flower_amount", 1, 4);
|
||||
public static final EnumProperty<FrontAndTop> ORIENTATION = EnumProperty.create("orientation", FrontAndTop.VALUES);
|
||||
public static final BasicEnumProperty ATTACH_FACE = BasicEnumProperty.create("face", "floor", "wall", "ceiling");
|
||||
public static final BasicEnumProperty BELL_ATTACHMENT = BasicEnumProperty.create("attachment", "floor", "ceiling", "single_wall", "double_wall");
|
||||
public static final BasicEnumProperty EAST_WALL = BasicEnumProperty.create("east", "none", "low", "tall");
|
||||
public static final BasicEnumProperty NORTH_WALL = BasicEnumProperty.create("north", "none", "low", "tall");
|
||||
public static final BasicEnumProperty SOUTH_WALL = BasicEnumProperty.create("south", "none", "low", "tall");
|
||||
public static final BasicEnumProperty WEST_WALL = BasicEnumProperty.create("west", "none", "low", "tall");
|
||||
public static final BasicEnumProperty EAST_REDSTONE = BasicEnumProperty.create("east", "up", "side", "none");
|
||||
public static final BasicEnumProperty NORTH_REDSTONE = BasicEnumProperty.create("north", "up", "side", "none");
|
||||
public static final BasicEnumProperty SOUTH_REDSTONE = BasicEnumProperty.create("south", "up", "side", "none");
|
||||
public static final BasicEnumProperty WEST_REDSTONE = BasicEnumProperty.create("west", "up", "side", "none");
|
||||
public static final BasicEnumProperty DOUBLE_BLOCK_HALF = BasicEnumProperty.create("half", "upper", "lower");
|
||||
public static final BasicEnumProperty HALF = BasicEnumProperty.create("half", "top", "bottom");
|
||||
public static final BasicEnumProperty RAIL_SHAPE = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south", "south_east", "south_west", "north_west", "north_east");
|
||||
public static final BasicEnumProperty RAIL_SHAPE_STRAIGHT = BasicEnumProperty.create("shape", "north_south", "east_west", "ascending_east", "ascending_west", "ascending_north", "ascending_south");
|
||||
public static final IntegerProperty AGE_1 = IntegerProperty.create("age", 0, 1);
|
||||
public static final IntegerProperty AGE_2 = IntegerProperty.create("age", 0, 2);
|
||||
public static final IntegerProperty AGE_3 = IntegerProperty.create("age", 0, 3);
|
||||
public static final IntegerProperty AGE_4 = IntegerProperty.create("age", 0, 4);
|
||||
public static final IntegerProperty AGE_5 = IntegerProperty.create("age", 0, 5);
|
||||
public static final IntegerProperty AGE_7 = IntegerProperty.create("age", 0, 7);
|
||||
public static final IntegerProperty AGE_15 = IntegerProperty.create("age", 0, 15);
|
||||
public static final IntegerProperty AGE_25 = IntegerProperty.create("age", 0, 25);
|
||||
public static final IntegerProperty BITES = IntegerProperty.create("bites", 0, 6);
|
||||
public static final IntegerProperty CANDLES = IntegerProperty.create("candles", 1, 4);
|
||||
public static final IntegerProperty DELAY = IntegerProperty.create("delay", 1, 4);
|
||||
public static final IntegerProperty DISTANCE = IntegerProperty.create("distance", 1, 7);
|
||||
public static final IntegerProperty EGGS = IntegerProperty.create("eggs", 1, 4);
|
||||
public static final IntegerProperty HATCH = IntegerProperty.create("hatch", 0, 2);
|
||||
public static final IntegerProperty LAYERS = IntegerProperty.create("layers", 1, 8);
|
||||
public static final IntegerProperty LEVEL_CAULDRON = IntegerProperty.create("level", 1, 3);
|
||||
public static final IntegerProperty LEVEL_COMPOSTER = IntegerProperty.create("level", 0, 8);
|
||||
public static final IntegerProperty LEVEL_FLOWING = IntegerProperty.create("level", 1, 8);
|
||||
public static final IntegerProperty LEVEL_HONEY = IntegerProperty.create("honey_level", 0, 5);
|
||||
public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, 15);
|
||||
public static final IntegerProperty MOISTURE = IntegerProperty.create("moisture", 0, 7);
|
||||
public static final IntegerProperty NOTE = IntegerProperty.create("note", 0, 24);
|
||||
public static final IntegerProperty PICKLES = IntegerProperty.create("pickles", 1, 4);
|
||||
public static final IntegerProperty POWER = IntegerProperty.create("power", 0, 15);
|
||||
public static final IntegerProperty STAGE = IntegerProperty.create("stage", 0, 1);
|
||||
public static final IntegerProperty STABILITY_DISTANCE = IntegerProperty.create("distance", 0, 7);
|
||||
public static final IntegerProperty RESPAWN_ANCHOR_CHARGES = IntegerProperty.create("charges", 0, 4);
|
||||
public static final IntegerProperty ROTATION_16 = IntegerProperty.create("rotation", 0, 15);
|
||||
public static final BasicEnumProperty BED_PART = BasicEnumProperty.create("part", "head", "foot");
|
||||
public static final EnumProperty<ChestType> CHEST_TYPE = EnumProperty.create("type", ChestType.VALUES);
|
||||
public static final BasicEnumProperty MODE_COMPARATOR = BasicEnumProperty.create("mode", "compare", "subtract");
|
||||
public static final BasicEnumProperty DOOR_HINGE = BasicEnumProperty.create("hinge", "left", "right");
|
||||
public static final BasicEnumProperty NOTEBLOCK_INSTRUMENT = BasicEnumProperty.create("instrument", "harp", "basedrum", "snare", "hat", "bass", "flute", "bell", "guitar", "chime", "xylophone", "iron_xylophone", "cow_bell", "didgeridoo", "bit", "banjo", "pling", "zombie", "skeleton", "creeper", "dragon", "wither_skeleton", "piglin", "custom_head");
|
||||
public static final BasicEnumProperty PISTON_TYPE = BasicEnumProperty.create("type", "normal", "sticky");
|
||||
public static final BasicEnumProperty SLAB_TYPE = BasicEnumProperty.create("type", "top", "bottom", "double");
|
||||
public static final BasicEnumProperty STAIRS_SHAPE = BasicEnumProperty.create("shape", "straight", "inner_left", "inner_right", "outer_left", "outer_right");
|
||||
public static final BasicEnumProperty STRUCTUREBLOCK_MODE = BasicEnumProperty.create("mode", "save", "load", "corner", "data");
|
||||
public static final BasicEnumProperty BAMBOO_LEAVES = BasicEnumProperty.create("leaves", "none", "small", "large");
|
||||
public static final BasicEnumProperty TILT = BasicEnumProperty.create("tilt", "none", "unstable", "partial", "full");
|
||||
public static final EnumProperty<Direction> VERTICAL_DIRECTION = EnumProperty.create("vertical_direction", Direction.UP, Direction.DOWN);
|
||||
public static final BasicEnumProperty DRIPSTONE_THICKNESS = BasicEnumProperty.create("thickness", "tip_merge", "tip", "frustum", "middle", "base");
|
||||
public static final BasicEnumProperty SCULK_SENSOR_PHASE = BasicEnumProperty.create("sculk_sensor_phase", "inactive", "active", "cooldown");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_0_OCCUPIED = BooleanProperty.create("slot_0_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_1_OCCUPIED = BooleanProperty.create("slot_1_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_2_OCCUPIED = BooleanProperty.create("slot_2_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_3_OCCUPIED = BooleanProperty.create("slot_3_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_4_OCCUPIED = BooleanProperty.create("slot_4_occupied");
|
||||
public static final BooleanProperty CHISELED_BOOKSHELF_SLOT_5_OCCUPIED = BooleanProperty.create("slot_5_occupied");
|
||||
public static final IntegerProperty DUSTED = IntegerProperty.create("dusted", 0, 3);
|
||||
public static final BooleanProperty CRACKED = BooleanProperty.create("cracked");
|
||||
public static final BooleanProperty CRAFTING = BooleanProperty.create("crafting");
|
||||
public static final BasicEnumProperty TRIAL_SPAWNER_STATE = BasicEnumProperty.create("trial_spawner_state", "inactive", "waiting_for_players", "active", "waiting_for_reward_ejection", "ejecting_reward", "cooldown");
|
||||
public static final BasicEnumProperty VAULT_STATE = BasicEnumProperty.create("vault_state", "inactive", "active", "unlocking", "ejecting");
|
||||
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
|
||||
}
|
|
@ -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.property;
|
||||
|
||||
public abstract class Property<T extends Comparable<T>> {
|
||||
private final String name;
|
||||
|
||||
protected Property(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public abstract int valuesCount();
|
||||
|
||||
public abstract int indexOf(T value);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + name + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class BannerBlock extends Block {
|
||||
private final int dyeColor;
|
||||
|
||||
public BannerBlock(String javaIdentifier, int dyeColor, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.dyeColor = dyeColor;
|
||||
}
|
||||
|
||||
public int dyeColor() {
|
||||
return dyeColor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class BedBlock extends Block {
|
||||
private final int dyeColor;
|
||||
|
||||
public BedBlock(String javaIdentifier, int dyeColor, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.dyeColor = dyeColor;
|
||||
}
|
||||
|
||||
public int dyeColor() {
|
||||
return dyeColor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* 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 it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.kyori.adventure.key.Key;
|
||||
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.item.type.Item;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.BasicEnumProperty;
|
||||
import org.geysermc.geyser.level.block.property.IntegerProperty;
|
||||
import org.geysermc.geyser.level.block.property.Property;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Block {
|
||||
public static final int JAVA_AIR_ID = 0;
|
||||
|
||||
private final Key javaIdentifier;
|
||||
/**
|
||||
* Can you harvest this with your hand.
|
||||
*/
|
||||
private final boolean requiresCorrectToolForDrops;
|
||||
private final boolean hasBlockEntity;
|
||||
private final float destroyTime;
|
||||
private final @NonNull PistonBehavior pushReaction;
|
||||
/**
|
||||
* Used for classes we don't have implemented yet that override Mojmap getCloneItemStack with their own item.
|
||||
* A supplier prevents any issues arising where the Items class finishes before the Blocks class.
|
||||
*/
|
||||
private final Supplier<Item> pickItem;
|
||||
protected Item item = null;
|
||||
private int javaId = -1;
|
||||
|
||||
/**
|
||||
* Used for switching a given block state to different states.
|
||||
*/
|
||||
private final Property<?>[] propertyKeys;
|
||||
private final BlockState defaultState;
|
||||
|
||||
public Block(@Subst("empty") String javaIdentifier, Builder builder) {
|
||||
this.javaIdentifier = Key.key(javaIdentifier);
|
||||
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
|
||||
this.hasBlockEntity = builder.hasBlockEntity;
|
||||
this.destroyTime = builder.destroyTime;
|
||||
this.pushReaction = builder.pushReaction;
|
||||
this.pickItem = builder.pickItem;
|
||||
|
||||
BlockState firstState = builder.build(this).get(0);
|
||||
this.propertyKeys = builder.propertyKeys; // Ensure this is not null before iterating over states
|
||||
this.defaultState = setDefaultState(firstState);
|
||||
}
|
||||
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
checkForEmptySkull(session, state, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
if (!(state.block() instanceof SkullBlock)) {
|
||||
// Skull is gone
|
||||
session.getSkullCache().removeSkull(position);
|
||||
}
|
||||
}
|
||||
|
||||
public Item asItem() {
|
||||
if (this.item == null) {
|
||||
return this.item = Item.byBlock(this);
|
||||
}
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.pickItem != null) {
|
||||
return new ItemStack(this.pickItem.get().javaId());
|
||||
}
|
||||
return new ItemStack(this.asItem().javaId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be ran on block creation. Can be overridden.
|
||||
* @param firstState the first state created from this block
|
||||
*/
|
||||
protected BlockState setDefaultState(BlockState firstState) {
|
||||
return firstState;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Key javaIdentifier() {
|
||||
return javaIdentifier;
|
||||
}
|
||||
|
||||
public boolean requiresCorrectToolForDrops() {
|
||||
return requiresCorrectToolForDrops;
|
||||
}
|
||||
|
||||
public boolean hasBlockEntity() {
|
||||
return hasBlockEntity;
|
||||
}
|
||||
|
||||
public float destroyTime() {
|
||||
return destroyTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PistonBehavior pushReaction() {
|
||||
return this.pushReaction;
|
||||
}
|
||||
|
||||
public BlockState defaultBlockState() {
|
||||
return this.defaultState;
|
||||
}
|
||||
|
||||
public int javaId() {
|
||||
return javaId;
|
||||
}
|
||||
|
||||
public void setJavaId(int javaId) {
|
||||
if (this.javaId != -1) {
|
||||
throw new RuntimeException("Block ID has already been set!");
|
||||
}
|
||||
this.javaId = javaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Block{" +
|
||||
"javaIdentifier='" + javaIdentifier + '\'' +
|
||||
", javaId=" + javaId +
|
||||
'}';
|
||||
}
|
||||
|
||||
Property<?>[] propertyKeys() {
|
||||
return propertyKeys;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
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;
|
||||
private Supplier<Item> pickItem;
|
||||
|
||||
// We'll use this field after building
|
||||
private Property<?>[] propertyKeys;
|
||||
|
||||
/**
|
||||
* For states that we're just tracking for mirroring Java states.
|
||||
*/
|
||||
public Builder enumState(BasicEnumProperty property) {
|
||||
states.put(property, property.values());
|
||||
return this;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final <T extends Enum<T>> Builder enumState(Property<T> property, T... enums) {
|
||||
states.put(property, List.of(enums));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder booleanState(Property<Boolean> property) {
|
||||
states.put(property, List.of(Boolean.TRUE, Boolean.FALSE)); // Make this list a static constant if it'll survive past initialization
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder intState(IntegerProperty property) {
|
||||
int low = property.low();
|
||||
int high = property.high();
|
||||
IntList list = new IntArrayList();
|
||||
// There is a state for every number between the low and high.
|
||||
for (int i = low; i <= high; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
states.put(property, List.copyOf(list)); // Boxing reasons for that copy I guess.
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder requiresCorrectToolForDrops() {
|
||||
this.requiresCorrectToolForDrops = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBlockEntity() {
|
||||
this.hasBlockEntity = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder destroyTime(float destroyTime) {
|
||||
this.destroyTime = destroyTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pushReaction(PistonBehavior pushReaction) {
|
||||
this.pushReaction = pushReaction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pickItem(Supplier<Item> pickItem) {
|
||||
this.pickItem = pickItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<BlockState> build(Block block) {
|
||||
if (states.isEmpty()) {
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size());
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
propertyKeys = null;
|
||||
return List.of(state);
|
||||
} else if (states.size() == 1) {
|
||||
// We can optimize because we don't need to worry about combinations
|
||||
Map.Entry<Property<?>, List<Comparable<?>>> property = this.states.entrySet().stream().findFirst().orElseThrow();
|
||||
List<BlockState> states = new ArrayList<>(property.getValue().size());
|
||||
property.getValue().forEach(value -> {
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), new Comparable[] {value});
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
states.add(state);
|
||||
});
|
||||
this.propertyKeys = new Property[]{property.getKey()};
|
||||
return states;
|
||||
} else {
|
||||
// Think of this stream as another list containing, at the start, one empty list.
|
||||
// It's two collections. Not a stream from the empty list.
|
||||
Stream<List<Comparable<?>>> stream = Stream.of(Collections.emptyList());
|
||||
for (var values : this.states.values()) {
|
||||
// OK, so here's how I understand this works. Because this was staring at vanilla Java code trying
|
||||
// to figure out exactly how it works so we don't have any discrepencies.
|
||||
// For each existing pair in the list, a new list is created, adding one of the new values.
|
||||
// Property up [true/false] would exist as true and false
|
||||
// Both entries will get duplicated, adding down, true and false.
|
||||
stream = stream.flatMap(aPreviousPropertiesList ->
|
||||
// So the above is a list. It may be empty if this is the first property,
|
||||
// or it may be populated if this is not the first property.
|
||||
// We're about to create a new stream, each with a new list,
|
||||
// for every previous property
|
||||
values.stream().map(value -> {
|
||||
var newProperties = new ArrayList<>(aPreviousPropertiesList);
|
||||
newProperties.add(value);
|
||||
return newProperties;
|
||||
}));
|
||||
}
|
||||
|
||||
List<BlockState> states = new ArrayList<>();
|
||||
// Now we have a list of Pair<Property, Value>s. Each list is a block state!
|
||||
// If we have two boolean properties: up [true/false] and down [true/false],
|
||||
// We'll see [up=true,down=true], [up=false,down=true], [up=true,down=false], [up=false,down=false]
|
||||
List<List<Comparable<?>>> result = stream.toList();
|
||||
// Ensure each block state shares the same key array. Creating a keySet here shouldn't be an issue since
|
||||
// this states map should be removed after build.
|
||||
Property<?>[] keys = this.states.keySet().toArray(new Property<?>[0]);
|
||||
result.forEach(properties -> {
|
||||
Comparable<?>[] values = properties.toArray(new Comparable<?>[0]);
|
||||
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size(), values);
|
||||
BlockRegistries.BLOCK_STATES.get().add(state);
|
||||
states.add(state);
|
||||
});
|
||||
this.propertyKeys = keys;
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.level.block.property.Property;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class BlockState {
|
||||
private final Block block;
|
||||
private final int javaId;
|
||||
/**
|
||||
* The values of each property of this block state. These should be treated as keys to {@link Block#propertyKeys()}.
|
||||
* Of note - the comparable part probably doesn't do anything because we occasionally use strings in place of enums.
|
||||
* Will be null if there's only one block state for a block.
|
||||
*/
|
||||
private final Comparable<?>[] states;
|
||||
|
||||
public BlockState(Block block, int javaId) {
|
||||
this(block, javaId, null);
|
||||
}
|
||||
|
||||
BlockState(Block block, int javaId, Comparable<?>[] states) {
|
||||
this.block = block;
|
||||
this.javaId = javaId;
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValue(Property<T> property) {
|
||||
//noinspection unchecked
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T getValueNullable(Property<T> property) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) get(property);
|
||||
}
|
||||
|
||||
public boolean getValue(Property<Boolean> property, boolean def) {
|
||||
var value = get(property);
|
||||
if (value == null) {
|
||||
return def;
|
||||
}
|
||||
return (Boolean) value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Comparable<?> get(Property<?> property) {
|
||||
Property<?>[] keys = this.block.propertyKeys();
|
||||
if (keys == null) {
|
||||
return null;
|
||||
}
|
||||
// We're copying the behavior Reference2ObjectArrayMap uses
|
||||
for (int i = keys.length; i-- != 0;) {
|
||||
if (keys[i] == property) {
|
||||
return this.states[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link BlockState} instance with the given value.
|
||||
*/
|
||||
public <T extends Comparable<T>> BlockState withValue(Property<T> property, T value) {
|
||||
Property<?>[] keys = this.block.propertyKeys();
|
||||
if (keys == null) {
|
||||
throw new IllegalStateException(this + " does not have any different states!");
|
||||
}
|
||||
|
||||
T currentValue = getValue(property);
|
||||
if (currentValue == null) {
|
||||
throw new IllegalArgumentException("This BlockState does not have the property " + property);
|
||||
}
|
||||
if (currentValue.equals(value)) {
|
||||
// No action required. This block state is the state we're looking for.
|
||||
return this;
|
||||
}
|
||||
|
||||
// Diff is how much we will have to traverse as a sort of offset
|
||||
|
||||
// Block states are calculated in a predictable structure:
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=tall]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=none]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=low]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=false,west=tall]
|
||||
// minecraft:cobblestone_wall[east=none,north=none,south=none,up=false,waterlogged=true,west=none]
|
||||
|
||||
// The last value goes through all its iterations, then the next state goes through all its iterations.
|
||||
// West goes none -> low -> tall, then waterlogged is toggled as west cycles again.
|
||||
// Then when waterlogged goes through all its properties, up is toggled, and west goes through again
|
||||
// If we want to find the "up" property in order, then we need to find how many iterations each property
|
||||
// after it goes in. West goes for 3, waterlogged goes for 2. Adding those together, we find that we need to
|
||||
// add five to get to the next toggle of the up property
|
||||
int diff = 0;
|
||||
for (int i = keys.length - 1; i >= 0; i--) {
|
||||
if (keys[i] != property) {
|
||||
diff += keys[i].valuesCount();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// How many times do we have to jump by diff? This depends on how far away each value is from each other.
|
||||
// piston_head[facing=north] might be right next to piston_head[facing=south], which just one diff'd hop.
|
||||
// But piston_head[facing=west] is further away, requiring more hops.
|
||||
int thatOffset = property.indexOf(value);
|
||||
int thisOffset = property.indexOf(currentValue);
|
||||
if (diff == 0) {
|
||||
// This can happen if the property is at the tail end of the block and there are no other properties to look through
|
||||
// If we have minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]
|
||||
// And want minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]
|
||||
// The above for loop will always stop at the first break because the last property has already been found
|
||||
diff = 1;
|
||||
}
|
||||
return of(this.javaId + ((thatOffset - thisOffset) * diff));
|
||||
}
|
||||
|
||||
public Block block() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
public int javaId() {
|
||||
return this.javaId;
|
||||
}
|
||||
|
||||
public boolean is(Block block) {
|
||||
return this.block == block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.states == null) {
|
||||
return this.block.javaIdentifier().toString();
|
||||
}
|
||||
return this.block.javaIdentifier().toString() + "[" + paramsToString() + "]";
|
||||
}
|
||||
|
||||
private String paramsToString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Property<?>[] propertyKeys = this.block.propertyKeys();
|
||||
if (propertyKeys != null) {
|
||||
for (int i = 0; i < propertyKeys.length; i++) {
|
||||
builder.append(propertyKeys[i].name())
|
||||
.append("=")
|
||||
.append(this.states[i].toString().toLowerCase(Locale.ROOT)); // lowercase covers enums
|
||||
if (i < propertyKeys.length - 1) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
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,64 +23,36 @@
|
|||
* @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.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;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
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, int blockState, Vector3i position) {
|
||||
NbtMap tag = getTag(session, blockState, 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));
|
||||
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(state));
|
||||
updateBlockPacket.setBlockPosition(position);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||
|
@ -88,4 +60,33 @@ 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.
|
||||
// TODO flattening might make this nicer in the future!
|
||||
NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower);
|
||||
if (plant != null) {
|
||||
tagBuilder.putCompound("PlantBlock", plant.toBuilder().build());
|
||||
}
|
||||
}
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.flower != Blocks.AIR) {
|
||||
return new ItemStack(this.flower.asItem().javaId());
|
||||
}
|
||||
return super.pickItem(state);
|
||||
}
|
||||
|
||||
public Block flower() {
|
||||
return flower;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public class FurnaceBlock extends Block {
|
||||
public FurnaceBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState setDefaultState(BlockState firstState) {
|
||||
// Both furnace minecart states look north.
|
||||
return firstState.withValue(Properties.HORIZONTAL_FACING, Direction.NORTH);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class HoneyBlock extends Block {
|
||||
public HoneyBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
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.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class LecternBlock extends Block implements BedrockChunkWantsBlockEntityTag {
|
||||
public LecternBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtMap createTag(GeyserSession session, Vector3i position, BlockState blockState) {
|
||||
return getBaseLecternTag(position, blockState.getValue(Properties.HAS_BOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlock(GeyserSession session, BlockState state, Vector3i position) {
|
||||
WorldManager worldManager = session.getGeyser().getWorldManager();
|
||||
boolean currentHasBook = state.getValue(Properties.HAS_BOOK);
|
||||
Boolean previousHasBook = worldManager.blockAt(session, position).getValueNullable(Properties.HAS_BOOK); // Can be null if not a lectern, watch out
|
||||
if (previousHasBook == null || currentHasBook != previousHasBook) {
|
||||
BlockEntityUtils.updateBlockEntity(session, getBaseLecternTag(position, currentHasBook), position);
|
||||
}
|
||||
super.updateBlock(session, state, position);
|
||||
}
|
||||
|
||||
public static NbtMap getBaseLecternTag(Vector3i position, boolean hasBook) {
|
||||
if (hasBook) {
|
||||
return getBaseLecternTag(position, 1)
|
||||
.putCompound("book", NbtMap.builder()
|
||||
.putByte("Count", (byte) 1)
|
||||
.putShort("Damage", (short) 0)
|
||||
.putString("Name", "minecraft:writable_book")
|
||||
.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, Collections.singletonList(
|
||||
NbtMap.builder()
|
||||
.putString("photoname", "")
|
||||
.putString("text", "")
|
||||
.build()
|
||||
)).build())
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
return getBaseLecternTag(position, 0).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static NbtMapBuilder getBaseLecternTag(Vector3i position, int pages) {
|
||||
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Lectern", position);
|
||||
builder.putBoolean("isMovable", true);
|
||||
|
||||
if (pages != 0) {
|
||||
builder.putByte("hasBook", (byte) 1);
|
||||
builder.putInt("totalPages", 1); // we'll override it anyway
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
|
@ -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,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.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
public class PistonHeadBlock extends Block {
|
||||
public PistonHeadBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
Block block = state.getValue(Properties.PISTON_TYPE).equals("sticky") ? Blocks.STICKY_PISTON : Blocks.PISTON;
|
||||
return new ItemStack(block.asItem().javaId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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 com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SkullBlock extends Block {
|
||||
private final Type type;
|
||||
|
||||
public SkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) {
|
||||
if (this.type == Type.PLAYER) {
|
||||
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state);
|
||||
if (skull != null && skull.getBlockDefinition() != null) {
|
||||
definition = skull.getBlockDefinition();
|
||||
}
|
||||
}
|
||||
super.sendBlockUpdatePacket(session, state, definition, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) {
|
||||
// It's not an empty skull.
|
||||
}
|
||||
|
||||
public ItemStack pickItem(GeyserSession session, BlockState state, Vector3i position) {
|
||||
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(position);
|
||||
if (skull == null) {
|
||||
return new ItemStack(pickItem(state).getId());
|
||||
}
|
||||
|
||||
GeyserItemStack itemStack = GeyserItemStack.of(pickItem(state).getId(), 1);
|
||||
// This is a universal block entity behavior, but hardcode how it works for now.
|
||||
NbtMapBuilder builder = NbtMap.builder()
|
||||
.putString("id", "minecraft:skull")
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ());
|
||||
DataComponents components = itemStack.getOrCreateComponents();
|
||||
components.put(DataComponentType.BLOCK_ENTITY_DATA, builder.build());
|
||||
|
||||
UUID uuid = skull.getUuid();
|
||||
String texturesProperty = skull.getTexturesProperty();
|
||||
GameProfile profile = new GameProfile(uuid, null);
|
||||
if (texturesProperty != null) {
|
||||
profile.setProperties(Collections.singletonList(new GameProfile.Property("textures", texturesProperty)));
|
||||
}
|
||||
components.put(DataComponentType.PROFILE, profile);
|
||||
return itemStack.getItemStack();
|
||||
}
|
||||
|
||||
public Type skullType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum order matches Java.
|
||||
*/
|
||||
public enum Type {
|
||||
SKELETON(0),
|
||||
WITHER_SKELETON(1),
|
||||
PLAYER(3),
|
||||
ZOMBIE(2),
|
||||
CREEPER(4),
|
||||
PIGLIN(6),
|
||||
DRAGON(5);
|
||||
|
||||
private final int bedrockId;
|
||||
|
||||
Type(int bedrockId) {
|
||||
this.bedrockId = bedrockId;
|
||||
}
|
||||
|
||||
public int bedrockId() {
|
||||
return bedrockId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class SpawnerBlock extends Block {
|
||||
public SpawnerBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class TrapDoorBlock extends Block {
|
||||
public TrapDoorBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
|
||||
public class WallSkullBlock extends SkullBlock {
|
||||
public WallSkullBlock(String javaIdentifier, Type type, Builder builder) {
|
||||
super(javaIdentifier, type, builder);
|
||||
}
|
||||
|
||||
public static int getDegrees(BlockState state) {
|
||||
return getDegrees(state.getValue(Properties.HORIZONTAL_FACING));
|
||||
}
|
||||
|
||||
public static int getDegrees(Direction direction) {
|
||||
return switch (direction) {
|
||||
case NORTH -> 180;
|
||||
case WEST -> 90;
|
||||
case EAST -> 270;
|
||||
case SOUTH -> 0;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class WaterBlock extends Block {
|
||||
public WaterBlock(String javaIdentifier, Builder builder) {
|
||||
super(javaIdentifier, builder);
|
||||
}
|
||||
}
|
|
@ -39,6 +39,9 @@ import org.geysermc.geyser.entity.EntityDefinitions;
|
|||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
|
@ -405,7 +408,8 @@ public class CollisionManager {
|
|||
* @return if the player is currently in a water block
|
||||
*/
|
||||
public boolean isPlayerInWater() {
|
||||
return session.getGeyser().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID;
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getPlayerEntity().getPosition().toInt());
|
||||
return state.is(Blocks.WATER) && state.getValue(Properties.LEVEL) == 0;
|
||||
}
|
||||
|
||||
public boolean isWaterInEyes() {
|
||||
|
|
|
@ -224,7 +224,7 @@ class CodecProcessor {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
static BedrockCodec processCodec(BedrockCodec codec) {
|
||||
return codec.toBuilder()
|
||||
BedrockCodec.Builder codecBuilder = codec.toBuilder()
|
||||
// Illegal unused serverbound EDU packets
|
||||
.updateSerializer(PhotoTransferPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(LabTablePacket.class, ILLEGAL_SERIALIZER)
|
||||
|
@ -232,10 +232,11 @@ class CodecProcessor {
|
|||
.updateSerializer(CreatePhotoPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(NpcRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(PhotoInfoRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Illegal unused serverbound packets for featured servers
|
||||
.updateSerializer(PurchaseReceiptPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Unused serverbound packets for featured servers, which is for some reason still occasionally sent
|
||||
.updateSerializer(PurchaseReceiptPacket.class, IGNORED_SERIALIZER)
|
||||
// Illegal unused serverbound packets that are deprecated
|
||||
.updateSerializer(ClientCheatAbilityPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(CraftingEventPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Illegal unusued serverbound packets that relate to unused features
|
||||
.updateSerializer(PlayerAuthInputPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(ClientCacheBlobStatusPacket.class, ILLEGAL_SERIALIZER)
|
||||
|
@ -243,7 +244,6 @@ class CodecProcessor {
|
|||
.updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(GameTestRequestPacket.class, ILLEGAL_SERIALIZER)
|
||||
// Ignored serverbound packets
|
||||
.updateSerializer(CraftingEventPacket.class, IGNORED_SERIALIZER) // Make illegal when 1.20.40 is removed
|
||||
.updateSerializer(ClientToServerHandshakePacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(EntityFallPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(MapCreateLockedCopyPacket.class, IGNORED_SERIALIZER)
|
||||
|
@ -260,22 +260,25 @@ class CodecProcessor {
|
|||
.updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER)
|
||||
.updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER)
|
||||
.updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER)
|
||||
.updateSerializer(SetEntityMotionPacket.class, codec.getProtocolVersion() < 662 ?
|
||||
SET_ENTITY_MOTION_SERIALIZER_V291 :
|
||||
SET_ENTITY_MOTION_SERIALIZER_V662)
|
||||
.updateSerializer(SetEntityMotionPacket.class, SET_ENTITY_MOTION_SERIALIZER_V662)
|
||||
.updateSerializer(SetEntityLinkPacket.class, SET_ENTITY_LINK_SERIALIZER)
|
||||
// Valid serverbound packets where reading of some fields can be skipped
|
||||
.updateSerializer(MobEquipmentPacket.class, MOB_EQUIPMENT_SERIALIZER)
|
||||
// // Illegal bidirectional packets
|
||||
// Illegal bidirectional packets
|
||||
.updateSerializer(DebugInfoPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(EditorNetworkPacket.class, ILLEGAL_SERIALIZER)
|
||||
.updateSerializer(ScriptMessagePacket.class, ILLEGAL_SERIALIZER)
|
||||
// // Ignored bidirectional packets
|
||||
// Ignored bidirectional packets
|
||||
.updateSerializer(ClientCacheStatusPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER)
|
||||
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER)
|
||||
.build();
|
||||
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER);
|
||||
|
||||
if (codec.getProtocolVersion() < 685) {
|
||||
// Ignored bidirectional packets
|
||||
codecBuilder.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER);
|
||||
}
|
||||
|
||||
return codecBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,11 +27,8 @@ package org.geysermc.geyser.network;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
|
||||
import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
|
||||
|
@ -50,8 +47,8 @@ public final class GameProtocol {
|
|||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.81")
|
||||
public static final BedrockCodec DEFAULT_BEDROCK_CODEC = CodecProcessor.processCodec(Bedrock_v685.CODEC.toBuilder()
|
||||
.minecraftVersion("1.21.0")
|
||||
.build());
|
||||
|
||||
/**
|
||||
|
@ -66,20 +63,11 @@ public final class GameProtocol {
|
|||
private static final PacketCodec DEFAULT_JAVA_CODEC = MinecraftCodec.CODEC;
|
||||
|
||||
static {
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v622.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.40/1.20.41")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v630.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.50/1.20.51")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v649.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.60/1.20.62")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v662.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.70/1.20.73")
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(Bedrock_v671.CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.build()));
|
||||
SUPPORTED_BEDROCK_CODECS.add(CodecProcessor.processCodec(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.20.80/1.20.81")
|
||||
.minecraftVersion("1.21.0")
|
||||
.build()));
|
||||
}
|
||||
|
||||
|
@ -99,16 +87,8 @@ public final class GameProtocol {
|
|||
|
||||
/* Bedrock convenience methods to gatekeep features and easily remove the check on version removal */
|
||||
|
||||
public static boolean isPre1_20_50(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v630.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean isPre1_20_70(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v662.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
public static boolean is1_20_60orHigher(int protocolVersion) {
|
||||
return protocolVersion >= Bedrock_v649.CODEC.getProtocolVersion();
|
||||
public static boolean isPre1_21_0(GeyserSession session) {
|
||||
return session.getUpstream().getProtocolVersion() < Bedrock_v685.CODEC.getProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,7 +47,9 @@ public class InvalidPacketHandler extends ChannelInboundHandlerAdapter {
|
|||
|
||||
|
||||
if (!(rootCause instanceof IllegalArgumentException)) {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
// Kick users that cause exceptions
|
||||
session.getGeyser().getLogger().warning("Exception caught in session of" + session.bedrockUsername() + ": " + rootCause.getMessage());
|
||||
session.disconnect("An internal error occurred!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
}
|
||||
|
||||
session.getUpstream().getSession().setCodec(packetCodec);
|
||||
// FIXME temporary until 1.20.80 is dropped
|
||||
session.getPlayerEntity().resetAir();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,10 +35,7 @@ import org.cloudburstmc.nbt.util.VarInts;
|
|||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -139,6 +136,9 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||
this.geyser.getLogger().debug("Connection timeout for ping passthrough.");
|
||||
} catch (JsonParseException | JsonMappingException ex) {
|
||||
this.geyser.getLogger().error("Failed to parse json when pinging server!", ex);
|
||||
} catch (EOFException e) {
|
||||
this.pingInfo = null;
|
||||
this.geyser.getLogger().warning("Failed to ping the remote Java server! Is it online and configured in Geyser's config?");
|
||||
} catch (UnknownHostException ex) {
|
||||
// Don't reset pingInfo, as we want to keep the last known value
|
||||
this.geyser.getLogger().warning("Unable to resolve remote host! Is the remote server down or invalid?");
|
||||
|
|
|
@ -34,18 +34,20 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.registry.loader.CollisionRegistryLoader;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -59,34 +61,32 @@ public class BlockRegistries {
|
|||
public static final VersionedRegistry<BlockMappings> BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A mapped registry which stores Java to Bedrock block identifiers.
|
||||
* A registry which stores Java IDs to Java {@link BlockState}s, each with their specific state differences and a link
|
||||
* to the overarching block.
|
||||
*/
|
||||
public static final SimpleMappedRegistry<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link BlockMapping}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ArrayRegistry<BlockMapping> JAVA_BLOCKS = ArrayRegistry.create(RegistryLoaders.uninitialized());
|
||||
public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
|
||||
*/
|
||||
public static final IntMappedRegistry<BlockCollision> COLLISIONS;
|
||||
public static final ListRegistry<BlockCollision> COLLISIONS;
|
||||
|
||||
/**
|
||||
* A registry which stores Java IDs to {@link Block}, containing miscellaneous information about
|
||||
* blocks and their behavior in many cases.
|
||||
*/
|
||||
public static final ListRegistry<Block> JAVA_BLOCKS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
/**
|
||||
* A mapped registry containing the Java identifiers to IDs.
|
||||
*/
|
||||
public static final MappedRegistry<String, Integer, Object2IntMap<String>> JAVA_IDENTIFIER_TO_ID = MappedRegistry.create(RegistryLoaders.empty(Object2IntOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A registry which stores unique Java IDs to its clean identifier
|
||||
* This is used in the statistics form.
|
||||
*/
|
||||
public static final ArrayRegistry<String> CLEAN_JAVA_IDENTIFIERS = ArrayRegistry.create(RegistryLoaders.uninitialized());
|
||||
|
||||
/**
|
||||
* A registry containing all the waterlogged blockstates.
|
||||
* Properties.WATERLOGGED should not be relied on for two reasons:
|
||||
* - Custom blocks
|
||||
* - Seagrass, kelp, and bubble columns are assumed waterlogged and don't have a waterlogged property
|
||||
*/
|
||||
public static final SimpleRegistry<BitSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(BitSet::new));
|
||||
|
||||
|
@ -131,12 +131,13 @@ public class BlockRegistries {
|
|||
public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
static {
|
||||
Blocks.VAULT.javaId(); // FIXME
|
||||
CustomSkullRegistryPopulator.populate();
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA);
|
||||
COLLISIONS = IntMappedRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collision.json"), CollisionRegistryLoader::new);
|
||||
COLLISIONS = ListRegistry.create(Pair.of("org.geysermc.geyser.translator.collision.CollisionRemapper", "mappings/collisions.nbt"), CollisionRegistryLoader::new);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION);
|
||||
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
|
||||
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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.registry;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ListRegistry<M> extends Registry<List<M>> {
|
||||
private boolean frozen = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class with the given input and
|
||||
* {@link RegistryLoader}. The input specified is what the registry
|
||||
* loader needs to take in.
|
||||
*
|
||||
* @param input the input
|
||||
* @param registryLoader the registry loader
|
||||
*/
|
||||
protected <I> ListRegistry(I input, RegistryLoader<I, List<M>> registryLoader) {
|
||||
super(input, registryLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value registered by the given index.
|
||||
*
|
||||
* @param index the index
|
||||
* @return the value registered by the given index.
|
||||
*/
|
||||
@Nullable
|
||||
public M get(int index) {
|
||||
if (index >= this.mappings.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.mappings.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value registered by the given index or the default value
|
||||
* specified if null.
|
||||
*
|
||||
* @param index the index
|
||||
* @param defaultValue the default value
|
||||
* @return the value registered by the given key or the default value
|
||||
* specified if null.
|
||||
*/
|
||||
public M getOrDefault(int index, M defaultValue) {
|
||||
M value = this.get(index);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new value into this registry with the given index.
|
||||
*
|
||||
* @param index the index
|
||||
* @param value the value
|
||||
* @return a new value into this registry with the given index.
|
||||
*/
|
||||
public M register(int index, M value) {
|
||||
if (this.frozen) {
|
||||
throw new IllegalStateException("Registry should not be modified after frozen!");
|
||||
}
|
||||
return this.mappings.set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new value into this registry with the given index, even if this value would normally be outside
|
||||
* the range of a list.
|
||||
*
|
||||
* @param index the index
|
||||
* @param value the value
|
||||
* @param defaultValue the default value to fill empty spaces in the registry with.
|
||||
* @return a new value into this registry with the given index.
|
||||
*/
|
||||
public M registerWithAnyIndex(int index, M value, M defaultValue) {
|
||||
if (this.frozen) {
|
||||
throw new IllegalStateException("Registry should not be modified after frozen!");
|
||||
}
|
||||
if (this.mappings.size() <= index) {
|
||||
this.mappings.addAll(Collections.nCopies(index - this.mappings.size() + 1, defaultValue));
|
||||
}
|
||||
return this.mappings.set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this registry as unsuitable for new additions. The backing list will then be optimized for storage.
|
||||
*/
|
||||
public void freeze() {
|
||||
if (!this.frozen) {
|
||||
this.frozen = true;
|
||||
if (this.mappings instanceof ArrayList<M> arrayList) {
|
||||
arrayList.trimToSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array registry with the given {@link RegistryLoader}. The
|
||||
* input type is not specified here, meaning the loader return type is either
|
||||
* predefined, or the registry is populated at a later point.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the returned mappings type
|
||||
* @return a new registry with the given RegistryLoader supplier
|
||||
*/
|
||||
public static <I, M> ListRegistry<M> create(RegistryLoader<I, List<M>> registryLoader) {
|
||||
return new ListRegistry<>(null, registryLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new integer mapped registry with the given {@link RegistryLoader} and input.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param <I> the input
|
||||
* @param <M> the type value
|
||||
* @return a new registry with the given RegistryLoader supplier
|
||||
*/
|
||||
public static <I, M> ListRegistry<M> create(I input, Supplier<RegistryLoader<I, List<M>>> registryLoader) {
|
||||
return new ListRegistry<>(input, registryLoader.get());
|
||||
}
|
||||
}
|
|
@ -127,7 +127,10 @@ public final class Registries {
|
|||
*/
|
||||
public static final PacketTranslatorRegistry<Packet> JAVA_PACKET_TRANSLATORS = PacketTranslatorRegistry.create();
|
||||
|
||||
public static final SimpleRegistry<List<Item>> JAVA_ITEMS = SimpleRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
/**
|
||||
* A registry containing all Java items ordered by their network ID.
|
||||
*/
|
||||
public static final ListRegistry<Item> JAVA_ITEMS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
|
||||
|
||||
public static final SimpleMappedRegistry<String, Item> JAVA_ITEM_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||
|
||||
|
|
|
@ -25,18 +25,19 @@
|
|||
|
||||
package org.geysermc.geyser.registry.loader;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.nbt.NbtUtils;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||
import org.geysermc.geyser.translator.collision.CollisionRemapper;
|
||||
import org.geysermc.geyser.translator.collision.OtherCollision;
|
||||
|
@ -51,41 +52,43 @@ import java.util.regex.Pattern;
|
|||
/**
|
||||
* Loads collision data from the given resource path.
|
||||
*/
|
||||
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, Int2ObjectMap<BlockCollision>> {
|
||||
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, List<BlockCollision>> {
|
||||
|
||||
@Override
|
||||
public Int2ObjectMap<BlockCollision> load(Pair<String, String> input) {
|
||||
Int2ObjectMap<BlockCollision> collisions = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public List<BlockCollision> load(Pair<String, String> input) {
|
||||
Map<Class<?>, CollisionInfo> annotationMap = new IdentityHashMap<>();
|
||||
for (Class<?> clazz : FileUtils.getGeneratedClassesForAnnotation(CollisionRemapper.class.getName())) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName());
|
||||
|
||||
CollisionRemapper collisionRemapper = clazz.getAnnotation(CollisionRemapper.class);
|
||||
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex()), Pattern.compile(collisionRemapper.paramRegex())));
|
||||
annotationMap.put(clazz, new CollisionInfo(collisionRemapper, Pattern.compile(collisionRemapper.regex())));
|
||||
}
|
||||
|
||||
// Load collision mappings file
|
||||
int[] indices;
|
||||
List<BoundingBox[]> collisionList;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input.value())) {
|
||||
ArrayNode collisionNode = (ArrayNode) GeyserImpl.JSON_MAPPER.readTree(stream);
|
||||
collisionList = loadBoundingBoxes(collisionNode);
|
||||
NbtMap collisionData = (NbtMap) NbtUtils.createGZIPReader(stream).readTag();
|
||||
indices = collisionData.getIntArray("indices");
|
||||
//SuppressWarnings unchecked
|
||||
collisionList = loadBoundingBoxes(collisionData.getList("collisions", NbtType.LIST));
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load collision data", e);
|
||||
}
|
||||
|
||||
BlockMapping[] blockMappings = BlockRegistries.JAVA_BLOCKS.get();
|
||||
List<BlockState> blockStates = BlockRegistries.BLOCK_STATES.get();
|
||||
var collisions = new ObjectArrayList<BlockCollision>(blockStates.size());
|
||||
|
||||
// Map of unique collisions to its instance
|
||||
Map<BlockCollision, BlockCollision> collisionInstances = new Object2ObjectOpenHashMap<>();
|
||||
for (int i = 0; i < blockMappings.length; i++) {
|
||||
BlockMapping blockMapping = blockMappings[i];
|
||||
if (blockMapping == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing block mapping for Java block " + i);
|
||||
for (int i = 0; i < blockStates.size(); i++) {
|
||||
BlockState state = blockStates.get(i);
|
||||
if (state == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing block state for Java block " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockCollision newCollision = instantiateCollision(blockMapping, annotationMap, collisionList);
|
||||
BlockCollision newCollision = instantiateCollision(state, annotationMap, indices[i], collisionList);
|
||||
|
||||
if (newCollision != null) {
|
||||
// If there's an existing instance equal to this one, use that instead
|
||||
|
@ -97,33 +100,28 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
|||
}
|
||||
}
|
||||
|
||||
collisions.put(i, newCollision);
|
||||
collisions.add(newCollision);
|
||||
}
|
||||
collisions.trim();
|
||||
return collisions;
|
||||
}
|
||||
|
||||
private @Nullable BlockCollision instantiateCollision(BlockMapping mapping, Map<Class<?>, CollisionInfo> annotationMap, List<BoundingBox[]> collisionList) {
|
||||
String[] blockIdParts = mapping.getJavaIdentifier().split("\\[");
|
||||
String blockName = blockIdParts[0].replace("minecraft:", "");
|
||||
String params = "";
|
||||
if (blockIdParts.length == 2) {
|
||||
params = "[" + blockIdParts[1];
|
||||
}
|
||||
int collisionIndex = mapping.getCollisionIndex();
|
||||
private @Nullable BlockCollision instantiateCollision(BlockState state, Map<Class<?>, CollisionInfo> annotationMap, int collisionIndex, List<BoundingBox[]> collisionList) {
|
||||
String blockName = state.block().javaIdentifier().value();
|
||||
|
||||
for (Map.Entry<Class<?>, CollisionInfo> collisionRemappers : annotationMap.entrySet()) {
|
||||
Class<?> type = collisionRemappers.getKey();
|
||||
CollisionInfo collisionInfo = collisionRemappers.getValue();
|
||||
CollisionRemapper annotation = collisionInfo.collisionRemapper;
|
||||
|
||||
if (collisionInfo.pattern.matcher(blockName).find() && collisionInfo.paramsPattern.matcher(params).find()) {
|
||||
if (collisionInfo.pattern.matcher(blockName).find()) {
|
||||
try {
|
||||
if (annotation.passDefaultBoxes()) {
|
||||
// Create an OtherCollision instance and get the bounding boxes
|
||||
BoundingBox[] defaultBoxes = collisionList.get(collisionIndex);
|
||||
return (BlockCollision) type.getDeclaredConstructor(String.class, BoundingBox[].class).newInstance(params, defaultBoxes);
|
||||
return (BlockCollision) type.getDeclaredConstructor(BlockState.class, BoundingBox[].class).newInstance(state, defaultBoxes);
|
||||
} else {
|
||||
return (BlockCollision) type.getDeclaredConstructor(String.class).newInstance(params);
|
||||
return (BlockCollision) type.getDeclaredConstructor(BlockState.class).newInstance(state);
|
||||
}
|
||||
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -138,25 +136,25 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
|||
|
||||
// Unless some of the low IDs are changed, which is unlikely, the second item should always be full collision
|
||||
if (collisionIndex == 1) {
|
||||
return new SolidCollision(params);
|
||||
return new SolidCollision(state);
|
||||
}
|
||||
return new OtherCollision(collisionList.get(collisionIndex));
|
||||
}
|
||||
|
||||
private List<BoundingBox[]> loadBoundingBoxes(ArrayNode collisionNode) {
|
||||
private List<BoundingBox[]> loadBoundingBoxes(List<NbtList> collisionNode) {
|
||||
List<BoundingBox[]> collisions = new ObjectArrayList<>();
|
||||
for (int collisionIndex = 0; collisionIndex < collisionNode.size(); collisionIndex++) {
|
||||
ArrayNode boundingBoxArray = (ArrayNode) collisionNode.get(collisionIndex);
|
||||
@SuppressWarnings("unchecked") NbtList<NbtList<Double>> boundingBoxArray = (NbtList<NbtList<Double>>) collisionNode.get(collisionIndex);
|
||||
|
||||
BoundingBox[] boundingBoxes = new BoundingBox[boundingBoxArray.size()];
|
||||
for (int i = 0; i < boundingBoxArray.size(); i++) {
|
||||
ArrayNode boxProperties = (ArrayNode) boundingBoxArray.get(i);
|
||||
boundingBoxes[i] = new BoundingBox(boxProperties.get(0).asDouble(),
|
||||
boxProperties.get(1).asDouble(),
|
||||
boxProperties.get(2).asDouble(),
|
||||
boxProperties.get(3).asDouble(),
|
||||
boxProperties.get(4).asDouble(),
|
||||
boxProperties.get(5).asDouble());
|
||||
NbtList<Double> boxProperties = boundingBoxArray.get(i);
|
||||
boundingBoxes[i] = new BoundingBox(boxProperties.get(0),
|
||||
boxProperties.get(1),
|
||||
boxProperties.get(2),
|
||||
boxProperties.get(3),
|
||||
boxProperties.get(4),
|
||||
boxProperties.get(5));
|
||||
}
|
||||
|
||||
// Sorting by lowest Y first fixes some bugs
|
||||
|
@ -173,6 +171,5 @@ public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String,
|
|||
public static class CollisionInfo {
|
||||
private final CollisionRemapper collisionRemapper;
|
||||
private final Pattern pattern;
|
||||
private final Pattern paramsPattern;
|
||||
}
|
||||
}
|
|
@ -32,36 +32,37 @@ import com.google.common.collect.Interner;
|
|||
import com.google.common.collect.Interners;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import org.cloudburstmc.blockstateupdater.BlockStateUpdater;
|
||||
import org.cloudburstmc.blockstateupdater.util.tagupdater.CompoundTagUpdaterContext;
|
||||
import org.cloudburstmc.nbt.NBTInputStream;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||
import org.cloudburstmc.nbt.*;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
|
||||
import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.FlowerPotBlock;
|
||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
||||
import org.geysermc.geyser.util.BlockUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
|
@ -82,24 +83,11 @@ public final class BlockRegistryPopulator {
|
|||
interface Remapper {
|
||||
|
||||
NbtMap remap(NbtMap tag);
|
||||
|
||||
static Remapper of(BlockStateUpdater... updaters) {
|
||||
CompoundTagUpdaterContext context = new CompoundTagUpdaterContext();
|
||||
for (BlockStateUpdater updater : updaters) {
|
||||
updater.registerUpdaters(context);
|
||||
}
|
||||
|
||||
return tag -> {
|
||||
NbtMapBuilder updated = context.update(tag, 0).toBuilder();
|
||||
updated.remove("version"); // we already removed this, but the context adds it. remove it again.
|
||||
return updated.build();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void populate(Stage stage) {
|
||||
switch (stage) {
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNode();
|
||||
case PRE_INIT, POST_INIT -> nullifyBlocksNbt();
|
||||
case INIT_JAVA -> registerJavaBlocks();
|
||||
case INIT_BEDROCK -> registerBedrockBlocks();
|
||||
default -> throw new IllegalArgumentException("Unknown stage: " + stage);
|
||||
|
@ -107,24 +95,20 @@ public final class BlockRegistryPopulator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stores the raw blocks JSON until it is no longer needed.
|
||||
* Stores the raw blocks NBT until it is no longer needed.
|
||||
*/
|
||||
private static JsonNode BLOCKS_JSON;
|
||||
private static List<NbtMap> BLOCKS_NBT;
|
||||
private static int MIN_CUSTOM_RUNTIME_ID = -1;
|
||||
private static int JAVA_BLOCKS_SIZE = -1;
|
||||
|
||||
private static void nullifyBlocksNode() {
|
||||
BLOCKS_JSON = null;
|
||||
private static void nullifyBlocksNbt() {
|
||||
BLOCKS_NBT = null;
|
||||
}
|
||||
|
||||
private static void registerBedrockBlocks() {
|
||||
var blockMappers = ImmutableMap.<ObjectIntPair<String>, Remapper>builder()
|
||||
.put(ObjectIntPair.of("1_20_40", Bedrock_v622.CODEC.getProtocolVersion()), Conversion630_622::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_50", Bedrock_v630.CODEC.getProtocolVersion()), Conversion649_630::remapBlock)
|
||||
// Only changes in 1.20.60 are hard_stained_glass (an EDU only block)
|
||||
.put(ObjectIntPair.of("1_20_60", Bedrock_v649.CODEC.getProtocolVersion()), Conversion662_649::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_70", Bedrock_v662.CODEC.getProtocolVersion()), Conversion671_662::remapBlock)
|
||||
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.put(ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()), Conversion685_671::remapBlock)
|
||||
.put(ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()), tag -> tag)
|
||||
.build();
|
||||
|
||||
// We can keep this strong as nothing should be garbage collected
|
||||
|
@ -215,19 +199,34 @@ public final class BlockRegistryPopulator {
|
|||
|
||||
int javaRuntimeId = -1;
|
||||
|
||||
List<BlockState> javaBlockStates = BlockRegistries.BLOCK_STATES.get();
|
||||
|
||||
GeyserBedrockBlock airDefinition = null;
|
||||
BlockDefinition commandBlockDefinition = null;
|
||||
BlockDefinition mobSpawnerBlockDefinition = null;
|
||||
BlockDefinition waterDefinition = null;
|
||||
BlockDefinition movingBlockDefinition = null;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields();
|
||||
Iterator<NbtMap> blocksIterator = BLOCKS_NBT.iterator();
|
||||
|
||||
Remapper stateMapper = blockMappers.get(palette);
|
||||
|
||||
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
|
||||
|
||||
Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
var javaToBedrockIdentifiers = new Int2ObjectOpenHashMap<String>();
|
||||
Block lastBlockSeen = null;
|
||||
|
||||
// Stream isn't ideal.
|
||||
List<Block> javaPottable = BlockRegistries.JAVA_BLOCKS.get()
|
||||
.parallelStream()
|
||||
.flatMap(block -> {
|
||||
if (block instanceof FlowerPotBlock flowerPot && flowerPot.flower() != Blocks.AIR) {
|
||||
return Stream.of(flowerPot.flower());
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.toList();
|
||||
Map<Block, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
|
||||
Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
Set<BlockDefinition> jigsawDefinitions = new ObjectOpenHashSet<>();
|
||||
|
@ -236,10 +235,11 @@ public final class BlockRegistryPopulator {
|
|||
BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder();
|
||||
while (blocksIterator.hasNext()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
NbtMap entry = blocksIterator.next();
|
||||
BlockState blockState = javaBlockStates.get(javaRuntimeId);
|
||||
String javaId = blockState.toString();
|
||||
|
||||
NbtMap originalBedrockTag = buildBedrockState(entry.getValue());
|
||||
NbtMap originalBedrockTag = buildBedrockState(blockState, entry);
|
||||
NbtMap bedrockTag = stateMapper.remap(originalBedrockTag);
|
||||
|
||||
GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag);
|
||||
|
@ -271,35 +271,36 @@ public final class BlockRegistryPopulator {
|
|||
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
|
||||
}
|
||||
|
||||
if (javaId.contains("jigsaw")) {
|
||||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
if (javaId.contains("structure_block")) {
|
||||
int modeIndex = javaId.indexOf("mode=");
|
||||
if (modeIndex != -1) {
|
||||
int startIndex = modeIndex + 5; // Length of "mode=" is 5
|
||||
int endIndex = javaId.indexOf("]", startIndex);
|
||||
if (endIndex != -1) {
|
||||
String modeValue = javaId.substring(startIndex, endIndex);
|
||||
structureBlockDefinitions.put(modeValue.toUpperCase(), bedrockDefinition);
|
||||
}
|
||||
Block block = blockState.block();
|
||||
if (block != lastBlockSeen) {
|
||||
lastBlockSeen = block;
|
||||
String bedrockName = bedrockDefinition.getState().getString("name");
|
||||
if (!block.javaIdentifier().toString().equals(bedrockName)) {
|
||||
javaToBedrockIdentifiers.put(block.javaId(), bedrockName.substring("minecraft:".length()).intern());
|
||||
}
|
||||
}
|
||||
|
||||
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|
||||
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
|
||||
|
||||
if (waterlogged) {
|
||||
int finalJavaRuntimeId = javaRuntimeId;
|
||||
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
|
||||
if (block == Blocks.JIGSAW) {
|
||||
jigsawDefinitions.add(bedrockDefinition);
|
||||
}
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
if (block == Blocks.STRUCTURE_BLOCK) {
|
||||
String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
|
||||
structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
|
||||
}
|
||||
|
||||
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|
||||
|| block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.KELP_PLANT
|
||||
|| block == Blocks.SEAGRASS || block == Blocks.TALL_SEAGRASS;
|
||||
|
||||
if (waterlogged) {
|
||||
BlockRegistries.WATERLOGGED.get().set(javaRuntimeId);
|
||||
}
|
||||
|
||||
// Get the tag needed for non-empty flower pots
|
||||
if (entry.getValue().get("pottable") != null) {
|
||||
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
if (javaPottable.contains(block)) {
|
||||
// Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
|
||||
flowerPotBlocks.put(block, blockStates.get(bedrockDefinition.getRuntimeId()));
|
||||
}
|
||||
|
||||
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
|
||||
|
@ -355,9 +356,12 @@ public final class BlockRegistryPopulator {
|
|||
|
||||
javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this?
|
||||
javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition;
|
||||
javaToBedrockIdentifiers.put(entry.getKey().stateGroupId(), entry.getValue().block().identifier());
|
||||
}
|
||||
}
|
||||
|
||||
javaToBedrockIdentifiers.trim();
|
||||
|
||||
// Loop around again to find all item frame runtime IDs
|
||||
Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> {
|
||||
String name = entry.getKey().getString("name");
|
||||
|
@ -369,6 +373,7 @@ public final class BlockRegistryPopulator {
|
|||
BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap)
|
||||
.javaToBedrockBlocks(javaToBedrockBlocks)
|
||||
.javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks)
|
||||
.javaToBedrockIdentifiers(javaToBedrockIdentifiers)
|
||||
.stateDefinitionMap(blockStateOrderedMap)
|
||||
.itemFrames(itemFrames)
|
||||
.flowerPotBlocks(flowerPotBlocks)
|
||||
|
@ -383,208 +388,81 @@ public final class BlockRegistryPopulator {
|
|||
}
|
||||
|
||||
private static void registerJavaBlocks() {
|
||||
JsonNode blocksJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) {
|
||||
blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream);
|
||||
List<NbtMap> blocksNbt;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) {
|
||||
blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag())
|
||||
.getList("bedrock_mappings", NbtType.COMPOUND);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Java block mappings", e);
|
||||
}
|
||||
|
||||
JAVA_BLOCKS_SIZE = blocksJson.size();
|
||||
JAVA_BLOCKS_SIZE = BlockRegistries.BLOCK_STATES.get().size();
|
||||
|
||||
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
|
||||
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
|
||||
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) {
|
||||
if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) {
|
||||
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
|
||||
}
|
||||
|
||||
JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
|
||||
}
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates
|
||||
|
||||
Deque<String> cleanIdentifiers = new ArrayDeque<>();
|
||||
|
||||
int javaRuntimeId = -1;
|
||||
int cobwebBlockId = -1;
|
||||
int furnaceRuntimeId = -1;
|
||||
int furnaceLitRuntimeId = -1;
|
||||
int honeyBlockRuntimeId = -1;
|
||||
int slimeBlockRuntimeId = -1;
|
||||
int spawnerRuntimeId = -1;
|
||||
int uniqueJavaId = -1;
|
||||
int waterRuntimeId = -1;
|
||||
Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocksJson.fields();
|
||||
while (blocksIterator.hasNext()) {
|
||||
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
|
||||
javaRuntimeId++;
|
||||
Map.Entry<String, JsonNode> entry = blocksIterator.next();
|
||||
String javaId = entry.getKey();
|
||||
|
||||
// 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());
|
||||
} else {
|
||||
builder.isBlockEntity(false);
|
||||
}
|
||||
|
||||
BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue());
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey());
|
||||
String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
uniqueJavaId++;
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
}
|
||||
|
||||
builder.javaIdentifier(javaId);
|
||||
builder.javaBlockId(uniqueJavaId);
|
||||
String javaId = javaBlockState.toString().intern();
|
||||
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build());
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
|
||||
if (javaId.contains("cobweb")) {
|
||||
cobwebBlockId = uniqueJavaId;
|
||||
|
||||
} else if (javaId.startsWith("minecraft:furnace[facing=north")) {
|
||||
if (javaId.contains("lit=true")) {
|
||||
furnaceLitRuntimeId = javaRuntimeId;
|
||||
} else {
|
||||
furnaceRuntimeId = javaRuntimeId;
|
||||
}
|
||||
|
||||
} else if (javaId.startsWith("minecraft:spawner")) {
|
||||
spawnerRuntimeId = javaRuntimeId;
|
||||
|
||||
} else if ("minecraft:water[level=0]".equals(javaId)) {
|
||||
waterRuntimeId = javaRuntimeId;
|
||||
} else if (javaId.equals("minecraft:honey_block")) {
|
||||
honeyBlockRuntimeId = javaRuntimeId;
|
||||
} else if (javaId.equals("minecraft:slime_block")) {
|
||||
slimeBlockRuntimeId = javaRuntimeId;
|
||||
}
|
||||
}
|
||||
|
||||
if (cobwebBlockId == -1) {
|
||||
throw new AssertionError("Unable to find cobwebs in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId;
|
||||
|
||||
if (furnaceRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find furnace in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_FURNACE_ID = furnaceRuntimeId;
|
||||
|
||||
if (furnaceLitRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find lit furnace in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_FURNACE_LIT_ID = furnaceLitRuntimeId;
|
||||
|
||||
if (honeyBlockRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find honey block in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_HONEY_BLOCK_ID = honeyBlockRuntimeId;
|
||||
|
||||
if (slimeBlockRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find slime block in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_SLIME_BLOCK_ID = slimeBlockRuntimeId;
|
||||
|
||||
if (spawnerRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find spawner in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_SPAWNER_ID = spawnerRuntimeId;
|
||||
|
||||
if (waterRuntimeId == -1) {
|
||||
throw new AssertionError("Unable to find Java water in palette");
|
||||
}
|
||||
BlockStateValues.JAVA_WATER_ID = waterRuntimeId;
|
||||
|
||||
if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
|
||||
Set<Integer> usedNonVanillaRuntimeIDs = new HashSet<>();
|
||||
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();
|
||||
|
||||
for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
|
||||
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
|
||||
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier());
|
||||
}
|
||||
|
||||
CustomBlockState customBlockState = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().get(javaBlockState);
|
||||
|
||||
String javaId = javaBlockState.identifier();
|
||||
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();
|
||||
|
||||
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
|
||||
String bedrockIdentifier = customBlockState.block().identifier();
|
||||
|
||||
if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) {
|
||||
uniqueJavaId++;
|
||||
cleanIdentifiers.add(cleanJavaIdentifier.intern());
|
||||
Block.Builder builder = Block.builder()
|
||||
.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());
|
||||
String pickItem = javaBlockState.pickItem();
|
||||
Block block = new Block(cleanJavaIdentifier, builder) {
|
||||
@Override
|
||||
public ItemStack pickItem(BlockState state) {
|
||||
if (this.item == null) {
|
||||
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
|
||||
if (this.item == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
|
||||
+ " for getting the item for block " + javaBlockState.identifier());
|
||||
this.item = Items.AIR;
|
||||
}
|
||||
}
|
||||
return new ItemStack(this.item.javaId());
|
||||
}
|
||||
};
|
||||
block.setJavaId(javaBlockState.stateGroupId());
|
||||
|
||||
BlockRegistries.JAVA_BLOCKS.registerWithAnyIndex(javaBlockState.stateGroupId(), block, Blocks.AIR);
|
||||
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
|
||||
BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping);
|
||||
|
||||
// Keeping this here since this is currently unchanged between versions
|
||||
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
|
||||
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
|
||||
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
|
||||
}
|
||||
}
|
||||
|
||||
BlockRegistries.CLEAN_JAVA_IDENTIFIERS.set(cleanIdentifiers.toArray(new String[0]));
|
||||
|
||||
BLOCKS_JSON = blocksJson;
|
||||
BLOCKS_NBT = blocksNbt;
|
||||
|
||||
JsonNode blockInteractionsJson;
|
||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
|
||||
|
@ -595,6 +473,8 @@ public final class BlockRegistryPopulator {
|
|||
|
||||
BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
|
||||
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));
|
||||
|
||||
BlockRegistries.BLOCK_STATES.freeze();
|
||||
}
|
||||
|
||||
private static BitSet toBlockStateSet(ArrayNode node) {
|
||||
|
@ -605,29 +485,11 @@ public final class BlockRegistryPopulator {
|
|||
return blockStateSet;
|
||||
}
|
||||
|
||||
private static NbtMap buildBedrockState(JsonNode node) {
|
||||
private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) {
|
||||
NbtMapBuilder tagBuilder = NbtMap.builder();
|
||||
String bedrockIdentifier = node.get("bedrock_identifier").textValue();
|
||||
String bedrockIdentifier = "minecraft:" + nbt.getString("bedrock_identifier", state.block().javaIdentifier().value());
|
||||
tagBuilder.putString("name", bedrockIdentifier);
|
||||
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder();
|
||||
|
||||
// check for states
|
||||
JsonNode states = node.get("bedrock_states");
|
||||
if (states != null) {
|
||||
Iterator<Map.Entry<String, JsonNode>> statesIterator = states.fields();
|
||||
|
||||
while (statesIterator.hasNext()) {
|
||||
Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
|
||||
JsonNode stateValue = stateEntry.getValue();
|
||||
switch (stateValue.getNodeType()) {
|
||||
case BOOLEAN -> statesBuilder.putBoolean(stateEntry.getKey(), stateValue.booleanValue());
|
||||
case STRING -> statesBuilder.putString(stateEntry.getKey(), stateValue.textValue());
|
||||
case NUMBER -> statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
tagBuilder.put("states", statesBuilder.build());
|
||||
tagBuilder.put("states", nbt.getCompound("state"));
|
||||
return tagBuilder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 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.registry.populator;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Backwards-maps the blocks and items of 1.20.50 (630) to 1.20.40 (622)
|
||||
*/
|
||||
class Conversion630_622 {
|
||||
|
||||
private static final List<String> NEW_STONES = List.of("minecraft:stone", "minecraft:granite", "minecraft:polished_granite", "minecraft:diorite", "minecraft:polished_diorite", "minecraft:andesite", "minecraft:polished_andesite");
|
||||
private static final List<String> NEW_WOODS = List.of("minecraft:oak_planks", "minecraft:spruce_planks", "minecraft:birch_planks", "minecraft:jungle_planks", "minecraft:acacia_planks", "minecraft:dark_oak_planks");
|
||||
|
||||
private static final Map<String, String> ITEMS = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
ITEMS.put("minecraft:acacia_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:birch_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:dark_oak_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:jungle_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:oak_planks", "minecraft:planks");
|
||||
ITEMS.put("minecraft:spruce_planks", "minecraft:planks");
|
||||
|
||||
ITEMS.put("minecraft:diorite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:andesite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:granite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_andesite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_diorite", "minecraft:stone");
|
||||
ITEMS.put("minecraft:polished_granite", "minecraft:stone");
|
||||
|
||||
ITEMS.put("minecraft:chiseled_tuff", "minecraft:chiseled_deepslate");
|
||||
ITEMS.put("minecraft:chiseled_tuff_bricks", "minecraft:chiseled_deepslate");
|
||||
ITEMS.put("minecraft:polished_tuff", "minecraft:polished_deepslate");
|
||||
ITEMS.put("minecraft:polished_tuff_double_slab", "minecraft:polished_deepslate_double_slab");
|
||||
ITEMS.put("minecraft:polished_tuff_slab", "minecraft:polished_deepslate_slab");
|
||||
ITEMS.put("minecraft:polished_tuff_stairs", "minecraft:polished_deepslate_stairs");
|
||||
ITEMS.put("minecraft:polished_tuff_wall", "minecraft:polished_deepslate_wall");
|
||||
ITEMS.put("minecraft:tuff_brick_double_slab", "minecraft:deepslate_brick_double_slab");
|
||||
ITEMS.put("minecraft:tuff_brick_slab", "minecraft:deepslate_brick_slab");
|
||||
ITEMS.put("minecraft:tuff_brick_stairs", "minecraft:deepslate_brick_stairs");
|
||||
ITEMS.put("minecraft:tuff_brick_wall", "minecraft:deepslate_brick_wall");
|
||||
ITEMS.put("minecraft:tuff_bricks", "minecraft:deepslate_bricks");
|
||||
ITEMS.put("minecraft:tuff_double_slab", "minecraft:cobbled_deepslate_double_slab");
|
||||
ITEMS.put("minecraft:tuff_slab", "minecraft:cobbled_deepslate_slab");
|
||||
ITEMS.put("minecraft:tuff_stairs", "minecraft:cobbled_deepslate_stairs");
|
||||
ITEMS.put("minecraft:tuff_wall", "minecraft:cobbled_deepslate_wall");
|
||||
|
||||
ITEMS.put("minecraft:chiseled_copper", "minecraft:copper_block");
|
||||
ITEMS.put("minecraft:copper_bulb", "minecraft:copper_block");
|
||||
ITEMS.put("minecraft:copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:exposed_chiseled_copper", "minecraft:exposed_copper");
|
||||
ITEMS.put("minecraft:exposed_copper_bulb", "minecraft:exposed_copper");
|
||||
ITEMS.put("minecraft:exposed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:exposed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:exposed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:oxidized_chiseled_copper", "minecraft:oxidized_copper");
|
||||
ITEMS.put("minecraft:oxidized_copper_bulb", "minecraft:oxidized_copper");
|
||||
ITEMS.put("minecraft:oxidized_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:oxidized_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:oxidized_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_chiseled_copper", "minecraft:waxed_copper");
|
||||
ITEMS.put("minecraft:waxed_copper_bulb", "minecraft:waxed_copper");
|
||||
ITEMS.put("minecraft:waxed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_exposed_chiseled_copper", "minecraft:waxed_exposed_copper");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_bulb", "minecraft:waxed_exposed_copper");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_exposed_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_oxidized_chiseled_copper", "minecraft:waxed_oxidized_copper");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_bulb", "minecraft:waxed_oxidized_copper");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_oxidized_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:waxed_weathered_chiseled_copper", "minecraft:waxed_weathered_copper");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_bulb", "minecraft:waxed_weathered_copper");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:waxed_weathered_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
ITEMS.put("minecraft:weathered_chiseled_copper", "minecraft:weathered_copper");
|
||||
ITEMS.put("minecraft:weathered_copper_bulb", "minecraft:weathered_copper");
|
||||
ITEMS.put("minecraft:weathered_copper_door", "minecraft:iron_door");
|
||||
ITEMS.put("minecraft:weathered_copper_grate", "minecraft:raw_iron_block");
|
||||
ITEMS.put("minecraft:weathered_copper_trapdoor", "minecraft:iron_trapdoor");
|
||||
|
||||
ITEMS.put("minecraft:crafter", "minecraft:crafting_table");
|
||||
}
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion649_630.remapItem(item, mapping);
|
||||
|
||||
String replacement = ITEMS.get(mapping.getBedrockIdentifier());
|
||||
if (replacement == null) {
|
||||
return mapping;
|
||||
} else {
|
||||
return mapping.withBedrockIdentifier(replacement);
|
||||
}
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion649_630.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
String replacement;
|
||||
if (NEW_STONES.contains(name) || NEW_WOODS.contains(name)) {
|
||||
|
||||
String typeKey;
|
||||
String type = name.substring(10);
|
||||
if (NEW_STONES.contains(name)) {
|
||||
replacement = "minecraft:stone";
|
||||
typeKey = "stone_type";
|
||||
if (type.startsWith("polished_")) {
|
||||
type = type.substring(9) + "_smooth";
|
||||
}
|
||||
} else {
|
||||
replacement = "minecraft:planks";
|
||||
typeKey = "wood_type";
|
||||
type = type.substring(0, type.indexOf("_planks"));
|
||||
}
|
||||
|
||||
return tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", NbtMap.builder().putString(typeKey, type).build())
|
||||
.build();
|
||||
} else if (name.contains("tuff") && !name.equals("minecraft:tuff")) {
|
||||
|
||||
if (name.contains("brick") || name.contains("polished") || name.contains("chiseled")) {
|
||||
replacement = name.replace("tuff", "deepslate");
|
||||
|
||||
if (name.contains("chiseled")) {
|
||||
// chiseled deepslate bricks don't exist. just use chiseled deepslate instead
|
||||
replacement = replacement.replace("_bricks", "");
|
||||
}
|
||||
} else {
|
||||
replacement = name.replace("tuff", "cobbled_deepslate");
|
||||
}
|
||||
|
||||
return tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.build();
|
||||
} else if (name.contains("copper")) {
|
||||
|
||||
boolean removeStates = false;
|
||||
if (name.contains("chiseled")) {
|
||||
replacement = name.replace("_chiseled", ""); // special chiseled
|
||||
replacement = replacement.replace("chiseled_", ""); // plain chiseled
|
||||
} else if (name.endsWith("bulb")) {
|
||||
replacement = name.replace("_bulb", "");
|
||||
removeStates = true;
|
||||
} else if (name.endsWith("grate")) {
|
||||
replacement = "minecraft:raw_iron_block";
|
||||
} else if (name.endsWith("door")) {
|
||||
if (name.contains("trap")) {
|
||||
replacement = "minecraft:iron_trapdoor";
|
||||
} else {
|
||||
replacement = "minecraft:iron_door";
|
||||
}
|
||||
} else {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (replacement.endsWith(":copper")) {
|
||||
// case for plain chiseled copper and plain bulb
|
||||
replacement = replacement + "_block";
|
||||
}
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
if (removeStates) {
|
||||
builder.putCompound("states", NbtMap.EMPTY);
|
||||
}
|
||||
return builder.build();
|
||||
} else if (name.equals("minecraft:crafter")) {
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.put("name", "minecraft:crafting_table");
|
||||
builder.put("states", NbtMap.EMPTY);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
public class Conversion649_630 {
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion662_649.remapItem(item, mapping);
|
||||
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:armadillo_scute", "minecraft:turtle_scute" -> { return mapping.withBedrockIdentifier("minecraft:scute"); }
|
||||
case "minecraft:armadillo_spawn_egg" -> { return mapping.withBedrockIdentifier("minecraft:rabbit_spawn_egg"); }
|
||||
case "minecraft:trial_spawner" -> { return mapping.withBedrockIdentifier("minecraft:mob_spawner"); }
|
||||
case "minecraft:trial_key" -> { return mapping.withBedrockIdentifier("minecraft:echo_shard"); }
|
||||
case "minecraft:wolf_armor" -> { return mapping.withBedrockIdentifier("minecraft:leather_horse_armor"); }
|
||||
default -> { return mapping; }
|
||||
}
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion662_649.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (name.equals("minecraft:trial_spawner")) {
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", "minecraft:mob_spawner")
|
||||
.putCompound("states", NbtMap.EMPTY);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion662_649 {
|
||||
|
||||
private static final List<String> NEW_MISC = List.of("minecraft:grass_block", "minecraft:vault");
|
||||
private static final List<String> NEW_WOODS = List.of("minecraft:oak_wood", "minecraft:spruce_wood", "minecraft:birch_wood", "minecraft:jungle_wood", "minecraft:acacia_wood", "minecraft:dark_oak_wood", "minecraft:stripped_oak_wood", "minecraft:stripped_spruce_wood", "minecraft:stripped_birch_wood", "minecraft:stripped_jungle_wood", "minecraft:stripped_acacia_wood", "minecraft:stripped_dark_oak_wood");
|
||||
private static final List<String> NEW_LEAVES = List.of("minecraft:oak_leaves", "minecraft:spruce_leaves", "minecraft:birch_leaves", "minecraft:jungle_leaves");
|
||||
private static final List<String> NEW_LEAVES2 = List.of("minecraft:acacia_leaves", "minecraft:dark_oak_leaves");
|
||||
private static final List<String> NEW_SLABS = List.of("minecraft:oak_slab", "minecraft:spruce_slab", "minecraft:birch_slab", "minecraft:jungle_slab", "minecraft:acacia_slab", "minecraft:dark_oak_slab", "minecraft:oak_double_slab", "minecraft:spruce_double_slab", "minecraft:birch_double_slab", "minecraft:jungle_double_slab", "minecraft:acacia_double_slab", "minecraft:dark_oak_double_slab");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_WOODS, NEW_LEAVES, NEW_LEAVES2, NEW_SLABS, NEW_MISC).flatMap(List::stream).toList();
|
||||
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
mapping = Conversion671_662.remapItem(item, mapping);
|
||||
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:bogged_spawn_egg" -> { return mapping.withBedrockIdentifier("minecraft:creeper_spawn_egg"); }
|
||||
case "minecraft:grass_block" -> { return mapping.withBedrockIdentifier("minecraft:grass"); }
|
||||
case "minecraft:vault" -> { return mapping.withBedrockIdentifier("minecraft:trial_spawner"); }
|
||||
case "minecraft:wind_charge" -> { return mapping.withBedrockIdentifier("minecraft:snowball"); }
|
||||
};
|
||||
|
||||
if (NEW_WOODS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(0); }
|
||||
case "minecraft:spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(1); }
|
||||
case "minecraft:birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(2); }
|
||||
case "minecraft:jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(3); }
|
||||
case "minecraft:acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(5); }
|
||||
case "minecraft:stripped_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(8); }
|
||||
case "minecraft:stripped_spruce_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(9); }
|
||||
case "minecraft:stripped_birch_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(10); }
|
||||
case "minecraft:stripped_jungle_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(11); }
|
||||
case "minecraft:stripped_acacia_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(12); }
|
||||
case "minecraft:stripped_dark_oak_wood" -> { return mapping.withBedrockIdentifier("minecraft:wood").withBedrockData(13); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_SLABS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(0); }
|
||||
case "minecraft:spruce_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(1); }
|
||||
case "minecraft:birch_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(2); }
|
||||
case "minecraft:jungle_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(3); }
|
||||
case "minecraft:acacia_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:wooden_slab").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_LEAVES.contains(identifer) || NEW_LEAVES2.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(0); }
|
||||
case "minecraft:spruce_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(1); }
|
||||
case "minecraft:birch_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(2); }
|
||||
case "minecraft:jungle_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves").withBedrockData(3); }
|
||||
case "minecraft:acacia_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(0); }
|
||||
case "minecraft:dark_oak_leaves" -> { return mapping.withBedrockIdentifier("minecraft:leaves2").withBedrockData(1); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
tag = Conversion671_662.remapBlock(tag);
|
||||
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!NEW_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (name.equals("minecraft:grass_block")) {
|
||||
replacement = "minecraft:grass";
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (name.equals("minecraft:vault")) {
|
||||
replacement = "minecraft:trial_spawner";
|
||||
|
||||
NbtMapBuilder statesBuilder = NbtMap.builder()
|
||||
.putInt("trial_spawner_state", 0);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
builder.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_WOODS.contains(name)) {
|
||||
replacement = "minecraft:wood";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
boolean stripped = name.startsWith("minecraft:stripped_");
|
||||
String woodType = name.replaceAll("minecraft:|_wood|stripped_", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString("wood_type", woodType)
|
||||
.putBoolean("stripped_bit", stripped);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_LEAVES.contains(name) || NEW_LEAVES2.contains(name)) {
|
||||
boolean leaves2 = NEW_LEAVES2.contains(name);
|
||||
replacement = leaves2 ? "minecraft:leaves2" : "minecraft:leaves";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
String leafType = name.replaceAll("minecraft:|_leaves", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString(leaves2 ? "new_leaf_type" : "old_leaf_type", leafType);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
if (NEW_SLABS.contains(name)) {
|
||||
replacement = name.contains("double") ? "minecraft:double_wooden_slab" : "minecraft:wooden_slab";
|
||||
|
||||
NbtMap states = tag.getCompound("states");
|
||||
String woodType = name.replaceAll("minecraft:|_double|_slab", "");
|
||||
|
||||
NbtMapBuilder statesBuilder = states.toBuilder()
|
||||
.putString("wood_type", woodType);
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder()
|
||||
.putString("name", replacement)
|
||||
.putCompound("states", statesBuilder.build());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion671_662 {
|
||||
private static final List<String> NEW_MISC = List.of("minecraft:heavy_core", "minecraft:mace", "minecraft:flow_banner_pattern", "minecraft:guster_banner_pattern", "minecraft:flow_armor_trim_smithing_template", "minecraft:bolt_armor_trim_smithing_template", "minecraft:flow_pottery_sherd", "minecraft:guster_pottery_sherd", "minecraft:scrape_pottery_sherd", "minecraft:breeze_rod");
|
||||
private static final List<String> NEW_CORAL_FANS = List.of("minecraft:tube_coral_fan", "minecraft:brain_coral_fan", "minecraft:bubble_coral_fan", "minecraft:fire_coral_fan", "minecraft:horn_coral_fan");
|
||||
private static final List<String> NEW_DEAD_CORAL_FANS = List.of("minecraft:dead_tube_coral_fan", "minecraft:dead_brain_coral_fan", "minecraft:dead_bubble_coral_fan", "minecraft:dead_fire_coral_fan", "minecraft:dead_horn_coral_fan");
|
||||
private static final List<String> NEW_FLOWERS = List.of("minecraft:poppy", "minecraft:blue_orchid", "minecraft:allium", "minecraft:azure_bluet", "minecraft:red_tulip", "minecraft:orange_tulip", "minecraft:white_tulip", "minecraft:pink_tulip", "minecraft:oxeye_daisy", "minecraft:cornflower", "minecraft:lily_of_the_valley");
|
||||
private static final List<String> NEW_SAPLINGS = List.of("minecraft:oak_sapling", "minecraft:spruce_sapling", "minecraft:birch_sapling", "minecraft:jungle_sapling", "minecraft:acacia_sapling", "minecraft:dark_oak_sapling", "minecraft:bamboo_sapling");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_MISC, NEW_CORAL_FANS, NEW_DEAD_CORAL_FANS, NEW_FLOWERS, NEW_SAPLINGS).flatMap(List::stream).toList();
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
if (!NEW_BLOCKS.contains(identifer)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
switch (identifer) {
|
||||
case "minecraft:bolt_armor_trim_smithing_template" -> { return mapping.withBedrockIdentifier("minecraft:wayfinder_armor_trim_smithing_template"); }
|
||||
case "minecraft:breeze_rod" -> { return mapping.withBedrockIdentifier("minecraft:blaze_rod"); }
|
||||
case "minecraft:flow_armor_trim_smithing_template" -> { return mapping.withBedrockIdentifier("minecraft:spire_armor_trim_smithing_template"); }
|
||||
case "minecraft:flow_banner_pattern", "minecraft:guster_banner_pattern" -> { return mapping.withBedrockIdentifier("minecraft:globe_banner_pattern"); }
|
||||
case "minecraft:flow_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:skull_pottery_sherd"); }
|
||||
case "minecraft:guster_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:shelter_pottery_sherd"); }
|
||||
case "minecraft:scrape_pottery_sherd" -> { return mapping.withBedrockIdentifier("minecraft:heartbreak_pottery_sherd"); }
|
||||
case "minecraft:heavy_core" -> { return mapping.withBedrockIdentifier("minecraft:conduit"); }
|
||||
case "minecraft:mace" -> { return mapping.withBedrockIdentifier("minecraft:netherite_axe"); }
|
||||
}
|
||||
|
||||
if (NEW_FLOWERS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:poppy" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(0); }
|
||||
case "minecraft:blue_orchid" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(1); }
|
||||
case "minecraft:allium" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(2); }
|
||||
case "minecraft:azure_bluet" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(3); }
|
||||
case "minecraft:red_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(4); }
|
||||
case "minecraft:orange_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(5); }
|
||||
case "minecraft:white_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(6); }
|
||||
case "minecraft:pink_tulip" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(7); }
|
||||
case "minecraft:oxeye_daisy" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(8); }
|
||||
case "minecraft:cornflower" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(9); }
|
||||
case "minecraft:lily_of_the_valley" -> { return mapping.withBedrockIdentifier("minecraft:red_flower").withBedrockData(10); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_SAPLINGS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:oak_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(0); }
|
||||
case "minecraft:spruce_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(1); }
|
||||
case "minecraft:birch_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(2); }
|
||||
case "minecraft:jungle_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(3); }
|
||||
case "minecraft:acacia_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(4); }
|
||||
case "minecraft:dark_oak_sapling" -> { return mapping.withBedrockIdentifier("minecraft:sapling").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_CORAL_FANS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:tube_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(0); }
|
||||
case "minecraft:brain_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(1); }
|
||||
case "minecraft:bubble_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(2); }
|
||||
case "minecraft:fire_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(3); }
|
||||
case "minecraft:horn_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan").withBedrockData(4); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_DEAD_CORAL_FANS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:dead_tube_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(0); }
|
||||
case "minecraft:dead_brain_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(1); }
|
||||
case "minecraft:dead_bubble_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(2); }
|
||||
case "minecraft:dead_fire_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(3); }
|
||||
case "minecraft:dead_horn_coral_fan" -> { return mapping.withBedrockIdentifier("minecraft:coral_fan_dead").withBedrockData(4); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!NEW_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (name.equals("minecraft:bamboo_sapling")) {
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("sapling_type", "oak")
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putCompound("states", states).build();
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (name.equals("minecraft:heavy_core")) {
|
||||
replacement = "minecraft:conduit";
|
||||
|
||||
NbtMapBuilder builder = tag.toBuilder();
|
||||
builder.putString("name", replacement);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
if (NEW_SAPLINGS.contains(name)) {
|
||||
replacement = "minecraft:sapling";
|
||||
String saplingType = name.replaceAll("minecraft:|_sapling", "");;
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("sapling_type", saplingType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_FLOWERS.contains(name)) {
|
||||
replacement = "minecraft:red_flower";
|
||||
String flowerType;
|
||||
|
||||
switch (name) {
|
||||
case "minecraft:poppy" -> flowerType = "poppy";
|
||||
case "minecraft:blue_orchid" -> flowerType = "orchid";
|
||||
case "minecraft:allium" -> flowerType = "allium";
|
||||
case "minecraft:azure_bluet" -> flowerType = "houstonia";
|
||||
case "minecraft:red_tulip" -> flowerType = "tulip_red";
|
||||
case "minecraft:orange_tulip" -> flowerType = "tulip_orange";
|
||||
case "minecraft:white_tulip" -> flowerType = "tulip_white";
|
||||
case "minecraft:pink_tulip" -> flowerType = "tulip_pink";
|
||||
case "minecraft:oxeye_daisy" -> flowerType = "oxeye";
|
||||
case "minecraft:cornflower" -> flowerType = "cornflower";
|
||||
case "minecraft:lily_of_the_valley" -> flowerType = "lily_of_the_valley";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("flower_type", flowerType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
boolean isLiveCoralFan = NEW_CORAL_FANS.contains(name);
|
||||
boolean isDeadCoralFan = NEW_DEAD_CORAL_FANS.contains(name);
|
||||
|
||||
if (isLiveCoralFan || isDeadCoralFan) {
|
||||
replacement = isLiveCoralFan ? "minecraft:coral_fan" : "minecraft:coral_fan_dead";
|
||||
String coralColor;
|
||||
|
||||
switch (name) {
|
||||
case "minecraft:tube_coral_fan", "minecraft:dead_tube_coral_fan" -> coralColor = "blue";
|
||||
case "minecraft:brain_coral_fan", "minecraft:dead_brain_coral_fan" -> coralColor = "pink";
|
||||
case "minecraft:bubble_coral_fan", "minecraft:dead_bubble_coral_fan" -> coralColor = "purple";
|
||||
case "minecraft:fire_coral_fan", "minecraft:dead_fire_coral_fan" -> coralColor = "yellow";
|
||||
case "minecraft:horn_coral_fan", "minecraft:dead_horn_coral_fan" -> coralColor = "red";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("coral_color", coralColor)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) 2019-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.registry.populator;
|
||||
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Conversion685_671 {
|
||||
private static final List<String> NEW_CORAL_BLOCKS = List.of("minecraft:tube_coral_block", "minecraft:brain_coral_block", "minecraft:bubble_coral_block", "minecraft:fire_coral_block", "minecraft:horn_coral_block", "minecraft:dead_tube_coral_block", "minecraft:dead_brain_coral_block", "minecraft:dead_bubble_coral_block", "minecraft:dead_fire_coral_block", "minecraft:dead_horn_coral_block");
|
||||
private static final List<String> NEW_DOUBLE_PLANTS = List.of("minecraft:sunflower", "minecraft:lilac", "minecraft:tall_grass", "minecraft:large_fern", "minecraft:rose_bush", "minecraft:peony");
|
||||
private static final List<String> NEW_STONE_BLOCK_SLABS = List.of("minecraft:smooth_stone_slab", "minecraft:sandstone_slab", "minecraft:petrified_oak_slab", "minecraft:cobblestone_slab", "minecraft:brick_slab", "minecraft:stone_brick_slab", "minecraft:quartz_slab", "minecraft:nether_brick_slab");
|
||||
private static final List<String> NEW_TALLGRASSES = List.of("minecraft:fern", "minecraft:short_grass");
|
||||
private static final List<String> OMINOUS_BLOCKS = List.of("minecraft:trial_spawner", "minecraft:vault");
|
||||
private static final List<String> NEW_BLOCKS = Stream.of(NEW_CORAL_BLOCKS, NEW_DOUBLE_PLANTS, NEW_STONE_BLOCK_SLABS, NEW_TALLGRASSES).flatMap(List::stream).toList();
|
||||
private static final List<String> MODIFIED_BLOCKS = Stream.of(NEW_BLOCKS, OMINOUS_BLOCKS).flatMap(List::stream).toList();
|
||||
|
||||
static GeyserMappingItem remapItem(@SuppressWarnings("unused") Item item, GeyserMappingItem mapping) {
|
||||
String identifer = mapping.getBedrockIdentifier();
|
||||
|
||||
if (!NEW_BLOCKS.contains(identifer)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
if (NEW_CORAL_BLOCKS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:tube_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(0); }
|
||||
case "minecraft:brain_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(1); }
|
||||
case "minecraft:bubble_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(2); }
|
||||
case "minecraft:fire_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(3); }
|
||||
case "minecraft:horn_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(4); }
|
||||
case "minecraft:dead_tube_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(8); }
|
||||
case "minecraft:dead_brain_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(9); }
|
||||
case "minecraft:dead_bubble_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(10); }
|
||||
case "minecraft:dead_fire_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(11); }
|
||||
case "minecraft:dead_horn_coral_block" -> { return mapping.withBedrockIdentifier("minecraft:coral_block").withBedrockData(12); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_DOUBLE_PLANTS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:sunflower" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(0); }
|
||||
case "minecraft:lilac" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(1); }
|
||||
case "minecraft:tall_grass" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(2); }
|
||||
case "minecraft:large_fern" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(3); }
|
||||
case "minecraft:rose_bush" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(4); }
|
||||
case "minecraft:peony" -> { return mapping.withBedrockIdentifier("minecraft:double_plant").withBedrockData(5); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_STONE_BLOCK_SLABS.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:smooth_stone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(0); }
|
||||
case "minecraft:sandstone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(1); }
|
||||
case "minecraft:petrified_oak_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(2); }
|
||||
case "minecraft:cobblestone_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(3); }
|
||||
case "minecraft:brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(4); }
|
||||
case "minecraft:stone_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(5); }
|
||||
case "minecraft:quartz_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(6); }
|
||||
case "minecraft:nether_brick_slab" -> { return mapping.withBedrockIdentifier("minecraft:stone_block_slab").withBedrockData(7); }
|
||||
}
|
||||
}
|
||||
|
||||
if (NEW_TALLGRASSES.contains(identifer)) {
|
||||
switch (identifer) {
|
||||
case "minecraft:short_grass" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(1); }
|
||||
case "minecraft:fern" -> { return mapping.withBedrockIdentifier("minecraft:tallgrass").withBedrockData(2); }
|
||||
}
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static NbtMap remapBlock(NbtMap tag) {
|
||||
final String name = tag.getString("name");
|
||||
|
||||
if (!MODIFIED_BLOCKS.contains(name)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
if (OMINOUS_BLOCKS.contains(name)) {
|
||||
NbtMapBuilder builder = tag.getCompound("states").toBuilder();
|
||||
builder.remove("ominous");
|
||||
return tag.toBuilder().putCompound("states", builder.build()).build();
|
||||
}
|
||||
|
||||
String replacement;
|
||||
|
||||
if (NEW_CORAL_BLOCKS.contains(name)) {
|
||||
replacement = "minecraft:coral_block";
|
||||
String coralColor;
|
||||
boolean deadBit = name.startsWith("minecraft:dead_");
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:tube_coral_block", "minecraft:dead_tube_coral_block" -> coralColor = "blue";
|
||||
case "minecraft:brain_coral_block", "minecraft:dead_brain_coral_block" -> coralColor = "pink";
|
||||
case "minecraft:bubble_coral_block", "minecraft:dead_bubble_coral_block" -> coralColor = "purple";
|
||||
case "minecraft:fire_coral_block", "minecraft:dead_fire_coral_block" -> coralColor = "yellow";
|
||||
case "minecraft:horn_coral_block", "minecraft:dead_horn_coral_block" -> coralColor = "red";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("coral_color", coralColor)
|
||||
.putBoolean("dead_bit", deadBit)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_DOUBLE_PLANTS.contains(name)) {
|
||||
replacement = "minecraft:double_plant";
|
||||
String doublePlantType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:sunflower" -> doublePlantType = "sunflower";
|
||||
case "minecraft:lilac" -> doublePlantType = "syringa";
|
||||
case "minecraft:tall_grass" -> doublePlantType = "grass";
|
||||
case "minecraft:large_fern" -> doublePlantType = "fern";
|
||||
case "minecraft:rose_bush" -> doublePlantType = "rose";
|
||||
case "minecraft:peony" -> doublePlantType = "paeonia";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("double_plant_type", doublePlantType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_STONE_BLOCK_SLABS.contains(name)) {
|
||||
replacement = "minecraft:stone_block_slab";
|
||||
String stoneSlabType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:smooth_stone_slab" -> stoneSlabType = "smooth_stone";
|
||||
case "minecraft:sandstone_slab" -> stoneSlabType = "sandstone";
|
||||
case "minecraft:petrified_oak_slab" -> stoneSlabType = "wood";
|
||||
case "minecraft:cobblestone_slab" -> stoneSlabType = "cobblestone";
|
||||
case "minecraft:brick_slab" -> stoneSlabType = "brick";
|
||||
case "minecraft:stone_brick_slab" -> stoneSlabType = "stone_brick";
|
||||
case "minecraft:quartz_slab" -> stoneSlabType = "quartz";
|
||||
case "minecraft:nether_brick_slab" -> stoneSlabType = "nether_brick";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("stone_slab_type", stoneSlabType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
if (NEW_TALLGRASSES.contains(name)) {
|
||||
replacement = "minecraft:tallgrass";
|
||||
String tallGrassType;
|
||||
|
||||
switch(name) {
|
||||
case "minecraft:short_grass" -> tallGrassType = "tall";
|
||||
case "minecraft:fern" -> tallGrassType = "fern";
|
||||
default -> throw new IllegalStateException("Unexpected value: " + name);
|
||||
}
|
||||
|
||||
NbtMap states = tag.getCompound("states")
|
||||
.toBuilder()
|
||||
.putString("tall_grass_type", tallGrassType)
|
||||
.build();
|
||||
|
||||
return tag.toBuilder().putString("name", replacement).putCompound("states", states).build();
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -53,7 +53,6 @@ import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
|||
import org.geysermc.geyser.level.block.GeyserCustomBlockState;
|
||||
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
|
@ -325,13 +324,11 @@ public class CustomBlockRegistryPopulator {
|
|||
// meaning of this version is unknown, but it's required for tags to work and should probably be checked periodically
|
||||
.putInt("molangVersion", 1)
|
||||
.putList("permutations", NbtType.COMPOUND, permutations)
|
||||
.putList("properties", NbtType.COMPOUND, properties);
|
||||
|
||||
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
|
||||
propertyTag.putCompound("vanilla_block_data", NbtMap.builder()
|
||||
.putList("properties", NbtType.COMPOUND, properties)
|
||||
.putCompound("vanilla_block_data", NbtMap.builder()
|
||||
.putInt("block_id", BLOCK_ID.getAndIncrement())
|
||||
.build());
|
||||
}
|
||||
|
||||
return new BlockPropertyData(customBlock.identifier(), propertyTag.build());
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.geysermc.geyser.item.GeyserCustomMappingData;
|
|||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.components.WearableSlot;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
|
||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
|
@ -260,18 +259,11 @@ public class CustomItemRegistryPopulator {
|
|||
}
|
||||
|
||||
private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean displayHandheld, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder, int protocolVersion) {
|
||||
NbtMap iconMap;
|
||||
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
|
||||
iconMap = NbtMap.builder()
|
||||
.putCompound("textures", NbtMap.builder()
|
||||
.putString("default", customItemData.icon())
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
iconMap = NbtMap.builder()
|
||||
.putString("texture", customItemData.icon())
|
||||
.build();
|
||||
}
|
||||
NbtMap iconMap = NbtMap.builder()
|
||||
.putCompound("textures", NbtMap.builder()
|
||||
.putString("default", customItemData.icon())
|
||||
.build())
|
||||
.build();
|
||||
itemProperties.putCompound("minecraft:icon", iconMap);
|
||||
|
||||
if (customItemData.creativeCategory().isPresent()) {
|
||||
|
@ -427,64 +419,56 @@ public class CustomItemRegistryPopulator {
|
|||
// Make bows, tridents, and crossbows enchantable
|
||||
itemProperties.putInt("enchantable_value", 1);
|
||||
|
||||
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
|
||||
componentBuilder.putCompound("minecraft:use_modifiers", NbtMap.builder()
|
||||
.putFloat("use_duration", 100F)
|
||||
.putFloat("movement_modifier", 0.35F)
|
||||
.build());
|
||||
componentBuilder.putCompound("minecraft:use_modifiers", NbtMap.builder()
|
||||
.putFloat("use_duration", 100F)
|
||||
.putFloat("movement_modifier", 0.35F)
|
||||
.build());
|
||||
|
||||
switch (mapping) {
|
||||
case "minecraft:bow" -> {
|
||||
itemProperties.putString("enchantable_slot", "bow");
|
||||
itemProperties.putInt("frame_count", 3);
|
||||
switch (mapping) {
|
||||
case "minecraft:bow" -> {
|
||||
itemProperties.putString("enchantable_slot", "bow");
|
||||
itemProperties.putInt("frame_count", 3);
|
||||
|
||||
componentBuilder.putCompound("minecraft:shooter", NbtMap.builder()
|
||||
.putList("ammunition", NbtType.COMPOUND, List.of(
|
||||
NbtMap.builder()
|
||||
.putCompound("item", NbtMap.builder()
|
||||
.putString("name", "minecraft:arrow")
|
||||
.build())
|
||||
.putBoolean("use_offhand", true)
|
||||
.putBoolean("search_inventory", true)
|
||||
.build()
|
||||
))
|
||||
.putFloat("max_draw_duration", 0f)
|
||||
.putBoolean("charge_on_draw", true)
|
||||
.putBoolean("scale_power_by_draw_duration", true)
|
||||
.build());
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
case "minecraft:trident" -> {
|
||||
itemProperties.putString("enchantable_slot", "trident");
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
case "minecraft:crossbow" -> {
|
||||
itemProperties.putString("enchantable_slot", "crossbow");
|
||||
itemProperties.putInt("frame_count", 10);
|
||||
|
||||
componentBuilder.putCompound("minecraft:shooter", NbtMap.builder()
|
||||
.putList("ammunition", NbtType.COMPOUND, List.of(
|
||||
NbtMap.builder()
|
||||
.putCompound("item", NbtMap.builder()
|
||||
.putString("name", "minecraft:arrow")
|
||||
.build())
|
||||
.putBoolean("use_offhand", true)
|
||||
.putBoolean("search_inventory", true)
|
||||
.build()
|
||||
))
|
||||
.putFloat("max_draw_duration", 1f)
|
||||
.putBoolean("charge_on_draw", true)
|
||||
.putBoolean("scale_power_by_draw_duration", true)
|
||||
.build());
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
componentBuilder.putCompound("minecraft:shooter", NbtMap.builder()
|
||||
.putList("ammunition", NbtType.COMPOUND, List.of(
|
||||
NbtMap.builder()
|
||||
.putCompound("item", NbtMap.builder()
|
||||
.putString("name", "minecraft:arrow")
|
||||
.build())
|
||||
.putBoolean("use_offhand", true)
|
||||
.putBoolean("search_inventory", true)
|
||||
.build()
|
||||
))
|
||||
.putFloat("max_draw_duration", 0f)
|
||||
.putBoolean("charge_on_draw", true)
|
||||
.putBoolean("scale_power_by_draw_duration", true)
|
||||
.build());
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
} else {
|
||||
// ensure client moves at slow speed while charging (note: this was calculated by hand as the movement modifer value does not seem to scale linearly)
|
||||
componentBuilder.putCompound("minecraft:chargeable", NbtMap.builder().putFloat("movement_modifier", 0.35F).build());
|
||||
case "minecraft:trident" -> {
|
||||
itemProperties.putString("enchantable_slot", "trident");
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
case "minecraft:crossbow" -> {
|
||||
itemProperties.putString("enchantable_slot", "crossbow");
|
||||
itemProperties.putInt("frame_count", 10);
|
||||
|
||||
// keep item enchantable; also works on 1.20.50
|
||||
itemProperties.putString("enchantable_slot", mapping.replace("minecraft:", ""));
|
||||
componentBuilder.putCompound("minecraft:shooter", NbtMap.builder()
|
||||
.putList("ammunition", NbtType.COMPOUND, List.of(
|
||||
NbtMap.builder()
|
||||
.putCompound("item", NbtMap.builder()
|
||||
.putString("name", "minecraft:arrow")
|
||||
.build())
|
||||
.putBoolean("use_offhand", true)
|
||||
.putBoolean("search_inventory", true)
|
||||
.build()
|
||||
))
|
||||
.putFloat("max_draw_duration", 1f)
|
||||
.putBoolean("charge_on_draw", true)
|
||||
.putBoolean("scale_power_by_draw_duration", true)
|
||||
.build());
|
||||
componentBuilder.putInt("minecraft:use_duration", 999);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662;
|
||||
import org.cloudburstmc.nbt.NbtUtils;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
|
||||
|
@ -62,7 +60,6 @@ import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
|||
import org.geysermc.geyser.item.GeyserCustomMappingData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.type.*;
|
||||
|
@ -91,11 +88,8 @@ public class ItemRegistryPopulator {
|
|||
|
||||
public static void populate() {
|
||||
List<PaletteVersion> paletteVersions = new ArrayList<>(3);
|
||||
paletteVersions.add(new PaletteVersion("1_20_40", Bedrock_v622.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion630_622::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_20_50", Bedrock_v630.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion649_630::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_20_60", Bedrock_v649.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion662_649::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_20_70", Bedrock_v662.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion671_662::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()));
|
||||
paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion685_671::remapItem));
|
||||
paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()));
|
||||
|
||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||
|
||||
|
@ -109,6 +103,13 @@ public class ItemRegistryPopulator {
|
|||
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
||||
}
|
||||
|
||||
NbtMap vanillaComponents;
|
||||
try (InputStream stream = bootstrap.getResourceOrThrow("mappings/item_components.nbt")) {
|
||||
vanillaComponents = (NbtMap) NbtUtils.createGZIPReader(stream, true, true).readTag();
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to load Bedrock item components", e);
|
||||
}
|
||||
|
||||
boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems();
|
||||
|
||||
// List values here is important compared to HashSet - we need to preserve the order of what's given to us
|
||||
|
@ -538,6 +539,25 @@ public class ItemRegistryPopulator {
|
|||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : vanillaComponents.entrySet()) {
|
||||
String id = entry.getKey();
|
||||
ItemDefinition definition = definitions.get(id);
|
||||
if (definition == null) {
|
||||
// Newer item most likely
|
||||
GeyserImpl.getInstance().getLogger().debug(
|
||||
"Skipping vanilla component " + id + " for protocol " + palette.protocolVersion()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
NbtMapBuilder root = NbtMap.builder()
|
||||
.putString("name", id)
|
||||
.putInt("id", definition.getRuntimeId())
|
||||
.putCompound("components", (NbtMap) entry.getValue());
|
||||
|
||||
componentItemData.add(new ComponentItemData(id, root.build()));
|
||||
}
|
||||
|
||||
// Register the item forms of custom blocks
|
||||
if (BlockRegistries.CUSTOM_BLOCKS.get().length != 0) {
|
||||
for (CustomBlockData customBlock : BlockRegistries.CUSTOM_BLOCKS.get()) {
|
||||
|
@ -602,18 +622,11 @@ public class ItemRegistryPopulator {
|
|||
NbtMapBuilder componentBuilder = NbtMap.builder();
|
||||
// Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already.
|
||||
// Not so conveniently, the way to set an icon changed in 1.20.60
|
||||
NbtMap iconMap;
|
||||
if (GameProtocol.is1_20_60orHigher(protocolVersion)) {
|
||||
iconMap = NbtMap.builder()
|
||||
.putCompound("textures", NbtMap.builder()
|
||||
.putString("default", "minecart_furnace")
|
||||
.build())
|
||||
.build();
|
||||
} else {
|
||||
iconMap = NbtMap.builder()
|
||||
.putString("texture", "minecart_furnace")
|
||||
.build();
|
||||
}
|
||||
NbtMap iconMap = NbtMap.builder()
|
||||
.putCompound("textures", NbtMap.builder()
|
||||
.putString("default", "minecart_furnace")
|
||||
.build())
|
||||
.build();
|
||||
itemProperties.putCompound("minecraft:icon", iconMap);
|
||||
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", "item.minecartFurnace.name").build());
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtUtils;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.RecipeUnlockingRequirement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.MultiRecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.RecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.ShapedRecipeData;
|
||||
|
@ -173,7 +174,7 @@ public class RecipeRegistryPopulator {
|
|||
/* Convert end */
|
||||
|
||||
return ShapedRecipeData.shaped(uuid.toString(), shape.get(0).length(), shape.size(),
|
||||
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId, false);
|
||||
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId, false, RecipeUnlockingRequirement.INVALID);
|
||||
}
|
||||
List<ItemData> inputs = new ObjectArrayList<>();
|
||||
for (JsonNode entry : node.get("inputs")) {
|
||||
|
@ -196,7 +197,7 @@ public class RecipeRegistryPopulator {
|
|||
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId);
|
||||
}
|
||||
return ShapelessRecipeData.shapeless(uuid.toString(),
|
||||
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId);
|
||||
inputs.stream().map(ItemDescriptorWithCount::fromItem).toList(), Collections.singletonList(output), uuid, "crafting_table", 0, netId, RecipeUnlockingRequirement.INVALID);
|
||||
}
|
||||
|
||||
private static ItemData getBedrockItemFromIdentifierJson(ItemMapping mapping, JsonNode itemNode) {
|
||||
|
|
|
@ -1,91 +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.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();
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @return the identifier without the additional block states
|
||||
*/
|
||||
public String getCleanJavaIdentifier() {
|
||||
return BlockUtils.getCleanIdentifier(javaIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the corresponding Java identifier for this item
|
||||
*/
|
||||
public String getItemIdentifier() {
|
||||
if (pickItem != null && !pickItem.equals("minecraft:air")) {
|
||||
// Spawners can have air as their pick item which we are not interested in.
|
||||
return pickItem;
|
||||
}
|
||||
|
||||
return getCleanJavaIdentifier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item a Java client would receive when pressing
|
||||
* the Pick Block key on a specific Java block state.
|
||||
*
|
||||
* @return The Java identifier of the item
|
||||
*/
|
||||
public String getPickItem() {
|
||||
if (pickItem != null) {
|
||||
return pickItem;
|
||||
}
|
||||
|
||||
return getCleanJavaIdentifier();
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData;
|
|||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.common.DefinitionRegistry;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -50,6 +52,12 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
|||
GeyserBedrockBlock[] javaToBedrockBlocks;
|
||||
GeyserBedrockBlock[] javaToVanillaBedrockBlocks;
|
||||
|
||||
/**
|
||||
* Java block ID -> Bedrock block ID (without minecraft:), IF they are different
|
||||
* While Bedrock is progressing slowly through their flattening, some Bedrock identifiers may differ.
|
||||
*/
|
||||
Int2ObjectMap<String> javaToBedrockIdentifiers;
|
||||
|
||||
Map<NbtMap, GeyserBedrockBlock> stateDefinitionMap;
|
||||
GeyserBedrockBlock[] bedrockRuntimeMap;
|
||||
int[] remappedVanillaIds;
|
||||
|
@ -58,7 +66,7 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
|||
BlockDefinition mobSpawnerBlock;
|
||||
|
||||
Map<NbtMap, BlockDefinition> itemFrames;
|
||||
Map<String, NbtMap> flowerPotBlocks;
|
||||
Map<Block, NbtMap> flowerPotBlocks;
|
||||
|
||||
Set<BlockDefinition> jigsawStates;
|
||||
Map<String, BlockDefinition> structureBlockStates;
|
||||
|
@ -78,6 +86,14 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
|
|||
return this.javaToBedrockBlocks[javaState];
|
||||
}
|
||||
|
||||
public GeyserBedrockBlock getBedrockBlock(BlockState javaState) {
|
||||
return this.getBedrockBlock(javaState.javaId());
|
||||
}
|
||||
|
||||
public GeyserBedrockBlock getVanillaBedrockBlock(BlockState javaState) {
|
||||
return getVanillaBedrockBlock(javaState.javaId());
|
||||
}
|
||||
|
||||
public GeyserBedrockBlock getVanillaBedrockBlock(int javaState) {
|
||||
if (javaState < 0 || javaState >= this.javaToVanillaBedrockBlocks.length) {
|
||||
return bedrockAir;
|
||||
|
|
|
@ -28,35 +28,6 @@ package org.geysermc.geyser.session;
|
|||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.auth.exception.request.RequestException;
|
||||
import com.github.steveice10.mc.auth.service.MsaAuthenticationService;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||
import org.geysermc.mcprotocollib.protocol.data.ProtocolState;
|
||||
import org.geysermc.mcprotocollib.protocol.data.UnexpectedEncryptionException;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.statistic.CustomStatistic;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.statistic.Statistic;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.handshake.serverbound.ClientIntentionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandSignedPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
|
||||
import org.geysermc.mcprotocollib.network.BuiltinFlags;
|
||||
import org.geysermc.mcprotocollib.network.Session;
|
||||
import org.geysermc.mcprotocollib.network.event.session.*;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
@ -126,8 +97,8 @@ import org.geysermc.geyser.inventory.PlayerInventory;
|
|||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.BlockItem;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||
import org.geysermc.geyser.network.netty.LocalSession;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
|
@ -145,6 +116,38 @@ import org.geysermc.geyser.util.ChunkUtils;
|
|||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||
import org.geysermc.mcprotocollib.network.BuiltinFlags;
|
||||
import org.geysermc.mcprotocollib.network.Session;
|
||||
import org.geysermc.mcprotocollib.network.event.session.*;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
|
||||
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
|
||||
import org.geysermc.mcprotocollib.protocol.ClientListener;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||
import org.geysermc.mcprotocollib.protocol.data.ProtocolState;
|
||||
import org.geysermc.mcprotocollib.protocol.data.UnexpectedEncryptionException;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.statistic.CustomStatistic;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.statistic.Statistic;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundFinishConfigurationPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.configuration.serverbound.ServerboundFinishConfigurationPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.handshake.serverbound.ClientIntentionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandSignedPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -253,13 +256,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
private final Map<Vector3i, ItemFrameEntity> itemFrameCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Stores a list of all lectern locations and their block entity tags.
|
||||
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
|
||||
* for more information.
|
||||
*/
|
||||
private final @Nullable Set<Vector3i> lecternCache;
|
||||
|
||||
/**
|
||||
* A list of all players that have a player head on with a custom texture.
|
||||
* Our workaround for these players is to give them a custom skin and geometry to emulate wearing a custom skull.
|
||||
|
@ -288,7 +284,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
private volatile boolean closed;
|
||||
|
||||
@Setter
|
||||
private GameMode gameMode = GameMode.SURVIVAL;
|
||||
|
||||
/**
|
||||
|
@ -313,22 +308,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
@Setter
|
||||
private boolean sprinting;
|
||||
|
||||
/**
|
||||
* Whether the player is swimming in water.
|
||||
* Used to update speed when crawling.
|
||||
*/
|
||||
@Setter
|
||||
private boolean swimmingInWater;
|
||||
|
||||
/**
|
||||
* Tracks the original speed attribute.
|
||||
* <p>
|
||||
* We need to do this in order to emulate speeds when sneaking under 1.5-blocks-tall areas if the player isn't sneaking,
|
||||
* and when crawling.
|
||||
*/
|
||||
@Setter
|
||||
private float originalSpeedAttribute;
|
||||
|
||||
/**
|
||||
* The dimension of the player.
|
||||
* As all entities are in the same world, this can be safely applied to all other entities.
|
||||
|
@ -346,7 +325,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
private Vector3i lastBlockPlacePosition;
|
||||
|
||||
@Setter
|
||||
private String lastBlockPlacedId;
|
||||
private BlockItem lastBlockPlaced;
|
||||
|
||||
@Setter
|
||||
private boolean interacting;
|
||||
|
@ -621,13 +600,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
this.spawned = false;
|
||||
this.loggedIn = false;
|
||||
|
||||
if (geyser.getWorldManager().shouldExpectLecternHandled(this)) {
|
||||
// Unneeded on these platforms
|
||||
this.lecternCache = null;
|
||||
} else {
|
||||
this.lecternCache = new ObjectOpenHashSet<>();
|
||||
}
|
||||
|
||||
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
||||
this.emotes = new HashSet<>();
|
||||
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||
|
@ -646,9 +618,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
sentSpawnPacket = true;
|
||||
syncEntityProperties();
|
||||
|
||||
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
||||
// upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId());
|
||||
|
||||
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
||||
ItemComponentPacket componentPacket = new ItemComponentPacket();
|
||||
componentPacket.getItems().addAll(itemMappings.getComponentItemData());
|
||||
|
@ -875,6 +844,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
// Start ticking
|
||||
tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
||||
|
||||
this.protocol.setUseDefaultListeners(false);
|
||||
|
||||
TcpSession downstream;
|
||||
if (geyser.getBootstrap().getSocketAddress() != null) {
|
||||
// We're going to connect through the JVM and not through TCP
|
||||
|
@ -907,6 +878,25 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
// Let Geyser handle sending the keep alive
|
||||
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
||||
}
|
||||
// We'll handle this since we have the registry data on hand
|
||||
downstream.setFlag(MinecraftConstants.SEND_BLANK_KNOWN_PACKS_RESPONSE, false);
|
||||
|
||||
// This isn't a great solution, but... we want to make sure the finish configuration packet cannot be sent
|
||||
// before the KnownPacks packet.
|
||||
this.downstream.getSession().addListener(new ClientListener(ProtocolState.LOGIN, loginEvent.transferring()) {
|
||||
@Override
|
||||
public void packetReceived(Session session, Packet packet) {
|
||||
if (protocol.getState() == ProtocolState.CONFIGURATION) {
|
||||
if (packet instanceof ClientboundFinishConfigurationPacket) {
|
||||
// Prevent
|
||||
GeyserSession.this.ensureInEventLoop(() -> GeyserSession.this.sendDownstreamPacket(new ServerboundFinishConfigurationPacket()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.packetReceived(session, packet);
|
||||
}
|
||||
});
|
||||
|
||||
downstream.addListener(new SessionAdapter() {
|
||||
@Override
|
||||
public void packetSending(PacketSendingEvent event) {
|
||||
|
@ -1264,21 +1254,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
this.sneaking = sneaking;
|
||||
|
||||
// Update pose and bounding box on our end
|
||||
AttributeData speedAttribute;
|
||||
if (!sneaking && (speedAttribute = adjustSpeed()) != null) {
|
||||
// Update attributes since we're still "sneaking" under a 1.5-block-tall area
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(playerEntity.getGeyserId());
|
||||
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
|
||||
sendUpstreamPacket(attributesPacket);
|
||||
// the server *should* update our pose once it has returned to normal
|
||||
} else {
|
||||
if (!flying) {
|
||||
// The pose and bounding box should not be updated if the player is flying
|
||||
setSneakingPose(sneaking);
|
||||
}
|
||||
collisionManager.updateScaffoldingFlags(false);
|
||||
if (!flying) {
|
||||
// The pose and bounding box should not be updated if the player is flying
|
||||
setSneakingPose(sneaking);
|
||||
}
|
||||
collisionManager.updateScaffoldingFlags(false);
|
||||
|
||||
playerEntity.updateBedrockMetadata();
|
||||
|
||||
|
@ -1321,28 +1301,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts speed if the player is crawling.
|
||||
*
|
||||
* @return not null if attributes should be updated.
|
||||
*/
|
||||
public @Nullable AttributeData adjustSpeed() {
|
||||
AttributeData currentPlayerSpeed = playerEntity.getAttributes().get(GeyserAttributeType.MOVEMENT_SPEED);
|
||||
if (currentPlayerSpeed != null) {
|
||||
if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.mustPlayerSneakHere()) ||
|
||||
(!swimmingInWater && playerEntity.getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) {
|
||||
// Either of those conditions means that Bedrock goes zoom when they shouldn't be
|
||||
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute / 3.32f);
|
||||
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
|
||||
return speedAttribute;
|
||||
} else if (originalSpeedAttribute != currentPlayerSpeed.getValue()) {
|
||||
// Speed has reset to normal
|
||||
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute);
|
||||
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
|
||||
return speedAttribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
public void setGameMode(GameMode newGamemode) {
|
||||
boolean currentlySpectator = this.gameMode == GameMode.SPECTATOR;
|
||||
this.gameMode = newGamemode;
|
||||
this.cameraData.handleGameModeChange(currentlySpectator, newGamemode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1546,8 +1508,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
startGamePacket.setEnchantmentSeed(0);
|
||||
startGamePacket.setMultiplayerCorrelationId("");
|
||||
|
||||
startGamePacket.setItemDefinitions(this.itemMappings.getItemDefinitions().values().stream().toList()); // TODO
|
||||
// startGamePacket.setBlockPalette(this.blockMappings.getBedrockBlockPalette());
|
||||
startGamePacket.getItemDefinitions().addAll(this.itemMappings.getItemDefinitions().values());
|
||||
|
||||
// Needed for custom block mappings and custom skulls system
|
||||
startGamePacket.getBlockProperties().addAll(this.blockMappings.getBlockProperties());
|
||||
|
@ -1575,8 +1536,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
startGamePacket.setRewindHistorySize(0);
|
||||
startGamePacket.setServerAuthoritativeBlockBreaking(false);
|
||||
|
||||
// Entity properties for older versions
|
||||
startGamePacket.getExperiments().add(new ExperimentData("upcoming_creator_features", true));
|
||||
startGamePacket.setServerId("");
|
||||
startGamePacket.setWorldId("");
|
||||
startGamePacket.setScenarioId("");
|
||||
|
||||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.chunk.DataPalette;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
@ -92,11 +93,11 @@ public class ChunkCache {
|
|||
|
||||
DataPalette palette = chunk.sections()[(y - minY) >> 4];
|
||||
if (palette == null) {
|
||||
if (block != BlockStateValues.JAVA_AIR_ID) {
|
||||
if (block != Block.JAVA_AIR_ID) {
|
||||
// A previously empty chunk, which is no longer empty as a block has been added to it
|
||||
palette = DataPalette.createForChunk();
|
||||
// Fixes the chunk assuming that all blocks is the `block` variable we are updating. /shrug
|
||||
palette.getPalette().stateToId(BlockStateValues.JAVA_AIR_ID);
|
||||
palette.getPalette().stateToId(Block.JAVA_AIR_ID);
|
||||
chunk.sections()[(y - minY) >> 4] = palette;
|
||||
} else {
|
||||
// Nothing to update
|
||||
|
@ -109,17 +110,17 @@ public class ChunkCache {
|
|||
|
||||
public int getBlockAt(int x, int y, int z) {
|
||||
if (!cache) {
|
||||
return BlockStateValues.JAVA_AIR_ID;
|
||||
return Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
GeyserChunk column = this.getChunk(x >> 4, z >> 4);
|
||||
if (column == null) {
|
||||
return BlockStateValues.JAVA_AIR_ID;
|
||||
return Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
if (y < minY || ((y - minY) >> 4) > column.sections().length - 1) {
|
||||
// Y likely goes above or below the height limit of this world
|
||||
return BlockStateValues.JAVA_AIR_ID;
|
||||
return Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
DataPalette chunk = column.sections()[(y - minY) >> 4];
|
||||
|
@ -127,7 +128,7 @@ public class ChunkCache {
|
|||
return chunk.get(x & 0xF, y & 0xF, z & 0xF);
|
||||
}
|
||||
|
||||
return BlockStateValues.JAVA_AIR_ID;
|
||||
return Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
public void removeChunk(int chunkX, int chunkZ) {
|
||||
|
|
|
@ -30,6 +30,8 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
|||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
@ -42,6 +44,7 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
|||
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket;
|
||||
|
||||
|
@ -63,6 +66,7 @@ import java.util.function.ToIntFunction;
|
|||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
public final class RegistryCache {
|
||||
private static final Map<String, Map<String, NbtMap>> DEFAULTS;
|
||||
private static final Map<String, BiConsumer<RegistryCache, List<RegistryEntry>>> REGISTRIES = new HashMap<>();
|
||||
|
||||
static {
|
||||
|
@ -73,6 +77,24 @@ public final class RegistryCache {
|
|||
register("worldgen/biome", (cache, array) -> cache.biomeTranslations = array, BiomeTranslator::loadServerBiome);
|
||||
register("banner_pattern", cache -> cache.bannerPatterns, ($, entry) -> BannerPattern.getByJavaIdentifier(entry.getId()));
|
||||
register("wolf_variant", cache -> cache.wolfVariants, ($, entry) -> WolfEntity.WolfVariant.getByJavaIdentifier(entry.getId()));
|
||||
|
||||
// Load from MCProtocolLib's classloader
|
||||
NbtMap tag = MinecraftProtocol.loadNetworkCodec();
|
||||
Map<String, Map<String, NbtMap>> defaults = new HashMap<>();
|
||||
// Don't create a keySet - no need to create the cached object in HashMap if we don't use it again
|
||||
REGISTRIES.forEach((key, $) -> {
|
||||
List<NbtMap> rawValues = tag.getCompound(key)
|
||||
.getList("value", NbtType.COMPOUND);
|
||||
Map<String, NbtMap> values = new HashMap<>();
|
||||
for (NbtMap value : rawValues) {
|
||||
String name = value.getString("name");
|
||||
values.put(name, value.getCompound("element"));
|
||||
}
|
||||
// Can make these maps immutable and as efficient as possible after initialization
|
||||
defaults.put(key, Map.copyOf(values));
|
||||
});
|
||||
|
||||
DEFAULTS = Map.copyOf(defaults);
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
|
@ -116,13 +138,22 @@ public final class RegistryCache {
|
|||
* @param <T> the class that represents these entries.
|
||||
*/
|
||||
private static <T> void register(String registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, BiFunction<GeyserSession, RegistryEntry, T> reader) {
|
||||
REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> {
|
||||
String key = "minecraft:" + registry;
|
||||
REGISTRIES.put(key, (registryCache, entries) -> {
|
||||
Map<String, NbtMap> localRegistry = null;
|
||||
JavaRegistry<T> localCache = localCacheFunction.apply(registryCache);
|
||||
// Clear each local cache every time a new registry entry is given to us
|
||||
// (e.g. proxy server switches)
|
||||
List<T> builder = new ArrayList<>(entries.size());
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
// If the data is null, that's the server telling us we need to use our default values.
|
||||
if (entry.getData() == null) {
|
||||
if (localRegistry == null) { // Lazy initialize
|
||||
localRegistry = DEFAULTS.get(key);
|
||||
}
|
||||
entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId()));
|
||||
}
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
T cacheEntry = reader.apply(registryCache.session, entry);
|
||||
builder.add(i, cacheEntry);
|
||||
|
@ -156,4 +187,8 @@ public final class RegistryCache {
|
|||
localCacheFunction.accept(registryCache, array);
|
||||
});
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,18 +25,20 @@
|
|||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.block.type.WallSkullBlock;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -80,7 +82,7 @@ public class SkullCache {
|
|||
this.skullRenderDistanceSquared = distance * distance;
|
||||
}
|
||||
|
||||
public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, int blockState) {
|
||||
public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, BlockState blockState) {
|
||||
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
||||
skull.uuid = uuid;
|
||||
if (!texturesProperty.equals(skull.texturesProperty)) {
|
||||
|
@ -147,7 +149,7 @@ public class SkullCache {
|
|||
}
|
||||
}
|
||||
|
||||
public Skull updateSkull(Vector3i position, int blockState) {
|
||||
public Skull updateSkull(Vector3i position, BlockState blockState) {
|
||||
Skull skull = skulls.get(position);
|
||||
if (skull != null) {
|
||||
putSkull(position, skull.uuid, skull.texturesProperty, blockState);
|
||||
|
@ -248,17 +250,14 @@ public class SkullCache {
|
|||
lastPlayerPosition = null;
|
||||
}
|
||||
|
||||
private @Nullable BlockDefinition translateCustomSkull(String skinHash, int blockState) {
|
||||
private @Nullable BlockDefinition translateCustomSkull(String skinHash, BlockState blockState) {
|
||||
CustomSkull customSkull = BlockRegistries.CUSTOM_SKULLS.get(skinHash);
|
||||
if (customSkull != null) {
|
||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||
CustomBlockState customBlockState;
|
||||
if (floorRotation == -1) {
|
||||
// Wall skull
|
||||
int wallDirection = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||
customBlockState = customSkull.getWallBlockState(wallDirection);
|
||||
if (blockState.block() instanceof WallSkullBlock) {
|
||||
customBlockState = customSkull.getWallBlockState(WallSkullBlock.getDegrees(blockState));
|
||||
} else {
|
||||
customBlockState = customSkull.getFloorBlockState(floorRotation);
|
||||
customBlockState = customSkull.getFloorBlockState(blockState.getValue(Properties.ROTATION_16));
|
||||
}
|
||||
|
||||
return session.getBlockMappings().getCustomBlockStateDefinitions().get(customBlockState);
|
||||
|
@ -273,7 +272,7 @@ public class SkullCache {
|
|||
private String texturesProperty;
|
||||
private String skinHash;
|
||||
|
||||
private int blockState;
|
||||
private BlockState blockState;
|
||||
private BlockDefinition blockDefinition;
|
||||
private SkullPlayerEntity entity;
|
||||
|
||||
|
|
|
@ -25,15 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.BlockTag;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.EnumMap;
|
||||
|
@ -98,10 +98,10 @@ public final class TagCache {
|
|||
/**
|
||||
* @return true if the block tag is present and contains this block mapping's Java ID.
|
||||
*/
|
||||
public boolean is(BlockTag tag, BlockMapping mapping) {
|
||||
public boolean is(BlockTag tag, Block block) {
|
||||
IntList values = this.blocks.get(tag);
|
||||
if (values != null) {
|
||||
return values.contains(mapping.getJavaBlockId());
|
||||
return values.contains(block.javaId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +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.text;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.chat.BuiltinChatType;
|
||||
|
||||
public record ChatTypeEntry(TextPacket.@NonNull Type bedrockChatType, @Nullable TextDecoration textDecoration) {
|
||||
private static final ChatTypeEntry CHAT = new ChatTypeEntry(TextPacket.Type.CHAT, null);
|
||||
private static final ChatTypeEntry RAW = new ChatTypeEntry(TextPacket.Type.RAW, null);
|
||||
|
||||
/**
|
||||
* Apply defaults to a map so it isn't empty in the event a chat message is sent before the login packet.
|
||||
*/
|
||||
public static void applyDefaults(Int2ObjectMap<ChatTypeEntry> chatTypes) {
|
||||
// So the proper way to do this, probably, would be to dump the NBT data from vanilla and load it.
|
||||
// But, the only way this happens is if a chat message is sent to us before the login packet, which is rare.
|
||||
// So we'll just make sure chat ends up in the right place.
|
||||
chatTypes.put(BuiltinChatType.CHAT.ordinal(), CHAT);
|
||||
chatTypes.put(BuiltinChatType.SAY_COMMAND.ordinal(), RAW);
|
||||
chatTypes.put(BuiltinChatType.MSG_COMMAND_INCOMING.ordinal(), RAW);
|
||||
chatTypes.put(BuiltinChatType.MSG_COMMAND_OUTGOING.ordinal(), RAW);
|
||||
chatTypes.put(BuiltinChatType.TEAM_MSG_COMMAND_INCOMING.ordinal(), RAW);
|
||||
chatTypes.put(BuiltinChatType.TEAM_MSG_COMMAND_OUTGOING.ordinal(), RAW);
|
||||
chatTypes.put(BuiltinChatType.EMOTE_COMMAND.ordinal(), RAW);
|
||||
}
|
||||
}
|
|
@ -148,9 +148,9 @@ public class GeyserLocale {
|
|||
} catch (IOException ignored) {}
|
||||
}
|
||||
} else {
|
||||
if (GeyserImpl.getInstance() != null && !validLocalLanguage) {
|
||||
if (!validLocalLanguage) {
|
||||
// Don't warn on missing locales if a local file has been found
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing locale: " + locale);
|
||||
bootstrap.getGeyserLogger().warning("Missing locale: " + locale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,12 +162,7 @@ public class GeyserLocale {
|
|||
localeProp.load(stream);
|
||||
} catch (IOException e) {
|
||||
String message = "Unable to load custom language override!";
|
||||
if (GeyserImpl.getInstance() != null) {
|
||||
GeyserImpl.getInstance().getLogger().error(message, e);
|
||||
} else {
|
||||
System.err.println(message);
|
||||
e.printStackTrace();
|
||||
}
|
||||
bootstrap.getGeyserLogger().error(message, e);
|
||||
}
|
||||
|
||||
LOCALE_MAPPINGS.putIfAbsent(locale, localeProp);
|
||||
|
|
|
@ -61,7 +61,7 @@ public class BlockCollision {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overridden in classes like SnowCollision and GrassPathCollision when correction code needs to be run before the
|
||||
* Overridden in classes like GrassPathCollision when correction code needs to be run before the
|
||||
* main correction
|
||||
*/
|
||||
public void beforeCorrectPosition(int x, int y, int z, BoundingBox playerCollision) {}
|
||||
|
|
|
@ -37,12 +37,6 @@ public @interface CollisionRemapper {
|
|||
*/
|
||||
String regex();
|
||||
|
||||
/**
|
||||
* Regex of block state parameters to apply this collision to
|
||||
* Defaults to matching any value
|
||||
*/
|
||||
String paramRegex() default ".*";
|
||||
|
||||
/**
|
||||
* Signals if a new instance needs to created for every block state
|
||||
*/
|
||||
|
|
|
@ -26,13 +26,14 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "^dirt_path$", passDefaultBoxes = true)
|
||||
public class DirtPathCollision extends BlockCollision {
|
||||
public DirtPathCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
public DirtPathCollision(BlockState state, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -40,20 +42,18 @@ public class DoorCollision extends BlockCollision {
|
|||
*/
|
||||
private int facing;
|
||||
|
||||
public DoorCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
public DoorCollision(BlockState state, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
if (params.contains("facing=north")) {
|
||||
facing = 1;
|
||||
} else if (params.contains("facing=east")) {
|
||||
facing = 2;
|
||||
} else if (params.contains("facing=south")) {
|
||||
facing = 3;
|
||||
} else if (params.contains("facing=west")) {
|
||||
facing = 4;
|
||||
}
|
||||
facing = switch (state.getValue(Properties.HORIZONTAL_FACING)) {
|
||||
case NORTH -> 1;
|
||||
case EAST -> 2;
|
||||
case SOUTH -> 3;
|
||||
case WEST -> 4;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
|
||||
// If the door is open it changes direction
|
||||
if (params.contains("open=true")) {
|
||||
if (state.getValue(Properties.OPEN)) {
|
||||
facing = facing % 2 + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -44,24 +46,23 @@ public class GlassPaneAndIronBarsCollision extends BlockCollision {
|
|||
*/
|
||||
private int facing;
|
||||
|
||||
public GlassPaneAndIronBarsCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
public GlassPaneAndIronBarsCollision(BlockState state, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
//east=true,north=true,south=true,west=true
|
||||
if (params.contains("north=true") && params.contains("east=true")) {
|
||||
if (state.getValue(Properties.NORTH) && state.getValue(Properties.EAST)) {
|
||||
facing = 5;
|
||||
} else if (params.contains("east=true") && params.contains("south=true")) {
|
||||
} else if (state.getValue(Properties.EAST) && state.getValue(Properties.SOUTH)) {
|
||||
facing = 6;
|
||||
} else if (params.contains("south=true") && params.contains("west=true")) {
|
||||
} else if (state.getValue(Properties.SOUTH) && state.getValue(Properties.WEST)) {
|
||||
facing = 7;
|
||||
} else if (params.contains("west=true") && params.contains("north=true")) {
|
||||
} else if (state.getValue(Properties.WEST) && state.getValue(Properties.NORTH)) {
|
||||
facing = 8;
|
||||
} else if (params.contains("north=true")) {
|
||||
} else if (state.getValue(Properties.NORTH)) {
|
||||
facing = 1;
|
||||
} else if (params.contains("east=true")) {
|
||||
} else if (state.getValue(Properties.EAST)) {
|
||||
facing = 2;
|
||||
} else if (params.contains("south=true")) {
|
||||
} else if (state.getValue(Properties.SOUTH)) {
|
||||
facing = 3;
|
||||
} else if (params.contains("west=true")) {
|
||||
} else if (state.getValue(Properties.WEST)) {
|
||||
facing = 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
|
@ -35,7 +36,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "^scaffolding$", usesParams = true, passDefaultBoxes = true)
|
||||
public class ScaffoldingCollision extends BlockCollision {
|
||||
public ScaffoldingCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
public ScaffoldingCollision(BlockState state, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,89 +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.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "^snow$", passDefaultBoxes = true, usesParams = true)
|
||||
public class SnowCollision extends BlockCollision {
|
||||
private final int layers;
|
||||
|
||||
public SnowCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
int layerCharIndex = params.indexOf("=") + 1;
|
||||
layers = Integer.parseInt(params.substring(layerCharIndex, layerCharIndex + 1));
|
||||
|
||||
pushUpTolerance = 0.125;
|
||||
}
|
||||
|
||||
// Needs to run before the main correction code or it can move the player into blocks
|
||||
// This is counteracted by the main collision code pushing them out
|
||||
@Override
|
||||
public void beforeCorrectPosition(int x, int y, int z, BoundingBox playerCollision) {
|
||||
// In Bedrock, snow layers round down to half blocks but you can't sink into them at all
|
||||
// This means the collision each half block reaches above where it should be on Java so the player has to be
|
||||
// pushed down
|
||||
if (layers == 4 || layers == 8) {
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
|
||||
// If the player is in the buggy area, push them down
|
||||
if (playerMinY > boxMaxY &&
|
||||
playerMinY <= (boxMaxY + 0.125)) {
|
||||
playerCollision.translate(0, boxMaxY - playerMinY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
|
||||
if (layers == 1) {
|
||||
// 1 layer of snow does not have collision
|
||||
return true;
|
||||
}
|
||||
// Hack to prevent false positives
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() - 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
|
||||
|
||||
if (this.checkIntersection(x, y, z, playerCollision)) {
|
||||
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
|
||||
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
|
||||
// If the player actually can't step onto it (they can step onto it from other snow layers)
|
||||
if ((boxMaxY - playerMinY) > 0.5) {
|
||||
// Cancel the movement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
playerCollision.setSizeX(playerCollision.getSizeX() + 0.0001);
|
||||
playerCollision.setSizeY(playerCollision.getSizeY() + 0.0001);
|
||||
playerCollision.setSizeZ(playerCollision.getSizeZ() + 0.0001);
|
||||
return super.correctPosition(session, x, y, z, playerCollision);
|
||||
}
|
||||
}
|
|
@ -26,12 +26,13 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "shulker_box$") // These have no collision in the mappings as it depends on the NBT data
|
||||
public class SolidCollision extends BlockCollision {
|
||||
public SolidCollision(String params) {
|
||||
public SolidCollision(BlockState state) {
|
||||
super(new BoundingBox[] {
|
||||
new BoundingBox(0.5, 0.5, 0.5, 1, 1, 1)
|
||||
});
|
||||
|
|
|
@ -26,42 +26,27 @@
|
|||
package org.geysermc.geyser.translator.collision;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.level.physics.CollisionManager;
|
||||
import org.geysermc.geyser.level.physics.Direction;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@CollisionRemapper(regex = "_trapdoor$", usesParams = true, passDefaultBoxes = true)
|
||||
public class TrapdoorCollision extends BlockCollision {
|
||||
/**
|
||||
* 1 = north
|
||||
* 2 = east
|
||||
* 3 = south
|
||||
* 4 = west
|
||||
* 5 = up
|
||||
* 6 = down
|
||||
*/
|
||||
private int facing;
|
||||
private final Direction facing;
|
||||
|
||||
public TrapdoorCollision(String params, BoundingBox[] defaultBoxes) {
|
||||
public TrapdoorCollision(BlockState state, BoundingBox[] defaultBoxes) {
|
||||
super(defaultBoxes);
|
||||
if (params.contains("open=true")) {
|
||||
if (params.contains("facing=north")) {
|
||||
facing = 1;
|
||||
} else if (params.contains("facing=east")) {
|
||||
facing = 2;
|
||||
} else if (params.contains("facing=south")) {
|
||||
facing = 3;
|
||||
} else if (params.contains("facing=west")) {
|
||||
facing = 4;
|
||||
}
|
||||
if (state.getValue(Properties.OPEN)) {
|
||||
facing = state.getValue(Properties.HORIZONTAL_FACING);
|
||||
} else {
|
||||
if (params.contains("half=bottom")) {
|
||||
// Up
|
||||
facing = 5;
|
||||
if (state.getValue(Properties.HALF).equals("bottom")) {
|
||||
facing = Direction.UP;
|
||||
} else {
|
||||
// Down
|
||||
facing = 6;
|
||||
facing = Direction.DOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,22 +57,22 @@ public class TrapdoorCollision extends BlockCollision {
|
|||
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
|
||||
if (this.checkIntersection(x, y, z, playerCollision)) {
|
||||
switch (facing) {
|
||||
case 1: // North
|
||||
case NORTH:
|
||||
playerCollision.setMiddleZ(z + 0.5125);
|
||||
break;
|
||||
case 2: // East
|
||||
case EAST:
|
||||
playerCollision.setMiddleX(x + 0.5125);
|
||||
break;
|
||||
case 3: // South
|
||||
case SOUTH:
|
||||
playerCollision.setMiddleZ(z + 0.4875);
|
||||
break;
|
||||
case 4: // West
|
||||
case WEST:
|
||||
playerCollision.setMiddleX(x + 0.4875);
|
||||
break;
|
||||
case 5:
|
||||
case UP:
|
||||
// Up-facing trapdoors are handled by the step-up check
|
||||
break;
|
||||
case 6: // Down
|
||||
case DOWN:
|
||||
// (top y of trap door) - (trap door thickness) = top y of player
|
||||
playerCollision.setMiddleY(y + 1 - (3.0 / 16.0) - playerCollision.getSizeY() / 2.0 - CollisionManager.COLLISION_TOLERANCE);
|
||||
break;
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.geysermc.geyser.inventory.Inventory;
|
|||
import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
|
||||
import org.geysermc.geyser.inventory.holder.InventoryHolder;
|
||||
import org.geysermc.geyser.inventory.updater.InventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.type.Block;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
/**
|
||||
|
@ -39,17 +41,25 @@ public abstract class AbstractBlockInventoryTranslator extends BaseInventoryTran
|
|||
private final InventoryHolder holder;
|
||||
private final InventoryUpdater updater;
|
||||
|
||||
/**
|
||||
* @param javaBlock a Java block that is used as a temporary block
|
||||
*/
|
||||
public AbstractBlockInventoryTranslator(int size, Block javaBlock, ContainerType containerType, InventoryUpdater updater,
|
||||
Block... additionalValidBlocks) {
|
||||
this(size, javaBlock.defaultBlockState(), containerType, updater, additionalValidBlocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param size the amount of slots that the inventory adds alongside the base inventory slots
|
||||
* @param javaBlockIdentifier a Java block identifier that is used as a temporary block
|
||||
* @param javaBlockState a Java block state that is used as a temporary block
|
||||
* @param containerType the container type of this inventory
|
||||
* @param updater updater
|
||||
* @param additionalValidBlocks any other block identifiers that can safely use this inventory without a fake block
|
||||
* @param additionalValidBlocks any other blocks that can safely use this inventory without a fake block
|
||||
*/
|
||||
public AbstractBlockInventoryTranslator(int size, String javaBlockIdentifier, ContainerType containerType, InventoryUpdater updater,
|
||||
String... additionalValidBlocks) {
|
||||
public AbstractBlockInventoryTranslator(int size, BlockState javaBlockState, ContainerType containerType, InventoryUpdater updater,
|
||||
Block... additionalValidBlocks) {
|
||||
super(size);
|
||||
this.holder = new BlockInventoryHolder(javaBlockIdentifier, containerType, additionalValidBlocks);
|
||||
this.holder = new BlockInventoryHolder(javaBlockState, containerType, additionalValidBlocks);
|
||||
this.updater = updater;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.geysermc.geyser.inventory.BedrockContainerSlot;
|
|||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.updater.AnvilInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
||||
|
@ -44,8 +45,8 @@ import java.util.Objects;
|
|||
|
||||
public class AnvilInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
public AnvilInventoryTranslator() {
|
||||
super(3, "minecraft:anvil[facing=north]", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE,
|
||||
"minecraft:chipped_anvil", "minecraft:damaged_anvil");
|
||||
super(3, Blocks.ANVIL, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.ANVIL, AnvilInventoryUpdater.INSTANCE,
|
||||
Blocks.CHIPPED_ANVIL, Blocks.DAMAGED_ANVIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.inventory;
|
|||
|
||||
import it.unimi.dsi.fastutil.ints.IntSets;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
|
||||
|
@ -43,7 +42,9 @@ import org.geysermc.geyser.inventory.Inventory;
|
|||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.holder.BlockInventoryHolder;
|
||||
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetBeaconPacket;
|
||||
|
@ -52,7 +53,7 @@ import java.util.OptionalInt;
|
|||
|
||||
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
public BeaconInventoryTranslator() {
|
||||
super(1, new BlockInventoryHolder("minecraft:beacon", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.BEACON) {
|
||||
super(1, new BlockInventoryHolder(Blocks.BEACON, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.BEACON) {
|
||||
@Override
|
||||
protected boolean checkInteractionPosition(GeyserSession session) {
|
||||
// Since we can't fall back to a virtual inventory, let's make opening one easier
|
||||
|
@ -89,12 +90,8 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator
|
|||
|
||||
// Send a block entity data packet update to the fake beacon inventory
|
||||
Vector3i position = inventory.getHolderPosition();
|
||||
NbtMapBuilder builder = NbtMap.builder()
|
||||
.putInt("x", position.getX())
|
||||
.putInt("y", position.getY())
|
||||
.putInt("z", position.getZ())
|
||||
NbtMapBuilder builder = BlockEntityTranslator.getConstantBedrockTag("Beacon", position)
|
||||
.putString("CustomName", inventory.getTitle())
|
||||
.putString("id", "Beacon")
|
||||
.putInt("primary", beaconContainer.getPrimaryId())
|
||||
.putInt("secondary", beaconContainer.getSecondaryId());
|
||||
|
||||
|
|
|
@ -32,11 +32,16 @@ import org.cloudburstmc.protocol.bedrock.packet.ContainerSetDataPacket;
|
|||
import org.geysermc.geyser.inventory.BedrockContainerSlot;
|
||||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.updater.ContainerInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.property.Properties;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
public class BrewingInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
public BrewingInventoryTranslator() {
|
||||
super(5, "minecraft:brewing_stand[has_bottle_0=false,has_bottle_1=false,has_bottle_2=false]", ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE);
|
||||
super(5, Blocks.BREWING_STAND.defaultBlockState()
|
||||
.withValue(Properties.HAS_BOTTLE_0, false)
|
||||
.withValue(Properties.HAS_BOTTLE_1, false)
|
||||
.withValue(Properties.HAS_BOTTLE_2, false), ContainerType.BREWING_STAND, ContainerInventoryUpdater.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,12 +30,13 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemSt
|
|||
import org.geysermc.geyser.inventory.*;
|
||||
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
||||
public class CartographyInventoryTranslator extends AbstractBlockInventoryTranslator {
|
||||
public CartographyInventoryTranslator() {
|
||||
super(3, "minecraft:cartography_table", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE);
|
||||
super(3, Blocks.CARTOGRAPHY_TABLE, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CARTOGRAPHY, UIInventoryUpdater.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
|
|||
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
|
||||
import org.geysermc.geyser.inventory.*;
|
||||
import org.geysermc.geyser.inventory.updater.CrafterInventoryUpdater;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
|
||||
|
@ -53,7 +54,7 @@ public class CrafterInventoryTranslator extends AbstractBlockInventoryTranslator
|
|||
private static final int TRIGGERED = 1; // triggered value
|
||||
|
||||
public CrafterInventoryTranslator() {
|
||||
super(10, "minecraft:crafter", org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CRAFTER, CrafterInventoryUpdater.INSTANCE);
|
||||
super(10, Blocks.CRAFTER, org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType.CRAFTER, CrafterInventoryUpdater.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue