1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

API 2.9.1: Support 1.21.124, add tint/isotropic method to custom block material, allowing updating entity properties immediately (#5991)

* Prepare for 1.21.124

* Add tint method and isotropic properties to block material instances (#5977)

* Add tint method and isotropic properties to block material instances

* Check if tint method is null before including it

* Lets cover the null check on render method too

* Allow updating properties immediately (#5961)

---------

Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
This commit is contained in:
chris
2025-11-20 14:52:56 +01:00
committed by GitHub
parent c0c7b51935
commit 53596d05bc
10 changed files with 116 additions and 39 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.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.

View File

@@ -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();
}
}

View File

@@ -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<BatchPropertyUpdater> consumer);
default void updatePropertiesBatched(Consumer<BatchPropertyUpdater> 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<BatchPropertyUpdater> consumer, boolean immediate);
}

View File

@@ -778,40 +778,44 @@ public class Entity implements GeyserEntity {
}
@Override
public void updatePropertiesBatched(Consumer<BatchPropertyUpdater> consumer) {
if (this.propertyManager != null) {
Objects.requireNonNull(consumer);
GeyserEntityProperties propertyDefinitions = definition.registeredProperties();
consumer.accept(new BatchPropertyUpdater() {
@Override
public <T> void update(@NonNull GeyserEntityProperty<T> property, @Nullable T value) {
Objects.requireNonNull(property, "property must not be null!");
if (!(property instanceof PropertyType<T, ? extends EntityProperty> 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<BatchPropertyUpdater> 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 <T> void update(@NonNull GeyserEntityProperty<T> property, @Nullable T value) {
Objects.requireNonNull(property, "property must not be null!");
if (!(property instanceof PropertyType<T, ? extends EntityProperty> 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!");
}
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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

View File

@@ -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<String, MaterialInstance> 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());

View File

@@ -194,12 +194,13 @@ public class ItemRegistryPopulator {
Map<Item, Item> eightOneEightFallbacks = new HashMap<>(eightOneNineFallbacks);
eightOneEightFallbacks.put(Items.MUSIC_DISC_LAVA_CHICKEN, Items.MUSIC_DISC_CHIRP);
List<PaletteVersion> paletteVersions = new ArrayList<>(4);
List<PaletteVersion> 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();

View File

@@ -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.