Merge pull request #5106 from GeyserMC/feature/1.21.2

Java Edition 1.21.2 / 1.21.3 support
This commit is contained in:
Camotoy 2024-11-04 21:41:37 -05:00 committed by GitHub
commit 6983095d36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
148 changed files with 9864 additions and 4194 deletions

View file

@ -15,7 +15,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
## Supported Versions
Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.44 and Minecraft Java 1.21/1.21.1. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
Geyser is currently supporting Minecraft Bedrock 1.20.80 - 1.21.44 and Minecraft Java 1.21.2/1.21.3. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
## Setting Up
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.

View file

@ -46,7 +46,6 @@ tasks.withType<Jar> {
relocate("org.cloudburstmc.netty")
relocate("org.cloudburstmc.protocol")
relocate("com.github.steveice10.mc.auth")
tasks {
remapJar {

View file

@ -32,7 +32,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.CommandRegistry;
import org.geysermc.geyser.command.CommandSourceConverter;
@ -80,7 +80,7 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
var sourceConverter = CommandSourceConverter.layered(
CommandSourceStack.class,
id -> getServer().getPlayerList().getPlayer(id),
Player::createCommandSourceStack,
ServerPlayer::createCommandSourceStack,
() -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet
ModCommandSource::new
);

View file

@ -23,8 +23,8 @@
"geyser.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.11",
"fabricloader": ">=0.16.7",
"fabric": "*",
"minecraft": ">=1.21"
"minecraft": ">=1.21.2"
}
}

View file

@ -13,6 +13,9 @@ architectury {
provided("org.cloudburstmc.math", "api")
provided("com.google.errorprone", "error_prone_annotations")
// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version
relocate("com.fasterxml.jackson")
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
dependencies {
@ -31,6 +34,12 @@ dependencies {
}
shadow(projects.core) { isTransitive = false }
// Minecraft (1.21.2+) includes jackson. But an old version!
shadow(libs.jackson.core) { isTransitive = false }
shadow(libs.jackson.databind) { isTransitive = false }
shadow(libs.jackson.dataformat.yaml) { isTransitive = false }
shadow(libs.jackson.annotations) { isTransitive = false }
// Let's shade in our own api
shadow(projects.api) { isTransitive = false }
@ -56,6 +65,11 @@ tasks {
remapModrinthJar {
archiveBaseName.set("geyser-neoforge")
}
shadowJar {
// Without this, jackson's service files are not relocated
mergeServiceFiles()
}
}
modrinth {

View file

@ -26,7 +26,7 @@
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.bus.api.EventPriority;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
@ -72,7 +72,7 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
var sourceConverter = CommandSourceConverter.layered(
CommandSourceStack.class,
id -> getServer().getPlayerList().getPlayer(id),
Player::createCommandSourceStack,
ServerPlayer::createCommandSourceStack,
() -> getServer().createCommandSourceStack(),
ModCommandSource::new
);
@ -104,7 +104,9 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
}
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
GeyserModUpdateListener.onPlayReady(event.getEntity());
if (event.getEntity() instanceof ServerPlayer player) {
GeyserModUpdateListener.onPlayReady(player);
}
}
@Override

View file

@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.mod;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;
/**
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
@ -45,7 +45,7 @@ public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter
Class<?> msgClass = msg.getClass();
// Don't let any compression packet get through
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
if (ClientboundLoginFinishedPacket.class.isAssignableFrom(msgClass)) {
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
ctx.channel().pipeline().remove(this);

View file

@ -25,13 +25,13 @@
package org.geysermc.geyser.platform.mod;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.geyser.Permissions;
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserModUpdateListener {
public static void onPlayReady(Player player) {
public static void onPlayReady(ServerPlayer player) {
// Should be creating this in the supplier, but we need it for the permission check.
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());

View file

@ -33,8 +33,10 @@ import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
@ -43,7 +45,6 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.DecoratedPotBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.level.GeyserWorldManager;
@ -84,16 +85,17 @@ public class GeyserModWorldManager extends GeyserWorldManager {
}
Level level = player.level();
if (y < level.getMinBuildHeight()) {
if (y < level.getMinY()) {
return 0;
}
ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false);
// Only loads active chunks, and doesn't delegate to main thread
ChunkAccess chunk = ((ServerChunkCache) level.getChunkSource()).chunkMap.getChunkToSend(ChunkPos.asLong(x >> 4, z >> 4));
if (chunk == null) {
return 0;
}
int worldOffset = level.getMinBuildHeight() >> 4;
int worldOffset = level.getMinY() >> 4;
int chunkOffset = (y >> 4) - worldOffset;
if (chunkOffset < chunk.getSections().length) {
LevelChunkSection section = chunk.getSections()[chunkOffset];

View file

@ -33,10 +33,24 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.text.ChatColor;
import org.jline.reader.Candidate;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
@Slf4j
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, GeyserCommandSource {
@Override
protected LineReader buildReader(LineReaderBuilder builder) {
builder.completer((reader, line, candidates) -> {
var suggestions = GeyserImpl.getInstance().commandRegistry().suggestionsFor(this, line.line());
for (var suggestion : suggestions.list()) {
candidates.add(new Candidate(suggestion.suggestion()));
}
});
return super.buildReader(builder);
}
@Override
protected boolean isRunning() {
return !GeyserImpl.getInstance().isShuttingDown();

View file

@ -7,12 +7,13 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody)
modrinth {
token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
debugMode.set(System.getenv("MODRINTH_TOKEN") == null)
projectId.set("geyser")
versionName.set(versionName(project))
versionNumber.set(projectVersion(project))
versionType.set("beta")
changelog.set(System.getenv("CHANGELOG") ?: "")
gameVersions.addAll("1.21", libs.minecraft.get().version as String)
gameVersions.addAll("1.21.2", libs.minecraft.get().version as String)
failSilently.set(true)
syncBodyFrom.set(rootProject.file("README.md").readText())

View file

@ -27,6 +27,13 @@ package org.geysermc.geyser.command;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.command.CommandData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumConstraint;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParam;
import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.event.EventRegistrar;
@ -51,14 +58,29 @@ import org.geysermc.geyser.command.defaults.StopCommand;
import org.geysermc.geyser.command.defaults.VersionCommand;
import org.geysermc.geyser.event.type.GeyserDefineCommandsEventImpl;
import org.geysermc.geyser.extension.command.GeyserExtensionCommand;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
import org.incendo.cloud.Command.Builder;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.internal.CommandNode;
import org.incendo.cloud.parser.standard.EnumParser;
import org.incendo.cloud.parser.standard.IntegerParser;
import org.incendo.cloud.parser.standard.LiteralParser;
import org.incendo.cloud.parser.standard.StringArrayParser;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.Suggestions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static org.geysermc.geyser.command.GeyserCommand.DEFAULT_ROOT_COMMAND;
@ -299,4 +321,90 @@ public class CommandRegistry implements EventRegistrar {
public void runCommand(@NonNull GeyserCommandSource source, @NonNull String command) {
cloud.commandExecutor().executeCommand(source, command);
}
public Suggestions<GeyserCommandSource, ? extends Suggestion> suggestionsFor(GeyserCommandSource source, String input) {
return cloud.suggestionFactory().suggestImmediately(source, input);
}
public void export(GeyserSession session, List<CommandData> bedrockCommands, Set<String> knownAliases) {
cloud.commandTree().rootNodes().forEach(commandTree -> {
var command = commandTree.command();
// Command null happens if you register an extension command with custom Cloud parameters...
if (command == null || session.hasPermission(command.commandPermission().permissionString())) {
var rootComponent = commandTree.component();
String name = rootComponent.name();
if (!knownAliases.add(name)) {
// If the server already defined the command, let's not crash.
return;
}
LinkedHashMap<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
for (String s : rootComponent.aliases()) {
values.put(s, EnumSet.of(CommandEnumConstraint.ALLOW_ALIASES));
}
CommandEnumData aliases = new CommandEnumData(name + "Aliases", values, false);
List<CommandOverloadData> data = new ArrayList<>();
for (var node : commandTree.children()) {
List<List<CommandParamData>> params = createParamData(session, node);
params.forEach(param -> data.add(new CommandOverloadData(false, param.toArray(CommandParamData[]::new))));
}
CommandData bedrockCommand = new CommandData(name, rootComponent.description().textDescription(),
Set.of(CommandData.Flag.NOT_CHEAT), CommandPermission.ANY, aliases,
Collections.emptyList(), data.toArray(new CommandOverloadData[0]));
bedrockCommands.add(bedrockCommand);
}
});
}
private List<List<CommandParamData>> createParamData(GeyserSession session, CommandNode<GeyserCommandSource> node) {
var command = node.command();
if (command != null && !session.hasPermission(command.commandPermission().permissionString())) {
// Triggers with subcommands like Geyser dump, stop, etc.
return Collections.emptyList();
}
CommandParamData data = new CommandParamData();
var component = node.component();
data.setName(component.name());
data.setOptional(component.optional());
var suggestionProvider = component.suggestionProvider();
if (suggestionProvider instanceof LiteralParser<GeyserCommandSource> parser) {
Map<String, Set<CommandEnumConstraint>> values = new LinkedHashMap<>();
for (String alias : parser.aliases()) {
values.put(alias, Set.of());
}
data.setEnumData(new CommandEnumData(component.name(), values, false));
} else if (suggestionProvider instanceof IntegerParser<GeyserCommandSource>) {
data.setType(CommandParam.INT);
} else if (suggestionProvider instanceof EnumParser<?,?> parser) {
LinkedHashMap<String, Set<CommandEnumConstraint>> map = new LinkedHashMap<>();
for (Enum<?> e : parser.acceptedValues()) {
map.put(e.name().toLowerCase(Locale.ROOT), Set.of());
}
data.setEnumData(new CommandEnumData(component.name().toLowerCase(Locale.ROOT), map, false));
} else if (component.parser() instanceof StringArrayParser<?>) {
data.setType(CommandParam.TEXT);
} else {
data.setType(CommandParam.STRING);
}
var children = node.children();
if (children.isEmpty()) {
List<CommandParamData> list = new ArrayList<>(); // Must be mutable; parents will be added to list.
list.add(data);
return Collections.singletonList(list); // Safe to do; will be consumed in an addAll call.
}
List<List<CommandParamData>> collectiveData = new ArrayList<>();
// If a node has multiple children, this will need to be represented
// by creating a new list/branch for each and cloning this node down each line.
for (var child : children) {
collectiveData.addAll(createParamData(session, child));
}
collectiveData.forEach(list -> list.add(0, data));
return collectiveData;
}
}

View file

@ -151,29 +151,37 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatE
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
public final class EntityDefinitions {
public static final EntityDefinition<BoatEntity> ACACIA_BOAT;
public static final EntityDefinition<ChestBoatEntity> ACACIA_CHEST_BOAT;
public static final EntityDefinition<AllayEntity> ALLAY;
public static final EntityDefinition<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
public static final EntityDefinition<ArmadilloEntity> ARMADILLO;
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
public static final EntityDefinition<ArrowEntity> ARROW;
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
public static final EntityDefinition<BoatEntity> BAMBOO_RAFT;
public static final EntityDefinition<ChestBoatEntity> BAMBOO_CHEST_RAFT;
public static final EntityDefinition<BatEntity> BAT;
public static final EntityDefinition<BeeEntity> BEE;
public static final EntityDefinition<BoatEntity> BIRCH_BOAT;
public static final EntityDefinition<ChestBoatEntity> BIRCH_CHEST_BOAT;
public static final EntityDefinition<BlazeEntity> BLAZE;
public static final EntityDefinition<BoatEntity> BOAT;
public static final EntityDefinition<BoggedEntity> BOGGED;
public static final EntityDefinition<BreezeEntity> BREEZE;
public static final EntityDefinition<AbstractWindChargeEntity> BREEZE_WIND_CHARGE;
public static final EntityDefinition<CamelEntity> CAMEL;
public static final EntityDefinition<CatEntity> CAT;
public static final EntityDefinition<SpiderEntity> CAVE_SPIDER;
public static final EntityDefinition<BoatEntity> CHERRY_BOAT;
public static final EntityDefinition<ChestBoatEntity> CHERRY_CHEST_BOAT;
public static final EntityDefinition<MinecartEntity> CHEST_MINECART;
public static final EntityDefinition<ChickenEntity> CHICKEN;
public static final EntityDefinition<ChestBoatEntity> CHEST_BOAT;
public static final EntityDefinition<AbstractFishEntity> COD;
public static final EntityDefinition<CommandBlockMinecartEntity> COMMAND_BLOCK_MINECART;
public static final EntityDefinition<CowEntity> COW;
public static final EntityDefinition<CreeperEntity> CREEPER;
public static final EntityDefinition<BoatEntity> DARK_OAK_BOAT;
public static final EntityDefinition<ChestBoatEntity> DARK_OAK_CHEST_BOAT;
public static final EntityDefinition<DolphinEntity> DOLPHIN;
public static final EntityDefinition<ChestedHorseEntity> DONKEY;
public static final EntityDefinition<FireballEntity> DRAGON_FIREBALL;
@ -212,14 +220,20 @@ public final class EntityDefinitions {
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
public static final EntityDefinition<ItemEntity> ITEM;
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
public static final EntityDefinition<BoatEntity> JUNGLE_BOAT;
public static final EntityDefinition<ChestBoatEntity> JUNGLE_CHEST_BOAT;
public static final EntityDefinition<LeashKnotEntity> LEASH_KNOT;
public static final EntityDefinition<LightningEntity> LIGHTNING_BOLT;
public static final EntityDefinition<LlamaEntity> LLAMA;
public static final EntityDefinition<ThrowableEntity> LLAMA_SPIT;
public static final EntityDefinition<MagmaCubeEntity> MAGMA_CUBE;
public static final EntityDefinition<BoatEntity> MANGROVE_BOAT;
public static final EntityDefinition<ChestBoatEntity> MANGROVE_CHEST_BOAT;
public static final EntityDefinition<MinecartEntity> MINECART;
public static final EntityDefinition<MooshroomEntity> MOOSHROOM;
public static final EntityDefinition<ChestedHorseEntity> MULE;
public static final EntityDefinition<BoatEntity> OAK_BOAT;
public static final EntityDefinition<ChestBoatEntity> OAK_CHEST_BOAT;
public static final EntityDefinition<OcelotEntity> OCELOT;
public static final EntityDefinition<PaintingEntity> PAINTING;
public static final EntityDefinition<PandaEntity> PANDA;
@ -250,6 +264,8 @@ public final class EntityDefinitions {
public static final EntityDefinition<SpawnerMinecartEntity> SPAWNER_MINECART; // Not present on Bedrock
public static final EntityDefinition<AbstractArrowEntity> SPECTRAL_ARROW;
public static final EntityDefinition<SpiderEntity> SPIDER;
public static final EntityDefinition<BoatEntity> SPRUCE_BOAT;
public static final EntityDefinition<ChestBoatEntity> SPRUCE_CHEST_BOAT;
public static final EntityDefinition<SquidEntity> SQUID;
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
public static final EntityDefinition<StriderEntity> STRIDER;
@ -308,23 +324,6 @@ public final class EntityDefinitions {
.addTranslator(null) // Waiting
.addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle)
.build();
BOAT = EntityDefinition.inherited(BoatEntity::new, entityBase)
.type(EntityType.BOAT)
.height(0.6f).width(1.6f)
.offset(0.35f)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction
.addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) ->
// 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock
boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue())))
.addTranslator(MetadataType.INT, BoatEntity::setVariant)
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft)
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything
.build();
CHEST_BOAT = EntityDefinition.inherited(ChestBoatEntity::new, BOAT)
.type(EntityType.CHEST_BOAT)
.build();
DRAGON_FIREBALL = EntityDefinition.inherited(FireballEntity::new, entityBase)
.type(EntityType.DRAGON_FIREBALL)
.heightAndWidth(1.0f)
@ -567,6 +566,45 @@ public final class EntityDefinitions {
.build(false);
}
// Boats
{
EntityDefinition<BoatEntity> boatBase = EntityDefinition.<BoatEntity>inherited(null, entityBase)
.height(0.6f).width(1.6f)
.offset(0.35f)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_TICKS, entityMetadata.getValue())) // Time since last hit
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction
.addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) ->
// 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock
boatEntity.getDirtyMetadata().put(EntityDataTypes.STRUCTURAL_INTEGRITY, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue())))
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft)
.addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight)
.addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityDataTypes.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything
.build();
ACACIA_BOAT = buildBoat(boatBase, EntityType.ACACIA_BOAT, BoatEntity.BoatVariant.ACACIA);
BAMBOO_RAFT = buildBoat(boatBase, EntityType.BAMBOO_RAFT, BoatEntity.BoatVariant.BAMBOO);
BIRCH_BOAT = buildBoat(boatBase, EntityType.BIRCH_BOAT, BoatEntity.BoatVariant.BIRCH);
CHERRY_BOAT = buildBoat(boatBase, EntityType.CHERRY_BOAT, BoatEntity.BoatVariant.CHERRY);
DARK_OAK_BOAT = buildBoat(boatBase, EntityType.DARK_OAK_BOAT, BoatEntity.BoatVariant.DARK_OAK);
JUNGLE_BOAT = buildBoat(boatBase, EntityType.JUNGLE_BOAT, BoatEntity.BoatVariant.JUNGLE);
MANGROVE_BOAT = buildBoat(boatBase, EntityType.MANGROVE_BOAT, BoatEntity.BoatVariant.MANGROVE);
OAK_BOAT = buildBoat(boatBase, EntityType.OAK_BOAT, BoatEntity.BoatVariant.OAK);
SPRUCE_BOAT = buildBoat(boatBase, EntityType.SPRUCE_BOAT, BoatEntity.BoatVariant.SPRUCE);
EntityDefinition<ChestBoatEntity> chestBoatBase = EntityDefinition.<ChestBoatEntity>inherited(null, boatBase)
.build();
ACACIA_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.ACACIA_CHEST_BOAT, BoatEntity.BoatVariant.ACACIA);
BAMBOO_CHEST_RAFT = buildChestBoat(chestBoatBase, EntityType.BAMBOO_CHEST_RAFT, BoatEntity.BoatVariant.BAMBOO);
BIRCH_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.BIRCH_CHEST_BOAT, BoatEntity.BoatVariant.BIRCH);
CHERRY_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.CHERRY_CHEST_BOAT, BoatEntity.BoatVariant.CHERRY);
DARK_OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.DARK_OAK_CHEST_BOAT, BoatEntity.BoatVariant.DARK_OAK);
JUNGLE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.JUNGLE_CHEST_BOAT, BoatEntity.BoatVariant.JUNGLE);
MANGROVE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.MANGROVE_CHEST_BOAT, BoatEntity.BoatVariant.MANGROVE);
OAK_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.OAK_CHEST_BOAT, BoatEntity.BoatVariant.OAK);
SPRUCE_CHEST_BOAT = buildChestBoat(chestBoatBase, EntityType.SPRUCE_CHEST_BOAT, BoatEntity.BoatVariant.SPRUCE);
}
EntityDefinition<LivingEntity> livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags)
.addTranslator(MetadataType.FLOAT, LivingEntity::setHealth)
@ -640,14 +678,6 @@ public final class EntityDefinitions {
.addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.addTranslator(MetadataType.BOOLEAN, CreeperEntity::setIgnited)
.build();
DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, mobEntityBase)
.type(EntityType.DOLPHIN)
.height(0.6f).width(0.9f)
//TODO check
.addTranslator(null) // treasure position
.addTranslator(null) // "got fish"
.addTranslator(null) // "moistness level"
.build();
ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase)
.type(EntityType.ENDERMAN)
.height(2.9f).width(0.6f)
@ -717,10 +747,6 @@ public final class EntityDefinitions {
.type(EntityType.CAVE_SPIDER)
.height(0.5f).width(0.7f)
.build();
SQUID = EntityDefinition.inherited(SquidEntity::new, mobEntityBase)
.type(EntityType.SQUID)
.heightAndWidth(0.8f)
.build();
STRAY = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase)
.type(EntityType.STRAY)
.height(1.8f).width(0.6f)
@ -819,6 +845,7 @@ public final class EntityDefinitions {
SALMON = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase)
.type(EntityType.SALMON)
.height(0.5f).width(0.7f)
.addTranslator(null) // Scale/variant - TODO
.build();
TADPOLE = EntityDefinition.inherited(TadpoleEntity::new, abstractFishEntityBase)
.type(EntityType.TADPOLE)
@ -846,11 +873,6 @@ public final class EntityDefinitions {
.height(1.95f).width(0.6f)
.build();
GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID)
.type(EntityType.GLOW_SQUID)
.addTranslator(null) // Set dark ticks remaining, possible TODO
.build();
EntityDefinition<RaidParticipantEntity> raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase)
.addTranslator(null) // Celebrating //TODO
.build();
@ -1042,6 +1064,26 @@ public final class EntityDefinitions {
.build();
}
// Water creatures (AgeableWaterCreature)
{
DOLPHIN = EntityDefinition.inherited(DolphinEntity::new, ageableEntityBase)
.type(EntityType.DOLPHIN)
.height(0.6f).width(0.9f)
//TODO check
.addTranslator(null) // treasure position
.addTranslator(null) // "got fish"
.addTranslator(null) // "moistness level"
.build();
SQUID = EntityDefinition.inherited(SquidEntity::new, ageableEntityBase)
.type(EntityType.SQUID)
.heightAndWidth(0.8f)
.build();
GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID)
.type(EntityType.GLOW_SQUID)
.addTranslator(null) // Set dark ticks remaining, possible TODO
.build();
}
// Horses
{
EntityDefinition<AbstractHorseEntity> abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase)
@ -1124,6 +1166,22 @@ public final class EntityDefinitions {
Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network
}
private static EntityDefinition<BoatEntity> buildBoat(EntityDefinition<BoatEntity> base, EntityType entityType, BoatEntity.BoatVariant variant) {
return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
new BoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
.type(entityType)
.identifier("minecraft:boat")
.build();
}
private static EntityDefinition<ChestBoatEntity> buildChestBoat(EntityDefinition<ChestBoatEntity> base, EntityType entityType, BoatEntity.BoatVariant variant) {
return EntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
new ChestBoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
.type(entityType)
.identifier("minecraft:chest_boat")
.build();
}
public static void init() {
// no-op
}

View file

@ -32,12 +32,14 @@ import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket;
import java.util.UUID;
@ -63,16 +65,19 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
* Saved for using the "pick" functionality on a boat.
*/
@Getter
private int variant;
protected final BoatVariant variant;
private long leashHolderBedrockId = -1;
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
private final float ROWING_SPEED = 0.1f;
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
public BoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
// Initial rotation is incorrect
super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw + 90, 0, yaw + 90);
this.variant = variant;
dirtyMetadata.put(EntityDataTypes.VARIANT, variant.ordinal());
// Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+
dirtyMetadata.put(EntityDataTypes.IS_BUOYANT, true);
@ -124,15 +129,6 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround);
}
public void setVariant(IntEntityMetadata entityMetadata) {
variant = entityMetadata.getPrimitiveValue();
dirtyMetadata.put(EntityDataTypes.VARIANT, switch (variant) {
case 6, 7, 8 -> variant - 1; // dark_oak, mangrove, bamboo
case 5 -> 8; // cherry
default -> variant;
});
}
public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
isPaddlingLeft = entityMetadata.getPrimitiveValue();
if (!isPaddlingLeft) {
@ -187,6 +183,12 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
@Override
public void tick() {
// Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing
if (session.getPlayerEntity().getVehicle() == this) {
// For packet timing accuracy, we'll send the packets here, as that's what Java Edition 1.21.3 does.
ServerboundPaddleBoatPacket steerPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerPacket);
return;
}
doTick = !doTick; // Run every 100 ms
if (!doTick || passengers.isEmpty()) {
return;
@ -212,6 +214,10 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
return leashHolderBedrockId;
}
public Item getPickItem() {
return variant.pickItem;
}
private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
AnimatePacket packet = new AnimatePacket();
packet.setRuntimeEntityId(rower.getGeyserId());
@ -219,4 +225,27 @@ public class BoatEntity extends Entity implements Leashable, Tickable {
packet.setRowingTime(rowTime);
session.sendUpstreamPacket(packet);
}
/**
* Ordered by Bedrock ordinal
*/
public enum BoatVariant {
OAK(Items.OAK_BOAT, Items.OAK_CHEST_BOAT),
SPRUCE(Items.SPRUCE_BOAT, Items.SPRUCE_CHEST_BOAT),
BIRCH(Items.BIRCH_BOAT, Items.BIRCH_CHEST_BOAT),
JUNGLE(Items.JUNGLE_BOAT, Items.JUNGLE_CHEST_BOAT),
ACACIA(Items.ACACIA_BOAT, Items.ACACIA_CHEST_BOAT),
DARK_OAK(Items.DARK_OAK_BOAT, Items.DARK_OAK_CHEST_BOAT),
MANGROVE(Items.MANGROVE_BOAT, Items.MANGROVE_CHEST_BOAT),
BAMBOO(Items.BAMBOO_RAFT, Items.BAMBOO_CHEST_RAFT),
CHERRY(Items.CHERRY_BOAT, Items.CHERRY_CHEST_BOAT);
private final Item pickItem;
final Item chestPickItem;
BoatVariant(Item pickItem, Item chestPickItem) {
this.pickItem = pickItem;
this.chestPickItem = chestPickItem;
}
}
}

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.entity.type;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -35,8 +36,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
public class ChestBoatEntity extends BoatEntity {
public ChestBoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
public ChestBoatEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, BoatVariant variant) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, variant);
}
@Override
@ -48,4 +49,9 @@ public class ChestBoatEntity extends BoatEntity {
public InteractionResult interact(Hand hand) {
return passengers.isEmpty() && !session.isSneaking() ? super.interact(hand) : InteractionResult.SUCCESS;
}
@Override
public Item getPickItem() {
return this.variant.chestPickItem;
}
}

View file

@ -25,12 +25,6 @@
package org.geysermc.geyser.entity.type;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@ -67,6 +61,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEnt
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@Getter
@Setter
public class Entity implements GeyserEntity {
@ -89,6 +90,7 @@ public class Entity implements GeyserEntity {
/**
* x = Yaw, y = Pitch, z = HeadYaw
* Java: Y = Yaw, X = Pitch
*/
protected float yaw;
protected float pitch;
@ -699,9 +701,4 @@ public class Entity implements GeyserEntity {
packet.setData(data);
session.sendUpstreamPacket(packet);
}
@SuppressWarnings("unchecked")
public <I extends Entity> @Nullable I as(Class<I> entityClass) {
return entityClass.isInstance(this) ? (I) this : null;
}
}

View file

@ -25,11 +25,6 @@
package org.geysermc.geyser.entity.type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@ -71,6 +66,12 @@ import org.geysermc.mcprotocollib.protocol.data.game.level.particle.EntityEffect
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Getter
@Setter
public class LivingEntity extends Entity {
@ -445,35 +446,35 @@ public class LivingEntity extends Entity {
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
if (javaAttribute.getType() instanceof AttributeType.Builtin type) {
switch (type) {
case GENERIC_MAX_HEALTH -> {
case MAX_HEALTH -> {
// Since 1.18.0, setting the max health to 0 or below causes the entity to die on Bedrock but not on Java
// See https://github.com/GeyserMC/Geyser/issues/2971
this.maxHealth = Math.max((float) AttributeUtils.calculateValue(javaAttribute), 1f);
newAttributes.add(createHealthAttribute());
}
case GENERIC_MOVEMENT_SPEED -> {
case MOVEMENT_SPEED -> {
AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED);
newAttributes.add(attributeData);
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setMoveSpeed(attributeData.getValue());
}
}
case GENERIC_STEP_HEIGHT -> {
case STEP_HEIGHT -> {
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setStepHeight((float) AttributeUtils.calculateValue(javaAttribute));
}
}
case GENERIC_GRAVITY -> {
case GRAVITY -> {
if (this instanceof ClientVehicle clientVehicle) {
clientVehicle.getVehicleComponent().setGravity(AttributeUtils.calculateValue(javaAttribute));
}
}
case GENERIC_ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
case GENERIC_FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
case GENERIC_FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
case GENERIC_KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
case GENERIC_JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
case GENERIC_SCALE -> {
case ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
case FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
case KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
case JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
case SCALE -> {
// Attribute on Java, entity data on Bedrock
setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute));
updateBedrockMetadata();

View file

@ -0,0 +1,43 @@
/*
* 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.entity.type.living;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
public abstract class AgeableWaterEntity extends AgeableEntity {
public AgeableWaterEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
@Override
public boolean canBeLeashed() {
return false;
}
}

View file

@ -37,7 +37,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import java.util.UUID;
public class DolphinEntity extends WaterEntity {
public class DolphinEntity extends AgeableWaterEntity {
public DolphinEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}

View file

@ -36,7 +36,7 @@ import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class SquidEntity extends WaterEntity implements Tickable {
public class SquidEntity extends AgeableWaterEntity implements Tickable {
private float targetPitch;
private float targetYaw;

View file

@ -33,8 +33,9 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.living.AgeableEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@ -48,7 +49,7 @@ public abstract class AnimalEntity extends AgeableEntity {
}
protected final boolean canEat(GeyserItemStack itemStack) {
ItemTag tag = getFoodTag();
Tag<Item> tag = getFoodTag();
if (tag == null) {
return false;
}
@ -58,7 +59,7 @@ public abstract class AnimalEntity extends AgeableEntity {
/**
* @return the tag associated with this animal for eating food. Null for nothing or different behavior.
*/
protected abstract @Nullable ItemTag getFoodTag();
protected abstract @Nullable Tag<Item> getFoodTag();
@NonNull
@Override

View file

@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.ArmadilloState;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
@ -75,7 +77,7 @@ public class ArmadilloEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.ARMADILLO_FOOD;
}
}

View file

@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
@ -62,7 +64,7 @@ public class AxolotlEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.AXOLOTL_FOOD;
}

View file

@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
@ -69,7 +71,7 @@ public class BeeEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.BEE_FOOD;
}
}

View file

@ -28,8 +28,10 @@ package org.geysermc.geyser.entity.type.living.animal;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import java.util.UUID;
@ -41,7 +43,7 @@ public class ChickenEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.CHICKEN_FOOD;
}
}

View file

@ -33,8 +33,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@ -69,7 +71,7 @@ public class CowEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.COW_FOOD;
}
}

View file

@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
@ -57,7 +59,7 @@ public class FoxEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.FOX_FOOD;
}
}

View file

@ -31,8 +31,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
@ -77,7 +79,7 @@ public class FrogEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.FROG_FOOD;
}
}

View file

@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
@ -99,7 +101,7 @@ public class GoatEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.GOAT_FOOD;
}
}

View file

@ -30,8 +30,10 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.UUID;
@ -58,7 +60,7 @@ public class HoglinEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.HOGLIN_FOOD;
}

View file

@ -31,8 +31,10 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@ -47,7 +49,7 @@ public class OcelotEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.OCELOT_FOOD;
}

View file

@ -34,8 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
@ -90,7 +92,7 @@ public class PandaEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.PANDA_FOOD;
}

View file

@ -38,9 +38,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -58,7 +60,7 @@ public class PigEntity extends AnimalEntity implements Tickable, ClientVehicle {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.PIG_FOOD;
}

View file

@ -28,8 +28,9 @@ package org.geysermc.geyser.entity.type.living.animal;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import java.util.UUID;
@ -41,7 +42,7 @@ public class PolarBearEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return null;
}
}

View file

@ -31,8 +31,10 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
@ -79,7 +81,7 @@ public class RabbitEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.RABBIT_FOOD;
}
}

View file

@ -34,8 +34,10 @@ import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
@ -59,7 +61,7 @@ public class SheepEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.SHEEP_FOOD;
}
@ -103,4 +105,4 @@ public class SheepEntity extends AnimalEntity {
private boolean canDye(GeyserItemStack item) {
return item.asItem() instanceof DyeItem dyeItem && dyeItem.dyeColor() != this.color && !getFlag(EntityFlag.SHEARED);
}
}
}

View file

@ -35,8 +35,10 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.SnifferState;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
@ -73,7 +75,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.SNIFFER_FOOD;
}

View file

@ -39,9 +39,11 @@ import org.geysermc.geyser.entity.vehicle.BoostableVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -105,7 +107,7 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.STRIDER_FOOD;
}

View file

@ -29,8 +29,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import java.util.UUID;
@ -51,7 +53,7 @@ public class TurtleEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.TURTLE_FOOD;
}

View file

@ -39,8 +39,10 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
@ -119,7 +121,7 @@ public class AbstractHorseEntity extends AnimalEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.HORSE_FOOD;
}

View file

@ -35,12 +35,14 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.vehicle.CamelVehicleComponent;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
@ -100,7 +102,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
}
@Override
protected @Nullable ItemTag getFoodTag() {
protected @Nullable Tag<Item> getFoodTag() {
return ItemTag.CAMEL_FOOD;
}
@ -141,7 +143,7 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
@Override
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_JUMP_STRENGTH) {
if (javaAttribute.getType() == AttributeType.Builtin.JUMP_STRENGTH) {
vehicleComponent.setHorseJumpStrength(attributeData.getValue());
}
return attributeData;

View file

@ -30,8 +30,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
@ -56,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity {
}
@Override
protected @Nullable ItemTag getFoodTag() {
protected @Nullable Tag<Item> getFoodTag() {
return ItemTag.LLAMA_FOOD;
}
}

View file

@ -32,8 +32,10 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
@ -109,7 +111,7 @@ public class CatEntity extends TameableEntity {
}
@Override
protected @Nullable ItemTag getFoodTag() {
protected @Nullable Tag<Item> getFoodTag() {
return ItemTag.CAT_FOOD;
}

View file

@ -34,6 +34,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
@ -47,7 +48,7 @@ public class ParrotEntity extends TameableEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return null;
}

View file

@ -36,8 +36,10 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.EnchantmentComponent;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
import org.geysermc.geyser.util.ItemUtils;
@ -116,7 +118,7 @@ public class WolfEntity extends TameableEntity {
@Override
@Nullable
protected ItemTag getFoodTag() {
protected Tag<Item> getFoodTag() {
return ItemTag.WOLF_FOOD;
}

View file

@ -25,12 +25,6 @@
package org.geysermc.geyser.entity.type.player;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
@ -65,6 +59,13 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Boolea
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Getter @Setter
public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
public static final float SNEAKING_POSE_HEIGHT = 1.5f;
@ -250,10 +251,6 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity {
}
}
public void updateRotation(float yaw, float pitch, float headYaw, boolean isOnGround) {
moveRelative(0, 0, 0, yaw, pitch, headYaw, isOnGround);
}
@Override
public void setPosition(Vector3f position) {
if (this.bedPosition != null) {

View file

@ -38,8 +38,8 @@ import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
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.level.BedrockDimension;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.DimensionUtils;
@ -143,9 +143,32 @@ public class SessionPlayerEntity extends PlayerEntity {
this.position = position.add(0, definition.offset(), 0);
}
/**
* Special method used only when updating the session player's rotation.
* For some reason, Mode#NORMAL ignored rotation. Yay.
* @param yaw the new yaw
* @param pitch the new pitch
* @param headYaw the head yaw
*/
public void updateOwnRotation(float yaw, float pitch, float headYaw) {
setYaw(yaw);
setPitch(pitch);
setHeadYaw(headYaw);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
movePlayerPacket.setRuntimeEntityId(geyserId);
movePlayerPacket.setPosition(position);
movePlayerPacket.setRotation(getBedrockRotation());
movePlayerPacket.setOnGround(isOnGround());
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
session.sendUpstreamPacket(movePlayerPacket);
}
/**
* Set the player's position without applying an offset or moving the bounding box
* This is used in BedrockMovePlayerTranslator which receives the player's position
* This is used in BedrockMovePlayer which receives the player's position
* with the offset pre-applied
*
* @param position the new position of the Bedrock player
@ -247,9 +270,9 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
if (javaAttribute.getType() == AttributeType.Builtin.GENERIC_ATTACK_SPEED) {
if (javaAttribute.getType() == AttributeType.Builtin.ATTACK_SPEED) {
session.setAttackSpeed(AttributeUtils.calculateValue(javaAttribute));
} else if (javaAttribute.getType() == AttributeType.Builtin.PLAYER_BLOCK_INTERACTION_RANGE) {
} else if (javaAttribute.getType() == AttributeType.Builtin.BLOCK_INTERACTION_RANGE) {
this.blockInteractionRange = AttributeUtils.calculateValue(javaAttribute);
} else {
super.updateAttribute(javaAttribute, newAttributes);

View file

@ -76,8 +76,8 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
public VehicleComponent(T vehicle, float stepHeight) {
this.vehicle = vehicle;
this.stepHeight = stepHeight;
this.moveSpeed = (float) AttributeType.Builtin.GENERIC_MOVEMENT_SPEED.getDef();
this.gravity = AttributeType.Builtin.GENERIC_GRAVITY.getDef();
this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef();
this.gravity = AttributeType.Builtin.GRAVITY.getDef();
double width = vehicle.getBoundingBoxWidth();
double height = vehicle.getBoundingBoxHeight();
@ -758,7 +758,6 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos.getX(), javaPos.getY(), javaPos.getZ(), rotation.getX(), rotation.getY());
vehicle.getSession().sendDownstreamPacket(moveVehiclePacket);
vehicle.getSession().setLastVehicleMoveTimestamp(System.currentTimeMillis());
}
protected double getGravity() {

View file

@ -29,6 +29,7 @@ import lombok.*;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries;
@ -38,6 +39,10 @@ import org.geysermc.geyser.translator.item.ItemTranslator;
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 org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.ItemStackSlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import java.util.HashMap;
@ -77,6 +82,20 @@ public class GeyserItemStack {
return itemStack == null ? EMPTY : new GeyserItemStack(itemStack.getId(), itemStack.getAmount(), itemStack.getDataComponents());
}
public static @NonNull GeyserItemStack from(@NonNull SlotDisplay slotDisplay) {
if (slotDisplay instanceof EmptySlotDisplay) {
return GeyserItemStack.EMPTY;
}
if (slotDisplay instanceof ItemSlotDisplay itemSlotDisplay) {
return GeyserItemStack.of(itemSlotDisplay.item(), 1);
}
if (slotDisplay instanceof ItemStackSlotDisplay itemStackSlotDisplay) {
return GeyserItemStack.from(itemStackSlotDisplay.itemStack());
}
GeyserImpl.getInstance().getLogger().warning("Unsure how to convert to ItemStack: " + slotDisplay);
return GeyserItemStack.EMPTY;
}
public int getJavaId() {
return isEmpty() ? 0 : javaId;
}
@ -163,7 +182,17 @@ public class GeyserItemStack {
return session.getItemMappings().getMapping(this.javaId);
}
public SlotDisplay asSlotDisplay() {
if (isEmpty()) {
return EmptySlotDisplay.INSTANCE;
}
return new ItemStackSlotDisplay(this.getItemStack());
}
public Item asItem() {
if (isEmpty()) {
return Items.AIR;
}
if (item == null) {
return (item = Registries.JAVA_ITEMS.get().get(javaId));
}

View file

@ -0,0 +1,166 @@
/*
* 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.inventory.item;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.geyser.util.SoundUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument;
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
import java.util.Locale;
public interface GeyserInstrument {
static GeyserInstrument read(RegistryEntryContext context) {
NbtMap data = context.data();
String soundEvent = SoundUtils.readSoundEvent(data, "instrument " + context.id());
float range = data.getFloat("range");
String description = MessageTranslator.deserializeDescriptionForTooltip(context.session(), data);
BedrockInstrument bedrockInstrument = BedrockInstrument.getByJavaIdentifier(context.id());
return new GeyserInstrument.Impl(soundEvent, range, description, bedrockInstrument);
}
String soundEvent();
float range();
/**
* In Bedrock format
*/
String description();
BedrockInstrument bedrockInstrument();
/**
* @return the ID of the Bedrock counterpart for this instrument. If there is none ({@link #bedrockInstrument()} is null), then -1 is returned.
*/
default int bedrockId() {
BedrockInstrument bedrockInstrument = bedrockInstrument();
if (bedrockInstrument != null) {
return bedrockInstrument.ordinal();
}
return -1;
}
/**
* @return the ID of the Java counterpart for the given Bedrock ID. If an invalid Bedrock ID was given, or there is no counterpart, -1 is returned.
*/
static int bedrockIdToJava(GeyserSession session, int id) {
JavaRegistry<GeyserInstrument> instruments = session.getRegistryCache().instruments();
BedrockInstrument bedrockInstrument = BedrockInstrument.getByBedrockId(id);
if (bedrockInstrument != null) {
for (int i = 0; i < instruments.values().size(); i++) {
GeyserInstrument instrument = instruments.byId(i);
if (instrument.bedrockInstrument() == bedrockInstrument) {
return i;
}
}
}
return -1;
}
static GeyserInstrument fromHolder(GeyserSession session, Holder<Instrument> holder) {
if (holder.isId()) {
return session.getRegistryCache().instruments().byId(holder.id());
}
Instrument custom = holder.custom();
return new Wrapper(custom, session.locale());
}
record Wrapper(Instrument instrument, String locale) implements GeyserInstrument {
@Override
public String soundEvent() {
return instrument.getSoundEvent().getName();
}
@Override
public float range() {
return instrument.getRange();
}
@Override
public String description() {
return MessageTranslator.convertMessageForTooltip(instrument.getDescription(), locale);
}
@Override
public BedrockInstrument bedrockInstrument() {
if (instrument.getSoundEvent() instanceof BuiltinSound) {
return BedrockInstrument.getByJavaIdentifier(MinecraftKey.key(instrument.getSoundEvent().getName()));
}
// Probably custom
return null;
}
}
record Impl(String soundEvent, float range, String description, @Nullable BedrockInstrument bedrockInstrument) implements GeyserInstrument {
}
/**
* Each vanilla instrument on Bedrock, ordered in their network IDs.
*/
enum BedrockInstrument {
PONDER,
SING,
SEEK,
FEEL,
ADMIRE,
CALL,
YEARN,
DREAM;
private static final BedrockInstrument[] VALUES = values();
private final Key javaIdentifier;
BedrockInstrument() {
this.javaIdentifier = MinecraftKey.key(this.name().toLowerCase(Locale.ENGLISH) + "_goat_horn");
}
public static @Nullable BedrockInstrument getByJavaIdentifier(Key javaIdentifier) {
for (BedrockInstrument instrument : VALUES) {
if (instrument.javaIdentifier.equals(javaIdentifier)) {
return instrument;
}
}
return null;
}
public static @Nullable BedrockInstrument getByBedrockId(int bedrockId) {
if (bedrockId >= 0 && bedrockId < VALUES.length) {
return VALUES[bedrockId];
}
return null;
}
}
}

View file

@ -99,7 +99,7 @@ public enum Potion {
}
public PotionContents toComponent() {
return new PotionContents(this.ordinal(), -1, Collections.emptyList());
return new PotionContents(this.ordinal(), -1, Collections.emptyList(), null);
}
public static Potion getByJavaIdentifier(String javaIdentifier) {

View file

@ -25,11 +25,10 @@
package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
/**
* A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.Recipe}.
* A more compact version of {@link org.geysermc.mcprotocollib.protocol.data.game.recipe.display.RecipeDisplay}.
*/
public interface GeyserRecipe {
/**
@ -37,6 +36,5 @@ public interface GeyserRecipe {
*/
boolean isShaped();
@Nullable
ItemStack result();
SlotDisplay result();
}

View file

@ -25,15 +25,15 @@
package org.geysermc.geyser.inventory.recipe;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.data.ShapedRecipeData;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapedCraftingRecipeDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
public record GeyserShapedRecipe(int width, int height, Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe {
import java.util.List;
public GeyserShapedRecipe(ShapedRecipeData data) {
this(data.getWidth(), data.getHeight(), data.getIngredients(), data.getResult());
public record GeyserShapedRecipe(int width, int height, List<SlotDisplay> ingredients, SlotDisplay result) implements GeyserRecipe {
public GeyserShapedRecipe(ShapedCraftingRecipeDisplay data) {
this(data.width(), data.height(), data.ingredients(), data.result());
}
@Override

View file

@ -25,15 +25,15 @@
package org.geysermc.geyser.inventory.recipe;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.data.ShapelessRecipeData;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.ShapelessCraftingRecipeDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
public record GeyserShapelessRecipe(Ingredient[] ingredients, @Nullable ItemStack result) implements GeyserRecipe {
import java.util.List;
public GeyserShapelessRecipe(ShapelessRecipeData data) {
this(data.getIngredients(), data.getResult());
public record GeyserShapelessRecipe(List<SlotDisplay> ingredients, SlotDisplay result) implements GeyserRecipe {
public GeyserShapelessRecipe(ShapelessCraftingRecipeDisplay data) {
this(data.ingredients(), data.result());
}
@Override

View file

@ -0,0 +1,43 @@
/*
* 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.inventory.recipe;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.SmithingRecipeDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
public record GeyserSmithingRecipe(SlotDisplay template,
SlotDisplay base,
SlotDisplay addition,
SlotDisplay result) implements GeyserRecipe {
public GeyserSmithingRecipe(SmithingRecipeDisplay display) {
this(display.template(), display.base(), display.addition(), display.result());
}
@Override
public boolean isShaped() {
return false;
}
}

View file

@ -41,17 +41,15 @@ import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.EnchantmentTag;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundRenameItemPacket;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.IntStream;
@ -314,14 +312,10 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
for (Object2IntMap.Entry<Enchantment> entry : getEnchantments(session, material).object2IntEntrySet()) {
Enchantment enchantment = entry.getKey();
HolderSet supportedItems = enchantment.supportedItems();
int[] supportedItemIds = supportedItems.resolve(tagId -> session.getTagCache().get(ItemTag.ALL_ITEM_TAGS.get(tagId)));
boolean canApply = isEnchantedBook(input) || IntStream.of(supportedItemIds).anyMatch(id -> id == input.getJavaId());
boolean canApply = isEnchantedBook(input) || session.getTagCache().is(enchantment.supportedItems(), input.asItem());
HolderSet exclusiveSet = enchantment.exclusiveSet();
int[] incompatibleEnchantments = exclusiveSet.resolve(tagId -> session.getTagCache().get(EnchantmentTag.ALL_ENCHANTMENT_TAGS.get(tagId)));
for (int i : incompatibleEnchantments) {
Enchantment incompatible = session.getRegistryCache().enchantments().byId(i);
List<Enchantment> incompatibleEnchantments = enchantment.exclusiveSet().resolve(session);
for (Enchantment incompatible : incompatibleEnchantments) {
if (combinedEnchantments.containsKey(incompatible)) {
canApply = false;
if (!bedrock) {

View file

@ -25,9 +25,39 @@
package org.geysermc.geyser.item;
import org.geysermc.geyser.item.components.Rarity;
import org.geysermc.geyser.item.components.ToolTier;
import org.geysermc.geyser.item.type.*;
import org.geysermc.geyser.item.type.ArmorItem;
import org.geysermc.geyser.item.type.ArrowItem;
import org.geysermc.geyser.item.type.AxolotlBucketItem;
import org.geysermc.geyser.item.type.BannerItem;
import org.geysermc.geyser.item.type.BlockItem;
import org.geysermc.geyser.item.type.BoatItem;
import org.geysermc.geyser.item.type.CompassItem;
import org.geysermc.geyser.item.type.CrossbowItem;
import org.geysermc.geyser.item.type.DecoratedPotItem;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.item.type.DyeableArmorItem;
import org.geysermc.geyser.item.type.ElytraItem;
import org.geysermc.geyser.item.type.EnchantedBookItem;
import org.geysermc.geyser.item.type.FilledMapItem;
import org.geysermc.geyser.item.type.FireworkRocketItem;
import org.geysermc.geyser.item.type.FireworkStarItem;
import org.geysermc.geyser.item.type.FishingRodItem;
import org.geysermc.geyser.item.type.GoatHornItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.item.type.MaceItem;
import org.geysermc.geyser.item.type.MapItem;
import org.geysermc.geyser.item.type.PlayerHeadItem;
import org.geysermc.geyser.item.type.PotionItem;
import org.geysermc.geyser.item.type.ShieldItem;
import org.geysermc.geyser.item.type.ShulkerBoxItem;
import org.geysermc.geyser.item.type.SpawnEggItem;
import org.geysermc.geyser.item.type.TieredItem;
import org.geysermc.geyser.item.type.TippedArrowItem;
import org.geysermc.geyser.item.type.TropicalFishBucketItem;
import org.geysermc.geyser.item.type.WolfArmorItem;
import org.geysermc.geyser.item.type.WritableBookItem;
import org.geysermc.geyser.item.type.WrittenBookItem;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.registry.Registries;
@ -81,6 +111,7 @@ public final class Items {
public static final Item ACACIA_PLANKS = register(new BlockItem(builder(), Blocks.ACACIA_PLANKS));
public static final Item CHERRY_PLANKS = register(new BlockItem(builder(), Blocks.CHERRY_PLANKS));
public static final Item DARK_OAK_PLANKS = register(new BlockItem(builder(), Blocks.DARK_OAK_PLANKS));
public static final Item PALE_OAK_PLANKS = register(new BlockItem(builder(), Blocks.PALE_OAK_PLANKS));
public static final Item MANGROVE_PLANKS = register(new BlockItem(builder(), Blocks.MANGROVE_PLANKS));
public static final Item BAMBOO_PLANKS = register(new BlockItem(builder(), Blocks.BAMBOO_PLANKS));
public static final Item CRIMSON_PLANKS = register(new BlockItem(builder(), Blocks.CRIMSON_PLANKS));
@ -93,6 +124,7 @@ public final class Items {
public static final Item ACACIA_SAPLING = register(new BlockItem(builder(), Blocks.ACACIA_SAPLING));
public static final Item CHERRY_SAPLING = register(new BlockItem(builder(), Blocks.CHERRY_SAPLING));
public static final Item DARK_OAK_SAPLING = register(new BlockItem(builder(), Blocks.DARK_OAK_SAPLING));
public static final Item PALE_OAK_SAPLING = register(new BlockItem(builder(), Blocks.PALE_OAK_SAPLING));
public static final Item MANGROVE_PROPAGULE = register(new BlockItem(builder(), Blocks.MANGROVE_PROPAGULE));
public static final Item BEDROCK = register(new BlockItem(builder(), Blocks.BEDROCK));
public static final Item SAND = register(new BlockItem(builder(), Blocks.SAND));
@ -123,7 +155,7 @@ public final class Items {
public static final Item RAW_IRON_BLOCK = register(new BlockItem(builder(), Blocks.RAW_IRON_BLOCK));
public static final Item RAW_COPPER_BLOCK = register(new BlockItem(builder(), Blocks.RAW_COPPER_BLOCK));
public static final Item RAW_GOLD_BLOCK = register(new BlockItem(builder(), Blocks.RAW_GOLD_BLOCK));
public static final Item HEAVY_CORE = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.HEAVY_CORE));
public static final Item HEAVY_CORE = register(new BlockItem(builder(), Blocks.HEAVY_CORE));
public static final Item AMETHYST_BLOCK = register(new BlockItem(builder(), Blocks.AMETHYST_BLOCK));
public static final Item BUDDING_AMETHYST = register(new BlockItem(builder(), Blocks.BUDDING_AMETHYST));
public static final Item IRON_BLOCK = register(new BlockItem(builder(), Blocks.IRON_BLOCK));
@ -176,6 +208,7 @@ public final class Items {
public static final Item JUNGLE_LOG = register(new BlockItem(builder(), Blocks.JUNGLE_LOG));
public static final Item ACACIA_LOG = register(new BlockItem(builder(), Blocks.ACACIA_LOG));
public static final Item CHERRY_LOG = register(new BlockItem(builder(), Blocks.CHERRY_LOG));
public static final Item PALE_OAK_LOG = register(new BlockItem(builder(), Blocks.PALE_OAK_LOG));
public static final Item DARK_OAK_LOG = register(new BlockItem(builder(), Blocks.DARK_OAK_LOG));
public static final Item MANGROVE_LOG = register(new BlockItem(builder(), Blocks.MANGROVE_LOG));
public static final Item MANGROVE_ROOTS = register(new BlockItem(builder(), Blocks.MANGROVE_ROOTS));
@ -190,6 +223,7 @@ public final class Items {
public static final Item STRIPPED_ACACIA_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_ACACIA_LOG));
public static final Item STRIPPED_CHERRY_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_CHERRY_LOG));
public static final Item STRIPPED_DARK_OAK_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_DARK_OAK_LOG));
public static final Item STRIPPED_PALE_OAK_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_PALE_OAK_LOG));
public static final Item STRIPPED_MANGROVE_LOG = register(new BlockItem(builder(), Blocks.STRIPPED_MANGROVE_LOG));
public static final Item STRIPPED_CRIMSON_STEM = register(new BlockItem(builder(), Blocks.STRIPPED_CRIMSON_STEM));
public static final Item STRIPPED_WARPED_STEM = register(new BlockItem(builder(), Blocks.STRIPPED_WARPED_STEM));
@ -200,6 +234,7 @@ public final class Items {
public static final Item STRIPPED_ACACIA_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_ACACIA_WOOD));
public static final Item STRIPPED_CHERRY_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_CHERRY_WOOD));
public static final Item STRIPPED_DARK_OAK_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_DARK_OAK_WOOD));
public static final Item STRIPPED_PALE_OAK_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_PALE_OAK_WOOD));
public static final Item STRIPPED_MANGROVE_WOOD = register(new BlockItem(builder(), Blocks.STRIPPED_MANGROVE_WOOD));
public static final Item STRIPPED_CRIMSON_HYPHAE = register(new BlockItem(builder(), Blocks.STRIPPED_CRIMSON_HYPHAE));
public static final Item STRIPPED_WARPED_HYPHAE = register(new BlockItem(builder(), Blocks.STRIPPED_WARPED_HYPHAE));
@ -210,6 +245,7 @@ public final class Items {
public static final Item JUNGLE_WOOD = register(new BlockItem(builder(), Blocks.JUNGLE_WOOD));
public static final Item ACACIA_WOOD = register(new BlockItem(builder(), Blocks.ACACIA_WOOD));
public static final Item CHERRY_WOOD = register(new BlockItem(builder(), Blocks.CHERRY_WOOD));
public static final Item PALE_OAK_WOOD = register(new BlockItem(builder(), Blocks.PALE_OAK_WOOD));
public static final Item DARK_OAK_WOOD = register(new BlockItem(builder(), Blocks.DARK_OAK_WOOD));
public static final Item MANGROVE_WOOD = register(new BlockItem(builder(), Blocks.MANGROVE_WOOD));
public static final Item CRIMSON_HYPHAE = register(new BlockItem(builder(), Blocks.CRIMSON_HYPHAE));
@ -221,6 +257,7 @@ public final class Items {
public static final Item ACACIA_LEAVES = register(new BlockItem(builder(), Blocks.ACACIA_LEAVES));
public static final Item CHERRY_LEAVES = register(new BlockItem(builder(), Blocks.CHERRY_LEAVES));
public static final Item DARK_OAK_LEAVES = register(new BlockItem(builder(), Blocks.DARK_OAK_LEAVES));
public static final Item PALE_OAK_LEAVES = register(new BlockItem(builder(), Blocks.PALE_OAK_LEAVES));
public static final Item MANGROVE_LEAVES = register(new BlockItem(builder(), Blocks.MANGROVE_LEAVES));
public static final Item AZALEA_LEAVES = register(new BlockItem(builder(), Blocks.AZALEA_LEAVES));
public static final Item FLOWERING_AZALEA_LEAVES = register(new BlockItem(builder(), Blocks.FLOWERING_AZALEA_LEAVES));
@ -283,9 +320,12 @@ public final class Items {
public static final Item TWISTING_VINES = register(new BlockItem(builder(), Blocks.TWISTING_VINES));
public static final Item SUGAR_CANE = register(new BlockItem(builder(), Blocks.SUGAR_CANE));
public static final Item KELP = register(new BlockItem(builder(), Blocks.KELP));
public static final Item MOSS_CARPET = register(new BlockItem(builder(), Blocks.MOSS_CARPET));
public static final Item PINK_PETALS = register(new BlockItem(builder(), Blocks.PINK_PETALS));
public static final Item MOSS_CARPET = register(new BlockItem(builder(), Blocks.MOSS_CARPET));
public static final Item MOSS_BLOCK = register(new BlockItem(builder(), Blocks.MOSS_BLOCK));
public static final Item PALE_MOSS_CARPET = register(new BlockItem(builder(), Blocks.PALE_MOSS_CARPET));
public static final Item PALE_HANGING_MOSS = register(new BlockItem(builder(), Blocks.PALE_HANGING_MOSS));
public static final Item PALE_MOSS_BLOCK = register(new BlockItem(builder(), Blocks.PALE_MOSS_BLOCK));
public static final Item HANGING_ROOTS = register(new BlockItem(builder(), Blocks.HANGING_ROOTS));
public static final Item BIG_DRIPLEAF = register(new BlockItem(builder(), Blocks.BIG_DRIPLEAF, Blocks.BIG_DRIPLEAF_STEM));
public static final Item SMALL_DRIPLEAF = register(new BlockItem(builder(), Blocks.SMALL_DRIPLEAF));
@ -297,6 +337,7 @@ public final class Items {
public static final Item ACACIA_SLAB = register(new BlockItem(builder(), Blocks.ACACIA_SLAB));
public static final Item CHERRY_SLAB = register(new BlockItem(builder(), Blocks.CHERRY_SLAB));
public static final Item DARK_OAK_SLAB = register(new BlockItem(builder(), Blocks.DARK_OAK_SLAB));
public static final Item PALE_OAK_SLAB = register(new BlockItem(builder(), Blocks.PALE_OAK_SLAB));
public static final Item MANGROVE_SLAB = register(new BlockItem(builder(), Blocks.MANGROVE_SLAB));
public static final Item BAMBOO_SLAB = register(new BlockItem(builder(), Blocks.BAMBOO_SLAB));
public static final Item BAMBOO_MOSAIC_SLAB = register(new BlockItem(builder(), Blocks.BAMBOO_MOSAIC_SLAB));
@ -337,6 +378,7 @@ public final class Items {
public static final Item PURPUR_PILLAR = register(new BlockItem(builder(), Blocks.PURPUR_PILLAR));
public static final Item PURPUR_STAIRS = register(new BlockItem(builder(), Blocks.PURPUR_STAIRS));
public static final Item SPAWNER = register(new BlockItem(builder(), Blocks.SPAWNER));
public static final Item CREAKING_HEART = register(new BlockItem(builder(), Blocks.CREAKING_HEART));
public static final Item CHEST = register(new BlockItem(builder(), Blocks.CHEST));
public static final Item CRAFTING_TABLE = register(new BlockItem(builder(), Blocks.CRAFTING_TABLE));
public static final Item FARMLAND = register(new BlockItem(builder(), Blocks.FARMLAND));
@ -356,6 +398,7 @@ public final class Items {
public static final Item ACACIA_FENCE = register(new BlockItem(builder(), Blocks.ACACIA_FENCE));
public static final Item CHERRY_FENCE = register(new BlockItem(builder(), Blocks.CHERRY_FENCE));
public static final Item DARK_OAK_FENCE = register(new BlockItem(builder(), Blocks.DARK_OAK_FENCE));
public static final Item PALE_OAK_FENCE = register(new BlockItem(builder(), Blocks.PALE_OAK_FENCE));
public static final Item MANGROVE_FENCE = register(new BlockItem(builder(), Blocks.MANGROVE_FENCE));
public static final Item BAMBOO_FENCE = register(new BlockItem(builder(), Blocks.BAMBOO_FENCE));
public static final Item CRIMSON_FENCE = register(new BlockItem(builder(), Blocks.CRIMSON_FENCE));
@ -417,7 +460,7 @@ public final class Items {
public static final Item END_PORTAL_FRAME = register(new BlockItem(builder(), Blocks.END_PORTAL_FRAME));
public static final Item END_STONE = register(new BlockItem(builder(), Blocks.END_STONE));
public static final Item END_STONE_BRICKS = register(new BlockItem(builder(), Blocks.END_STONE_BRICKS));
public static final Item DRAGON_EGG = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.DRAGON_EGG));
public static final Item DRAGON_EGG = register(new BlockItem(builder(), Blocks.DRAGON_EGG));
public static final Item SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SANDSTONE_STAIRS));
public static final Item ENDER_CHEST = register(new BlockItem(builder(), Blocks.ENDER_CHEST));
public static final Item EMERALD_BLOCK = register(new BlockItem(builder(), Blocks.EMERALD_BLOCK));
@ -428,13 +471,14 @@ public final class Items {
public static final Item ACACIA_STAIRS = register(new BlockItem(builder(), Blocks.ACACIA_STAIRS));
public static final Item CHERRY_STAIRS = register(new BlockItem(builder(), Blocks.CHERRY_STAIRS));
public static final Item DARK_OAK_STAIRS = register(new BlockItem(builder(), Blocks.DARK_OAK_STAIRS));
public static final Item PALE_OAK_STAIRS = register(new BlockItem(builder(), Blocks.PALE_OAK_STAIRS));
public static final Item MANGROVE_STAIRS = register(new BlockItem(builder(), Blocks.MANGROVE_STAIRS));
public static final Item BAMBOO_STAIRS = register(new BlockItem(builder(), Blocks.BAMBOO_STAIRS));
public static final Item BAMBOO_MOSAIC_STAIRS = register(new BlockItem(builder(), Blocks.BAMBOO_MOSAIC_STAIRS));
public static final Item CRIMSON_STAIRS = register(new BlockItem(builder(), Blocks.CRIMSON_STAIRS));
public static final Item WARPED_STAIRS = register(new BlockItem(builder(), Blocks.WARPED_STAIRS));
public static final Item COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.COMMAND_BLOCK));
public static final Item BEACON = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.BEACON));
public static final Item COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.COMMAND_BLOCK));
public static final Item BEACON = register(new BlockItem(builder(), Blocks.BEACON));
public static final Item COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.COBBLESTONE_WALL));
public static final Item MOSSY_COBBLESTONE_WALL = register(new BlockItem(builder(), Blocks.MOSSY_COBBLESTONE_WALL));
public static final Item BRICK_WALL = register(new BlockItem(builder(), Blocks.BRICK_WALL));
@ -481,8 +525,8 @@ public final class Items {
public static final Item GREEN_TERRACOTTA = register(new BlockItem(builder(), Blocks.GREEN_TERRACOTTA));
public static final Item RED_TERRACOTTA = register(new BlockItem(builder(), Blocks.RED_TERRACOTTA));
public static final Item BLACK_TERRACOTTA = register(new BlockItem(builder(), Blocks.BLACK_TERRACOTTA));
public static final Item BARRIER = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.BARRIER));
public static final Item LIGHT = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.LIGHT));
public static final Item BARRIER = register(new BlockItem(builder(), Blocks.BARRIER));
public static final Item LIGHT = register(new BlockItem(builder(), Blocks.LIGHT));
public static final Item HAY_BLOCK = register(new BlockItem(builder(), Blocks.HAY_BLOCK));
public static final Item WHITE_CARPET = register(new BlockItem(builder(), Blocks.WHITE_CARPET));
public static final Item ORANGE_CARPET = register(new BlockItem(builder(), Blocks.ORANGE_CARPET));
@ -552,14 +596,14 @@ public final class Items {
public static final Item CHISELED_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CHISELED_RED_SANDSTONE));
public static final Item CUT_RED_SANDSTONE = register(new BlockItem(builder(), Blocks.CUT_RED_SANDSTONE));
public static final Item RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.RED_SANDSTONE_STAIRS));
public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.REPEATING_COMMAND_BLOCK));
public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.CHAIN_COMMAND_BLOCK));
public static final Item REPEATING_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.REPEATING_COMMAND_BLOCK));
public static final Item CHAIN_COMMAND_BLOCK = register(new BlockItem(builder(), Blocks.CHAIN_COMMAND_BLOCK));
public static final Item MAGMA_BLOCK = register(new BlockItem(builder(), Blocks.MAGMA_BLOCK));
public static final Item NETHER_WART_BLOCK = register(new BlockItem(builder(), Blocks.NETHER_WART_BLOCK));
public static final Item WARPED_WART_BLOCK = register(new BlockItem(builder(), Blocks.WARPED_WART_BLOCK));
public static final Item RED_NETHER_BRICKS = register(new BlockItem(builder(), Blocks.RED_NETHER_BRICKS));
public static final Item BONE_BLOCK = register(new BlockItem(builder(), Blocks.BONE_BLOCK));
public static final Item STRUCTURE_VOID = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_VOID));
public static final Item STRUCTURE_VOID = register(new BlockItem(builder(), Blocks.STRUCTURE_VOID));
public static final Item SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.SHULKER_BOX));
public static final Item WHITE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.WHITE_SHULKER_BOX));
public static final Item ORANGE_SHULKER_BOX = register(new ShulkerBoxItem(builder().stackSize(1), Blocks.ORANGE_SHULKER_BOX));
@ -658,7 +702,7 @@ public final class Items {
public static final Item DEAD_FIRE_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_FIRE_CORAL_FAN, Blocks.DEAD_FIRE_CORAL_WALL_FAN));
public static final Item DEAD_HORN_CORAL_FAN = register(new BlockItem(builder(), Blocks.DEAD_HORN_CORAL_FAN, Blocks.DEAD_HORN_CORAL_WALL_FAN));
public static final Item BLUE_ICE = register(new BlockItem(builder(), Blocks.BLUE_ICE));
public static final Item CONDUIT = register(new BlockItem(builder().rarity(Rarity.RARE), Blocks.CONDUIT));
public static final Item CONDUIT = register(new BlockItem(builder(), Blocks.CONDUIT));
public static final Item POLISHED_GRANITE_STAIRS = register(new BlockItem(builder(), Blocks.POLISHED_GRANITE_STAIRS));
public static final Item SMOOTH_RED_SANDSTONE_STAIRS = register(new BlockItem(builder(), Blocks.SMOOTH_RED_SANDSTONE_STAIRS));
public static final Item MOSSY_STONE_BRICK_STAIRS = register(new BlockItem(builder(), Blocks.MOSSY_STONE_BRICK_STAIRS));
@ -729,6 +773,7 @@ public final class Items {
public static final Item ACACIA_BUTTON = register(new BlockItem(builder(), Blocks.ACACIA_BUTTON));
public static final Item CHERRY_BUTTON = register(new BlockItem(builder(), Blocks.CHERRY_BUTTON));
public static final Item DARK_OAK_BUTTON = register(new BlockItem(builder(), Blocks.DARK_OAK_BUTTON));
public static final Item PALE_OAK_BUTTON = register(new BlockItem(builder(), Blocks.PALE_OAK_BUTTON));
public static final Item MANGROVE_BUTTON = register(new BlockItem(builder(), Blocks.MANGROVE_BUTTON));
public static final Item BAMBOO_BUTTON = register(new BlockItem(builder(), Blocks.BAMBOO_BUTTON));
public static final Item CRIMSON_BUTTON = register(new BlockItem(builder(), Blocks.CRIMSON_BUTTON));
@ -744,6 +789,7 @@ public final class Items {
public static final Item ACACIA_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.ACACIA_PRESSURE_PLATE));
public static final Item CHERRY_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.CHERRY_PRESSURE_PLATE));
public static final Item DARK_OAK_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.DARK_OAK_PRESSURE_PLATE));
public static final Item PALE_OAK_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.PALE_OAK_PRESSURE_PLATE));
public static final Item MANGROVE_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.MANGROVE_PRESSURE_PLATE));
public static final Item BAMBOO_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.BAMBOO_PRESSURE_PLATE));
public static final Item CRIMSON_PRESSURE_PLATE = register(new BlockItem(builder(), Blocks.CRIMSON_PRESSURE_PLATE));
@ -756,6 +802,7 @@ public final class Items {
public static final Item ACACIA_DOOR = register(new BlockItem(builder(), Blocks.ACACIA_DOOR));
public static final Item CHERRY_DOOR = register(new BlockItem(builder(), Blocks.CHERRY_DOOR));
public static final Item DARK_OAK_DOOR = register(new BlockItem(builder(), Blocks.DARK_OAK_DOOR));
public static final Item PALE_OAK_DOOR = register(new BlockItem(builder(), Blocks.PALE_OAK_DOOR));
public static final Item MANGROVE_DOOR = register(new BlockItem(builder(), Blocks.MANGROVE_DOOR));
public static final Item BAMBOO_DOOR = register(new BlockItem(builder(), Blocks.BAMBOO_DOOR));
public static final Item CRIMSON_DOOR = register(new BlockItem(builder(), Blocks.CRIMSON_DOOR));
@ -776,6 +823,7 @@ public final class Items {
public static final Item ACACIA_TRAPDOOR = register(new BlockItem(builder(), Blocks.ACACIA_TRAPDOOR));
public static final Item CHERRY_TRAPDOOR = register(new BlockItem(builder(), Blocks.CHERRY_TRAPDOOR));
public static final Item DARK_OAK_TRAPDOOR = register(new BlockItem(builder(), Blocks.DARK_OAK_TRAPDOOR));
public static final Item PALE_OAK_TRAPDOOR = register(new BlockItem(builder(), Blocks.PALE_OAK_TRAPDOOR));
public static final Item MANGROVE_TRAPDOOR = register(new BlockItem(builder(), Blocks.MANGROVE_TRAPDOOR));
public static final Item BAMBOO_TRAPDOOR = register(new BlockItem(builder(), Blocks.BAMBOO_TRAPDOOR));
public static final Item CRIMSON_TRAPDOOR = register(new BlockItem(builder(), Blocks.CRIMSON_TRAPDOOR));
@ -795,6 +843,7 @@ public final class Items {
public static final Item ACACIA_FENCE_GATE = register(new BlockItem(builder(), Blocks.ACACIA_FENCE_GATE));
public static final Item CHERRY_FENCE_GATE = register(new BlockItem(builder(), Blocks.CHERRY_FENCE_GATE));
public static final Item DARK_OAK_FENCE_GATE = register(new BlockItem(builder(), Blocks.DARK_OAK_FENCE_GATE));
public static final Item PALE_OAK_FENCE_GATE = register(new BlockItem(builder(), Blocks.PALE_OAK_FENCE_GATE));
public static final Item MANGROVE_FENCE_GATE = register(new BlockItem(builder(), Blocks.MANGROVE_FENCE_GATE));
public static final Item BAMBOO_FENCE_GATE = register(new BlockItem(builder(), Blocks.BAMBOO_FENCE_GATE));
public static final Item CRIMSON_FENCE_GATE = register(new BlockItem(builder(), Blocks.CRIMSON_FENCE_GATE));
@ -811,7 +860,8 @@ public final class Items {
public static final Item HOPPER_MINECART = register(new Item("hopper_minecart", builder().stackSize(1)));
public static final Item CARROT_ON_A_STICK = register(new Item("carrot_on_a_stick", builder().stackSize(1).maxDamage(25)));
public static final Item WARPED_FUNGUS_ON_A_STICK = register(new Item("warped_fungus_on_a_stick", builder().stackSize(1).maxDamage(100)));
public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432).rarity(Rarity.UNCOMMON)));
public static final Item PHANTOM_MEMBRANE = register(new Item("phantom_membrane", builder()));
public static final Item ELYTRA = register(new ElytraItem("elytra", builder().stackSize(1).maxDamage(432)));
public static final Item OAK_BOAT = register(new BoatItem("oak_boat", builder().stackSize(1)));
public static final Item OAK_CHEST_BOAT = register(new BoatItem("oak_chest_boat", builder().stackSize(1)));
public static final Item SPRUCE_BOAT = register(new BoatItem("spruce_boat", builder().stackSize(1)));
@ -826,12 +876,14 @@ public final class Items {
public static final Item CHERRY_CHEST_BOAT = register(new BoatItem("cherry_chest_boat", builder().stackSize(1)));
public static final Item DARK_OAK_BOAT = register(new BoatItem("dark_oak_boat", builder().stackSize(1)));
public static final Item DARK_OAK_CHEST_BOAT = register(new BoatItem("dark_oak_chest_boat", builder().stackSize(1)));
public static final Item PALE_OAK_BOAT = register(new BoatItem("pale_oak_boat", builder().stackSize(1)));
public static final Item PALE_OAK_CHEST_BOAT = register(new BoatItem("pale_oak_chest_boat", builder().stackSize(1)));
public static final Item MANGROVE_BOAT = register(new BoatItem("mangrove_boat", builder().stackSize(1)));
public static final Item MANGROVE_CHEST_BOAT = register(new BoatItem("mangrove_chest_boat", builder().stackSize(1)));
public static final Item BAMBOO_RAFT = register(new BoatItem("bamboo_raft", builder().stackSize(1)));
public static final Item BAMBOO_CHEST_RAFT = register(new BoatItem("bamboo_chest_raft", builder().stackSize(1)));
public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.STRUCTURE_BLOCK));
public static final Item JIGSAW = register(new BlockItem(builder().rarity(Rarity.EPIC), Blocks.JIGSAW));
public static final Item STRUCTURE_BLOCK = register(new BlockItem(builder(), Blocks.STRUCTURE_BLOCK));
public static final Item JIGSAW = register(new BlockItem(builder(), Blocks.JIGSAW));
public static final Item TURTLE_HELMET = register(new ArmorItem("turtle_helmet", ArmorMaterial.TURTLE, builder().stackSize(1).maxDamage(275)));
public static final Item TURTLE_SCUTE = register(new Item("turtle_scute", builder()));
public static final Item ARMADILLO_SCUTE = register(new Item("armadillo_scute", builder()));
@ -922,8 +974,8 @@ public final class Items {
public static final Item PORKCHOP = register(new Item("porkchop", builder()));
public static final Item COOKED_PORKCHOP = register(new Item("cooked_porkchop", builder()));
public static final Item PAINTING = register(new Item("painting", builder()));
public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder().rarity(Rarity.RARE)));
public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder().rarity(Rarity.EPIC)));
public static final Item GOLDEN_APPLE = register(new Item("golden_apple", builder()));
public static final Item ENCHANTED_GOLDEN_APPLE = register(new Item("enchanted_golden_apple", builder()));
public static final Item OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.OAK_SIGN, Blocks.OAK_WALL_SIGN));
public static final Item SPRUCE_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.SPRUCE_SIGN, Blocks.SPRUCE_WALL_SIGN));
public static final Item BIRCH_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BIRCH_SIGN, Blocks.BIRCH_WALL_SIGN));
@ -931,6 +983,7 @@ public final class Items {
public static final Item ACACIA_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.ACACIA_SIGN, Blocks.ACACIA_WALL_SIGN));
public static final Item CHERRY_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CHERRY_SIGN, Blocks.CHERRY_WALL_SIGN));
public static final Item DARK_OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.DARK_OAK_SIGN, Blocks.DARK_OAK_WALL_SIGN));
public static final Item PALE_OAK_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.PALE_OAK_SIGN, Blocks.PALE_OAK_WALL_SIGN));
public static final Item MANGROVE_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.MANGROVE_SIGN, Blocks.MANGROVE_WALL_SIGN));
public static final Item BAMBOO_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BAMBOO_SIGN, Blocks.BAMBOO_WALL_SIGN));
public static final Item CRIMSON_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CRIMSON_SIGN, Blocks.CRIMSON_WALL_SIGN));
@ -942,6 +995,7 @@ public final class Items {
public static final Item ACACIA_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.ACACIA_HANGING_SIGN, Blocks.ACACIA_WALL_HANGING_SIGN));
public static final Item CHERRY_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CHERRY_HANGING_SIGN, Blocks.CHERRY_WALL_HANGING_SIGN));
public static final Item DARK_OAK_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.DARK_OAK_HANGING_SIGN, Blocks.DARK_OAK_WALL_HANGING_SIGN));
public static final Item PALE_OAK_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.PALE_OAK_HANGING_SIGN, Blocks.PALE_OAK_WALL_HANGING_SIGN));
public static final Item MANGROVE_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.MANGROVE_HANGING_SIGN, Blocks.MANGROVE_WALL_HANGING_SIGN));
public static final Item BAMBOO_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.BAMBOO_HANGING_SIGN, Blocks.BAMBOO_WALL_HANGING_SIGN));
public static final Item CRIMSON_HANGING_SIGN = register(new BlockItem(builder().stackSize(16), Blocks.CRIMSON_HANGING_SIGN, Blocks.CRIMSON_WALL_HANGING_SIGN));
@ -969,6 +1023,22 @@ public final class Items {
public static final Item COMPASS = register(new CompassItem("compass", builder()));
public static final Item RECOVERY_COMPASS = register(new Item("recovery_compass", builder()));
public static final Item BUNDLE = register(new Item("bundle", builder().stackSize(1)));
public static final Item WHITE_BUNDLE = register(new Item("white_bundle", builder().stackSize(1)));
public static final Item ORANGE_BUNDLE = register(new Item("orange_bundle", builder().stackSize(1)));
public static final Item MAGENTA_BUNDLE = register(new Item("magenta_bundle", builder().stackSize(1)));
public static final Item LIGHT_BLUE_BUNDLE = register(new Item("light_blue_bundle", builder().stackSize(1)));
public static final Item YELLOW_BUNDLE = register(new Item("yellow_bundle", builder().stackSize(1)));
public static final Item LIME_BUNDLE = register(new Item("lime_bundle", builder().stackSize(1)));
public static final Item PINK_BUNDLE = register(new Item("pink_bundle", builder().stackSize(1)));
public static final Item GRAY_BUNDLE = register(new Item("gray_bundle", builder().stackSize(1)));
public static final Item LIGHT_GRAY_BUNDLE = register(new Item("light_gray_bundle", builder().stackSize(1)));
public static final Item CYAN_BUNDLE = register(new Item("cyan_bundle", builder().stackSize(1)));
public static final Item PURPLE_BUNDLE = register(new Item("purple_bundle", builder().stackSize(1)));
public static final Item BLUE_BUNDLE = register(new Item("blue_bundle", builder().stackSize(1)));
public static final Item BROWN_BUNDLE = register(new Item("brown_bundle", builder().stackSize(1)));
public static final Item GREEN_BUNDLE = register(new Item("green_bundle", builder().stackSize(1)));
public static final Item RED_BUNDLE = register(new Item("red_bundle", builder().stackSize(1)));
public static final Item BLACK_BUNDLE = register(new Item("black_bundle", builder().stackSize(1)));
public static final Item FISHING_ROD = register(new FishingRodItem("fishing_rod", builder().stackSize(1).maxDamage(64)));
public static final Item CLOCK = register(new Item("clock", builder()));
public static final Item SPYGLASS = register(new Item("spyglass", builder().stackSize(1)));
@ -1036,14 +1106,14 @@ public final class Items {
public static final Item GHAST_TEAR = register(new Item("ghast_tear", builder()));
public static final Item GOLD_NUGGET = register(new Item("gold_nugget", builder()));
public static final Item NETHER_WART = register(new BlockItem(builder(), Blocks.NETHER_WART));
public static final Item POTION = register(new PotionItem("potion", builder().stackSize(1)));
public static final Item GLASS_BOTTLE = register(new Item("glass_bottle", builder()));
public static final Item POTION = register(new PotionItem("potion", builder().stackSize(1)));
public static final Item SPIDER_EYE = register(new Item("spider_eye", builder()));
public static final Item FERMENTED_SPIDER_EYE = register(new Item("fermented_spider_eye", builder()));
public static final Item BLAZE_POWDER = register(new Item("blaze_powder", builder()));
public static final Item MAGMA_CREAM = register(new Item("magma_cream", builder()));
public static final Item BREWING_STAND = register(new BlockItem(builder(), Blocks.BREWING_STAND));
public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.WATER_CAULDRON, Blocks.POWDER_SNOW_CAULDRON, Blocks.LAVA_CAULDRON));
public static final Item CAULDRON = register(new BlockItem(builder(), Blocks.CAULDRON, Blocks.LAVA_CAULDRON, Blocks.WATER_CAULDRON, Blocks.POWDER_SNOW_CAULDRON));
public static final Item ENDER_EYE = register(new Item("ender_eye", builder()));
public static final Item GLISTERING_MELON_SLICE = register(new Item("glistering_melon_slice", builder()));
public static final Item ARMADILLO_SPAWN_EGG = register(new SpawnEggItem("armadillo_spawn_egg", builder()));
@ -1122,16 +1192,18 @@ public final class Items {
public static final Item WITHER_SKELETON_SPAWN_EGG = register(new SpawnEggItem("wither_skeleton_spawn_egg", builder()));
public static final Item WOLF_SPAWN_EGG = register(new SpawnEggItem("wolf_spawn_egg", builder()));
public static final Item ZOGLIN_SPAWN_EGG = register(new SpawnEggItem("zoglin_spawn_egg", builder()));
public static final Item CREAKING_SPAWN_EGG = register(new SpawnEggItem("creaking_spawn_egg", builder()));
public static final Item ZOMBIE_SPAWN_EGG = register(new SpawnEggItem("zombie_spawn_egg", builder()));
public static final Item ZOMBIE_HORSE_SPAWN_EGG = register(new SpawnEggItem("zombie_horse_spawn_egg", builder()));
public static final Item ZOMBIE_VILLAGER_SPAWN_EGG = register(new SpawnEggItem("zombie_villager_spawn_egg", builder()));
public static final Item ZOMBIFIED_PIGLIN_SPAWN_EGG = register(new SpawnEggItem("zombified_piglin_spawn_egg", builder()));
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder().rarity(Rarity.UNCOMMON)));
public static final Item EXPERIENCE_BOTTLE = register(new Item("experience_bottle", builder()));
public static final Item FIRE_CHARGE = register(new Item("fire_charge", builder()));
public static final Item WIND_CHARGE = register(new Item("wind_charge", builder()));
public static final Item WRITABLE_BOOK = register(new WritableBookItem("writable_book", builder().stackSize(1)));
public static final Item WRITTEN_BOOK = register(new WrittenBookItem("written_book", builder().stackSize(16)));
public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500).rarity(Rarity.EPIC)));
public static final Item BREEZE_ROD = register(new Item("breeze_rod", builder()));
public static final Item MACE = register(new MaceItem("mace", builder().stackSize(1).maxDamage(500)));
public static final Item ITEM_FRAME = register(new Item("item_frame", builder()));
public static final Item GLOW_ITEM_FRAME = register(new Item("glow_item_frame", builder()));
public static final Item FLOWER_POT = register(new BlockItem(builder(), Blocks.FLOWER_POT));
@ -1141,18 +1213,18 @@ public final class Items {
public static final Item POISONOUS_POTATO = register(new Item("poisonous_potato", builder()));
public static final Item MAP = register(new MapItem("map", builder()));
public static final Item GOLDEN_CARROT = register(new Item("golden_carrot", builder()));
public static final Item SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL));
public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL));
public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder().rarity(Rarity.UNCOMMON), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD));
public static final Item ZOMBIE_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD));
public static final Item CREEPER_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD));
public static final Item DRAGON_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD));
public static final Item PIGLIN_HEAD = register(new BlockItem(builder().rarity(Rarity.UNCOMMON), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD));
public static final Item NETHER_STAR = register(new Item("nether_star", builder().rarity(Rarity.UNCOMMON)));
public static final Item SKELETON_SKULL = register(new BlockItem(builder(), Blocks.SKELETON_SKULL, Blocks.SKELETON_WALL_SKULL));
public static final Item WITHER_SKELETON_SKULL = register(new BlockItem(builder(), Blocks.WITHER_SKELETON_SKULL, Blocks.WITHER_SKELETON_WALL_SKULL));
public static final Item PLAYER_HEAD = register(new PlayerHeadItem(builder(), Blocks.PLAYER_HEAD, Blocks.PLAYER_WALL_HEAD));
public static final Item ZOMBIE_HEAD = register(new BlockItem(builder(), Blocks.ZOMBIE_HEAD, Blocks.ZOMBIE_WALL_HEAD));
public static final Item CREEPER_HEAD = register(new BlockItem(builder(), Blocks.CREEPER_HEAD, Blocks.CREEPER_WALL_HEAD));
public static final Item DRAGON_HEAD = register(new BlockItem(builder(), Blocks.DRAGON_HEAD, Blocks.DRAGON_WALL_HEAD));
public static final Item PIGLIN_HEAD = register(new BlockItem(builder(), Blocks.PIGLIN_HEAD, Blocks.PIGLIN_WALL_HEAD));
public static final Item NETHER_STAR = register(new Item("nether_star", builder()));
public static final Item PUMPKIN_PIE = register(new Item("pumpkin_pie", builder()));
public static final Item FIREWORK_ROCKET = register(new FireworkRocketItem("firework_rocket", builder()));
public static final Item FIREWORK_STAR = register(new FireworkStarItem("firework_star", builder()));
public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
public static final Item ENCHANTED_BOOK = register(new EnchantedBookItem("enchanted_book", builder().stackSize(1)));
public static final Item NETHER_BRICK = register(new Item("nether_brick", builder()));
public static final Item PRISMARINE_SHARD = register(new Item("prismarine_shard", builder()));
public static final Item PRISMARINE_CRYSTALS = register(new Item("prismarine_crystals", builder()));
@ -1162,13 +1234,13 @@ public final class Items {
public static final Item RABBIT_FOOT = register(new Item("rabbit_foot", builder()));
public static final Item RABBIT_HIDE = register(new Item("rabbit_hide", builder()));
public static final Item ARMOR_STAND = register(new Item("armor_stand", builder().stackSize(16)));
public static final Item IRON_HORSE_ARMOR = register(new ArmorItem("iron_horse_armor", ArmorMaterial.IRON, builder().stackSize(1)));
public static final Item GOLDEN_HORSE_ARMOR = register(new ArmorItem("golden_horse_armor", ArmorMaterial.GOLD, builder().stackSize(1)));
public static final Item DIAMOND_HORSE_ARMOR = register(new ArmorItem("diamond_horse_armor", ArmorMaterial.DIAMOND, builder().stackSize(1)));
public static final Item IRON_HORSE_ARMOR = register(new Item("iron_horse_armor", builder().stackSize(1)));
public static final Item GOLDEN_HORSE_ARMOR = register(new Item("golden_horse_armor", builder().stackSize(1)));
public static final Item DIAMOND_HORSE_ARMOR = register(new Item("diamond_horse_armor", builder().stackSize(1)));
public static final Item LEATHER_HORSE_ARMOR = register(new DyeableArmorItem("leather_horse_armor", ArmorMaterial.LEATHER, builder().stackSize(1)));
public static final Item LEAD = register(new Item("lead", builder()));
public static final Item NAME_TAG = register(new Item("name_tag", builder()));
public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1).rarity(Rarity.EPIC)));
public static final Item COMMAND_BLOCK_MINECART = register(new Item("command_block_minecart", builder().stackSize(1)));
public static final Item MUTTON = register(new Item("mutton", builder()));
public static final Item COOKED_MUTTON = register(new Item("cooked_mutton", builder()));
public static final Item WHITE_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.WHITE_BANNER, Blocks.WHITE_WALL_BANNER));
@ -1187,7 +1259,7 @@ public final class Items {
public static final Item GREEN_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.GREEN_BANNER, Blocks.GREEN_WALL_BANNER));
public static final Item RED_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.RED_BANNER, Blocks.RED_WALL_BANNER));
public static final Item BLACK_BANNER = register(new BannerItem(builder().stackSize(16), Blocks.BLACK_BANNER, Blocks.BLACK_WALL_BANNER));
public static final Item END_CRYSTAL = register(new Item("end_crystal", builder().rarity(Rarity.RARE)));
public static final Item END_CRYSTAL = register(new Item("end_crystal", builder()));
public static final Item CHORUS_FRUIT = register(new Item("chorus_fruit", builder()));
public static final Item POPPED_CHORUS_FRUIT = register(new Item("popped_chorus_fruit", builder()));
public static final Item TORCHFLOWER_SEEDS = register(new BlockItem("torchflower_seeds", builder(), Blocks.TORCHFLOWER_CROP));
@ -1195,52 +1267,53 @@ public final class Items {
public static final Item BEETROOT = register(new Item("beetroot", builder()));
public static final Item BEETROOT_SEEDS = register(new BlockItem("beetroot_seeds", builder(), Blocks.BEETROOTS));
public static final Item BEETROOT_SOUP = register(new Item("beetroot_soup", builder().stackSize(1)));
public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder().rarity(Rarity.UNCOMMON)));
public static final Item DRAGON_BREATH = register(new Item("dragon_breath", builder()));
public static final Item SPLASH_POTION = register(new PotionItem("splash_potion", builder().stackSize(1)));
public static final Item SPECTRAL_ARROW = register(new Item("spectral_arrow", builder()));
public static final Item TIPPED_ARROW = register(new TippedArrowItem("tipped_arrow", builder()));
public static final Item LINGERING_POTION = register(new PotionItem("lingering_potion", builder().stackSize(1)));
public static final Item SHIELD = register(new ShieldItem("shield", builder().stackSize(1).maxDamage(336)));
public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
public static final Item TOTEM_OF_UNDYING = register(new Item("totem_of_undying", builder().stackSize(1)));
public static final Item SHULKER_SHELL = register(new Item("shulker_shell", builder()));
public static final Item IRON_NUGGET = register(new Item("iron_nugget", builder()));
public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1).rarity(Rarity.EPIC)));
public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1).rarity(Rarity.EPIC).glint(true)));
public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item KNOWLEDGE_BOOK = register(new Item("knowledge_book", builder().stackSize(1)));
public static final Item DEBUG_STICK = register(new Item("debug_stick", builder().stackSize(1)));
public static final Item MUSIC_DISC_13 = register(new Item("music_disc_13", builder().stackSize(1)));
public static final Item MUSIC_DISC_CAT = register(new Item("music_disc_cat", builder().stackSize(1)));
public static final Item MUSIC_DISC_BLOCKS = register(new Item("music_disc_blocks", builder().stackSize(1)));
public static final Item MUSIC_DISC_CHIRP = register(new Item("music_disc_chirp", builder().stackSize(1)));
public static final Item MUSIC_DISC_CREATOR = register(new Item("music_disc_creator", builder().stackSize(1)));
public static final Item MUSIC_DISC_CREATOR_MUSIC_BOX = register(new Item("music_disc_creator_music_box", builder().stackSize(1)));
public static final Item MUSIC_DISC_FAR = register(new Item("music_disc_far", builder().stackSize(1)));
public static final Item MUSIC_DISC_MALL = register(new Item("music_disc_mall", builder().stackSize(1)));
public static final Item MUSIC_DISC_MELLOHI = register(new Item("music_disc_mellohi", builder().stackSize(1)));
public static final Item MUSIC_DISC_STAL = register(new Item("music_disc_stal", builder().stackSize(1)));
public static final Item MUSIC_DISC_STRAD = register(new Item("music_disc_strad", builder().stackSize(1)));
public static final Item MUSIC_DISC_WARD = register(new Item("music_disc_ward", builder().stackSize(1)));
public static final Item MUSIC_DISC_11 = register(new Item("music_disc_11", builder().stackSize(1)));
public static final Item MUSIC_DISC_WAIT = register(new Item("music_disc_wait", builder().stackSize(1)));
public static final Item MUSIC_DISC_OTHERSIDE = register(new Item("music_disc_otherside", builder().stackSize(1)));
public static final Item MUSIC_DISC_RELIC = register(new Item("music_disc_relic", builder().stackSize(1)));
public static final Item MUSIC_DISC_5 = register(new Item("music_disc_5", builder().stackSize(1)));
public static final Item MUSIC_DISC_PIGSTEP = register(new Item("music_disc_pigstep", builder().stackSize(1)));
public static final Item MUSIC_DISC_PRECIPICE = register(new Item("music_disc_precipice", builder().stackSize(1)));
public static final Item DISC_FRAGMENT_5 = register(new Item("disc_fragment_5", builder()));
public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0).rarity(Rarity.EPIC)));
public static final Item PHANTOM_MEMBRANE = register(new Item("phantom_membrane", builder()));
public static final Item TRIDENT = register(new Item("trident", builder().stackSize(1).maxDamage(250).attackDamage(9.0)));
public static final Item NAUTILUS_SHELL = register(new Item("nautilus_shell", builder()));
public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder().rarity(Rarity.UNCOMMON)));
public static final Item HEART_OF_THE_SEA = register(new Item("heart_of_the_sea", builder()));
public static final Item CROSSBOW = register(new CrossbowItem("crossbow", builder().stackSize(1).maxDamage(465)));
public static final Item SUSPICIOUS_STEW = register(new Item("suspicious_stew", builder().stackSize(1)));
public static final Item LOOM = register(new BlockItem(builder(), Blocks.LOOM));
public static final Item FLOWER_BANNER_PATTERN = register(new Item("flower_banner_pattern", builder().stackSize(1)));
public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1).rarity(Rarity.EPIC)));
public static final Item CREEPER_BANNER_PATTERN = register(new Item("creeper_banner_pattern", builder().stackSize(1)));
public static final Item SKULL_BANNER_PATTERN = register(new Item("skull_banner_pattern", builder().stackSize(1)));
public static final Item MOJANG_BANNER_PATTERN = register(new Item("mojang_banner_pattern", builder().stackSize(1)));
public static final Item GLOBE_BANNER_PATTERN = register(new Item("globe_banner_pattern", builder().stackSize(1)));
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1).rarity(Rarity.UNCOMMON)));
public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1).rarity(Rarity.RARE)));
public static final Item PIGLIN_BANNER_PATTERN = register(new Item("piglin_banner_pattern", builder().stackSize(1)));
public static final Item FLOW_BANNER_PATTERN = register(new Item("flow_banner_pattern", builder().stackSize(1)));
public static final Item GUSTER_BANNER_PATTERN = register(new Item("guster_banner_pattern", builder().stackSize(1)));
public static final Item FIELD_MASONED_BANNER_PATTERN = register(new Item("field_masoned_banner_pattern", builder().stackSize(1)));
public static final Item BORDURE_INDENTED_BANNER_PATTERN = register(new Item("bordure_indented_banner_pattern", builder().stackSize(1)));
public static final Item GOAT_HORN = register(new GoatHornItem("goat_horn", builder().stackSize(1)));
public static final Item COMPOSTER = register(new BlockItem(builder(), Blocks.COMPOSTER));
public static final Item BARREL = register(new BlockItem(builder(), Blocks.BARREL));
@ -1369,8 +1442,7 @@ public final class Items {
public static final Item TRIAL_KEY = register(new Item("trial_key", builder()));
public static final Item OMINOUS_TRIAL_KEY = register(new Item("ominous_trial_key", builder()));
public static final Item VAULT = register(new BlockItem(builder(), Blocks.VAULT));
public static final Item OMINOUS_BOTTLE = register(new OminousBottleItem("ominous_bottle", builder()));
public static final Item BREEZE_ROD = register(new Item("breeze_rod", builder()));
public static final Item OMINOUS_BOTTLE = register(new Item("ominous_bottle", builder()));
public static final int AIR_ID = AIR.javaId();

View file

@ -25,52 +25,47 @@
package org.geysermc.geyser.item.enchantment;
import it.unimi.dsi.fastutil.ints.IntArrays;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.inventory.item.BedrockEnchantment;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.ToIntFunction;
/**
* @param description only populated if {@link #bedrockEnchantment()} is not null.
* @param description only populated if {@link #bedrockEnchantment()} is null.
* @param anvilCost also as a rarity multiplier
*/
public record Enchantment(String identifier,
Set<EnchantmentComponent> effects,
HolderSet supportedItems,
GeyserHolderSet<Item> supportedItems,
int maxLevel,
String description,
int anvilCost,
HolderSet exclusiveSet,
GeyserHolderSet<Enchantment> exclusiveSet,
@Nullable BedrockEnchantment bedrockEnchantment) {
public static Enchantment read(RegistryEntryContext context) {
NbtMap data = context.data();
Set<EnchantmentComponent> effects = readEnchantmentComponents(data.getCompound("effects"));
HolderSet supportedItems = readHolderSet(data.get("supported_items"), itemId -> Registries.JAVA_ITEM_IDENTIFIERS.getOrDefault(itemId.asString(), Items.AIR).javaId());
GeyserHolderSet<Item> supportedItems = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ITEM, data.get("supported_items"), itemId -> Registries.JAVA_ITEM_IDENTIFIERS.getOrDefault(itemId.asString(), Items.AIR).javaId());
int maxLevel = data.getInt("max_level");
int anvilCost = data.getInt("anvil_cost");
HolderSet exclusiveSet = readHolderSet(data.getOrDefault("exclusive_set", null), context::getNetworkId);
GeyserHolderSet<Enchantment> exclusiveSet = GeyserHolderSet.readHolderSet(context.session(), JavaRegistries.ENCHANTMENT, data.get("exclusive_set"), context::getNetworkId);
BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(context.id().asString());
// TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java,
// but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name.
String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null;
return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel,
@ -86,24 +81,4 @@ public record Enchantment(String identifier,
}
return Set.copyOf(components); // Also ensures any empty sets are consolidated
}
// TODO holder set util?
private static HolderSet readHolderSet(@Nullable Object holderSet, ToIntFunction<Key> keyIdMapping) {
if (holderSet == null) {
return new HolderSet(IntArrays.EMPTY_ARRAY);
}
if (holderSet instanceof String stringTag) {
// Tag
if (stringTag.startsWith("#")) {
return new HolderSet(MinecraftKey.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag
} else {
return new HolderSet(new int[]{keyIdMapping.applyAsInt(MinecraftKey.key(stringTag))});
}
} else if (holderSet instanceof List<?> list) {
// Assume the list is a list of strings
return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray());
}
throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs");
}
}

View file

@ -32,6 +32,7 @@ import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
@ -41,9 +42,9 @@ public class ArrowItem extends Item {
}
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
Potion potion = Potion.getByTippedArrowDamage(itemData.getDamage());
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
if (potion != null) {
itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getComponents());
PotionContents contents = potion.toComponent();

View file

@ -43,11 +43,11 @@ public class CompassItem extends Item {
}
@Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (isLodestoneCompass(components)) {
return super.translateToBedrock(count, components, mappings.getLodestoneCompass(), mappings);
return super.translateToBedrock(session, count, components, mappings.getLodestoneCompass(), mappings);
}
return super.translateToBedrock(count, components, mapping, mappings);
return super.translateToBedrock(session, count, components, mapping, mappings);
}
@Override
@ -78,12 +78,12 @@ public class CompassItem extends Item {
}
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) {
// Revert the entry back to the compass
mapping = mappings.getStoredItems().compass();
}
return super.translateToJava(itemData, mapping, mappings);
return super.translateToJava(session, itemData, mapping, mappings);
}
}

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.item.type;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -37,8 +38,8 @@ public class FilledMapItem extends MapItem {
}
@Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings);
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings);
if (components == null) {
// This is a fallback for maps with no nbt (Change added back in June 2020; is it needed in 2023?)
//return builder.tag(NbtMap.builder().putInt("map", 0).build()); TODO if this is *still* broken, let's move it to translateComponentsToBedrock

View file

@ -28,8 +28,11 @@ package org.geysermc.geyser.item.type;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.GeyserInstrument;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -41,24 +44,45 @@ public class GoatHornItem extends Item {
}
@Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings);
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings);
if (components == null) {
return builder;
}
Holder<Instrument> instrument = components.get(DataComponentType.INSTRUMENT);
if (instrument != null && instrument.isId()) {
builder.damage(instrument.id());
Holder<Instrument> holder = components.get(DataComponentType.INSTRUMENT);
if (holder != null) {
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
int bedrockId = instrument.bedrockId();
if (bedrockId >= 0) {
builder.damage(bedrockId);
}
}
return builder;
}
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) {
super.translateComponentsToBedrock(session, components, builder);
Holder<Instrument> holder = components.get(DataComponentType.INSTRUMENT);
if (holder != null && components.get(DataComponentType.HIDE_TOOLTIP) == null
&& components.get(DataComponentType.HIDE_ADDITIONAL_TOOLTIP) == null) {
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
if (instrument.bedrockInstrument() == null) {
builder.getOrCreateLore().add(instrument.description());
}
}
}
@Override
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
int damage = itemData.getDamage();
itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(damage));
// This could cause an issue since -1 is returned for non-vanilla goat horns
itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(GeyserInstrument.bedrockIdToJava(session, damage)));
return itemStack;
}

View file

@ -115,7 +115,7 @@ public class Item {
/* Translation methods to Bedrock and back */
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (this == Items.AIR || count <= 0) {
// Return, essentially, air
return ItemData.builder();
@ -130,7 +130,7 @@ public class Item {
return builder;
}
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
return GeyserItemStack.of(javaId, itemData.getCount());
}

View file

@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -40,8 +41,8 @@ public class OminousBottleItem extends Item {
}
@Override
public ItemData.Builder translateToBedrock(int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) {
var builder = super.translateToBedrock(count, components, mapping, mappings);
public ItemData.Builder translateToBedrock(GeyserSession session, int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) {
var builder = super.translateToBedrock(session, count, components, mapping, mappings);
if (components == null) {
// Level 1 ominous bottle is null components - Java 1.21.
return builder;
@ -54,9 +55,9 @@ public class OminousBottleItem extends Item {
}
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
// This item can be pulled from the creative inventory with amplifiers.
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
int damage = itemData.getDamage();
if (damage == 0) {
return itemStack;

View file

@ -33,6 +33,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.CustomItemTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -44,8 +45,8 @@ public class PotionItem extends Item {
}
@Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (components == null) return super.translateToBedrock(count, components, mapping, mappings);
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (components == null) return super.translateToBedrock(session, count, components, mapping, mappings);
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
if (potionContents != null) {
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(components, mapping);
@ -64,13 +65,13 @@ public class PotionItem extends Item {
.count(count);
}
}
return super.translateToBedrock(count, components, mapping, mappings);
return super.translateToBedrock(session, count, components, mapping, mappings);
}
@Override
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
Potion potion = Potion.getByBedrockId(itemData.getDamage());
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
if (potion != null) {
itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, potion.toComponent());
}

View file

@ -30,6 +30,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.Potion;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.GeyserSession;
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.item.component.PotionContents;
@ -40,7 +41,7 @@ public class TippedArrowItem extends ArrowItem {
}
@Override
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
if (components != null) {
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
if (potionContents != null) {
@ -54,6 +55,6 @@ public class TippedArrowItem extends ArrowItem {
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
}
}
return super.translateToBedrock(count, components, mapping, mappings);
return super.translateToBedrock(session, count, components, mapping, mappings);
}
}

View file

@ -26,24 +26,15 @@
package org.geysermc.geyser.level;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.SoundUtils;
public record JukeboxSong(String soundEvent, String description) {
public static JukeboxSong read(RegistryEntryContext context) {
NbtMap data = context.data();
Object soundEventObject = data.get("sound_event");
String soundEvent;
if (soundEventObject instanceof NbtMap map) {
soundEvent = map.getString("sound_id");
} else if (soundEventObject instanceof String string) {
soundEvent = string;
} else {
soundEvent = "";
GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject);
}
String soundEvent = SoundUtils.readSoundEvent(data, "jukebox song " + context.id());
String description = MessageTranslator.deserializeDescription(context.session(), data);
return new JukeboxSong(soundEvent, description);
}

File diff suppressed because it is too large Load diff

View file

@ -47,6 +47,7 @@ public final class Properties {
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 TIP = BooleanProperty.create("tip");
public static final BooleanProperty LOCKED = BooleanProperty.create("locked");
public static final BooleanProperty OCCUPIED = BooleanProperty.create("occupied");
public static final BooleanProperty OPEN = BooleanProperty.create("open");
@ -142,5 +143,6 @@ public final class Properties {
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 BasicEnumProperty CREAKING = BasicEnumProperty.create("creaking", "disabled", "dormant", "active");
public static final BooleanProperty OMINOUS = BooleanProperty.create("ominous");
}

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.level.physics;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.util.TriState;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.vector.Vector3d;
@ -153,11 +154,10 @@ public class CollisionManager {
* the two versions. Will also send corrected movement packets back to Bedrock if they collide with pistons.
*
* @param bedrockPosition the current Bedrock position of the client
* @param onGround whether the Bedrock player is on the ground
* @param teleported whether the Bedrock player has teleported to a new position. If true, movement correction is skipped.
* @return the position to send to the Java server, or null to cancel sending the packet
*/
public @Nullable Vector3d adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) {
public @Nullable CollisionResult adjustBedrockPosition(Vector3f bedrockPosition, boolean teleported) {
PistonCache pistonCache = session.getPistonCache();
// Bedrock clients tend to fall off of honey blocks, so we need to teleport them to the new position
if (pistonCache.isPlayerAttachedToHoney()) {
@ -176,7 +176,7 @@ public class CollisionManager {
playerBoundingBox.setMiddleY(position.getY() + playerBoundingBox.getSizeY() / 2);
playerBoundingBox.setMiddleZ(position.getZ());
return playerBoundingBox.getBottomCenter();
return new CollisionResult(playerBoundingBox.getBottomCenter(), TriState.NOT_SET);
}
Vector3d startingPos = playerBoundingBox.getBottomCenter();
@ -198,13 +198,13 @@ public class CollisionManager {
position = playerBoundingBox.getBottomCenter();
boolean newOnGround = adjustedMovement.getY() != movement.getY() && movement.getY() < 0 || onGround;
boolean onGround = (adjustedMovement.getY() != movement.getY() && movement.getY() < 0) || isOnGround();
// Send corrected position to Bedrock if they differ by too much to prevent de-syncs
if (onGround != newOnGround || movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) {
if (movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) {
PlayerEntity playerEntity = session.getPlayerEntity();
// Client will dismount if on a vehicle
if (playerEntity.getVehicle() == null && pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) {
playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), newOnGround, true);
playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), onGround, true);
}
}
@ -213,7 +213,7 @@ public class CollisionManager {
position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ());
}
return position;
return new CollisionResult(position, TriState.byBoolean(onGround));
}
// TODO: This makes the player look upwards for some reason, rotation values must be wrong
@ -415,44 +415,38 @@ public class CollisionManager {
return BlockUtils.getCollision(blockId);
}
/**
* @return true if the block located at the player's floor position plus 1 would intersect with the player,
* were they not sneaking
*/
public boolean mustPlayerSneakHere() {
return checkPose(EntityDefinitions.PLAYER.height());
}
/**
* @return true if the block located at the player's floor position plus 1 would intersect with the player,
* were they not crawling
*/
public boolean mustPlayerCrawlHere() {
return checkPose(PlayerEntity.SNEAKING_POSE_HEIGHT);
}
/**
* @param height check and see if this height is invalid in the current player position
*/
private boolean checkPose(float height) {
Vector3i position = session.getPlayerEntity().getPosition().toInt();
BlockCollision collision = BlockUtils.getCollisionAt(session, position);
if (collision != null) {
// Determine, if the player's bounding box *were* at full height, if it would intersect with the block
// at the current location.
double originalY = playerBoundingBox.getMiddleY();
double originalHeight = playerBoundingBox.getSizeY();
double standingY = originalY - (originalHeight / 2.0) + (height / 2.0);
playerBoundingBox.setSizeY(EntityDefinitions.PLAYER.height());
playerBoundingBox.setMiddleY(standingY);
boolean result = collision.checkIntersection(position, playerBoundingBox);
result |= session.getPistonCache().checkCollision(position, playerBoundingBox);
playerBoundingBox.setSizeY(originalHeight);
playerBoundingBox.setMiddleY(originalY);
return result;
private boolean isOnGround() {
// Someone smarter than me at collisions plz check this.
Vector3d bottomCenter = playerBoundingBox.getBottomCenter();
Vector3i groundPos = Vector3i.from(bottomCenter.getX(), bottomCenter.getY() - 1, bottomCenter.getZ());
BlockCollision collision = BlockUtils.getCollisionAt(session, groundPos);
if (collision == null) {
return false; // Probably air.
}
return false;
// Hack to not check below the player
playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() - 0.001);
playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() + 0.002);
boolean intersected = collision.checkIntersection(groundPos.getX(), groundPos.getY(), groundPos.getZ(), playerBoundingBox);
playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() + 0.001);
playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() - 0.002);
boolean result;
if (intersected) {
result = true;
} else {
// Hack to check slightly below the player
playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() + 0.001);
playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() - 0.002);
result = collision.checkIntersection(groundPos.getX(), groundPos.getY(), groundPos.getZ(), playerBoundingBox);
playerBoundingBox.setSizeY(playerBoundingBox.getSizeY() - 0.001);
playerBoundingBox.setMiddleY(playerBoundingBox.getMiddleY() + 0.002);
}
return result;
}
/**

View file

@ -23,11 +23,13 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.util;
package org.geysermc.geyser.level.physics;
import net.kyori.adventure.util.TriState;
import org.cloudburstmc.math.vector.Vector3d;
/**
* Represents anything that could be tracked like a enum, without also creating a name and enum-wide array.
* Holds the result of a collision check.
*/
public interface Ordered {
int ordinal();
public record CollisionResult(Vector3d correctedMovement, TriState onGround) {
}

View file

@ -31,11 +31,13 @@ import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
import org.cloudburstmc.protocol.bedrock.codec.BedrockPacketSerializer;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobArmorEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MoveEntityAbsoluteSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v419.serializer.MovePlayerSerializer_v419;
import org.cloudburstmc.protocol.bedrock.codec.v486.serializer.BossEventSerializer_v486;
import org.cloudburstmc.protocol.bedrock.codec.v557.serializer.SetEntityDataSerializer_v557;
import org.cloudburstmc.protocol.bedrock.codec.v662.serializer.SetEntityMotionSerializer_v662;
@ -67,15 +69,18 @@ import org.cloudburstmc.protocol.bedrock.packet.MapCreateLockedCopyPacket;
import org.cloudburstmc.protocol.bedrock.packet.MapInfoRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket;
import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.MultiplayerSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.NpcRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.PhotoInfoRequestPacket;
import org.cloudburstmc.protocol.bedrock.packet.PhotoTransferPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerHotbarPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerInputPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerSkinPacket;
import org.cloudburstmc.protocol.bedrock.packet.PurchaseReceiptPacket;
import org.cloudburstmc.protocol.bedrock.packet.RefreshEntitlementsPacket;
import org.cloudburstmc.protocol.bedrock.packet.RiderJumpPacket;
import org.cloudburstmc.protocol.bedrock.packet.ScriptMessagePacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
@ -190,6 +195,20 @@ class CodecProcessor {
}
};
private static final BedrockPacketSerializer<MovePlayerPacket> MOVE_PLAYER_SERIALIZER = new MovePlayerSerializer_v419() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, MovePlayerPacket packet) {
throw new IllegalArgumentException("Client cannot send MovePlayerPacket in server-auth movement environment!");
}
};
private static final BedrockPacketSerializer<MoveEntityAbsolutePacket> MOVE_ENTITY_SERIALIZER = new MoveEntityAbsoluteSerializer_v291() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, MoveEntityAbsolutePacket packet) {
throw new IllegalArgumentException("Client cannot send MoveEntityAbsolutePacket in server-auth movement environment!");
}
};
/**
* Serializer that does nothing when trying to deserialize BossEventPacket since it is not used from the client.
*/
@ -318,7 +337,6 @@ class CodecProcessor {
.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)
.updateSerializer(SubClientLoginPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(SubChunkRequestPacket.class, ILLEGAL_SERIALIZER)
@ -334,6 +352,10 @@ class CodecProcessor {
// Illegal when serverbound due to Geyser specific setup
.updateSerializer(InventoryContentPacket.class, inventoryContentSerializer)
.updateSerializer(InventorySlotPacket.class, inventorySlotSerializer)
.updateSerializer(MovePlayerPacket.class, MOVE_PLAYER_SERIALIZER)
.updateSerializer(MoveEntityAbsolutePacket.class, MOVE_ENTITY_SERIALIZER)
.updateSerializer(RiderJumpPacket.class, ILLEGAL_SERIALIZER)
.updateSerializer(PlayerInputPacket.class, ILLEGAL_SERIALIZER)
// Ignored only when serverbound
.updateSerializer(BossEventPacket.class, BOSS_EVENT_SERIALIZER)
.updateSerializer(MobArmorEquipmentPacket.class, is712OrAbove ? MOB_ARMOR_EQUIPMENT_SERIALIZER_V712 : MOB_ARMOR_EQUIPMENT_SERIALIZER_V291)

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.network;
import io.netty.buffer.Unpooled;
import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
import org.cloudburstmc.protocol.bedrock.codec.compat.BedrockCompat;
@ -38,9 +39,9 @@ import org.cloudburstmc.protocol.bedrock.netty.codec.compression.ZlibCompression
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ModalFormResponsePacket;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.ResourcePackChunkRequestPacket;
@ -290,8 +291,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
}
@Override
public PacketSignal handle(MovePlayerPacket packet) {
if (session.isLoggingIn()) {
public PacketSignal handle(PlayerAuthInputPacket packet) {
// This doesn't catch rotation, but for a niche case I don't exactly want to cache rotation...
if (session.isLoggingIn() && !packet.getMotion().equals(Vector2f.ZERO)) {
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
titlePacket.setText(GeyserLocale.getPlayerLocaleString("geyser.auth.login.wait", session.locale()));

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.registry;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
@ -42,13 +43,13 @@ import org.geysermc.geyser.registry.loader.BlockEntityRegistryLoader;
import org.geysermc.geyser.registry.loader.ParticleTypesRegistryLoader;
import org.geysermc.geyser.registry.loader.PotionMixRegistryLoader;
import org.geysermc.geyser.registry.loader.ProviderRegistryLoader;
import org.geysermc.geyser.registry.loader.RecipeRegistryLoader;
import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.registry.loader.SoundEventsRegistryLoader;
import org.geysermc.geyser.registry.loader.SoundRegistryLoader;
import org.geysermc.geyser.registry.loader.SoundTranslatorRegistryLoader;
import org.geysermc.geyser.registry.populator.ItemRegistryPopulator;
import org.geysermc.geyser.registry.populator.PacketRegistryPopulator;
import org.geysermc.geyser.registry.populator.TagRegistryPopulator;
import org.geysermc.geyser.registry.provider.ProviderSupplier;
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.registry.type.ParticleMapping;
@ -62,13 +63,11 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityType;
import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent;
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.RecipeType;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -159,13 +158,18 @@ public final class Registries {
/**
* A versioned registry holding all the recipes, with the net ID being the key, and {@link GeyserRecipe} as the value.
*/
public static final SimpleMappedDeferredRegistry<RecipeType, List<GeyserRecipe>> RECIPES = SimpleMappedDeferredRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new);
//public static final SimpleMappedDeferredRegistry<RecipeType, List<GeyserRecipe>> RECIPES = SimpleMappedDeferredRegistry.create("mappings/recipes.nbt", RecipeRegistryLoader::new);
/**
* A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys.
*/
public static final SimpleMappedDeferredRegistry<String, ResourcePack> RESOURCE_PACKS = SimpleMappedDeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), RegistryLoaders.RESOURCE_PACKS);
/**
* A versioned registry holding most Bedrock tags, with the Java item list (sorted) being the key, and the tag name as the value.
*/
public static final VersionedRegistry<Object2ObjectMap<int[], String>> TAGS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
/**
* A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}.
*/
@ -195,7 +199,7 @@ public final class Registries {
BLOCK_ENTITIES.load();
PARTICLES.load();
// load potion mixes later
RECIPES.load();
//RECIPES.load();
RESOURCE_PACKS.load();
SOUNDS.load();
SOUND_LEVEL_EVENTS.load();
@ -205,6 +209,7 @@ public final class Registries {
public static void populate() {
PacketRegistryPopulator.populate();
ItemRegistryPopulator.populate();
TagRegistryPopulator.populate();
// potion mixes depend on other registries
POTION_MIXES.load();

View file

@ -27,26 +27,15 @@ package org.geysermc.geyser.registry.loader;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.cloudburstmc.nbt.NBTInputStream;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapedRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserShapelessRecipe;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.RecipeType;
import java.io.DataInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
@ -56,38 +45,41 @@ import java.util.Map;
* Populates the recipe registry with some recipes that Java does not send, to ensure they show up as intended
* in the recipe book.
*/
public final class RecipeRegistryLoader implements RegistryLoader<String, Map<RecipeType, List<GeyserRecipe>>> {
public abstract class RecipeRegistryLoader implements RegistryLoader<String, Map<Object, List<GeyserRecipe>>> {
@Override
public Map<RecipeType, List<GeyserRecipe>> load(String input) {
Map<RecipeType, List<GeyserRecipe>> deserializedRecipes = new Object2ObjectOpenHashMap<>();
// @Override
// public Map<RecipeType, List<GeyserRecipe>> load(String input) {
// if (true) {
// return Collections.emptyMap();
// }
// Map<RecipeType, List<GeyserRecipe>> deserializedRecipes = new Object2ObjectOpenHashMap<>();
//
// List<NbtMap> recipes;
// try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/recipes.nbt")) {
// try (NBTInputStream nbtStream = new NBTInputStream(new DataInputStream(stream))) {
// recipes = ((NbtMap) nbtStream.readTag()).getList("recipes", NbtType.COMPOUND);
// }
// } catch (Exception e) {
// throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e);
// }
//
// MinecraftCodecHelper helper = MinecraftCodec.CODEC.getHelperFactory().get();
// for (NbtMap recipeCollection : recipes) {
// var pair = getRecipes(recipeCollection, helper);
// deserializedRecipes.put(pair.key(), pair.value());
// }
// return deserializedRecipes;
// }
List<NbtMap> recipes;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/recipes.nbt")) {
try (NBTInputStream nbtStream = new NBTInputStream(new DataInputStream(stream))) {
recipes = ((NbtMap) nbtStream.readTag()).getList("recipes", NbtType.COMPOUND);
}
} catch (Exception e) {
throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e);
}
MinecraftCodecHelper helper = MinecraftCodec.CODEC.getHelperFactory().get();
for (NbtMap recipeCollection : recipes) {
var pair = getRecipes(recipeCollection, helper);
deserializedRecipes.put(pair.key(), pair.value());
}
return deserializedRecipes;
}
private static Pair<RecipeType, List<GeyserRecipe>> getRecipes(NbtMap recipes, MinecraftCodecHelper helper) {
List<NbtMap> typedRecipes = recipes.getList("recipes", NbtType.COMPOUND);
RecipeType recipeType = RecipeType.from(recipes.getInt("recipe_type", -1));
if (recipeType == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) {
return Pair.of(recipeType, getShapedRecipes(typedRecipes, helper));
} else {
return Pair.of(recipeType, getShapelessRecipes(typedRecipes, helper));
}
}
// private static Pair<RecipeType, List<GeyserRecipe>> getRecipes(NbtMap recipes, MinecraftCodecHelper helper) {
// List<NbtMap> typedRecipes = recipes.getList("recipes", NbtType.COMPOUND);
// RecipeType recipeType = RecipeType.from(recipes.getInt("recipe_type", -1));
// if (recipeType == RecipeType.CRAFTING_SPECIAL_TIPPEDARROW) {
// return Pair.of(recipeType, getShapedRecipes(typedRecipes, helper));
// } else {
// return Pair.of(recipeType, getShapelessRecipes(typedRecipes, helper));
// }
// }
private static List<GeyserRecipe> getShapelessRecipes(List<NbtMap> recipes, MinecraftCodecHelper helper) {
List<GeyserRecipe> deserializedRecipes = new ObjectArrayList<>(recipes.size());
@ -96,9 +88,9 @@ public final class RecipeRegistryLoader implements RegistryLoader<String, Map<Re
List<NbtMap> rawInputs = recipe.getList("inputs", NbtType.COMPOUND);
Ingredient[] javaInputs = new Ingredient[rawInputs.size()];
for (int i = 0; i < rawInputs.size(); i++) {
javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)});
//javaInputs[i] = new Ingredient(new ItemStack[] {toItemStack(rawInputs.get(i), helper)});
}
deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output));
//deserializedRecipes.add(new GeyserShapelessRecipe(javaInputs, output));
}
return deserializedRecipes;
}
@ -121,10 +113,10 @@ public final class RecipeRegistryLoader implements RegistryLoader<String, Map<Re
for (int j = 0; i < shape.size() * shape.get(0).length; j++) {
for (int index : shape.get(j)) {
ItemStack stack = letterToRecipe.get(index);
inputs[i++] = new Ingredient(new ItemStack[] {stack});
//inputs[i++] = new Ingredient(new ItemStack[] {stack});
}
}
deserializedRecipes.add(new GeyserShapedRecipe(shape.size(), shape.get(0).length, inputs, output));
//deserializedRecipes.add(new GeyserShapedRecipe(shape.size(), shape.get(0).length, inputs, output));
}
return deserializedRecipes;
}

View file

@ -196,11 +196,10 @@ public final class BlockRegistryPopulator {
GeyserBedrockBlock[] bedrockRuntimeMap = new GeyserBedrockBlock[blockStates.size()];
for (int i = 0; i < blockStates.size(); i++) {
NbtMap tag = blockStates.get(i);
if (blockStateOrderedMap.containsKey(tag)) {
GeyserBedrockBlock block = new GeyserBedrockBlock(i, tag);
if (blockStateOrderedMap.put(tag, block) != null) {
throw new AssertionError("Duplicate block states in Bedrock palette: " + tag);
}
GeyserBedrockBlock block = new GeyserBedrockBlock(i, tag);
blockStateOrderedMap.put(tag, block);
bedrockRuntimeMap[i] = block;
}

View file

@ -84,6 +84,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -95,7 +96,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class ItemRegistryPopulator {
record PaletteVersion(String version, int protocolVersion, Map<Item, String> javaOnlyItems, Remapper remapper) {
record PaletteVersion(String version, int protocolVersion, Map<Item, Item> javaOnlyItems, Remapper remapper) {
public PaletteVersion(String version, int protocolVersion) {
this(version, protocolVersion, Collections.emptyMap(), (item, mapping) -> mapping);
@ -109,11 +110,17 @@ public class ItemRegistryPopulator {
}
public static void populate() {
List<Item> bundles = List.of(Items.BUNDLE, Items.BLACK_BUNDLE, Items.BLUE_BUNDLE, Items.BROWN_BUNDLE, Items.CYAN_BUNDLE, Items.GRAY_BUNDLE,
Items.GREEN_BUNDLE, Items.LIGHT_BLUE_BUNDLE, Items.LIGHT_GRAY_BUNDLE, Items.LIME_BUNDLE, Items.MAGENTA_BUNDLE, Items.ORANGE_BUNDLE, Items.RED_BUNDLE,
Items.PINK_BUNDLE, Items.PURPLE_BUNDLE, Items.WHITE_BUNDLE, Items.YELLOW_BUNDLE);
Map<Item, Item> pre1_21_2Items = new HashMap<>();
bundles.forEach(bundle -> pre1_21_2Items.put(bundle, Items.SHULKER_SHELL));
List<PaletteVersion> paletteVersions = new ArrayList<>(3);
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(), Collections.emptyMap(), Conversion712_685::remapItem));
paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion729_712::remapItem));
paletteVersions.add(new PaletteVersion("1_21_30", Bedrock_v729.CODEC.getProtocolVersion(), Collections.emptyMap(), Conversion748_729::remapItem));
paletteVersions.add(new PaletteVersion("1_20_80", Bedrock_v671.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion685_671::remapItem));
paletteVersions.add(new PaletteVersion("1_21_0", Bedrock_v685.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion712_685::remapItem));
paletteVersions.add(new PaletteVersion("1_21_20", Bedrock_v712.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion729_712::remapItem));
paletteVersions.add(new PaletteVersion("1_21_30", Bedrock_v729.CODEC.getProtocolVersion(), pre1_21_2Items, Conversion748_729::remapItem));
paletteVersions.add(new PaletteVersion("1_21_40", Bedrock_v748.CODEC.getProtocolVersion()));
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
@ -227,7 +234,7 @@ public class ItemRegistryPopulator {
Set<Item> javaOnlyItems = new ObjectOpenHashSet<>();
Collections.addAll(javaOnlyItems, Items.SPECTRAL_ARROW, Items.DEBUG_STICK,
Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW, Items.BUNDLE);
Items.KNOWLEDGE_BOOK, Items.TIPPED_ARROW);
if (!customItemsAllowed) {
javaOnlyItems.add(Items.FURNACE_MINECART);
}
@ -243,9 +250,9 @@ public class ItemRegistryPopulator {
throw new RuntimeException("Extra item in mappings? " + entry.getKey());
}
GeyserMappingItem mappingItem;
String replacementItem = palette.javaOnlyItems().get(javaItem);
Item replacementItem = palette.javaOnlyItems().get(javaItem);
if (replacementItem != null) {
mappingItem = items.get(replacementItem); // java only item, a java id fallback has been provided
mappingItem = items.get(replacementItem.javaIdentifier()); // java only item, a java id fallback has been provided
} else {
// check if any mapping changes need to be made on this version
mappingItem = palette.remapper().remap(javaItem, entry.getValue());

View file

@ -0,0 +1,135 @@
/*
* 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.populator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671;
import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685;
import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712;
import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729;
import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public final class TagRegistryPopulator {
private static final Gson GSON = new GsonBuilder().create(); // temporary
public static void populate() {
Hash.Strategy<int[]> hashStrategy = new Hash.Strategy<>() {
// Necessary so arrays can actually be compared
@Override
public int hashCode(int[] o) {
return Arrays.hashCode(o);
}
@Override
public boolean equals(int[] a, int[] b) {
return Arrays.equals(a, b);
}
};
List<ObjectIntPair<String>> paletteVersions = List.of(
ObjectIntPair.of("1_20_80", Bedrock_v671.CODEC.getProtocolVersion()),
ObjectIntPair.of("1_21_0", Bedrock_v685.CODEC.getProtocolVersion()),
ObjectIntPair.of("1_21_20", Bedrock_v712.CODEC.getProtocolVersion()),
ObjectIntPair.of("1_21_30", Bedrock_v729.CODEC.getProtocolVersion()),
ObjectIntPair.of("1_21_40", Bedrock_v748.CODEC.getProtocolVersion())
);
TypeToken<Map<String, List<String>>> type = new TypeToken<>() {};
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
for (var palette : paletteVersions) {
ItemMappings mappings = Registries.ITEMS.forVersion(palette.rightInt());
Map<String, List<String>> bedrockTags;
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/item_tags.%s.json", palette.left()))) {
bedrockTags = GSON.fromJson(new InputStreamReader(stream), type);
} catch (Exception e) {
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
}
var javaItemsToBedrockTag = new Object2ObjectOpenCustomHashMap<int[], String>(hashStrategy);
for (var entry : bedrockTags.entrySet()) {
List<String> value = entry.getValue();
if (value.isEmpty() || value.size() == 1) {
// For our usecase, we don't need this. Empty values are worthless; one value can just be a reference
// to the item itself, instead of the tag.
continue;
}
// In some cases, the int list will need to be minimized
IntList javaNetworkIds = new IntArrayList(value.size());
for (int i = 0; i < value.size(); i++) {
String bedrockIdentifier = value.get(i);
Item javaItem = Registries.JAVA_ITEM_IDENTIFIERS.get(bedrockIdentifier);
if (javaItem == null) {
// Time to search the long way around.
for (ItemMapping mapping : mappings.getItems()) {
if (mapping.getBedrockIdentifier().equals(bedrockIdentifier)) {
javaItem = mapping.getJavaItem();
break;
}
}
}
if (javaItem == null) {
// Triggers for Bedrock-only spawn eggs. We don't care.
continue;
}
javaNetworkIds.add(javaItem.javaId());
}
int[] javaNetworkIdArray = javaNetworkIds.toIntArray();
// Sort IDs so equality checks just have to match if each is equal and not necessarily an order difference.
Arrays.sort(javaNetworkIdArray);
javaItemsToBedrockTag.put(javaNetworkIdArray, entry.getKey());
}
javaItemsToBedrockTag.trim();
Registries.TAGS.register(palette.rightInt(), javaItemsToBedrockTag);
}
}
}

View file

@ -29,6 +29,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
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.Object2IntMap;
@ -77,13 +78,13 @@ import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.CraftingRecipeData;
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
import org.cloudburstmc.protocol.bedrock.packet.ClientboundCloseFormPacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.DimensionDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
@ -142,6 +143,7 @@ import org.geysermc.geyser.impl.camera.GeyserCameraData;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.BlockItem;
@ -161,6 +163,7 @@ import org.geysermc.geyser.session.cache.ChunkCache;
import org.geysermc.geyser.session.cache.EntityCache;
import org.geysermc.geyser.session.cache.EntityEffectCache;
import org.geysermc.geyser.session.cache.FormCache;
import org.geysermc.geyser.session.cache.InputCache;
import org.geysermc.geyser.session.cache.LodestoneCache;
import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.session.cache.PreferencesCache;
@ -178,6 +181,7 @@ import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.geysermc.geyser.util.MinecraftAuthLogger;
import org.geysermc.mcprotocollib.auth.GameProfile;
@ -191,7 +195,6 @@ import org.geysermc.mcprotocollib.network.event.session.SessionAdapter;
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;
@ -203,16 +206,15 @@ 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.ParticleStatus;
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.ServerboundClientTickEndPacket;
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;
@ -254,7 +256,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private final EventLoop eventLoop;
@Setter
private AuthData authData;
@Setter
private BedrockClientData clientData;
/**
* Used for Floodgate skin uploading
@ -278,6 +279,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private final EntityCache entityCache;
private final EntityEffectCache effectCache;
private final FormCache formCache;
private final InputCache inputCache;
private final LodestoneCache lodestoneCache;
private final PistonCache pistonCache;
private final PreferencesCache preferencesCache;
@ -312,7 +314,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private final AtomicInteger itemNetId = new AtomicInteger(2);
@Setter
private ScheduledFuture<?> craftingGridFuture;
private ScheduledFuture<?> containerOutputFuture;
/**
* Stores session collision
@ -443,21 +445,22 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private Entity mouseoverEntity;
/**
* Stores all Java recipes by recipe identifier, and matches them to all possible Bedrock recipe identifiers.
* They are not 1:1, since Bedrock can have multiple recipes for the same Java recipe.
* Stores all Java recipes by ID, and matches them to all possible Bedrock recipe identifiers.
*/
private final Map<String, List<String>> javaToBedrockRecipeIds;
private final Int2ObjectMap<List<String>> javaToBedrockRecipeIds;
private final Int2ObjectMap<GeyserRecipe> craftingRecipes;
@Setter
private Int2ObjectMap<GeyserRecipe> craftingRecipes;
private Pair<CraftingRecipeData, GeyserRecipe> lastCreatedRecipe = null; // TODO try to prevent sending duplicate recipes
private final AtomicInteger lastRecipeNetId;
/**
* Saves a list of all stonecutter recipes, for use in a stonecutter inventory.
* The key is the Java ID of the item; the values are all the possible outputs' Java IDs sorted by their string identifier
* The key is the Bedrock recipe net ID; the values are their respective output and button ID.
*/
@Setter
private Int2ObjectMap<GeyserStonecutterData> stonecutterRecipes;
private final List<GeyserSmithingRecipe> smithingRecipes = new ArrayList<>();
/**
* Whether to work around 1.13's different behavior in villager trading menus.
@ -526,18 +529,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private boolean placedBucket;
/**
* Used to send a movement packet every three seconds if the player hasn't moved. Prevents timeouts when AFK in certain instances.
*/
@Setter
private long lastMovementTimestamp = System.currentTimeMillis();
/**
* Used to send a ServerboundMoveVehiclePacket for every PlayerInputPacket after idling on a boat/horse for more than 100ms
*/
@Setter
private long lastVehicleMoveTimestamp = System.currentTimeMillis();
/**
* Counts how many ticks have occurred since an arm animation started.
* -1 means there is no active arm swing; -2 means an arm swing will start in a tick.
@ -675,13 +666,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.entityCache = new EntityCache(this);
this.effectCache = new EntityEffectCache();
this.formCache = new FormCache(this);
this.inputCache = new InputCache(this);
this.lodestoneCache = new LodestoneCache();
this.pistonCache = new PistonCache(this);
this.preferencesCache = new PreferencesCache(this);
this.registryCache = new RegistryCache(this);
this.skullCache = new SkullCache(this);
this.structureBlockCache = new StructureBlockCache();
this.tagCache = new TagCache();
this.tagCache = new TagCache(this);
this.worldCache = new WorldCache(this);
this.cameraData = new GeyserCameraData(this);
this.entityData = new GeyserEntityData(this);
@ -696,8 +688,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.playerInventory = new PlayerInventory();
this.openInventory = null;
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
this.javaToBedrockRecipeIds = new Object2ObjectOpenHashMap<>();
this.lastRecipeNetId = new AtomicInteger(1);
this.javaToBedrockRecipeIds = new Int2ObjectOpenHashMap<>();
this.lastRecipeNetId = new AtomicInteger(InventoryUtils.LAST_RECIPE_NET_ID + 1);
this.spawned = false;
this.loggedIn = false;
@ -769,12 +761,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
creativePacket.setContents(this.itemMappings.getCreativeItems());
upstream.sendPacket(creativePacket);
// Potion mixes are registered by default, as they are needed to be able to put ingredients into the brewing stand.
CraftingDataPacket craftingDataPacket = new CraftingDataPacket();
craftingDataPacket.setCleanRecipes(true);
craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.forVersion(this.upstream.getProtocolVersion()));
upstream.sendPacket(craftingDataPacket);
PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
upstream.sendPacket(playStatusPacket);
@ -1091,6 +1077,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Download and load the language for the player
MinecraftLocale.downloadAndLoadLocale(locale);
// if (sentSpawnPacket && !GameProtocol.isPre1_21_2(GeyserSession.this)) {
// // Possible form to close.
// upstream.sendPacket(new ClientboundCloseFormPacket());
// }
}
@Override
@ -1269,18 +1260,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
protected void tick() {
try {
pistonCache.tick();
// Check to see if the player's position needs updating - a position update should be sent once every 3 seconds
if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) {
// Recalculate in case something else changed position
Vector3d position = collisionManager.adjustBedrockPosition(playerEntity.getPosition(), playerEntity.isOnGround(), false);
// A null return value cancels the packet
if (position != null) {
ServerboundMovePlayerPosPacket packet = new ServerboundMovePlayerPosPacket(playerEntity.isOnGround(),
position.getX(), position.getY(), position.getZ());
sendDownstreamGamePacket(packet);
}
lastMovementTimestamp = System.currentTimeMillis();
}
if (worldBorder.isResizing()) {
worldBorder.resize();
@ -1338,6 +1317,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
armAnimationTicks = -1;
}
}
if (spawned) {
// Could move this to the PlayerAuthInput translator, in the event the player lags
// but this will work once we implement matching Java custom tick cycles
sendDownstreamGamePacket(ServerboundClientTickEndPacket.INSTANCE);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
@ -1396,14 +1381,28 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}
public void setSwimming(boolean swimming) {
if (swimming) {
if (!swimming && playerEntity.getFlag(EntityFlag.CRAWLING)) {
// Do not update bounding box.
playerEntity.setFlag(EntityFlag.SWIMMING, false);
playerEntity.updateBedrockMetadata();
return;
}
toggleSwimmingPose(swimming, EntityFlag.SWIMMING);
}
public void setCrawling(boolean crawling) {
toggleSwimmingPose(crawling, EntityFlag.CRAWLING);
}
private void toggleSwimmingPose(boolean crawling, EntityFlag flag) {
if (crawling) {
this.pose = Pose.SWIMMING;
playerEntity.setBoundingBoxHeight(0.6f);
} else {
this.pose = Pose.STANDING;
playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height());
}
playerEntity.setFlag(EntityFlag.SWIMMING, swimming);
playerEntity.setFlag(flag, crawling);
playerEntity.updateBedrockMetadata();
}
@ -1423,6 +1422,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.cameraData.handleGameModeChange(currentlySpectator, newGamemode);
}
public void setClientData(BedrockClientData data) {
this.clientData = data;
this.inputCache.setInputMode(
org.cloudburstmc.protocol.bedrock.data.InputMode.values()[data.getCurrentInputMode().ordinal()]);
}
/**
* Convenience method to reduce amount of duplicate code. Sends ServerboundUseItemPacket.
*/
@ -1671,7 +1676,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE);
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT);
startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.SERVER);
startGamePacket.setRewindHistorySize(0);
startGamePacket.setServerAuthoritativeBlockBreaking(false);
@ -1969,7 +1974,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
public void sendJavaClientSettings() {
ServerboundClientInformationPacket clientSettingsPacket = new ServerboundClientInformationPacket(locale(),
getRenderDistance(), ChatVisibility.FULL, true, SKIN_PARTS,
HandPreference.RIGHT_HAND, false, true);
HandPreference.RIGHT_HAND, false, true, ParticleStatus.ALL); // TODO particle status
sendDownstreamPacket(clientSettingsPacket);
}
@ -2136,7 +2141,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Override
public @NonNull InputMode inputMode() {
return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo
return InputMode.values()[inputCache.getInputMode().ordinal()]; //todo
}
@Override

View file

@ -0,0 +1,112 @@
/*
* 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.session.cache;
import lombok.Getter;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.protocol.bedrock.data.InputMode;
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket;
import java.util.Set;
public final class InputCache {
private final GeyserSession session;
private ServerboundPlayerInputPacket inputPacket = new ServerboundPlayerInputPacket(false, false, false, false, false, false, false);
private boolean lastHorizontalCollision;
private int ticksSinceLastMovePacket;
@Getter @Setter
private int jumpingTicks;
@Getter @Setter
private float jumpScale;
@Getter @Setter
private @MonotonicNonNull InputMode inputMode;
public InputCache(GeyserSession session) {
this.session = session;
}
public void processInputs(PlayerAuthInputPacket packet) {
// Input is sent to the server before packet positions, as of 1.21.2
Set<PlayerAuthInputData> bedrockInput = packet.getInputData();
var oldInputPacket = this.inputPacket;
this.inputMode = packet.getInputMode();
boolean up, down, left, right;
if (this.inputMode == InputMode.MOUSE) {
up = bedrockInput.contains(PlayerAuthInputData.UP);
down = bedrockInput.contains(PlayerAuthInputData.DOWN);
left = bedrockInput.contains(PlayerAuthInputData.LEFT);
right = bedrockInput.contains(PlayerAuthInputData.RIGHT);
} else {
// The above flags don't fire TODO test console
Vector2f analogMovement = packet.getAnalogMoveVector();
up = analogMovement.getY() > 0;
down = analogMovement.getY() < 0;
left = analogMovement.getX() > 0;
right = analogMovement.getX() < 0;
}
// TODO when is UP_LEFT, etc. used?
this.inputPacket = this.inputPacket
.withForward(up)
.withBackward(down)
.withLeft(left)
.withRight(right)
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMPING)) // Looks like this only triggers when the JUMP key input is being pressed. There's also JUMP_DOWN?
.withShift(bedrockInput.contains(PlayerAuthInputData.SNEAKING))
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINTING)); // SPRINTING will trigger even if the player isn't moving
if (oldInputPacket != this.inputPacket) { // Simple equality check is fine since we're checking for an instance change.
session.sendDownstreamGamePacket(this.inputPacket);
}
}
public boolean wasJumping() {
return this.inputPacket.isJump();
}
public void markPositionPacketSent() {
this.ticksSinceLastMovePacket = 0;
}
public boolean shouldSendPositionReminder() {
// NOTE: if we implement spectating entities, DO NOT TICK THIS LOGIC THEN.
return ++this.ticksSinceLastMovePacket >= 20;
}
public boolean lastHorizontalCollision() {
return lastHorizontalCollision;
}
public void setLastHorizontalCollision(boolean lastHorizontalCollision) {
this.lastHorizontalCollision = lastHorizontalCollision;
}
}

View file

@ -40,13 +40,16 @@ import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity;
import org.geysermc.geyser.inventory.item.BannerPattern;
import org.geysermc.geyser.inventory.item.GeyserInstrument;
import org.geysermc.geyser.inventory.recipe.TrimRecipe;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.level.JukeboxSong;
import org.geysermc.geyser.level.PaintingType;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
import org.geysermc.geyser.text.ChatDecoration;
@ -80,7 +83,8 @@ public final class RegistryCache {
static {
register("chat_type", cache -> cache.chatTypes, ChatDecoration::readChatType);
register("dimension_type", cache -> cache.dimensions, JavaDimension::read);
register("enchantment", cache -> cache.enchantments, Enchantment::read);
register(JavaRegistries.ENCHANTMENT, cache -> cache.enchantments, Enchantment::read);
register("instrument", cache -> cache.instruments, GeyserInstrument::read);
register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read);
register("painting_variant", cache -> cache.paintings, context -> PaintingType.getByName(context.id()));
register("trim_material", cache -> cache.trimMaterials, TrimRecipe::readTrimMaterial);
@ -94,8 +98,7 @@ public final class RegistryCache {
Map<Key, Map<Key, 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.asString())
.getList("value", NbtType.COMPOUND);
List<NbtMap> rawValues = tag.getCompound(key.asString()).getList("value", NbtType.COMPOUND);
Map<Key, NbtMap> values = new HashMap<>();
for (NbtMap value : rawValues) {
Key name = MinecraftKey.key(value.getString("name"));
@ -128,6 +131,7 @@ public final class RegistryCache {
private final JavaRegistry<BannerPattern> bannerPatterns = new SimpleJavaRegistry<>();
private final JavaRegistry<WolfEntity.BuiltInWolfVariant> wolfVariants = new SimpleJavaRegistry<>();
private final JavaRegistry<GeyserInstrument> instruments = new SimpleJavaRegistry<>();
public RegistryCache(GeyserSession session) {
this.session = session;
@ -152,8 +156,27 @@ public final class RegistryCache {
* @param <T> the class that represents these entries.
*/
private static <T> void register(String registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, Function<RegistryEntryContext, T> reader) {
Key registryKey = MinecraftKey.key(registry);
REGISTRIES.put(registryKey, (registryCache, entries) -> {
register(MinecraftKey.key(registry), localCacheFunction, reader);
}
/**
* @param registry the Java registry resource location.
* @param localCacheFunction which local field in RegistryCache are we caching entries for this registry?
* @param reader converts the RegistryEntry NBT into a class file
* @param <T> the class that represents these entries.
*/
private static <T> void register(JavaRegistryKey<?> registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, Function<RegistryEntryContext, T> reader) {
register(registry.registryKey(), localCacheFunction, reader);
}
/**
* @param registry the Java registry resource location.
* @param localCacheFunction which local field in RegistryCache are we caching entries for this registry?
* @param reader converts the RegistryEntry NBT into a class file
* @param <T> the class that represents these entries.
*/
private static <T> void register(Key registry, Function<RegistryCache, JavaRegistry<T>> localCacheFunction, Function<RegistryEntryContext, T> reader) {
REGISTRIES.put(registry, (registryCache, entries) -> {
Map<Key, NbtMap> localRegistry = null;
JavaRegistry<T> localCache = localCacheFunction.apply(registryCache);
// Clear each local cache every time a new registry entry is given to us
@ -172,7 +195,7 @@ public final class RegistryCache {
// 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(registryKey);
localRegistry = DEFAULTS.get(registry);
}
entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId()));
}

View file

@ -26,116 +26,119 @@
package org.geysermc.geyser.session.cache;
import it.unimi.dsi.fastutil.ints.IntArrays;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.session.cache.tags.EnchantmentTag;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
import org.geysermc.geyser.session.cache.tags.Tag;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.geyser.util.Ordered;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.geysermc.geyser.session.cache.tags.BlockTag.ALL_BLOCK_TAGS;
import static org.geysermc.geyser.session.cache.tags.EnchantmentTag.ALL_ENCHANTMENT_TAGS;
import static org.geysermc.geyser.session.cache.tags.ItemTag.ALL_ITEM_TAGS;
/**
* Manages information sent from the {@link ClientboundUpdateTagsPacket}. If that packet is not sent, all lists here
* will remain empty, matching Java Edition behavior.
* will remain empty, matching Java Edition behavior. Looking up a tag that wasn't listed in that packet will return an empty array.
* Only tags from suitable registries in {@link JavaRegistries} are stored. Read {@link JavaRegistryKey} for more information.
*/
@ParametersAreNonnullByDefault
public final class TagCache {
private final int[][] blocks = new int[ALL_BLOCK_TAGS.size()][];
private final int[][] items = new int[ALL_ITEM_TAGS.size()][];
private final int[][] enchantments = new int[ALL_ENCHANTMENT_TAGS.size()][];
private final GeyserSession session;
private final Map<Tag<?>, int[]> tags = new Object2ObjectOpenHashMap<>();
public TagCache(GeyserSession session) {
this.session = session;
}
public void loadPacket(GeyserSession session, ClientboundUpdateTagsPacket packet) {
Map<Key, int[]> blockTags = packet.getTags().get(MinecraftKey.key("block"));
loadTags("Block", blockTags, ALL_BLOCK_TAGS, this.blocks);
// Hack btw
Map<Key, Map<Key, int[]>> allTags = packet.getTags();
GeyserLogger logger = session.getGeyser().getLogger();
int[] convertableToMud = blockTags.get(MinecraftKey.key("convertable_to_mud"));
boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0;
session.setEmulatePost1_18Logic(emulatePost1_18Logic);
if (logger.isDebug()) {
logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic);
}
Map<Key, int[]> itemTags = packet.getTags().get(MinecraftKey.key("item"));
loadTags("Item", itemTags, ALL_ITEM_TAGS, this.items);
this.tags.clear();
// Hack btw
boolean emulatePost1_13Logic = itemTags.get(MinecraftKey.key("signs")).length > 1;
session.setEmulatePost1_13Logic(emulatePost1_13Logic);
if (logger.isDebug()) {
logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic);
}
Map<Key, int[]> enchantmentTags = packet.getTags().get(MinecraftKey.key("enchantment"));
loadTags("Enchantment", enchantmentTags, ALL_ENCHANTMENT_TAGS, this.enchantments);
}
private <T extends Ordered> void loadTags(String type, @Nullable Map<Key, int[]> packetTags, Map<Key, T> allTags, int[][] localValues) {
if (packetTags == null) {
Arrays.fill(localValues, IntArrays.EMPTY_ARRAY);
GeyserImpl.getInstance().getLogger().debug("Not loading " + type + " tags; they do not exist here.");
return;
}
allTags.forEach((location, tag) -> {
int[] values = packetTags.get(location);
if (values != null) {
if (values.length != 0) {
localValues[tag.ordinal()] = values;
} else {
localValues[tag.ordinal()] = IntArrays.EMPTY_ARRAY;
}
} else {
localValues[tag.ordinal()] = IntArrays.EMPTY_ARRAY;
GeyserImpl.getInstance().getLogger().debug(type + " tag not found from server: " + location);
for (Key registryKey : allTags.keySet()) {
JavaRegistryKey<?> registry = JavaRegistries.fromKey(registryKey);
if (registry == null || !registry.shouldStoreTags()) {
logger.debug("Not loading tags for registry " + registryKey + " (registry not listed in JavaRegistries, or was not suitable to load tags)");
continue;
}
});
Map<Key, int[]> registryTags = allTags.get(registryKey);
if (registry == JavaRegistries.BLOCK) {
// Hack btw
int[] convertableToMud = registryTags.get(MinecraftKey.key("convertable_to_mud"));
boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0;
session.setEmulatePost1_18Logic(emulatePost1_18Logic);
if (logger.isDebug()) {
logger.debug("Emulating post 1.18 block predication logic for " + session.bedrockUsername() + "? " + emulatePost1_18Logic);
}
} else if (registry == JavaRegistries.ITEM) {
// Hack btw
boolean emulatePost1_13Logic = registryTags.get(MinecraftKey.key("signs")).length > 1;
session.setEmulatePost1_13Logic(emulatePost1_13Logic);
if (logger.isDebug()) {
logger.debug("Emulating post 1.13 villager logic for " + session.bedrockUsername() + "? " + emulatePost1_13Logic);
}
}
loadTags(registryTags, registry, registry == JavaRegistries.ITEM);
}
}
/**
* @return true if the block tag is present and contains this block mapping's Java ID.
*/
public boolean is(BlockTag tag, Block block) {
int[] values = this.blocks[tag.ordinal()];
return contains(values, block.javaId());
private void loadTags(Map<Key, int[]> packetTags, JavaRegistryKey<?> registry, boolean sort) {
for (Map.Entry<Key, int[]> tag : packetTags.entrySet()) {
int[] value = tag.getValue();
if (sort) {
// Used in RecipeBookAddTranslator
Arrays.sort(value);
}
this.tags.put(new Tag<>(registry, tag.getKey()), value);
}
}
public <T> boolean is(Tag<T> tag, T object) {
return contains(getRaw(tag), tag.registry().toNetworkId(session, object));
}
/**
* @return true if the item tag is present and contains this item stack's Java ID.
*/
public boolean is(ItemTag tag, GeyserItemStack itemStack) {
public boolean is(Tag<Item> tag, GeyserItemStack itemStack) {
return is(tag, itemStack.asItem());
}
/**
* @return true if the item tag is present and contains this item's Java ID.
* @return true if the specified network ID is in the given holder set.
*/
public boolean is(ItemTag tag, Item item) {
int[] values = this.items[tag.ordinal()];
return contains(values, item.javaId());
public <T> boolean is(GeyserHolderSet<T> holderSet, T object) {
return contains(holderSet.resolveRaw(this), holderSet.getRegistry().toNetworkId(session, object));
}
public int[] get(ItemTag itemTag) {
return this.items[itemTag.ordinal()];
public <T> List<T> get(Tag<T> tag) {
return mapRawArray(session, getRaw(tag), tag.registry());
}
public int[] get(EnchantmentTag enchantmentTag) {
return this.enchantments[enchantmentTag.ordinal()];
/**
* @return the network IDs in the given tag. This can be an empty list.
*/
public int[] getRaw(Tag<?> tag) {
return this.tags.getOrDefault(tag, IntArrays.EMPTY_ARRAY);
}
/**
* Maps a raw array of network IDs to their respective objects.
*/
public static <T> List<T> mapRawArray(GeyserSession session, int[] array, JavaRegistryKey<T> registry) {
return Arrays.stream(array).mapToObj(i -> registry.fromNetworkId(session, i)).toList();
}
private static boolean contains(int[] array, int i) {

View file

@ -48,7 +48,7 @@ public class TeleportCache {
/**
* How many move packets the teleport can be unconfirmed for before it gets resent to the client
*/
private static final int RESEND_THRESHOLD = 5;
private static final int RESEND_THRESHOLD = 20; // Make it one full second with auth input
private final double x, y, z;
private final float pitch, yaw;

View file

@ -31,15 +31,18 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.UseCooldown;
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
import java.util.Iterator;
@ -72,7 +75,7 @@ public final class WorldCache {
@Setter
private boolean editingSignOnFront;
private final Object2IntMap<Item> activeCooldowns = new Object2IntOpenHashMap<>(2);
private final Object2IntMap<String> activeCooldowns = new Object2IntOpenHashMap<>(2);
public WorldCache(GeyserSession session) {
this.session = session;
@ -204,17 +207,24 @@ public final class WorldCache {
return this.activeRecords.remove(pos);
}
public void setCooldown(Item item, int ticks) {
public void setCooldown(Key cooldownGroup, int ticks) {
if (ticks == 0) {
// As of Java 1.21
this.activeCooldowns.removeInt(item);
this.activeCooldowns.removeInt(cooldownGroup.asString());
return;
}
this.activeCooldowns.put(item, session.getTicks() + ticks);
this.activeCooldowns.put(cooldownGroup.asString(), session.getTicks() + ticks);
}
public boolean hasCooldown(Item item) {
return this.activeCooldowns.containsKey(item);
public boolean hasCooldown(GeyserItemStack item) {
UseCooldown cooldown = item.getComponent(DataComponentType.USE_COOLDOWN);
String cooldownGroup;
if (cooldown != null && cooldown.cooldownGroup() != null) {
cooldownGroup = cooldown.cooldownGroup().asString();
} else {
cooldownGroup = item.asItem().javaIdentifier();
}
return this.activeCooldowns.containsKey(cooldownGroup);
}
public void tick() {
@ -222,9 +232,9 @@ public final class WorldCache {
// but we don't want the cooldown field to balloon in size from overuse.
if (!this.activeCooldowns.isEmpty()) {
int ticks = session.getTicks();
Iterator<Object2IntMap.Entry<Item>> it = Object2IntMaps.fastIterator(this.activeCooldowns);
Iterator<Object2IntMap.Entry<String>> it = Object2IntMaps.fastIterator(this.activeCooldowns);
while (it.hasNext()) {
Object2IntMap.Entry<Item> entry = it.next();
Object2IntMap.Entry<String> entry = it.next();
if (entry.getIntValue() <= ticks) {
it.remove();
}

View file

@ -0,0 +1,87 @@
/*
* 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.session.cache.registry;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.ListRegistry;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.session.cache.RegistryCache;
import org.geysermc.geyser.util.MinecraftKey;
import java.util.ArrayList;
import java.util.List;
/**
* Stores {@link JavaRegistryKey} for Java registries that are used for loading of data-driven objects, tags, or both. Read {@link JavaRegistryKey} for more information on how to use one.
*/
public class JavaRegistries {
private static final List<JavaRegistryKey<?>> VALUES = new ArrayList<>();
public static final JavaRegistryKey<Block> BLOCK = create("block", BlockRegistries.JAVA_BLOCKS, Block::javaId);
public static final JavaRegistryKey<Item> ITEM = create("item", Registries.JAVA_ITEMS, Item::javaId);
public static final JavaRegistryKey<Enchantment> ENCHANTMENT = create("enchantment", RegistryCache::enchantments);
private static <T> JavaRegistryKey<T> create(String key, JavaRegistryKey.NetworkSerializer<T> networkSerializer, JavaRegistryKey.NetworkDeserializer<T> networkDeserializer) {
JavaRegistryKey<T> registry = new JavaRegistryKey<>(MinecraftKey.key(key), networkSerializer, networkDeserializer);
VALUES.add(registry);
return registry;
}
private static <T> JavaRegistryKey<T> create(String key, ListRegistry<T> registry, RegistryNetworkMapper<T> networkSerializer) {
return create(key, (session, object) -> networkSerializer.get(object), (session, id) -> registry.get(id));
}
private static <T> JavaRegistryKey<T> create(String key, RegistryGetter<T> getter) {
return create(key, (session, object) -> getter.get(session.getRegistryCache()).byValue(object), (session, id) -> getter.get(session.getRegistryCache()).byId(id));
}
@Nullable
public static JavaRegistryKey<?> fromKey(Key registryKey) {
for (JavaRegistryKey<?> registry : VALUES) {
if (registry.registryKey().equals(registryKey)) {
return registry;
}
}
return null;
}
@FunctionalInterface
interface RegistryGetter<T> {
JavaRegistry<T> get(RegistryCache cache);
}
@FunctionalInterface
interface RegistryNetworkMapper<T> {
int get(T object);
}
}

View file

@ -0,0 +1,86 @@
/*
* 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.session.cache.registry;
import net.kyori.adventure.key.Key;
import org.geysermc.geyser.session.GeyserSession;
import javax.annotation.Nullable;
/**
* Defines a Java registry, which can be hardcoded or data-driven. This class doesn't store registry contents itself, that is handled by {@link org.geysermc.geyser.session.cache.RegistryCache} in the case of
* data-driven registries and other classes in the case of hardcoded registries.
*
* <p>This class is used when, for a Java registry, data-driven objects or tags need to be loaded. Only one instance of this class should be created for each Java registry. Instances of this
* class are kept in {@link JavaRegistries}, which also has useful methods for creating instances of this class. When only using a registry to load data-driven objects, the network (de)serializer parameters
* can be null. For tag loading however, these are required, as {@link org.geysermc.geyser.session.cache.TagCache} relies on their functionality.</p>
*
* @param registryKey the registry key, as it appears on Java.
* @param networkSerializer a method that converts an object in this registry to its network ID.
* @param networkDeserializer a method that converts a network ID to an object in this registry.
* @param <T> the object type this registry holds.
*/
public record JavaRegistryKey<T>(Key registryKey, @Nullable NetworkSerializer<T> networkSerializer, @Nullable NetworkDeserializer<T> networkDeserializer) {
/**
* Converts an object in this registry to its network ID. This will fail if this registry doesn't have a network serializer.
*/
public int toNetworkId(GeyserSession session, T object) {
if (networkSerializer == null) {
throw new UnsupportedOperationException("Registry does not hava a network serializer");
}
return networkSerializer.toNetworkId(session, object);
}
/**
* Converts a network ID to an object in this registry. This will fail if this registry doesn't have a network deserializer.
*/
public T fromNetworkId(GeyserSession session, int networkId) {
if (networkDeserializer == null) {
throw new UnsupportedOperationException("Registry does not hava a network deserializer");
}
return networkDeserializer.fromNetworkId(session, networkId);
}
/**
* @return true if this registry has a network serializer and deserializer.
*/
public boolean shouldStoreTags() {
return networkSerializer != null && networkDeserializer != null;
}
@FunctionalInterface
public interface NetworkSerializer<T> {
int toNetworkId(GeyserSession session, T object);
}
@FunctionalInterface
public interface NetworkDeserializer<T> {
T fromNetworkId(GeyserSession session, int networkId);
}
}

View file

@ -25,214 +25,203 @@
package org.geysermc.geyser.session.cache.tags;
import net.kyori.adventure.key.Key;
import org.geysermc.geyser.util.Ordered;
import java.util.HashMap;
import java.util.Map;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.util.MinecraftKey;
/**
* Lists vanilla block tags.
*/
@SuppressWarnings("unused")
public final class BlockTag implements Ordered {
public static final Map<Key, BlockTag> ALL_BLOCK_TAGS = new HashMap<>();
public final class BlockTag {
public static final Tag<Block> WOOL = create("wool");
public static final Tag<Block> PLANKS = create("planks");
public static final Tag<Block> STONE_BRICKS = create("stone_bricks");
public static final Tag<Block> WOODEN_BUTTONS = create("wooden_buttons");
public static final Tag<Block> STONE_BUTTONS = create("stone_buttons");
public static final Tag<Block> BUTTONS = create("buttons");
public static final Tag<Block> WOOL_CARPETS = create("wool_carpets");
public static final Tag<Block> WOODEN_DOORS = create("wooden_doors");
public static final Tag<Block> MOB_INTERACTABLE_DOORS = create("mob_interactable_doors");
public static final Tag<Block> WOODEN_STAIRS = create("wooden_stairs");
public static final Tag<Block> WOODEN_SLABS = create("wooden_slabs");
public static final Tag<Block> WOODEN_FENCES = create("wooden_fences");
public static final Tag<Block> PRESSURE_PLATES = create("pressure_plates");
public static final Tag<Block> WOODEN_PRESSURE_PLATES = create("wooden_pressure_plates");
public static final Tag<Block> STONE_PRESSURE_PLATES = create("stone_pressure_plates");
public static final Tag<Block> WOODEN_TRAPDOORS = create("wooden_trapdoors");
public static final Tag<Block> DOORS = create("doors");
public static final Tag<Block> SAPLINGS = create("saplings");
public static final Tag<Block> LOGS_THAT_BURN = create("logs_that_burn");
public static final Tag<Block> OVERWORLD_NATURAL_LOGS = create("overworld_natural_logs");
public static final Tag<Block> LOGS = create("logs");
public static final Tag<Block> DARK_OAK_LOGS = create("dark_oak_logs");
public static final Tag<Block> OAK_LOGS = create("oak_logs");
public static final Tag<Block> BIRCH_LOGS = create("birch_logs");
public static final Tag<Block> ACACIA_LOGS = create("acacia_logs");
public static final Tag<Block> CHERRY_LOGS = create("cherry_logs");
public static final Tag<Block> JUNGLE_LOGS = create("jungle_logs");
public static final Tag<Block> SPRUCE_LOGS = create("spruce_logs");
public static final Tag<Block> MANGROVE_LOGS = create("mangrove_logs");
public static final Tag<Block> CRIMSON_STEMS = create("crimson_stems");
public static final Tag<Block> WARPED_STEMS = create("warped_stems");
public static final Tag<Block> BAMBOO_BLOCKS = create("bamboo_blocks");
public static final Tag<Block> WART_BLOCKS = create("wart_blocks");
public static final Tag<Block> BANNERS = create("banners");
public static final Tag<Block> SAND = create("sand");
public static final Tag<Block> SMELTS_TO_GLASS = create("smelts_to_glass");
public static final Tag<Block> STAIRS = create("stairs");
public static final Tag<Block> SLABS = create("slabs");
public static final Tag<Block> WALLS = create("walls");
public static final Tag<Block> ANVIL = create("anvil");
public static final Tag<Block> RAILS = create("rails");
public static final Tag<Block> LEAVES = create("leaves");
public static final Tag<Block> TRAPDOORS = create("trapdoors");
public static final Tag<Block> SMALL_FLOWERS = create("small_flowers");
public static final Tag<Block> BEDS = create("beds");
public static final Tag<Block> FENCES = create("fences");
public static final Tag<Block> TALL_FLOWERS = create("tall_flowers");
public static final Tag<Block> FLOWERS = create("flowers");
public static final Tag<Block> PIGLIN_REPELLENTS = create("piglin_repellents");
public static final Tag<Block> GOLD_ORES = create("gold_ores");
public static final Tag<Block> IRON_ORES = create("iron_ores");
public static final Tag<Block> DIAMOND_ORES = create("diamond_ores");
public static final Tag<Block> REDSTONE_ORES = create("redstone_ores");
public static final Tag<Block> LAPIS_ORES = create("lapis_ores");
public static final Tag<Block> COAL_ORES = create("coal_ores");
public static final Tag<Block> EMERALD_ORES = create("emerald_ores");
public static final Tag<Block> COPPER_ORES = create("copper_ores");
public static final Tag<Block> CANDLES = create("candles");
public static final Tag<Block> DIRT = create("dirt");
public static final Tag<Block> TERRACOTTA = create("terracotta");
public static final Tag<Block> BADLANDS_TERRACOTTA = create("badlands_terracotta");
public static final Tag<Block> CONCRETE_POWDER = create("concrete_powder");
public static final Tag<Block> COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial");
public static final Tag<Block> FLOWER_POTS = create("flower_pots");
public static final Tag<Block> ENDERMAN_HOLDABLE = create("enderman_holdable");
public static final Tag<Block> ICE = create("ice");
public static final Tag<Block> VALID_SPAWN = create("valid_spawn");
public static final Tag<Block> IMPERMEABLE = create("impermeable");
public static final Tag<Block> UNDERWATER_BONEMEALS = create("underwater_bonemeals");
public static final Tag<Block> CORAL_BLOCKS = create("coral_blocks");
public static final Tag<Block> WALL_CORALS = create("wall_corals");
public static final Tag<Block> CORAL_PLANTS = create("coral_plants");
public static final Tag<Block> CORALS = create("corals");
public static final Tag<Block> BAMBOO_PLANTABLE_ON = create("bamboo_plantable_on");
public static final Tag<Block> STANDING_SIGNS = create("standing_signs");
public static final Tag<Block> WALL_SIGNS = create("wall_signs");
public static final Tag<Block> SIGNS = create("signs");
public static final Tag<Block> CEILING_HANGING_SIGNS = create("ceiling_hanging_signs");
public static final Tag<Block> WALL_HANGING_SIGNS = create("wall_hanging_signs");
public static final Tag<Block> ALL_HANGING_SIGNS = create("all_hanging_signs");
public static final Tag<Block> ALL_SIGNS = create("all_signs");
public static final Tag<Block> DRAGON_IMMUNE = create("dragon_immune");
public static final Tag<Block> DRAGON_TRANSPARENT = create("dragon_transparent");
public static final Tag<Block> WITHER_IMMUNE = create("wither_immune");
public static final Tag<Block> WITHER_SUMMON_BASE_BLOCKS = create("wither_summon_base_blocks");
public static final Tag<Block> BEEHIVES = create("beehives");
public static final Tag<Block> CROPS = create("crops");
public static final Tag<Block> BEE_GROWABLES = create("bee_growables");
public static final Tag<Block> PORTALS = create("portals");
public static final Tag<Block> FIRE = create("fire");
public static final Tag<Block> NYLIUM = create("nylium");
public static final Tag<Block> BEACON_BASE_BLOCKS = create("beacon_base_blocks");
public static final Tag<Block> SOUL_SPEED_BLOCKS = create("soul_speed_blocks");
public static final Tag<Block> WALL_POST_OVERRIDE = create("wall_post_override");
public static final Tag<Block> CLIMBABLE = create("climbable");
public static final Tag<Block> FALL_DAMAGE_RESETTING = create("fall_damage_resetting");
public static final Tag<Block> SHULKER_BOXES = create("shulker_boxes");
public static final Tag<Block> HOGLIN_REPELLENTS = create("hoglin_repellents");
public static final Tag<Block> SOUL_FIRE_BASE_BLOCKS = create("soul_fire_base_blocks");
public static final Tag<Block> STRIDER_WARM_BLOCKS = create("strider_warm_blocks");
public static final Tag<Block> CAMPFIRES = create("campfires");
public static final Tag<Block> GUARDED_BY_PIGLINS = create("guarded_by_piglins");
public static final Tag<Block> PREVENT_MOB_SPAWNING_INSIDE = create("prevent_mob_spawning_inside");
public static final Tag<Block> FENCE_GATES = create("fence_gates");
public static final Tag<Block> UNSTABLE_BOTTOM_CENTER = create("unstable_bottom_center");
public static final Tag<Block> MUSHROOM_GROW_BLOCK = create("mushroom_grow_block");
public static final Tag<Block> INFINIBURN_OVERWORLD = create("infiniburn_overworld");
public static final Tag<Block> INFINIBURN_NETHER = create("infiniburn_nether");
public static final Tag<Block> INFINIBURN_END = create("infiniburn_end");
public static final Tag<Block> BASE_STONE_OVERWORLD = create("base_stone_overworld");
public static final Tag<Block> STONE_ORE_REPLACEABLES = create("stone_ore_replaceables");
public static final Tag<Block> DEEPSLATE_ORE_REPLACEABLES = create("deepslate_ore_replaceables");
public static final Tag<Block> BASE_STONE_NETHER = create("base_stone_nether");
public static final Tag<Block> OVERWORLD_CARVER_REPLACEABLES = create("overworld_carver_replaceables");
public static final Tag<Block> NETHER_CARVER_REPLACEABLES = create("nether_carver_replaceables");
public static final Tag<Block> CANDLE_CAKES = create("candle_cakes");
public static final Tag<Block> CAULDRONS = create("cauldrons");
public static final Tag<Block> CRYSTAL_SOUND_BLOCKS = create("crystal_sound_blocks");
public static final Tag<Block> INSIDE_STEP_SOUND_BLOCKS = create("inside_step_sound_blocks");
public static final Tag<Block> COMBINATION_STEP_SOUND_BLOCKS = create("combination_step_sound_blocks");
public static final Tag<Block> CAMEL_SAND_STEP_SOUND_BLOCKS = create("camel_sand_step_sound_blocks");
public static final Tag<Block> OCCLUDES_VIBRATION_SIGNALS = create("occludes_vibration_signals");
public static final Tag<Block> DAMPENS_VIBRATIONS = create("dampens_vibrations");
public static final Tag<Block> DRIPSTONE_REPLACEABLE_BLOCKS = create("dripstone_replaceable_blocks");
public static final Tag<Block> CAVE_VINES = create("cave_vines");
public static final Tag<Block> MOSS_REPLACEABLE = create("moss_replaceable");
public static final Tag<Block> LUSH_GROUND_REPLACEABLE = create("lush_ground_replaceable");
public static final Tag<Block> AZALEA_ROOT_REPLACEABLE = create("azalea_root_replaceable");
public static final Tag<Block> SMALL_DRIPLEAF_PLACEABLE = create("small_dripleaf_placeable");
public static final Tag<Block> BIG_DRIPLEAF_PLACEABLE = create("big_dripleaf_placeable");
public static final Tag<Block> SNOW = create("snow");
public static final Tag<Block> MINEABLE_AXE = create("mineable/axe");
public static final Tag<Block> MINEABLE_HOE = create("mineable/hoe");
public static final Tag<Block> MINEABLE_PICKAXE = create("mineable/pickaxe");
public static final Tag<Block> MINEABLE_SHOVEL = create("mineable/shovel");
public static final Tag<Block> SWORD_EFFICIENT = create("sword_efficient");
public static final Tag<Block> NEEDS_DIAMOND_TOOL = create("needs_diamond_tool");
public static final Tag<Block> NEEDS_IRON_TOOL = create("needs_iron_tool");
public static final Tag<Block> NEEDS_STONE_TOOL = create("needs_stone_tool");
public static final Tag<Block> INCORRECT_FOR_NETHERITE_TOOL = create("incorrect_for_netherite_tool");
public static final Tag<Block> INCORRECT_FOR_DIAMOND_TOOL = create("incorrect_for_diamond_tool");
public static final Tag<Block> INCORRECT_FOR_IRON_TOOL = create("incorrect_for_iron_tool");
public static final Tag<Block> INCORRECT_FOR_STONE_TOOL = create("incorrect_for_stone_tool");
public static final Tag<Block> INCORRECT_FOR_GOLD_TOOL = create("incorrect_for_gold_tool");
public static final Tag<Block> INCORRECT_FOR_WOODEN_TOOL = create("incorrect_for_wooden_tool");
public static final Tag<Block> FEATURES_CANNOT_REPLACE = create("features_cannot_replace");
public static final Tag<Block> LAVA_POOL_STONE_CANNOT_REPLACE = create("lava_pool_stone_cannot_replace");
public static final Tag<Block> GEODE_INVALID_BLOCKS = create("geode_invalid_blocks");
public static final Tag<Block> FROG_PREFER_JUMP_TO = create("frog_prefer_jump_to");
public static final Tag<Block> SCULK_REPLACEABLE = create("sculk_replaceable");
public static final Tag<Block> SCULK_REPLACEABLE_WORLD_GEN = create("sculk_replaceable_world_gen");
public static final Tag<Block> ANCIENT_CITY_REPLACEABLE = create("ancient_city_replaceable");
public static final Tag<Block> VIBRATION_RESONATORS = create("vibration_resonators");
public static final Tag<Block> ANIMALS_SPAWNABLE_ON = create("animals_spawnable_on");
public static final Tag<Block> ARMADILLO_SPAWNABLE_ON = create("armadillo_spawnable_on");
public static final Tag<Block> AXOLOTLS_SPAWNABLE_ON = create("axolotls_spawnable_on");
public static final Tag<Block> GOATS_SPAWNABLE_ON = create("goats_spawnable_on");
public static final Tag<Block> MOOSHROOMS_SPAWNABLE_ON = create("mooshrooms_spawnable_on");
public static final Tag<Block> PARROTS_SPAWNABLE_ON = create("parrots_spawnable_on");
public static final Tag<Block> POLAR_BEARS_SPAWNABLE_ON_ALTERNATE = create("polar_bears_spawnable_on_alternate");
public static final Tag<Block> RABBITS_SPAWNABLE_ON = create("rabbits_spawnable_on");
public static final Tag<Block> FOXES_SPAWNABLE_ON = create("foxes_spawnable_on");
public static final Tag<Block> WOLVES_SPAWNABLE_ON = create("wolves_spawnable_on");
public static final Tag<Block> FROGS_SPAWNABLE_ON = create("frogs_spawnable_on");
public static final Tag<Block> AZALEA_GROWS_ON = create("azalea_grows_on");
public static final Tag<Block> CONVERTABLE_TO_MUD = create("convertable_to_mud");
public static final Tag<Block> MANGROVE_LOGS_CAN_GROW_THROUGH = create("mangrove_logs_can_grow_through");
public static final Tag<Block> MANGROVE_ROOTS_CAN_GROW_THROUGH = create("mangrove_roots_can_grow_through");
public static final Tag<Block> DEAD_BUSH_MAY_PLACE_ON = create("dead_bush_may_place_on");
public static final Tag<Block> SNAPS_GOAT_HORN = create("snaps_goat_horn");
public static final Tag<Block> REPLACEABLE_BY_TREES = create("replaceable_by_trees");
public static final Tag<Block> SNOW_LAYER_CANNOT_SURVIVE_ON = create("snow_layer_cannot_survive_on");
public static final Tag<Block> SNOW_LAYER_CAN_SURVIVE_ON = create("snow_layer_can_survive_on");
public static final Tag<Block> INVALID_SPAWN_INSIDE = create("invalid_spawn_inside");
public static final Tag<Block> SNIFFER_DIGGABLE_BLOCK = create("sniffer_diggable_block");
public static final Tag<Block> SNIFFER_EGG_HATCH_BOOST = create("sniffer_egg_hatch_boost");
public static final Tag<Block> TRAIL_RUINS_REPLACEABLE = create("trail_ruins_replaceable");
public static final Tag<Block> REPLACEABLE = create("replaceable");
public static final Tag<Block> ENCHANTMENT_POWER_PROVIDER = create("enchantment_power_provider");
public static final Tag<Block> ENCHANTMENT_POWER_TRANSMITTER = create("enchantment_power_transmitter");
public static final Tag<Block> MAINTAINS_FARMLAND = create("maintains_farmland");
public static final Tag<Block> BLOCKS_WIND_CHARGE_EXPLOSIONS = create("blocks_wind_charge_explosions");
public static final Tag<Block> DOES_NOT_BLOCK_HOPPERS = create("does_not_block_hoppers");
public static final Tag<Block> AIR = create("air");
public static final BlockTag WOOL = new BlockTag("wool");
public static final BlockTag PLANKS = new BlockTag("planks");
public static final BlockTag STONE_BRICKS = new BlockTag("stone_bricks");
public static final BlockTag WOODEN_BUTTONS = new BlockTag("wooden_buttons");
public static final BlockTag STONE_BUTTONS = new BlockTag("stone_buttons");
public static final BlockTag BUTTONS = new BlockTag("buttons");
public static final BlockTag WOOL_CARPETS = new BlockTag("wool_carpets");
public static final BlockTag WOODEN_DOORS = new BlockTag("wooden_doors");
public static final BlockTag MOB_INTERACTABLE_DOORS = new BlockTag("mob_interactable_doors");
public static final BlockTag WOODEN_STAIRS = new BlockTag("wooden_stairs");
public static final BlockTag WOODEN_SLABS = new BlockTag("wooden_slabs");
public static final BlockTag WOODEN_FENCES = new BlockTag("wooden_fences");
public static final BlockTag PRESSURE_PLATES = new BlockTag("pressure_plates");
public static final BlockTag WOODEN_PRESSURE_PLATES = new BlockTag("wooden_pressure_plates");
public static final BlockTag STONE_PRESSURE_PLATES = new BlockTag("stone_pressure_plates");
public static final BlockTag WOODEN_TRAPDOORS = new BlockTag("wooden_trapdoors");
public static final BlockTag DOORS = new BlockTag("doors");
public static final BlockTag SAPLINGS = new BlockTag("saplings");
public static final BlockTag LOGS_THAT_BURN = new BlockTag("logs_that_burn");
public static final BlockTag OVERWORLD_NATURAL_LOGS = new BlockTag("overworld_natural_logs");
public static final BlockTag LOGS = new BlockTag("logs");
public static final BlockTag DARK_OAK_LOGS = new BlockTag("dark_oak_logs");
public static final BlockTag OAK_LOGS = new BlockTag("oak_logs");
public static final BlockTag BIRCH_LOGS = new BlockTag("birch_logs");
public static final BlockTag ACACIA_LOGS = new BlockTag("acacia_logs");
public static final BlockTag CHERRY_LOGS = new BlockTag("cherry_logs");
public static final BlockTag JUNGLE_LOGS = new BlockTag("jungle_logs");
public static final BlockTag SPRUCE_LOGS = new BlockTag("spruce_logs");
public static final BlockTag MANGROVE_LOGS = new BlockTag("mangrove_logs");
public static final BlockTag CRIMSON_STEMS = new BlockTag("crimson_stems");
public static final BlockTag WARPED_STEMS = new BlockTag("warped_stems");
public static final BlockTag BAMBOO_BLOCKS = new BlockTag("bamboo_blocks");
public static final BlockTag WART_BLOCKS = new BlockTag("wart_blocks");
public static final BlockTag BANNERS = new BlockTag("banners");
public static final BlockTag SAND = new BlockTag("sand");
public static final BlockTag SMELTS_TO_GLASS = new BlockTag("smelts_to_glass");
public static final BlockTag STAIRS = new BlockTag("stairs");
public static final BlockTag SLABS = new BlockTag("slabs");
public static final BlockTag WALLS = new BlockTag("walls");
public static final BlockTag ANVIL = new BlockTag("anvil");
public static final BlockTag RAILS = new BlockTag("rails");
public static final BlockTag LEAVES = new BlockTag("leaves");
public static final BlockTag TRAPDOORS = new BlockTag("trapdoors");
public static final BlockTag SMALL_FLOWERS = new BlockTag("small_flowers");
public static final BlockTag BEDS = new BlockTag("beds");
public static final BlockTag FENCES = new BlockTag("fences");
public static final BlockTag TALL_FLOWERS = new BlockTag("tall_flowers");
public static final BlockTag FLOWERS = new BlockTag("flowers");
public static final BlockTag PIGLIN_REPELLENTS = new BlockTag("piglin_repellents");
public static final BlockTag GOLD_ORES = new BlockTag("gold_ores");
public static final BlockTag IRON_ORES = new BlockTag("iron_ores");
public static final BlockTag DIAMOND_ORES = new BlockTag("diamond_ores");
public static final BlockTag REDSTONE_ORES = new BlockTag("redstone_ores");
public static final BlockTag LAPIS_ORES = new BlockTag("lapis_ores");
public static final BlockTag COAL_ORES = new BlockTag("coal_ores");
public static final BlockTag EMERALD_ORES = new BlockTag("emerald_ores");
public static final BlockTag COPPER_ORES = new BlockTag("copper_ores");
public static final BlockTag CANDLES = new BlockTag("candles");
public static final BlockTag DIRT = new BlockTag("dirt");
public static final BlockTag TERRACOTTA = new BlockTag("terracotta");
public static final BlockTag BADLANDS_TERRACOTTA = new BlockTag("badlands_terracotta");
public static final BlockTag CONCRETE_POWDER = new BlockTag("concrete_powder");
public static final BlockTag COMPLETES_FIND_TREE_TUTORIAL = new BlockTag("completes_find_tree_tutorial");
public static final BlockTag FLOWER_POTS = new BlockTag("flower_pots");
public static final BlockTag ENDERMAN_HOLDABLE = new BlockTag("enderman_holdable");
public static final BlockTag ICE = new BlockTag("ice");
public static final BlockTag VALID_SPAWN = new BlockTag("valid_spawn");
public static final BlockTag IMPERMEABLE = new BlockTag("impermeable");
public static final BlockTag UNDERWATER_BONEMEALS = new BlockTag("underwater_bonemeals");
public static final BlockTag CORAL_BLOCKS = new BlockTag("coral_blocks");
public static final BlockTag WALL_CORALS = new BlockTag("wall_corals");
public static final BlockTag CORAL_PLANTS = new BlockTag("coral_plants");
public static final BlockTag CORALS = new BlockTag("corals");
public static final BlockTag BAMBOO_PLANTABLE_ON = new BlockTag("bamboo_plantable_on");
public static final BlockTag STANDING_SIGNS = new BlockTag("standing_signs");
public static final BlockTag WALL_SIGNS = new BlockTag("wall_signs");
public static final BlockTag SIGNS = new BlockTag("signs");
public static final BlockTag CEILING_HANGING_SIGNS = new BlockTag("ceiling_hanging_signs");
public static final BlockTag WALL_HANGING_SIGNS = new BlockTag("wall_hanging_signs");
public static final BlockTag ALL_HANGING_SIGNS = new BlockTag("all_hanging_signs");
public static final BlockTag ALL_SIGNS = new BlockTag("all_signs");
public static final BlockTag DRAGON_IMMUNE = new BlockTag("dragon_immune");
public static final BlockTag DRAGON_TRANSPARENT = new BlockTag("dragon_transparent");
public static final BlockTag WITHER_IMMUNE = new BlockTag("wither_immune");
public static final BlockTag WITHER_SUMMON_BASE_BLOCKS = new BlockTag("wither_summon_base_blocks");
public static final BlockTag BEEHIVES = new BlockTag("beehives");
public static final BlockTag CROPS = new BlockTag("crops");
public static final BlockTag BEE_GROWABLES = new BlockTag("bee_growables");
public static final BlockTag PORTALS = new BlockTag("portals");
public static final BlockTag FIRE = new BlockTag("fire");
public static final BlockTag NYLIUM = new BlockTag("nylium");
public static final BlockTag BEACON_BASE_BLOCKS = new BlockTag("beacon_base_blocks");
public static final BlockTag SOUL_SPEED_BLOCKS = new BlockTag("soul_speed_blocks");
public static final BlockTag WALL_POST_OVERRIDE = new BlockTag("wall_post_override");
public static final BlockTag CLIMBABLE = new BlockTag("climbable");
public static final BlockTag FALL_DAMAGE_RESETTING = new BlockTag("fall_damage_resetting");
public static final BlockTag SHULKER_BOXES = new BlockTag("shulker_boxes");
public static final BlockTag HOGLIN_REPELLENTS = new BlockTag("hoglin_repellents");
public static final BlockTag SOUL_FIRE_BASE_BLOCKS = new BlockTag("soul_fire_base_blocks");
public static final BlockTag STRIDER_WARM_BLOCKS = new BlockTag("strider_warm_blocks");
public static final BlockTag CAMPFIRES = new BlockTag("campfires");
public static final BlockTag GUARDED_BY_PIGLINS = new BlockTag("guarded_by_piglins");
public static final BlockTag PREVENT_MOB_SPAWNING_INSIDE = new BlockTag("prevent_mob_spawning_inside");
public static final BlockTag FENCE_GATES = new BlockTag("fence_gates");
public static final BlockTag UNSTABLE_BOTTOM_CENTER = new BlockTag("unstable_bottom_center");
public static final BlockTag MUSHROOM_GROW_BLOCK = new BlockTag("mushroom_grow_block");
public static final BlockTag INFINIBURN_OVERWORLD = new BlockTag("infiniburn_overworld");
public static final BlockTag INFINIBURN_NETHER = new BlockTag("infiniburn_nether");
public static final BlockTag INFINIBURN_END = new BlockTag("infiniburn_end");
public static final BlockTag BASE_STONE_OVERWORLD = new BlockTag("base_stone_overworld");
public static final BlockTag STONE_ORE_REPLACEABLES = new BlockTag("stone_ore_replaceables");
public static final BlockTag DEEPSLATE_ORE_REPLACEABLES = new BlockTag("deepslate_ore_replaceables");
public static final BlockTag BASE_STONE_NETHER = new BlockTag("base_stone_nether");
public static final BlockTag OVERWORLD_CARVER_REPLACEABLES = new BlockTag("overworld_carver_replaceables");
public static final BlockTag NETHER_CARVER_REPLACEABLES = new BlockTag("nether_carver_replaceables");
public static final BlockTag CANDLE_CAKES = new BlockTag("candle_cakes");
public static final BlockTag CAULDRONS = new BlockTag("cauldrons");
public static final BlockTag CRYSTAL_SOUND_BLOCKS = new BlockTag("crystal_sound_blocks");
public static final BlockTag INSIDE_STEP_SOUND_BLOCKS = new BlockTag("inside_step_sound_blocks");
public static final BlockTag COMBINATION_STEP_SOUND_BLOCKS = new BlockTag("combination_step_sound_blocks");
public static final BlockTag CAMEL_SAND_STEP_SOUND_BLOCKS = new BlockTag("camel_sand_step_sound_blocks");
public static final BlockTag OCCLUDES_VIBRATION_SIGNALS = new BlockTag("occludes_vibration_signals");
public static final BlockTag DAMPENS_VIBRATIONS = new BlockTag("dampens_vibrations");
public static final BlockTag DRIPSTONE_REPLACEABLE_BLOCKS = new BlockTag("dripstone_replaceable_blocks");
public static final BlockTag CAVE_VINES = new BlockTag("cave_vines");
public static final BlockTag MOSS_REPLACEABLE = new BlockTag("moss_replaceable");
public static final BlockTag LUSH_GROUND_REPLACEABLE = new BlockTag("lush_ground_replaceable");
public static final BlockTag AZALEA_ROOT_REPLACEABLE = new BlockTag("azalea_root_replaceable");
public static final BlockTag SMALL_DRIPLEAF_PLACEABLE = new BlockTag("small_dripleaf_placeable");
public static final BlockTag BIG_DRIPLEAF_PLACEABLE = new BlockTag("big_dripleaf_placeable");
public static final BlockTag SNOW = new BlockTag("snow");
public static final BlockTag MINEABLE_AXE = new BlockTag("mineable/axe");
public static final BlockTag MINEABLE_HOE = new BlockTag("mineable/hoe");
public static final BlockTag MINEABLE_PICKAXE = new BlockTag("mineable/pickaxe");
public static final BlockTag MINEABLE_SHOVEL = new BlockTag("mineable/shovel");
public static final BlockTag SWORD_EFFICIENT = new BlockTag("sword_efficient");
public static final BlockTag NEEDS_DIAMOND_TOOL = new BlockTag("needs_diamond_tool");
public static final BlockTag NEEDS_IRON_TOOL = new BlockTag("needs_iron_tool");
public static final BlockTag NEEDS_STONE_TOOL = new BlockTag("needs_stone_tool");
public static final BlockTag INCORRECT_FOR_NETHERITE_TOOL = new BlockTag("incorrect_for_netherite_tool");
public static final BlockTag INCORRECT_FOR_DIAMOND_TOOL = new BlockTag("incorrect_for_diamond_tool");
public static final BlockTag INCORRECT_FOR_IRON_TOOL = new BlockTag("incorrect_for_iron_tool");
public static final BlockTag INCORRECT_FOR_STONE_TOOL = new BlockTag("incorrect_for_stone_tool");
public static final BlockTag INCORRECT_FOR_GOLD_TOOL = new BlockTag("incorrect_for_gold_tool");
public static final BlockTag INCORRECT_FOR_WOODEN_TOOL = new BlockTag("incorrect_for_wooden_tool");
public static final BlockTag FEATURES_CANNOT_REPLACE = new BlockTag("features_cannot_replace");
public static final BlockTag LAVA_POOL_STONE_CANNOT_REPLACE = new BlockTag("lava_pool_stone_cannot_replace");
public static final BlockTag GEODE_INVALID_BLOCKS = new BlockTag("geode_invalid_blocks");
public static final BlockTag FROG_PREFER_JUMP_TO = new BlockTag("frog_prefer_jump_to");
public static final BlockTag SCULK_REPLACEABLE = new BlockTag("sculk_replaceable");
public static final BlockTag SCULK_REPLACEABLE_WORLD_GEN = new BlockTag("sculk_replaceable_world_gen");
public static final BlockTag ANCIENT_CITY_REPLACEABLE = new BlockTag("ancient_city_replaceable");
public static final BlockTag VIBRATION_RESONATORS = new BlockTag("vibration_resonators");
public static final BlockTag ANIMALS_SPAWNABLE_ON = new BlockTag("animals_spawnable_on");
public static final BlockTag ARMADILLO_SPAWNABLE_ON = new BlockTag("armadillo_spawnable_on");
public static final BlockTag AXOLOTLS_SPAWNABLE_ON = new BlockTag("axolotls_spawnable_on");
public static final BlockTag GOATS_SPAWNABLE_ON = new BlockTag("goats_spawnable_on");
public static final BlockTag MOOSHROOMS_SPAWNABLE_ON = new BlockTag("mooshrooms_spawnable_on");
public static final BlockTag PARROTS_SPAWNABLE_ON = new BlockTag("parrots_spawnable_on");
public static final BlockTag POLAR_BEARS_SPAWNABLE_ON_ALTERNATE = new BlockTag("polar_bears_spawnable_on_alternate");
public static final BlockTag RABBITS_SPAWNABLE_ON = new BlockTag("rabbits_spawnable_on");
public static final BlockTag FOXES_SPAWNABLE_ON = new BlockTag("foxes_spawnable_on");
public static final BlockTag WOLVES_SPAWNABLE_ON = new BlockTag("wolves_spawnable_on");
public static final BlockTag FROGS_SPAWNABLE_ON = new BlockTag("frogs_spawnable_on");
public static final BlockTag AZALEA_GROWS_ON = new BlockTag("azalea_grows_on");
public static final BlockTag CONVERTABLE_TO_MUD = new BlockTag("convertable_to_mud");
public static final BlockTag MANGROVE_LOGS_CAN_GROW_THROUGH = new BlockTag("mangrove_logs_can_grow_through");
public static final BlockTag MANGROVE_ROOTS_CAN_GROW_THROUGH = new BlockTag("mangrove_roots_can_grow_through");
public static final BlockTag DEAD_BUSH_MAY_PLACE_ON = new BlockTag("dead_bush_may_place_on");
public static final BlockTag SNAPS_GOAT_HORN = new BlockTag("snaps_goat_horn");
public static final BlockTag REPLACEABLE_BY_TREES = new BlockTag("replaceable_by_trees");
public static final BlockTag SNOW_LAYER_CANNOT_SURVIVE_ON = new BlockTag("snow_layer_cannot_survive_on");
public static final BlockTag SNOW_LAYER_CAN_SURVIVE_ON = new BlockTag("snow_layer_can_survive_on");
public static final BlockTag INVALID_SPAWN_INSIDE = new BlockTag("invalid_spawn_inside");
public static final BlockTag SNIFFER_DIGGABLE_BLOCK = new BlockTag("sniffer_diggable_block");
public static final BlockTag SNIFFER_EGG_HATCH_BOOST = new BlockTag("sniffer_egg_hatch_boost");
public static final BlockTag TRAIL_RUINS_REPLACEABLE = new BlockTag("trail_ruins_replaceable");
public static final BlockTag REPLACEABLE = new BlockTag("replaceable");
public static final BlockTag ENCHANTMENT_POWER_PROVIDER = new BlockTag("enchantment_power_provider");
public static final BlockTag ENCHANTMENT_POWER_TRANSMITTER = new BlockTag("enchantment_power_transmitter");
public static final BlockTag MAINTAINS_FARMLAND = new BlockTag("maintains_farmland");
public static final BlockTag BLOCKS_WIND_CHARGE_EXPLOSIONS = new BlockTag("blocks_wind_charge_explosions");
public static final BlockTag DOES_NOT_BLOCK_HOPPERS = new BlockTag("does_not_block_hoppers");
public static final BlockTag AIR = new BlockTag("air");
private BlockTag() {}
private final int id;
private BlockTag(String identifier) {
this.id = ALL_BLOCK_TAGS.size();
register(identifier, this);
}
@Override
public int ordinal() {
return id;
}
private static void register(String name, BlockTag tag) {
ALL_BLOCK_TAGS.put(Key.key(name), tag);
private static Tag<Block> create(String name) {
return new Tag<>(JavaRegistries.BLOCK, MinecraftKey.key(name));
}
}

View file

@ -25,67 +25,55 @@
package org.geysermc.geyser.session.cache.tags;
import net.kyori.adventure.key.Key;
import org.geysermc.geyser.item.enchantment.Enchantment;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.geyser.util.Ordered;
import java.util.HashMap;
import java.util.Map;
/**
* Lists vanilla enchantment tags.
*/
@SuppressWarnings("unused")
public final class EnchantmentTag implements Ordered {
public static final Map<Key, EnchantmentTag> ALL_ENCHANTMENT_TAGS = new HashMap<>();
public final class EnchantmentTag {
public static final Tag<Enchantment> TOOLTIP_ORDER = create("tooltip_order");
public static final Tag<Enchantment> EXCLUSIVE_SET_ARMOR = create("exclusive_set/armor");
public static final Tag<Enchantment> EXCLUSIVE_SET_BOOTS = create("exclusive_set/boots");
public static final Tag<Enchantment> EXCLUSIVE_SET_BOW = create("exclusive_set/bow");
public static final Tag<Enchantment> EXCLUSIVE_SET_CROSSBOW = create("exclusive_set/crossbow");
public static final Tag<Enchantment> EXCLUSIVE_SET_DAMAGE = create("exclusive_set/damage");
public static final Tag<Enchantment> EXCLUSIVE_SET_MINING = create("exclusive_set/mining");
public static final Tag<Enchantment> EXCLUSIVE_SET_RIPTIDE = create("exclusive_set/riptide");
public static final Tag<Enchantment> TRADEABLE = create("tradeable");
public static final Tag<Enchantment> DOUBLE_TRADE_PRICE = create("double_trade_price");
public static final Tag<Enchantment> IN_ENCHANTING_TABLE = create("in_enchanting_table");
public static final Tag<Enchantment> ON_MOB_SPAWN_EQUIPMENT = create("on_mob_spawn_equipment");
public static final Tag<Enchantment> ON_TRADED_EQUIPMENT = create("on_traded_equipment");
public static final Tag<Enchantment> ON_RANDOM_LOOT = create("on_random_loot");
public static final Tag<Enchantment> CURSE = create("curse");
public static final Tag<Enchantment> SMELTS_LOOT = create("smelts_loot");
public static final Tag<Enchantment> PREVENTS_BEE_SPAWNS_WHEN_MINING = create("prevents_bee_spawns_when_mining");
public static final Tag<Enchantment> PREVENTS_DECORATED_POT_SHATTERING = create("prevents_decorated_pot_shattering");
public static final Tag<Enchantment> PREVENTS_ICE_MELTING = create("prevents_ice_melting");
public static final Tag<Enchantment> PREVENTS_INFESTED_SPAWNS = create("prevents_infested_spawns");
public static final Tag<Enchantment> TREASURE = create("treasure");
public static final Tag<Enchantment> NON_TREASURE = create("non_treasure");
public static final Tag<Enchantment> TRADES_DESERT_COMMON = create("trades/desert_common");
public static final Tag<Enchantment> TRADES_JUNGLE_COMMON = create("trades/jungle_common");
public static final Tag<Enchantment> TRADES_PLAINS_COMMON = create("trades/plains_common");
public static final Tag<Enchantment> TRADES_SAVANNA_COMMON = create("trades/savanna_common");
public static final Tag<Enchantment> TRADES_SNOW_COMMON = create("trades/snow_common");
public static final Tag<Enchantment> TRADES_SWAMP_COMMON = create("trades/swamp_common");
public static final Tag<Enchantment> TRADES_TAIGA_COMMON = create("trades/taiga_common");
public static final Tag<Enchantment> TRADES_DESERT_SPECIAL = create("trades/desert_special");
public static final Tag<Enchantment> TRADES_JUNGLE_SPECIAL = create("trades/jungle_special");
public static final Tag<Enchantment> TRADES_PLAINS_SPECIAL = create("trades/plains_special");
public static final Tag<Enchantment> TRADES_SAVANNA_SPECIAL = create("trades/savanna_special");
public static final Tag<Enchantment> TRADES_SNOW_SPECIAL = create("trades/snow_special");
public static final Tag<Enchantment> TRADES_SWAMP_SPECIAL = create("trades/swamp_special");
public static final Tag<Enchantment> TRADES_TAIGA_SPECIAL = create("trades/taiga_special");
public static final EnchantmentTag TOOLTIP_ORDER = new EnchantmentTag("tooltip_order");
public static final EnchantmentTag EXCLUSIVE_SET_ARMOR = new EnchantmentTag("exclusive_set/armor");
public static final EnchantmentTag EXCLUSIVE_SET_BOOTS = new EnchantmentTag("exclusive_set/boots");
public static final EnchantmentTag EXCLUSIVE_SET_BOW = new EnchantmentTag("exclusive_set/bow");
public static final EnchantmentTag EXCLUSIVE_SET_CROSSBOW = new EnchantmentTag("exclusive_set/crossbow");
public static final EnchantmentTag EXCLUSIVE_SET_DAMAGE = new EnchantmentTag("exclusive_set/damage");
public static final EnchantmentTag EXCLUSIVE_SET_MINING = new EnchantmentTag("exclusive_set/mining");
public static final EnchantmentTag EXCLUSIVE_SET_RIPTIDE = new EnchantmentTag("exclusive_set/riptide");
public static final EnchantmentTag TRADEABLE = new EnchantmentTag("tradeable");
public static final EnchantmentTag DOUBLE_TRADE_PRICE = new EnchantmentTag("double_trade_price");
public static final EnchantmentTag IN_ENCHANTING_TABLE = new EnchantmentTag("in_enchanting_table");
public static final EnchantmentTag ON_MOB_SPAWN_EQUIPMENT = new EnchantmentTag("on_mob_spawn_equipment");
public static final EnchantmentTag ON_TRADED_EQUIPMENT = new EnchantmentTag("on_traded_equipment");
public static final EnchantmentTag ON_RANDOM_LOOT = new EnchantmentTag("on_random_loot");
public static final EnchantmentTag CURSE = new EnchantmentTag("curse");
public static final EnchantmentTag SMELTS_LOOT = new EnchantmentTag("smelts_loot");
public static final EnchantmentTag PREVENTS_BEE_SPAWNS_WHEN_MINING = new EnchantmentTag("prevents_bee_spawns_when_mining");
public static final EnchantmentTag PREVENTS_DECORATED_POT_SHATTERING = new EnchantmentTag("prevents_decorated_pot_shattering");
public static final EnchantmentTag PREVENTS_ICE_MELTING = new EnchantmentTag("prevents_ice_melting");
public static final EnchantmentTag PREVENTS_INFESTED_SPAWNS = new EnchantmentTag("prevents_infested_spawns");
public static final EnchantmentTag TREASURE = new EnchantmentTag("treasure");
public static final EnchantmentTag NON_TREASURE = new EnchantmentTag("non_treasure");
public static final EnchantmentTag TRADES_DESERT_COMMON = new EnchantmentTag("trades/desert_common");
public static final EnchantmentTag TRADES_JUNGLE_COMMON = new EnchantmentTag("trades/jungle_common");
public static final EnchantmentTag TRADES_PLAINS_COMMON = new EnchantmentTag("trades/plains_common");
public static final EnchantmentTag TRADES_SAVANNA_COMMON = new EnchantmentTag("trades/savanna_common");
public static final EnchantmentTag TRADES_SNOW_COMMON = new EnchantmentTag("trades/snow_common");
public static final EnchantmentTag TRADES_SWAMP_COMMON = new EnchantmentTag("trades/swamp_common");
public static final EnchantmentTag TRADES_TAIGA_COMMON = new EnchantmentTag("trades/taiga_common");
public static final EnchantmentTag TRADES_DESERT_SPECIAL = new EnchantmentTag("trades/desert_special");
public static final EnchantmentTag TRADES_JUNGLE_SPECIAL = new EnchantmentTag("trades/jungle_special");
public static final EnchantmentTag TRADES_PLAINS_SPECIAL = new EnchantmentTag("trades/plains_special");
public static final EnchantmentTag TRADES_SAVANNA_SPECIAL = new EnchantmentTag("trades/savanna_special");
public static final EnchantmentTag TRADES_SNOW_SPECIAL = new EnchantmentTag("trades/snow_special");
public static final EnchantmentTag TRADES_SWAMP_SPECIAL = new EnchantmentTag("trades/swamp_special");
public static final EnchantmentTag TRADES_TAIGA_SPECIAL = new EnchantmentTag("trades/taiga_special");
private EnchantmentTag() {}
private final int id;
private EnchantmentTag(String identifier) {
this.id = ALL_ENCHANTMENT_TAGS.size();
register(identifier, this);
}
@Override
public int ordinal() {
return id;
}
private static void register(String name, EnchantmentTag tag) {
ALL_ENCHANTMENT_TAGS.put(MinecraftKey.key(name), tag);
private static Tag<Enchantment> create(String name) {
return new Tag<>(JavaRegistries.ENCHANTMENT, MinecraftKey.key(name));
}
}

View file

@ -0,0 +1,118 @@
/*
* 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.session.cache.tags;
import java.util.List;
import java.util.Objects;
import java.util.function.ToIntFunction;
import it.unimi.dsi.fastutil.ints.IntArrays;
import lombok.Data;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.TagCache;
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
/**
* Similar to vanilla Minecraft's HolderSets, stores either a tag or a list of IDs (this list can also be represented as a single ID in vanilla HolderSets).
*
* <p>Because HolderSets utilise tags, when loading a HolderSet, Geyser must store tags for the registry the HolderSet is for (see {@link JavaRegistryKey}).</p>
*
* <p>Use the {@link GeyserHolderSet#readHolderSet} method to easily read a HolderSet from NBT sent by a server. To turn the HolderSet into a list of network IDs, use the {@link GeyserHolderSet#resolveRaw} method.
* To turn the HolderSet into a list of objects, use the {@link GeyserHolderSet#resolve} method.</p>
*/
@Data
public final class GeyserHolderSet<T> {
private final JavaRegistryKey<T> registry;
private final @Nullable Tag<T> tag;
private final int @Nullable [] holders;
public GeyserHolderSet(JavaRegistryKey<T> registry, int @NonNull [] holders) {
this.registry = registry;
this.tag = null;
this.holders = holders;
}
public GeyserHolderSet(JavaRegistryKey<T> registry, @NonNull Tag<T> tagId) {
this.registry = registry;
this.tag = tagId;
this.holders = null;
}
/**
* Resolves the HolderSet, and automatically maps the network IDs to their respective object types. If the HolderSet is a list of IDs, this will be returned. If it is a tag, the tag will be resolved from the tag cache.
*
* @return the HolderSet turned into a list of objects.
*/
public List<T> resolve(GeyserSession session) {
return TagCache.mapRawArray(session, resolveRaw(session.getTagCache()), registry);
}
/**
* Resolves the HolderSet. If the HolderSet is a list of IDs, this will be returned. If it is a tag, the tag will be resolved from the tag cache.
*
* @return the HolderSet turned into a list of objects.
*/
public int[] resolveRaw(TagCache tagCache) {
if (holders != null) {
return holders;
}
return tagCache.getRaw(Objects.requireNonNull(tag, "HolderSet must have a tag if it doesn't have a list of IDs"));
}
/**
* Reads a HolderSet from an object from NBT.
*
* @param session session, only used for logging purposes.
* @param registry the registry the HolderSet contains IDs from.
* @param holderSet the HolderSet as an object from NBT.
* @param keyIdMapping a function that maps resource location IDs in the HolderSet's registry to their network IDs.
*/
public static <T> GeyserHolderSet<T> readHolderSet(GeyserSession session, JavaRegistryKey<T> registry, @Nullable Object holderSet, ToIntFunction<Key> keyIdMapping) {
if (holderSet == null) {
return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY);
}
if (holderSet instanceof String stringTag) {
if (stringTag.startsWith("#")) {
// Tag
return new GeyserHolderSet<>(registry, new Tag<>(registry, Key.key(stringTag.substring(1)))); // Remove '#' at beginning that indicates tag
} else if (stringTag.isEmpty()) {
return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY);
}
return new GeyserHolderSet<>(registry, new int[]{keyIdMapping.applyAsInt(Key.key(stringTag))});
} else if (holderSet instanceof List<?> list) {
// Assume the list is a list of strings
return new GeyserHolderSet<>(registry, list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray());
}
session.getGeyser().getLogger().warning("Failed parsing HolderSet for registry + " + registry + "! Expected either a tag, a string ID or a list of string IDs, found " + holderSet);
return new GeyserHolderSet<>(registry, IntArrays.EMPTY_ARRAY);
}
}

View file

@ -25,178 +25,166 @@
package org.geysermc.geyser.session.cache.tags;
import net.kyori.adventure.key.Key;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.geyser.util.Ordered;
import java.util.HashMap;
import java.util.Map;
/**
* Lists vanilla item tags.
*/
@SuppressWarnings("unused")
public final class ItemTag implements Ordered {
public static final Map<Key, ItemTag> ALL_ITEM_TAGS = new HashMap<>();
public final class ItemTag {
public static final Tag<Item> WOOL = create("wool");
public static final Tag<Item> PLANKS = create("planks");
public static final Tag<Item> STONE_BRICKS = create("stone_bricks");
public static final Tag<Item> WOODEN_BUTTONS = create("wooden_buttons");
public static final Tag<Item> STONE_BUTTONS = create("stone_buttons");
public static final Tag<Item> BUTTONS = create("buttons");
public static final Tag<Item> WOOL_CARPETS = create("wool_carpets");
public static final Tag<Item> WOODEN_DOORS = create("wooden_doors");
public static final Tag<Item> WOODEN_STAIRS = create("wooden_stairs");
public static final Tag<Item> WOODEN_SLABS = create("wooden_slabs");
public static final Tag<Item> WOODEN_FENCES = create("wooden_fences");
public static final Tag<Item> FENCE_GATES = create("fence_gates");
public static final Tag<Item> WOODEN_PRESSURE_PLATES = create("wooden_pressure_plates");
public static final Tag<Item> WOODEN_TRAPDOORS = create("wooden_trapdoors");
public static final Tag<Item> DOORS = create("doors");
public static final Tag<Item> SAPLINGS = create("saplings");
public static final Tag<Item> LOGS_THAT_BURN = create("logs_that_burn");
public static final Tag<Item> LOGS = create("logs");
public static final Tag<Item> DARK_OAK_LOGS = create("dark_oak_logs");
public static final Tag<Item> OAK_LOGS = create("oak_logs");
public static final Tag<Item> BIRCH_LOGS = create("birch_logs");
public static final Tag<Item> ACACIA_LOGS = create("acacia_logs");
public static final Tag<Item> CHERRY_LOGS = create("cherry_logs");
public static final Tag<Item> JUNGLE_LOGS = create("jungle_logs");
public static final Tag<Item> SPRUCE_LOGS = create("spruce_logs");
public static final Tag<Item> MANGROVE_LOGS = create("mangrove_logs");
public static final Tag<Item> CRIMSON_STEMS = create("crimson_stems");
public static final Tag<Item> WARPED_STEMS = create("warped_stems");
public static final Tag<Item> BAMBOO_BLOCKS = create("bamboo_blocks");
public static final Tag<Item> WART_BLOCKS = create("wart_blocks");
public static final Tag<Item> BANNERS = create("banners");
public static final Tag<Item> SAND = create("sand");
public static final Tag<Item> SMELTS_TO_GLASS = create("smelts_to_glass");
public static final Tag<Item> STAIRS = create("stairs");
public static final Tag<Item> SLABS = create("slabs");
public static final Tag<Item> WALLS = create("walls");
public static final Tag<Item> ANVIL = create("anvil");
public static final Tag<Item> RAILS = create("rails");
public static final Tag<Item> LEAVES = create("leaves");
public static final Tag<Item> TRAPDOORS = create("trapdoors");
public static final Tag<Item> SMALL_FLOWERS = create("small_flowers");
public static final Tag<Item> BEDS = create("beds");
public static final Tag<Item> FENCES = create("fences");
public static final Tag<Item> TALL_FLOWERS = create("tall_flowers");
public static final Tag<Item> FLOWERS = create("flowers");
public static final Tag<Item> PIGLIN_REPELLENTS = create("piglin_repellents");
public static final Tag<Item> PIGLIN_LOVED = create("piglin_loved");
public static final Tag<Item> IGNORED_BY_PIGLIN_BABIES = create("ignored_by_piglin_babies");
public static final Tag<Item> MEAT = create("meat");
public static final Tag<Item> SNIFFER_FOOD = create("sniffer_food");
public static final Tag<Item> PIGLIN_FOOD = create("piglin_food");
public static final Tag<Item> FOX_FOOD = create("fox_food");
public static final Tag<Item> COW_FOOD = create("cow_food");
public static final Tag<Item> GOAT_FOOD = create("goat_food");
public static final Tag<Item> SHEEP_FOOD = create("sheep_food");
public static final Tag<Item> WOLF_FOOD = create("wolf_food");
public static final Tag<Item> CAT_FOOD = create("cat_food");
public static final Tag<Item> HORSE_FOOD = create("horse_food");
public static final Tag<Item> HORSE_TEMPT_ITEMS = create("horse_tempt_items");
public static final Tag<Item> CAMEL_FOOD = create("camel_food");
public static final Tag<Item> ARMADILLO_FOOD = create("armadillo_food");
public static final Tag<Item> BEE_FOOD = create("bee_food");
public static final Tag<Item> CHICKEN_FOOD = create("chicken_food");
public static final Tag<Item> FROG_FOOD = create("frog_food");
public static final Tag<Item> HOGLIN_FOOD = create("hoglin_food");
public static final Tag<Item> LLAMA_FOOD = create("llama_food");
public static final Tag<Item> LLAMA_TEMPT_ITEMS = create("llama_tempt_items");
public static final Tag<Item> OCELOT_FOOD = create("ocelot_food");
public static final Tag<Item> PANDA_FOOD = create("panda_food");
public static final Tag<Item> PIG_FOOD = create("pig_food");
public static final Tag<Item> RABBIT_FOOD = create("rabbit_food");
public static final Tag<Item> STRIDER_FOOD = create("strider_food");
public static final Tag<Item> STRIDER_TEMPT_ITEMS = create("strider_tempt_items");
public static final Tag<Item> TURTLE_FOOD = create("turtle_food");
public static final Tag<Item> PARROT_FOOD = create("parrot_food");
public static final Tag<Item> PARROT_POISONOUS_FOOD = create("parrot_poisonous_food");
public static final Tag<Item> AXOLOTL_FOOD = create("axolotl_food");
public static final Tag<Item> GOLD_ORES = create("gold_ores");
public static final Tag<Item> IRON_ORES = create("iron_ores");
public static final Tag<Item> DIAMOND_ORES = create("diamond_ores");
public static final Tag<Item> REDSTONE_ORES = create("redstone_ores");
public static final Tag<Item> LAPIS_ORES = create("lapis_ores");
public static final Tag<Item> COAL_ORES = create("coal_ores");
public static final Tag<Item> EMERALD_ORES = create("emerald_ores");
public static final Tag<Item> COPPER_ORES = create("copper_ores");
public static final Tag<Item> NON_FLAMMABLE_WOOD = create("non_flammable_wood");
public static final Tag<Item> SOUL_FIRE_BASE_BLOCKS = create("soul_fire_base_blocks");
public static final Tag<Item> CANDLES = create("candles");
public static final Tag<Item> DIRT = create("dirt");
public static final Tag<Item> TERRACOTTA = create("terracotta");
public static final Tag<Item> COMPLETES_FIND_TREE_TUTORIAL = create("completes_find_tree_tutorial");
public static final Tag<Item> BOATS = create("boats");
public static final Tag<Item> CHEST_BOATS = create("chest_boats");
public static final Tag<Item> FISHES = create("fishes");
public static final Tag<Item> SIGNS = create("signs");
public static final Tag<Item> CREEPER_DROP_MUSIC_DISCS = create("creeper_drop_music_discs");
public static final Tag<Item> COALS = create("coals");
public static final Tag<Item> ARROWS = create("arrows");
public static final Tag<Item> LECTERN_BOOKS = create("lectern_books");
public static final Tag<Item> BOOKSHELF_BOOKS = create("bookshelf_books");
public static final Tag<Item> BEACON_PAYMENT_ITEMS = create("beacon_payment_items");
public static final Tag<Item> STONE_TOOL_MATERIALS = create("stone_tool_materials");
public static final Tag<Item> STONE_CRAFTING_MATERIALS = create("stone_crafting_materials");
public static final Tag<Item> FREEZE_IMMUNE_WEARABLES = create("freeze_immune_wearables");
public static final Tag<Item> DAMPENS_VIBRATIONS = create("dampens_vibrations");
public static final Tag<Item> CLUSTER_MAX_HARVESTABLES = create("cluster_max_harvestables");
public static final Tag<Item> COMPASSES = create("compasses");
public static final Tag<Item> HANGING_SIGNS = create("hanging_signs");
public static final Tag<Item> CREEPER_IGNITERS = create("creeper_igniters");
public static final Tag<Item> NOTEBLOCK_TOP_INSTRUMENTS = create("noteblock_top_instruments");
public static final Tag<Item> FOOT_ARMOR = create("foot_armor");
public static final Tag<Item> LEG_ARMOR = create("leg_armor");
public static final Tag<Item> CHEST_ARMOR = create("chest_armor");
public static final Tag<Item> HEAD_ARMOR = create("head_armor");
public static final Tag<Item> SKULLS = create("skulls");
public static final Tag<Item> TRIMMABLE_ARMOR = create("trimmable_armor");
public static final Tag<Item> TRIM_MATERIALS = create("trim_materials");
public static final Tag<Item> TRIM_TEMPLATES = create("trim_templates");
public static final Tag<Item> DECORATED_POT_SHERDS = create("decorated_pot_sherds");
public static final Tag<Item> DECORATED_POT_INGREDIENTS = create("decorated_pot_ingredients");
public static final Tag<Item> SWORDS = create("swords");
public static final Tag<Item> AXES = create("axes");
public static final Tag<Item> HOES = create("hoes");
public static final Tag<Item> PICKAXES = create("pickaxes");
public static final Tag<Item> SHOVELS = create("shovels");
public static final Tag<Item> BREAKS_DECORATED_POTS = create("breaks_decorated_pots");
public static final Tag<Item> VILLAGER_PLANTABLE_SEEDS = create("villager_plantable_seeds");
public static final Tag<Item> DYEABLE = create("dyeable");
public static final Tag<Item> ENCHANTABLE_FOOT_ARMOR = create("enchantable/foot_armor");
public static final Tag<Item> ENCHANTABLE_LEG_ARMOR = create("enchantable/leg_armor");
public static final Tag<Item> ENCHANTABLE_CHEST_ARMOR = create("enchantable/chest_armor");
public static final Tag<Item> ENCHANTABLE_HEAD_ARMOR = create("enchantable/head_armor");
public static final Tag<Item> ENCHANTABLE_ARMOR = create("enchantable/armor");
public static final Tag<Item> ENCHANTABLE_SWORD = create("enchantable/sword");
public static final Tag<Item> ENCHANTABLE_FIRE_ASPECT = create("enchantable/fire_aspect");
public static final Tag<Item> ENCHANTABLE_SHARP_WEAPON = create("enchantable/sharp_weapon");
public static final Tag<Item> ENCHANTABLE_WEAPON = create("enchantable/weapon");
public static final Tag<Item> ENCHANTABLE_MINING = create("enchantable/mining");
public static final Tag<Item> ENCHANTABLE_MINING_LOOT = create("enchantable/mining_loot");
public static final Tag<Item> ENCHANTABLE_FISHING = create("enchantable/fishing");
public static final Tag<Item> ENCHANTABLE_TRIDENT = create("enchantable/trident");
public static final Tag<Item> ENCHANTABLE_DURABILITY = create("enchantable/durability");
public static final Tag<Item> ENCHANTABLE_BOW = create("enchantable/bow");
public static final Tag<Item> ENCHANTABLE_EQUIPPABLE = create("enchantable/equippable");
public static final Tag<Item> ENCHANTABLE_CROSSBOW = create("enchantable/crossbow");
public static final Tag<Item> ENCHANTABLE_VANISHING = create("enchantable/vanishing");
public static final Tag<Item> ENCHANTABLE_MACE = create("enchantable/mace");
public static final ItemTag WOOL = new ItemTag("wool");
public static final ItemTag PLANKS = new ItemTag("planks");
public static final ItemTag STONE_BRICKS = new ItemTag("stone_bricks");
public static final ItemTag WOODEN_BUTTONS = new ItemTag("wooden_buttons");
public static final ItemTag STONE_BUTTONS = new ItemTag("stone_buttons");
public static final ItemTag BUTTONS = new ItemTag("buttons");
public static final ItemTag WOOL_CARPETS = new ItemTag("wool_carpets");
public static final ItemTag WOODEN_DOORS = new ItemTag("wooden_doors");
public static final ItemTag WOODEN_STAIRS = new ItemTag("wooden_stairs");
public static final ItemTag WOODEN_SLABS = new ItemTag("wooden_slabs");
public static final ItemTag WOODEN_FENCES = new ItemTag("wooden_fences");
public static final ItemTag FENCE_GATES = new ItemTag("fence_gates");
public static final ItemTag WOODEN_PRESSURE_PLATES = new ItemTag("wooden_pressure_plates");
public static final ItemTag WOODEN_TRAPDOORS = new ItemTag("wooden_trapdoors");
public static final ItemTag DOORS = new ItemTag("doors");
public static final ItemTag SAPLINGS = new ItemTag("saplings");
public static final ItemTag LOGS_THAT_BURN = new ItemTag("logs_that_burn");
public static final ItemTag LOGS = new ItemTag("logs");
public static final ItemTag DARK_OAK_LOGS = new ItemTag("dark_oak_logs");
public static final ItemTag OAK_LOGS = new ItemTag("oak_logs");
public static final ItemTag BIRCH_LOGS = new ItemTag("birch_logs");
public static final ItemTag ACACIA_LOGS = new ItemTag("acacia_logs");
public static final ItemTag CHERRY_LOGS = new ItemTag("cherry_logs");
public static final ItemTag JUNGLE_LOGS = new ItemTag("jungle_logs");
public static final ItemTag SPRUCE_LOGS = new ItemTag("spruce_logs");
public static final ItemTag MANGROVE_LOGS = new ItemTag("mangrove_logs");
public static final ItemTag CRIMSON_STEMS = new ItemTag("crimson_stems");
public static final ItemTag WARPED_STEMS = new ItemTag("warped_stems");
public static final ItemTag BAMBOO_BLOCKS = new ItemTag("bamboo_blocks");
public static final ItemTag WART_BLOCKS = new ItemTag("wart_blocks");
public static final ItemTag BANNERS = new ItemTag("banners");
public static final ItemTag SAND = new ItemTag("sand");
public static final ItemTag SMELTS_TO_GLASS = new ItemTag("smelts_to_glass");
public static final ItemTag STAIRS = new ItemTag("stairs");
public static final ItemTag SLABS = new ItemTag("slabs");
public static final ItemTag WALLS = new ItemTag("walls");
public static final ItemTag ANVIL = new ItemTag("anvil");
public static final ItemTag RAILS = new ItemTag("rails");
public static final ItemTag LEAVES = new ItemTag("leaves");
public static final ItemTag TRAPDOORS = new ItemTag("trapdoors");
public static final ItemTag SMALL_FLOWERS = new ItemTag("small_flowers");
public static final ItemTag BEDS = new ItemTag("beds");
public static final ItemTag FENCES = new ItemTag("fences");
public static final ItemTag TALL_FLOWERS = new ItemTag("tall_flowers");
public static final ItemTag FLOWERS = new ItemTag("flowers");
public static final ItemTag PIGLIN_REPELLENTS = new ItemTag("piglin_repellents");
public static final ItemTag PIGLIN_LOVED = new ItemTag("piglin_loved");
public static final ItemTag IGNORED_BY_PIGLIN_BABIES = new ItemTag("ignored_by_piglin_babies");
public static final ItemTag MEAT = new ItemTag("meat");
public static final ItemTag SNIFFER_FOOD = new ItemTag("sniffer_food");
public static final ItemTag PIGLIN_FOOD = new ItemTag("piglin_food");
public static final ItemTag FOX_FOOD = new ItemTag("fox_food");
public static final ItemTag COW_FOOD = new ItemTag("cow_food");
public static final ItemTag GOAT_FOOD = new ItemTag("goat_food");
public static final ItemTag SHEEP_FOOD = new ItemTag("sheep_food");
public static final ItemTag WOLF_FOOD = new ItemTag("wolf_food");
public static final ItemTag CAT_FOOD = new ItemTag("cat_food");
public static final ItemTag HORSE_FOOD = new ItemTag("horse_food");
public static final ItemTag HORSE_TEMPT_ITEMS = new ItemTag("horse_tempt_items");
public static final ItemTag CAMEL_FOOD = new ItemTag("camel_food");
public static final ItemTag ARMADILLO_FOOD = new ItemTag("armadillo_food");
public static final ItemTag BEE_FOOD = new ItemTag("bee_food");
public static final ItemTag CHICKEN_FOOD = new ItemTag("chicken_food");
public static final ItemTag FROG_FOOD = new ItemTag("frog_food");
public static final ItemTag HOGLIN_FOOD = new ItemTag("hoglin_food");
public static final ItemTag LLAMA_FOOD = new ItemTag("llama_food");
public static final ItemTag LLAMA_TEMPT_ITEMS = new ItemTag("llama_tempt_items");
public static final ItemTag OCELOT_FOOD = new ItemTag("ocelot_food");
public static final ItemTag PANDA_FOOD = new ItemTag("panda_food");
public static final ItemTag PIG_FOOD = new ItemTag("pig_food");
public static final ItemTag RABBIT_FOOD = new ItemTag("rabbit_food");
public static final ItemTag STRIDER_FOOD = new ItemTag("strider_food");
public static final ItemTag STRIDER_TEMPT_ITEMS = new ItemTag("strider_tempt_items");
public static final ItemTag TURTLE_FOOD = new ItemTag("turtle_food");
public static final ItemTag PARROT_FOOD = new ItemTag("parrot_food");
public static final ItemTag PARROT_POISONOUS_FOOD = new ItemTag("parrot_poisonous_food");
public static final ItemTag AXOLOTL_FOOD = new ItemTag("axolotl_food");
public static final ItemTag GOLD_ORES = new ItemTag("gold_ores");
public static final ItemTag IRON_ORES = new ItemTag("iron_ores");
public static final ItemTag DIAMOND_ORES = new ItemTag("diamond_ores");
public static final ItemTag REDSTONE_ORES = new ItemTag("redstone_ores");
public static final ItemTag LAPIS_ORES = new ItemTag("lapis_ores");
public static final ItemTag COAL_ORES = new ItemTag("coal_ores");
public static final ItemTag EMERALD_ORES = new ItemTag("emerald_ores");
public static final ItemTag COPPER_ORES = new ItemTag("copper_ores");
public static final ItemTag NON_FLAMMABLE_WOOD = new ItemTag("non_flammable_wood");
public static final ItemTag SOUL_FIRE_BASE_BLOCKS = new ItemTag("soul_fire_base_blocks");
public static final ItemTag CANDLES = new ItemTag("candles");
public static final ItemTag DIRT = new ItemTag("dirt");
public static final ItemTag TERRACOTTA = new ItemTag("terracotta");
public static final ItemTag COMPLETES_FIND_TREE_TUTORIAL = new ItemTag("completes_find_tree_tutorial");
public static final ItemTag BOATS = new ItemTag("boats");
public static final ItemTag CHEST_BOATS = new ItemTag("chest_boats");
public static final ItemTag FISHES = new ItemTag("fishes");
public static final ItemTag SIGNS = new ItemTag("signs");
public static final ItemTag CREEPER_DROP_MUSIC_DISCS = new ItemTag("creeper_drop_music_discs");
public static final ItemTag COALS = new ItemTag("coals");
public static final ItemTag ARROWS = new ItemTag("arrows");
public static final ItemTag LECTERN_BOOKS = new ItemTag("lectern_books");
public static final ItemTag BOOKSHELF_BOOKS = new ItemTag("bookshelf_books");
public static final ItemTag BEACON_PAYMENT_ITEMS = new ItemTag("beacon_payment_items");
public static final ItemTag STONE_TOOL_MATERIALS = new ItemTag("stone_tool_materials");
public static final ItemTag STONE_CRAFTING_MATERIALS = new ItemTag("stone_crafting_materials");
public static final ItemTag FREEZE_IMMUNE_WEARABLES = new ItemTag("freeze_immune_wearables");
public static final ItemTag DAMPENS_VIBRATIONS = new ItemTag("dampens_vibrations");
public static final ItemTag CLUSTER_MAX_HARVESTABLES = new ItemTag("cluster_max_harvestables");
public static final ItemTag COMPASSES = new ItemTag("compasses");
public static final ItemTag HANGING_SIGNS = new ItemTag("hanging_signs");
public static final ItemTag CREEPER_IGNITERS = new ItemTag("creeper_igniters");
public static final ItemTag NOTEBLOCK_TOP_INSTRUMENTS = new ItemTag("noteblock_top_instruments");
public static final ItemTag FOOT_ARMOR = new ItemTag("foot_armor");
public static final ItemTag LEG_ARMOR = new ItemTag("leg_armor");
public static final ItemTag CHEST_ARMOR = new ItemTag("chest_armor");
public static final ItemTag HEAD_ARMOR = new ItemTag("head_armor");
public static final ItemTag SKULLS = new ItemTag("skulls");
public static final ItemTag TRIMMABLE_ARMOR = new ItemTag("trimmable_armor");
public static final ItemTag TRIM_MATERIALS = new ItemTag("trim_materials");
public static final ItemTag TRIM_TEMPLATES = new ItemTag("trim_templates");
public static final ItemTag DECORATED_POT_SHERDS = new ItemTag("decorated_pot_sherds");
public static final ItemTag DECORATED_POT_INGREDIENTS = new ItemTag("decorated_pot_ingredients");
public static final ItemTag SWORDS = new ItemTag("swords");
public static final ItemTag AXES = new ItemTag("axes");
public static final ItemTag HOES = new ItemTag("hoes");
public static final ItemTag PICKAXES = new ItemTag("pickaxes");
public static final ItemTag SHOVELS = new ItemTag("shovels");
public static final ItemTag BREAKS_DECORATED_POTS = new ItemTag("breaks_decorated_pots");
public static final ItemTag VILLAGER_PLANTABLE_SEEDS = new ItemTag("villager_plantable_seeds");
public static final ItemTag DYEABLE = new ItemTag("dyeable");
public static final ItemTag ENCHANTABLE_FOOT_ARMOR = new ItemTag("enchantable/foot_armor");
public static final ItemTag ENCHANTABLE_LEG_ARMOR = new ItemTag("enchantable/leg_armor");
public static final ItemTag ENCHANTABLE_CHEST_ARMOR = new ItemTag("enchantable/chest_armor");
public static final ItemTag ENCHANTABLE_HEAD_ARMOR = new ItemTag("enchantable/head_armor");
public static final ItemTag ENCHANTABLE_ARMOR = new ItemTag("enchantable/armor");
public static final ItemTag ENCHANTABLE_SWORD = new ItemTag("enchantable/sword");
public static final ItemTag ENCHANTABLE_FIRE_ASPECT = new ItemTag("enchantable/fire_aspect");
public static final ItemTag ENCHANTABLE_SHARP_WEAPON = new ItemTag("enchantable/sharp_weapon");
public static final ItemTag ENCHANTABLE_WEAPON = new ItemTag("enchantable/weapon");
public static final ItemTag ENCHANTABLE_MINING = new ItemTag("enchantable/mining");
public static final ItemTag ENCHANTABLE_MINING_LOOT = new ItemTag("enchantable/mining_loot");
public static final ItemTag ENCHANTABLE_FISHING = new ItemTag("enchantable/fishing");
public static final ItemTag ENCHANTABLE_TRIDENT = new ItemTag("enchantable/trident");
public static final ItemTag ENCHANTABLE_DURABILITY = new ItemTag("enchantable/durability");
public static final ItemTag ENCHANTABLE_BOW = new ItemTag("enchantable/bow");
public static final ItemTag ENCHANTABLE_EQUIPPABLE = new ItemTag("enchantable/equippable");
public static final ItemTag ENCHANTABLE_CROSSBOW = new ItemTag("enchantable/crossbow");
public static final ItemTag ENCHANTABLE_VANISHING = new ItemTag("enchantable/vanishing");
public static final ItemTag ENCHANTABLE_MACE = new ItemTag("enchantable/mace");
private ItemTag() {}
private final int id;
private ItemTag(String identifier) {
this.id = ALL_ITEM_TAGS.size();
register(identifier, this);
}
@Override
public int ordinal() {
return id;
}
private static void register(String name, ItemTag tag) {
ALL_ITEM_TAGS.put(MinecraftKey.key(name), tag);
private static Tag<Item> create(String name) {
return new Tag<>(JavaRegistries.ITEM, MinecraftKey.key(name));
}
}

View file

@ -0,0 +1,35 @@
/*
* 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.session.cache.tags;
import net.kyori.adventure.key.Key;
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
/**
* A tag in any of the registries that tags are loaded for by Geyser.
*/
public record Tag<T>(JavaRegistryKey<T> registry, Key tag) {
}

View file

@ -25,21 +25,40 @@
package org.geysermc.geyser.translator.inventory;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import lombok.AllArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerSlotType;
import org.cloudburstmc.protocol.bedrock.data.inventory.FullContainerName;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequest;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.ItemStackRequestSlotData;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.*;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.AutoCraftRecipeAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ConsumeAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.CraftResultsDeprecatedAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.DropAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.SwapAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.TransferItemStackRequestAction;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseContainer;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseSlot;
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponseStatus;
import org.cloudburstmc.protocol.bedrock.packet.ItemStackResponsePacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.*;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.CartographyContainer;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.click.Click;
import org.geysermc.geyser.inventory.click.ClickPlan;
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
@ -56,12 +75,17 @@ import org.geysermc.geyser.translator.inventory.furnace.SmokerInventoryTranslato
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
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 org.geysermc.mcprotocollib.protocol.data.game.recipe.Ingredient;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.EmptySlotDisplay;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.SlotDisplay;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
public abstract class InventoryTranslator {
@ -642,8 +666,8 @@ public abstract class InventoryTranslator {
}
int gridDimensions = gridSize == 4 ? 2 : 3;
Ingredient[] ingredients = new Ingredient[0];
ItemStack output = null;
List<SlotDisplay> ingredients = Collections.emptyList();
SlotDisplay output = null;
int recipeWidth = 0;
int ingRemaining = 0;
int ingredientIndex = -1;
@ -697,7 +721,7 @@ public abstract class InventoryTranslator {
ingredients = shapelessRecipe.ingredients();
recipeWidth = gridDimensions;
output = shapelessRecipe.result();
if (ingredients.length > gridSize) {
if (ingredients.size() > gridSize) {
return rejectRequest(request);
}
}
@ -728,8 +752,8 @@ public abstract class InventoryTranslator {
craftState = CraftState.INGREDIENTS;
if (ingRemaining == 0) {
while (++ingredientIndex < ingredients.length) {
if (ingredients[ingredientIndex].getOptions().length != 0) {
while (++ingredientIndex < ingredients.size()) {
if (!(ingredients.get(ingredientIndex) instanceof EmptySlotDisplay)) { // TODO I guess can technically other options be empty?
ingRemaining = timesCrafted;
break;
}

View file

@ -33,6 +33,11 @@ import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.level.block.Blocks;
public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslator {
public static final int TEMPLATE = 0;
public static final int INPUT = 1;
public static final int MATERIAL = 2;
public static final int OUTPUT = 3;
public SmithingInventoryTranslator() {
super(4, Blocks.SMITHING_TABLE, ContainerType.SMITHING_TABLE, UIInventoryUpdater.INSTANCE);
}
@ -40,10 +45,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
@Override
public int bedrockSlotToJava(ItemStackRequestSlotData slotInfoData) {
return switch (slotInfoData.getContainer()) {
case SMITHING_TABLE_TEMPLATE -> 0;
case SMITHING_TABLE_INPUT -> 1;
case SMITHING_TABLE_MATERIAL -> 2;
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> 3;
case SMITHING_TABLE_TEMPLATE -> TEMPLATE;
case SMITHING_TABLE_INPUT -> INPUT;
case SMITHING_TABLE_MATERIAL -> MATERIAL;
case SMITHING_TABLE_RESULT, CREATED_OUTPUT -> OUTPUT;
default -> super.bedrockSlotToJava(slotInfoData);
};
}
@ -51,10 +56,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
@Override
public BedrockContainerSlot javaSlotToBedrockContainer(int slot) {
return switch (slot) {
case 0 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53);
case 1 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
case 2 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
case 3 -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
case TEMPLATE -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_TEMPLATE, 53);
case INPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_INPUT, 51);
case MATERIAL -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_MATERIAL, 52);
case OUTPUT -> new BedrockContainerSlot(ContainerSlotType.SMITHING_TABLE_RESULT, 50);
default -> super.javaSlotToBedrockContainer(slot);
};
}
@ -62,10 +67,10 @@ public class SmithingInventoryTranslator extends AbstractBlockInventoryTranslato
@Override
public int javaSlotToBedrock(int slot) {
return switch (slot) {
case 0 -> 53;
case 1 -> 51;
case 2 -> 52;
case 3 -> 50;
case TEMPLATE -> 53;
case INPUT -> 51;
case MATERIAL -> 52;
case OUTPUT -> 50;
default -> super.javaSlotToBedrock(slot);
};
}

View file

@ -108,7 +108,7 @@ public final class ItemTranslator {
ItemMapping bedrockItem = session.getItemMappings().getMapping(data);
Item javaItem = bedrockItem.getJavaItem();
GeyserItemStack itemStack = javaItem.translateToJava(data, bedrockItem, session.getItemMappings());
GeyserItemStack itemStack = javaItem.translateToJava(session, data, bedrockItem, session.getItemMappings());
NbtMap nbt = data.getTag();
if (nbt != null && !nbt.isEmpty()) {
@ -198,7 +198,7 @@ public final class ItemTranslator {
nbtMapBuilder.putIfAbsent("ench", NbtList.EMPTY);
}
ItemData.Builder builder = javaItem.translateToBedrock(count, components, bedrockItem, session.getItemMappings());
ItemData.Builder builder = javaItem.translateToBedrock(session, count, components, bedrockItem, session.getItemMappings());
// Finalize the Bedrock NBT
builder.tag(nbtBuilder.build());
if (bedrockItem.isBlock()) {

View file

@ -25,13 +25,12 @@
package org.geysermc.geyser.translator.protocol.bedrock;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPaddleBoatPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import java.util.concurrent.TimeUnit;
@ -45,45 +44,31 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
return;
}
switch (packet.getAction()) {
case SWING_ARM -> {
session.armSwingPending();
// Delay so entity damage can be processed first
session.scheduleInEventLoop(() -> {
if (session.getArmAnimationTicks() != 0) {
// So, generally, a Java player can only do one *thing* at a time.
// If a player right-clicks, for example, then there's probably only one action associated with
// that right-click that will send a swing.
// The only exception I can think of to this, *maybe*, is a player dropping items
// Bedrock is a little funkier than this - it can send several arm animation packets in the
// same tick, notably with high levels of haste applied.
// Packet limiters do not like this and can crash the player.
// If arm animation ticks is 0, then we just sent an arm swing packet this tick.
// See https://github.com/GeyserMC/Geyser/issues/2875
// This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5,
// and Bedrock 1.19.51.
// Note for the future: we should probably largely ignore this packet and instead replicate
// all actions on our end, and send swings where needed.
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
}
},
25,
TimeUnit.MILLISECONDS
);
}
// These two might need to be flipped, but my recommendation is getting moving working first
case ROW_LEFT -> {
// Packet value is a float of how long one has been rowing, so we convert that into a boolean
session.setSteeringLeft(packet.getRowingTime() > 0.0);
ServerboundPaddleBoatPacket steerLeftPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerLeftPacket);
}
case ROW_RIGHT -> {
session.setSteeringRight(packet.getRowingTime() > 0.0);
ServerboundPaddleBoatPacket steerRightPacket = new ServerboundPaddleBoatPacket(session.isSteeringLeft(), session.isSteeringRight());
session.sendDownstreamGamePacket(steerRightPacket);
}
if (packet.getAction() == AnimatePacket.Action.SWING_ARM) {
session.armSwingPending();
// Delay so entity damage can be processed first
session.scheduleInEventLoop(() -> {
if (session.getArmAnimationTicks() != 0) {
// So, generally, a Java player can only do one *thing* at a time.
// If a player right-clicks, for example, then there's probably only one action associated with
// that right-click that will send a swing.
// The only exception I can think of to this, *maybe*, is a player dropping items
// Bedrock is a little funkier than this - it can send several arm animation packets in the
// same tick, notably with high levels of haste applied.
// Packet limiters do not like this and can crash the player.
// If arm animation ticks is 0, then we just sent an arm swing packet this tick.
// See https://github.com/GeyserMC/Geyser/issues/2875
// This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5,
// and Bedrock 1.19.51.
// Note for the future: we should probably largely ignore this packet and instead replicate
// all actions on our end, and send swings where needed.
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
}
},
25,
TimeUnit.MILLISECONDS
);
}
}
}

View file

@ -51,29 +51,14 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator<EntityP
Entity entity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
if (entity == null) return;
if (entity instanceof BoatEntity boat) {
InventoryUtils.findOrCreateItem(session, boat.getPickItem());
return;
}
// Get the corresponding item
String itemName;
switch (entity.getDefinition().entityType()) {
case BOAT, CHEST_BOAT -> {
// Include type of boat in the name
int variant = ((BoatEntity) entity).getVariant();
String typeOfBoat = switch (variant) {
case 1 -> "spruce";
case 2 -> "birch";
case 3 -> "jungle";
case 4 -> "acacia";
case 5 -> "cherry";
case 6 -> "dark_oak";
case 7 -> "mangrove";
case 8 -> "bamboo";
default -> "oak";
};
itemName = typeOfBoat + "_" + entity.getDefinition().entityType().name().toLowerCase(Locale.ROOT);
// Bamboo boat is a raft
if (variant == 8) {
itemName = itemName.replace("boat", "raft");
}
}
case LEASH_KNOT -> itemName = "lead";
case CHEST_MINECART, COMMAND_BLOCK_MINECART, FURNACE_MINECART, HOPPER_MINECART, TNT_MINECART ->
// The Bedrock identifier matches the item name which moves MINECART to the end of the name

View file

@ -30,7 +30,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
@ -42,8 +41,8 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.InventoryTra
import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetItemSlotData;
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
@ -53,6 +52,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.click.Click;
import org.geysermc.geyser.inventory.item.GeyserInstrument;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.BlockItem;
import org.geysermc.geyser.item.type.BoatItem;
@ -77,6 +77,7 @@ import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.geyser.util.SoundUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
@ -187,7 +188,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
default -> false;
};
if (isGodBridging) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
}
@ -207,7 +208,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, belowBlockPos);
BlockDefinition extendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
if (extendedCollisionDefinition != null && (System.currentTimeMillis() - session.getLastInteractionTime()) < 200) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
}
@ -227,7 +228,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}
if (isIncorrectHeldItem(session, packet)) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
@ -247,7 +248,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
*/
// Blocks cannot be placed or destroyed outside of the world border
if (!session.getWorldBorder().isInsideBorderBoundaries()) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
@ -256,7 +257,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
if (!canInteractWithBlock(session, playerPosition, packetBlockPosition)) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
@ -270,7 +271,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
double clickDistanceY = clickPositionFullY - blockCenter.getY();
double clickDistanceZ = clickPositionFullZ - blockCenter.getZ();
if (!(Math.abs(clickDistanceX) < 1.0000001D && Math.abs(clickDistanceY) < 1.0000001D && Math.abs(clickDistanceZ) < 1.0000001D)) {
restoreCorrectBlock(session, blockPos, packet);
restoreCorrectBlock(session, blockPos);
return;
}
@ -296,6 +297,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
Hand.MAIN_HAND,
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
false,
false,
sequence);
session.sendDownstreamGamePacket(blockPacket);
@ -380,18 +382,28 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.setCurrentBook(packet.getItemInHand());
} else if (session.getPlayerInventory().getItemInHand().asItem() == Items.GOAT_HORN) {
// Temporary workaround while we don't have full item/block use tracking.
if (!session.getWorldCache().hasCooldown(Items.GOAT_HORN)) {
Holder<Instrument> instrument = session.getPlayerInventory()
if (!session.getWorldCache().hasCooldown(session.getPlayerInventory().getItemInHand())) {
Holder<Instrument> holder = session.getPlayerInventory()
.getItemInHand()
.getComponent(DataComponentType.INSTRUMENT);
if (instrument != null && instrument.isId()) {
// BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20)
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.id()));
soundPacket.setPosition(session.getPlayerEntity().getPosition());
soundPacket.setIdentifier("minecraft:player");
soundPacket.setExtraData(-1);
session.sendUpstreamPacket(soundPacket);
if (holder != null) {
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
if (instrument.bedrockInstrument() != null) {
// BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20)
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.bedrockInstrument().ordinal()));
soundPacket.setPosition(session.getPlayerEntity().getPosition());
soundPacket.setIdentifier("minecraft:player");
soundPacket.setExtraData(-1);
session.sendUpstreamPacket(soundPacket);
} else {
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
playSoundPacket.setPosition(session.getPlayerEntity().position());
playSoundPacket.setSound(SoundUtils.translatePlaySound(instrument.soundEvent()));
playSoundPacket.setPitch(1.0F);
playSoundPacket.setVolume(instrument.range() / 16.0F);
session.sendUpstreamPacket(playSoundPacket);
}
}
}
}
@ -423,53 +435,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}
}
}
case 2 -> {
int blockState = session.getGameMode() == GameMode.CREATIVE ?
session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition()) : session.getBreakingBlock();
session.setLastBlockPlaced(null);
session.setLastBlockPlacePosition(null);
// Same deal with vanilla block placing as above.
if (!session.getWorldBorder().isInsideBorderBoundaries()) {
restoreCorrectBlock(session, packet.getBlockPosition(), packet);
return;
}
Vector3f playerPosition = session.getPlayerEntity().getPosition();
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
if (!canInteractWithBlock(session, playerPosition, packet.getBlockPosition())) {
restoreCorrectBlock(session, packet.getBlockPosition(), packet);
return;
}
int sequence = session.getWorldCache().nextPredictionSequence();
session.getWorldCache().markPositionInSequence(packet.getBlockPosition());
// -1 means we don't know what block they're breaking
if (blockState == -1) {
blockState = Block.JAVA_AIR_ID;
}
LevelEventPacket blockBreakPacket = new LevelEventPacket();
blockBreakPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
session.sendUpstreamPacket(blockBreakPacket);
session.setBreakingBlock(-1);
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
if (itemFrameEntity != null) {
ServerboundInteractPacket attackPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamGamePacket(attackPacket);
break;
}
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, packet.getBlockPosition(), Direction.VALUES[packet.getBlockFace()], sequence);
session.sendDownstreamGamePacket(breakPacket);
}
}
break;
case ITEM_RELEASE:
@ -549,7 +514,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}
}
private boolean canInteractWithBlock(GeyserSession session, Vector3f playerPosition, Vector3i packetBlockPosition) {
public static boolean canInteractWithBlock(GeyserSession session, Vector3f playerPosition, Vector3i packetBlockPosition) {
// ViaVersion sends this 1.20.5+ attribute also, so older servers will have correct range checks.
double blockInteractionRange = session.getPlayerEntity().getBlockInteractionRange();
@ -577,7 +542,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
* @param session the session of the Bedrock client
* @param blockPos the block position to restore
*/
private void restoreCorrectBlock(GeyserSession session, Vector3i blockPos, InventoryTransactionPacket packet) {
public static void restoreCorrectBlock(GeyserSession session, Vector3i blockPos) {
BlockState javaBlockState = session.getGeyser().getWorldManager().blockAt(session, blockPos);
BlockDefinition bedrockBlock = session.getBlockMappings().getBedrockBlock(javaBlockState);
@ -604,7 +569,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendUpstreamPacket(updateWaterPacket);
// Reset the item in hand to prevent "missing" blocks
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), session.getPlayerInventory().getOffsetForHotbar(packet.getHotbarSlot()));
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), session.getPlayerInventory().getHeldItemSlot()); // TODO test
}
private boolean isIncorrectHeldItem(GeyserSession session, InventoryTransactionPacket packet) {
@ -698,9 +663,11 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
float pitch = (float) -Math.toDegrees(Math.atan2(yDiff, xzHypot));
SessionPlayerEntity entity = session.getPlayerEntity();
ServerboundMovePlayerPosRotPacket returnPacket = new ServerboundMovePlayerPosRotPacket(entity.isOnGround(), playerPosition.getX(), playerPosition.getY(), playerPosition.getZ(), entity.getYaw(), entity.getPitch());
ServerboundMovePlayerPosRotPacket returnPacket = new ServerboundMovePlayerPosRotPacket(entity.isOnGround(), session.getInputCache().lastHorizontalCollision(),
playerPosition.getX(), playerPosition.getY(), playerPosition.getZ(), entity.getYaw(), entity.getPitch());
// This matches Java edition behavior
ServerboundMovePlayerPosRotPacket movementPacket = new ServerboundMovePlayerPosRotPacket(entity.isOnGround(), playerPosition.getX(), playerPosition.getY(), playerPosition.getZ(), yaw, pitch);
ServerboundMovePlayerPosRotPacket movementPacket = new ServerboundMovePlayerPosRotPacket(entity.isOnGround(), session.getInputCache().lastHorizontalCollision(),
playerPosition.getX(), playerPosition.getY(), playerPosition.getZ(), yaw, pitch);
session.sendDownstreamGamePacket(movementPacket);
if (session.getLookBackScheduledFuture() != null) {

View file

@ -1,77 +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.protocol.bedrock;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
/**
* Sent by the client when moving a horse or boat.
*/
@Translator(packet = MoveEntityAbsolutePacket.class)
public class BedrockMoveEntityAbsoluteTranslator extends PacketTranslator<MoveEntityAbsolutePacket> {
@Override
public void translate(GeyserSession session, MoveEntityAbsolutePacket packet) {
session.setLastVehicleMoveTimestamp(System.currentTimeMillis());
Entity ridingEntity = session.getPlayerEntity().getVehicle();
if (ridingEntity != null && session.getWorldBorder().isPassingIntoBorderBoundaries(packet.getPosition(), false)) {
Vector3f position = Vector3f.from(ridingEntity.getPosition().getX(), packet.getPosition().getY(),
ridingEntity.getPosition().getZ());
if (ridingEntity instanceof BoatEntity) {
// Undo the changes usually applied to the boat
ridingEntity.as(BoatEntity.class)
.moveAbsoluteWithoutAdjustments(position, ridingEntity.getYaw(),
ridingEntity.isOnGround(), true);
} else {
// This doesn't work if teleported is false
ridingEntity.moveAbsolute(position,
ridingEntity.getYaw(), ridingEntity.getPitch(), ridingEntity.getHeadYaw(),
ridingEntity.isOnGround(), true);
}
return;
}
float y = packet.getPosition().getY();
if (ridingEntity instanceof BoatEntity && !ridingEntity.isOnGround()) {
// Remove the offset to prevents boats from looking like they're floating in water
y -= EntityDefinitions.BOAT.offset();
}
ServerboundMoveVehiclePacket ServerboundMoveVehiclePacket = new ServerboundMoveVehiclePacket(
packet.getPosition().getX(), y, packet.getPosition().getZ(),
packet.getRotation().getY() - 90, packet.getRotation().getX()
);
session.sendDownstreamGamePacket(ServerboundMoveVehiclePacket);
}
}

View file

@ -1,92 +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.protocol.bedrock;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.packet.PlayerInputPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.BoatEntity;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.living.animal.horse.AbstractHorseEntity;
import org.geysermc.geyser.entity.type.living.animal.horse.LlamaEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
/**
* Sent by the client for minecarts and boats.
*/
@Translator(packet = PlayerInputPacket.class)
public class BedrockPlayerInputTranslator extends PacketTranslator<PlayerInputPacket> {
@Override
public void translate(GeyserSession session, PlayerInputPacket packet) {
ServerboundPlayerInputPacket playerInputPacket = new ServerboundPlayerInputPacket(
packet.getInputMotion().getX(), packet.getInputMotion().getY(), packet.isJumping(), packet.isSneaking()
);
session.sendDownstreamGamePacket(playerInputPacket);
session.getPlayerEntity().setVehicleInput(packet.getInputMotion());
// Bedrock only sends movement vehicle packets while moving
// This allows horses to take damage while standing on magma
Entity vehicle = session.getPlayerEntity().getVehicle();
boolean sendMovement = false;
if (vehicle instanceof AbstractHorseEntity && !(vehicle instanceof LlamaEntity)) {
sendMovement = vehicle.isOnGround();
} else if (vehicle instanceof BoatEntity) {
if (vehicle.getPassengers().size() == 1) {
// The player is the only rider
sendMovement = true;
} else {
// Check if the player is the front rider
if (session.getPlayerEntity().isRidingInFront()) {
sendMovement = true;
}
}
}
if (sendMovement) {
long timeSinceVehicleMove = System.currentTimeMillis() - session.getLastVehicleMoveTimestamp();
if (timeSinceVehicleMove >= 100) {
Vector3f vehiclePosition = vehicle.getPosition();
if (vehicle instanceof BoatEntity && !vehicle.isOnGround()) {
// Remove some Y position to prevents boats flying up
vehiclePosition = vehiclePosition.down(EntityDefinitions.BOAT.offset());
}
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(
vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(),
vehicle.getYaw() - 90, vehicle.getPitch()
);
session.sendDownstreamGamePacket(moveVehiclePacket);
}
}
}
}

View file

@ -1,387 +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.protocol.bedrock.entity.player;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.level.block.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.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.translator.item.CustomItemTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
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.ServerboundPlayerCommandPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundSwingPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
@Translator(packet = PlayerActionPacket.class)
public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket> {
@Override
public void translate(GeyserSession session, PlayerActionPacket packet) {
SessionPlayerEntity entity = session.getPlayerEntity();
// Send book update before any player action
if (packet.getAction() != PlayerActionType.RESPAWN) {
session.getBookEditCache().checkForSend();
}
Vector3i vector = packet.getBlockPosition();
switch (packet.getAction()) {
case RESPAWN -> {
// Respawn process is finished and the server and client are both OK with respawning.
EntityEventPacket eventPacket = new EntityEventPacket();
eventPacket.setRuntimeEntityId(entity.getGeyserId());
eventPacket.setType(EntityEventType.RESPAWN);
eventPacket.setData(0);
session.sendUpstreamPacket(eventPacket);
// Resend attributes or else in rare cases the user can think they're not dead when they are, upon joining the server
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
session.sendUpstreamPacket(attributesPacket);
// Bounding box must be sent after a player dies and respawns since 1.19.40
entity.updateBoundingBox();
// Needed here since 1.19.81 for dimension switching
session.getEntityCache().updateBossBars();
}
case START_SWIMMING -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamGamePacket(startSwimPacket);
session.setSwimming(true);
}
}
case STOP_SWIMMING -> {
// Prevent packet spam when Bedrock players are crawling near the edge of a block
if (!session.getCollisionManager().mustPlayerCrawlHere()) {
ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamGamePacket(stopSwimPacket);
session.setSwimming(false);
}
}
case START_GLIDE -> {
// Otherwise gliding will not work in creative
ServerboundPlayerAbilitiesPacket playerAbilitiesPacket = new ServerboundPlayerAbilitiesPacket(false);
session.sendDownstreamGamePacket(playerAbilitiesPacket);
sendPlayerGlideToggle(session, entity);
}
case STOP_GLIDE -> sendPlayerGlideToggle(session, entity);
case START_SNEAK -> {
ServerboundPlayerCommandPacket startSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SNEAKING);
session.sendDownstreamGamePacket(startSneakPacket);
session.startSneaking();
}
case STOP_SNEAK -> {
ServerboundPlayerCommandPacket stopSneakPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SNEAKING);
session.sendDownstreamGamePacket(stopSneakPacket);
session.stopSneaking();
}
case START_SPRINT -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamGamePacket(startSprintPacket);
session.setSprinting(true);
}
}
case STOP_SPRINT -> {
if (!entity.getFlag(EntityFlag.SWIMMING)) {
ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamGamePacket(stopSprintPacket);
}
session.setSprinting(false);
}
case DROP_ITEM -> {
ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM,
vector, Direction.VALUES[packet.getFace()], 0);
session.sendDownstreamGamePacket(dropItemPacket);
}
case STOP_SLEEP -> {
ServerboundPlayerCommandPacket stopSleepingPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.LEAVE_BED);
session.sendDownstreamGamePacket(stopSleepingPacket);
}
case START_BREAK -> {
// Ignore START_BREAK when the player is CREATIVE to avoid Spigot receiving 2 packets it interpets as block breaking. https://github.com/GeyserMC/Geyser/issues/4021
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
// Start the block breaking animation
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, vector);
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEvent.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
double breakTime = BlockUtils.getSessionBreakTime(session, BlockState.of(blockState).block()) * 20;
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves
GeyserItemStack item = session.getPlayerInventory().getItemInHand();
ItemMapping mapping = item.getMapping(session);
ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null;
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState);
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector);
session.setBlockBreakStartTime(0);
if (blockStateOverride != null || customItem != null || (skull != null && skull.getBlockDefinition() != null)) {
session.setBlockBreakStartTime(System.currentTimeMillis());
}
startBreak.setData((int) (65535 / breakTime));
session.setBreakingBlock(blockState);
session.sendUpstreamPacket(startBreak);
// Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(vector, packet.getFace());
Block block = session.getGeyser().getWorldManager().blockAt(session, fireBlockPos).block();
Direction direction = Direction.VALUES[packet.getFace()];
if (block == Blocks.FIRE || block == Blocks.SOUL_FIRE) {
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos,
direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(startBreakingPacket);
}
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING,
vector, direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(startBreakingPacket);
spawnBlockBreakParticles(session, direction, vector, BlockState.of(blockState));
}
case CONTINUE_BREAK -> {
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
int breakingBlock = session.getBreakingBlock();
if (breakingBlock == -1) {
breakingBlock = Block.JAVA_AIR_ID;
}
Vector3f vectorFloat = vector.toFloat();
BlockState breakingBlockState = BlockState.of(breakingBlock);
Direction direction = Direction.VALUES[packet.getFace()];
spawnBlockBreakParticles(session, direction, vector, breakingBlockState);
double breakTime = BlockUtils.getSessionBreakTime(session, breakingBlockState.block()) * 20;
// If the block is custom, we must keep track of when it should break ourselves
long blockBreakStartTime = session.getBlockBreakStartTime();
if (blockBreakStartTime != 0) {
long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime;
// We need to add a slight delay to the break time, otherwise the client breaks blocks too fast
if (timeSinceStart >= (breakTime += 2) * 50) {
// Play break sound and particle
LevelEventPacket effectPacket = new LevelEventPacket();
effectPacket.setPosition(vectorFloat);
effectPacket.setType(LevelEvent.PARTICLE_DESTROY_BLOCK);
effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock));
session.sendUpstreamPacket(effectPacket);
// Break the block
ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING,
vector, direction, session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(finishBreakingPacket);
session.setBlockBreakStartTime(0);
break;
}
}
// Update the break time in the event that player conditions changed (jumping, effects applied)
LevelEventPacket updateBreak = new LevelEventPacket();
updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK);
updateBreak.setPosition(vectorFloat);
updateBreak.setData((int) (65535 / breakTime));
session.sendUpstreamPacket(updateBreak);
}
case ABORT_BREAK -> {
if (session.getGameMode() != GameMode.CREATIVE) {
// As of 1.16.210: item frame items are taken out here.
// Survival also sends START_BREAK, but by attaching our process here adventure mode also works
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, vector);
if (itemFrameEntity != null) {
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrameEntity.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
session.sendDownstreamGamePacket(interactPacket);
break;
}
}
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0);
session.sendDownstreamGamePacket(abortBreakingPacket);
LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEvent.BLOCK_STOP_BREAK);
stopBreak.setPosition(vector.toFloat());
stopBreak.setData(0);
session.setBreakingBlock(-1);
session.sendUpstreamPacket(stopBreak);
}
// Handled in BedrockInventoryTransactionTranslator
case STOP_BREAK -> {
}
case DIMENSION_CHANGE_SUCCESS -> {
//sometimes the client doesn't feel like loading
PlayStatusPacket spawnPacket = new PlayStatusPacket();
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
session.sendUpstreamPacket(spawnPacket);
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
session.sendUpstreamPacket(attributesPacket);
}
case JUMP -> entity.setOnGround(false); // Increase block break time while jumping
case MISSED_SWING -> {
// Java edition sends a cooldown when hitting air.
// Normally handled by BedrockLevelSoundEventTranslator, but there is no sound on Java for this.
CooldownUtils.sendCooldown(session);
// TODO Re-evaluate after pre-1.20.10 is no longer supported?
if (session.getArmAnimationTicks() == -1) {
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
session.activateArmAnimationTicking();
// Send packet to Bedrock so it knows
AnimatePacket animatePacket = new AnimatePacket();
animatePacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
animatePacket.setAction(AnimatePacket.Action.SWING_ARM);
session.sendUpstreamPacket(animatePacket);
}
}
case START_FLYING -> { // Since 1.20.30
if (session.isCanFly()) {
if (session.getGameMode() == GameMode.SPECTATOR) {
// should already be flying
session.sendAdventureSettings();
break;
}
if (session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) {
// As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling
// If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE
session.sendAdventureSettings();
break;
}
session.setFlying(true);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(true));
} else {
// update whether we can fly
session.sendAdventureSettings();
// stop flying
PlayerActionPacket stopFlyingPacket = new PlayerActionPacket();
stopFlyingPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
stopFlyingPacket.setAction(PlayerActionType.STOP_FLYING);
stopFlyingPacket.setBlockPosition(Vector3i.ZERO);
stopFlyingPacket.setResultPosition(Vector3i.ZERO);
stopFlyingPacket.setFace(0);
session.sendUpstreamPacket(stopFlyingPacket);
}
}
case STOP_FLYING -> {
session.setFlying(false);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
}
case DIMENSION_CHANGE_REQUEST_OR_CREATIVE_DESTROY_BLOCK -> { // Used by client to get book from lecterns and items from item frame in creative mode since 1.20.70
BlockState state = session.getGeyser().getWorldManager().blockAt(session, vector);
if (state.getValue(Properties.HAS_BOOK, false)) {
session.setDroppingLecternBook(true);
ServerboundUseItemOnPacket blockPacket = new ServerboundUseItemOnPacket(
vector,
Direction.DOWN,
Hand.MAIN_HAND,
0, 0, 0,
false,
session.getWorldCache().nextPredictionSequence());
session.sendDownstreamGamePacket(blockPacket);
break;
}
Entity itemFrame = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
if (itemFrame != null) {
ServerboundInteractPacket interactPacket = new ServerboundInteractPacket(itemFrame.getEntityId(),
InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
session.sendDownstreamGamePacket(interactPacket);
}
}
}
}
private void spawnBlockBreakParticles(GeyserSession session, Direction direction, Vector3i position, BlockState blockState) {
LevelEventPacket levelEventPacket = new LevelEventPacket();
switch (direction) {
case UP -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_UP);
case DOWN -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_DOWN);
case NORTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_NORTH);
case EAST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_EAST);
case SOUTH -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_SOUTH);
case WEST -> levelEventPacket.setType(LevelEvent.PARTICLE_BREAK_BLOCK_WEST);
}
levelEventPacket.setPosition(position.toFloat());
levelEventPacket.setData(session.getBlockMappings().getBedrockBlock(blockState).getRuntimeId());
session.sendUpstreamPacket(levelEventPacket);
}
private void sendPlayerGlideToggle(GeyserSession session, Entity entity) {
ServerboundPlayerCommandPacket glidePacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING);
session.sendDownstreamGamePacket(glidePacket);
}
}

Some files were not shown because too many files have changed in this diff Show more