diff --git a/README.md b/README.md index 56dc74761..5a734f18b 100644 --- a/README.md +++ b/README.md @@ -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.21.90 - 1.21.120 and Minecraft Java 1.21.9 - 1.21.10. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/). +Geyser is currently supporting Minecraft Bedrock 1.21.90 - 1.21.124 and Minecraft Java 1.21.9 - 1.21.10. 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. diff --git a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java index bf30dd2b2..a7e86d89b 100644 --- a/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java +++ b/api/src/main/java/org/geysermc/geyser/api/block/custom/component/MaterialInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2025 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 @@ -46,6 +46,13 @@ public interface MaterialInstance { */ @Nullable String renderMethod(); + /** + * Gets the tint method of the block + * + * @return The tint method of the block. + */ + @Nullable String tintMethod(); + /** * Gets if the block should be dimmed on certain faces * @@ -60,6 +67,13 @@ public interface MaterialInstance { */ boolean ambientOcclusion(); + /** + * Gets if the block is isotropic + * + * @return If the block is isotropic. + */ + boolean isotropic(); + /** * Creates a builder for MaterialInstance. * @@ -74,10 +88,14 @@ public interface MaterialInstance { Builder renderMethod(@Nullable String renderMethod); + Builder tintMethod(@Nullable String tintMethod); + Builder faceDimming(boolean faceDimming); Builder ambientOcclusion(boolean ambientOcclusion); + Builder isotropic(boolean isotropic); + MaterialInstance build(); } } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java index 1f8698518..6dd3c4fe2 100644 --- a/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java @@ -62,7 +62,23 @@ public interface GeyserEntity { /** * Updates multiple properties with just one update packet. * @see BatchPropertyUpdater + * + * @param consumer a batch updater * @since 2.9.0 */ - void updatePropertiesBatched(Consumer consumer); + default void updatePropertiesBatched(Consumer consumer) { + this.updatePropertiesBatched(consumer, false); + } + + /** + * Updates multiple properties with just one update packet, which can be sent immediately to the client. + * Usually, sending updates immediately is not required except for specific situations where packet batching + * would result in update order issues. + * @see BatchPropertyUpdater + * + * @param consumer a batch updater + * @param immediate whether this update should be sent immediately + * @since 2.9.1 + */ + void updatePropertiesBatched(Consumer consumer, boolean immediate); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 096bd8a70..f82b09fe2 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -778,40 +778,44 @@ public class Entity implements GeyserEntity { } @Override - public void updatePropertiesBatched(Consumer consumer) { - if (this.propertyManager != null) { - Objects.requireNonNull(consumer); - GeyserEntityProperties propertyDefinitions = definition.registeredProperties(); - consumer.accept(new BatchPropertyUpdater() { - @Override - public void update(@NonNull GeyserEntityProperty property, @Nullable T value) { - Objects.requireNonNull(property, "property must not be null!"); - if (!(property instanceof PropertyType propertyType)) { - throw new IllegalArgumentException("Invalid property implementation! Got: " + property.getClass().getSimpleName()); - } - int index = propertyDefinitions.getPropertyIndex(property.identifier().toString()); - if (index < 0) { - throw new IllegalArgumentException("No property with the name " + property.identifier() + " has been registered."); - } + public void updatePropertiesBatched(Consumer consumer, boolean immediate) { + if (this.propertyManager == null) { + throw new IllegalArgumentException("Given entity has no registered properties!"); + } - var expectedProperty = propertyDefinitions.getProperties().get(index); - if (!expectedProperty.equals(propertyType)) { - throw new IllegalArgumentException("The supplied property was not registered with this entity type!"); - } - - propertyType.apply(propertyManager, value); + Objects.requireNonNull(consumer); + GeyserEntityProperties propertyDefinitions = definition.registeredProperties(); + consumer.accept(new BatchPropertyUpdater() { + @Override + public void update(@NonNull GeyserEntityProperty property, @Nullable T value) { + Objects.requireNonNull(property, "property must not be null!"); + if (!(property instanceof PropertyType propertyType)) { + throw new IllegalArgumentException("Invalid property implementation! Got: " + property.getClass().getSimpleName()); + } + int index = propertyDefinitions.getPropertyIndex(property.identifier().toString()); + if (index < 0) { + throw new IllegalArgumentException("No property with the name " + property.identifier() + " has been registered."); } - }); - if (propertyManager.hasProperties()) { - SetEntityDataPacket packet = new SetEntityDataPacket(); - packet.setRuntimeEntityId(getGeyserId()); - propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); - propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); + var expectedProperty = propertyDefinitions.getProperties().get(index); + if (!expectedProperty.equals(propertyType)) { + throw new IllegalArgumentException("The supplied property was not registered with this entity type!"); + } + + propertyType.apply(propertyManager, value); + } + }); + + if (propertyManager.hasProperties()) { + SetEntityDataPacket packet = new SetEntityDataPacket(); + packet.setRuntimeEntityId(getGeyserId()); + propertyManager.applyFloatProperties(packet.getProperties().getFloatProperties()); + propertyManager.applyIntProperties(packet.getProperties().getIntProperties()); + if (immediate) { + session.sendUpstreamPacketImmediately(packet); + } else { session.sendUpstreamPacket(packet); } - } else { - throw new IllegalArgumentException("Given entity has no registered properties!"); } } } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/GeyserMaterialInstance.java b/core/src/main/java/org/geysermc/geyser/level/block/GeyserMaterialInstance.java index acc16bf58..968d9f13b 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/GeyserMaterialInstance.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/GeyserMaterialInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2025 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 @@ -33,14 +33,18 @@ import org.geysermc.geyser.api.block.custom.component.MaterialInstance; public class GeyserMaterialInstance implements MaterialInstance { private final String texture; private final String renderMethod; + private final String tintMethod; private final boolean faceDimming; private final boolean ambientOcclusion; + private final boolean isotropic; GeyserMaterialInstance(Builder builder) { this.texture = builder.texture; this.renderMethod = builder.renderMethod; + this.tintMethod = builder.tintMethod; this.faceDimming = builder.faceDimming; this.ambientOcclusion = builder.ambientOcclusion; + this.isotropic = builder.isotropic; } @Override @@ -53,6 +57,11 @@ public class GeyserMaterialInstance implements MaterialInstance { return renderMethod; } + @Override + public @Nullable String tintMethod() { + return tintMethod; + } + @Override public boolean faceDimming() { return faceDimming; @@ -63,11 +72,18 @@ public class GeyserMaterialInstance implements MaterialInstance { return ambientOcclusion; } + @Override + public boolean isotropic() { + return isotropic; + } + public static class Builder implements MaterialInstance.Builder { private String texture; private String renderMethod; + private String tintMethod; private boolean faceDimming; private boolean ambientOcclusion; + private boolean isotropic; @Override public Builder texture(@Nullable String texture) { @@ -81,6 +97,12 @@ public class GeyserMaterialInstance implements MaterialInstance { return this; } + @Override + public Builder tintMethod(@Nullable String tintMethod) { + this.tintMethod = tintMethod; + return this; + } + @Override public Builder faceDimming(boolean faceDimming) { this.faceDimming = faceDimming; @@ -93,6 +115,12 @@ public class GeyserMaterialInstance implements MaterialInstance { return this; } + @Override + public MaterialInstance.Builder isotropic(boolean isotropic) { + this.isotropic = isotropic; + return this; + } + @Override public MaterialInstance build() { return new GeyserMaterialInstance(this); diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 909f6e405..d4f427124 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -89,6 +89,7 @@ public final class GameProtocol { register(Bedrock_v827.CODEC, "1.21.100", "1.21.101"); register(Bedrock_v844.CODEC, "1.21.111", "1.21.112", "1.21.113", "1.21.114"); register(Bedrock_v859.CODEC, "1.21.120", "1.21.121", "1.21.122", "1.21.123"); + register(Bedrock_v859.CODEC.toBuilder().protocolVersion(860).minecraftVersion("1.21.124").build()); MinecraftVersion latestBedrock = SUPPORTED_BEDROCK_VERSIONS.get(SUPPORTED_BEDROCK_VERSIONS.size() - 1); DEFAULT_BEDROCK_VERSION = latestBedrock.versionString(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index f1a1bcb99..e4296d7fa 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -34,7 +34,6 @@ import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -128,7 +127,9 @@ public final class BlockRegistryPopulator { .put(ObjectIntPair.of("1_21_90", Bedrock_v819.CODEC.getProtocolVersion()), Conversion827_819::remapBlock) .put(ObjectIntPair.of("1_21_100", Bedrock_v827.CODEC.getProtocolVersion()), Conversion844_827::remapBlock) .put(ObjectIntPair.of("1_21_110", Bedrock_v844.CODEC.getProtocolVersion()), tag -> tag) + // 1.21.110 -> 1.21.12x doesn't change the block palette .put(ObjectIntPair.of("1_21_110", Bedrock_v859.CODEC.getProtocolVersion()), tag -> tag) + .put(ObjectIntPair.of("1_21_110", 860), tag -> tag) .build(); // We can keep this strong as nothing should be garbage collected diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java index 06a132820..665ab9cd0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2025 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 @@ -448,8 +448,8 @@ public class CustomBlockRegistryPopulator { for (Map.Entry entry : components.materialInstances().entrySet()) { MaterialInstance materialInstance = entry.getValue(); NbtMapBuilder materialBuilder = NbtMap.builder() - .putString("render_method", materialInstance.renderMethod()) - .putBoolean("ambient_occlusion", materialInstance.ambientOcclusion()); + .putBoolean("ambient_occlusion", materialInstance.ambientOcclusion()) + .putBoolean("isotropic", materialInstance.isotropic()); if (GameProtocol.is1_21_110orHigher(protocolVersion)) { materialBuilder.putBoolean("packed_bools", materialInstance.faceDimming()); @@ -457,6 +457,14 @@ public class CustomBlockRegistryPopulator { materialBuilder.putBoolean("face_dimming", materialInstance.faceDimming()); } + if (materialInstance.renderMethod() != null) { + materialBuilder.putString("render_method", materialInstance.renderMethod()); + } + + if (materialInstance.tintMethod() != null) { + materialBuilder.putString("tint_method", materialInstance.tintMethod()); + } + // Texture can be unspecified when blocks.json is used in RP (https://wiki.bedrock.dev/blocks/blocks-stable.html#minecraft-material-instances) if (materialInstance.texture() != null) { materialBuilder.putString("texture", materialInstance.texture()); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 862e47c87..e68783e49 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -194,12 +194,13 @@ public class ItemRegistryPopulator { Map eightOneEightFallbacks = new HashMap<>(eightOneNineFallbacks); eightOneEightFallbacks.put(Items.MUSIC_DISC_LAVA_CHICKEN, Items.MUSIC_DISC_CHIRP); - List paletteVersions = new ArrayList<>(4); + List paletteVersions = new ArrayList<>(6); paletteVersions.add(new PaletteVersion("1_21_90", Bedrock_v818.CODEC.getProtocolVersion(), eightOneEightFallbacks, Conversion844_827::remapItem)); paletteVersions.add(new PaletteVersion("1_21_93", Bedrock_v819.CODEC.getProtocolVersion(), eightOneNineFallbacks, Conversion844_827::remapItem)); paletteVersions.add(new PaletteVersion("1_21_100", Bedrock_v827.CODEC.getProtocolVersion(), eightTwoSevenFallbacks, Conversion844_827::remapItem)); paletteVersions.add(new PaletteVersion("1_21_110", Bedrock_v844.CODEC.getProtocolVersion())); paletteVersions.add(new PaletteVersion("1_21_120", Bedrock_v859.CODEC.getProtocolVersion())); + paletteVersions.add(new PaletteVersion("1_21_120", 860)); GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); diff --git a/gradle.properties b/gradle.properties index 0dc5199e3..57c0249ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,5 +8,5 @@ org.gradle.vfs.watch=false group=org.geysermc id=geyser -version=2.9.0-SNAPSHOT +version=2.9.1-SNAPSHOT description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.