1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-04 15:31:36 +00:00

Custom entity properties API (#5788)

* Custom entity properties API

* Fix build fail

* Resolve comments

* Entity property registration improvements

* oops

* Add boolean and enum property sync API

* default value + no packet if value unchanged

* Don't send packet if no properties were updated

* small refactor

* the refactor, part two

* Move updateProperties to GeyserEntity

* Don't inherit properties from parent

* temp

* type-safe property updating

* Address review

* call the GeyserDefineEntityPropertiesEvent once, require specifying entity identifier instead of calling the event once for every entity type

* Migrate to identifiers (from custom items v2, thanks eclipse), remove duplicate logic

* fix test

* Merge 1.21.9, update copper golem entity property usage

* fixup javadocs

---------

Co-authored-by: onebeastchris <github@onechris.mozmail.com>
This commit is contained in:
Zigy
2025-10-08 16:09:34 +03:30
committed by GitHub
parent 2295814b22
commit d869e745e0
36 changed files with 1555 additions and 389 deletions

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 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
* 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.api.entity.property;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
/**
* Collects property changes to be applied as a single, batched update to an entity.
* <p>
* Notes:
* <ul>
* <li>Passing {@code null} as a value resets the property to its default.</li>
* <li>Numeric properties must be within declared ranges; enum properties must use an allowed value.</li>
* <li>Multiple updates to the same property within a single batch will result in the last value being applied.</li>
* <li>The updater is short-lived and should not be retained outside the batching callback.</li>
* </ul>
*
* <pre>{@code
* entity.updatePropertiesBatched(updater -> {
* updater.update(SOME_FLOAT_PROPERTY, 0.15f);
* updater.update(SOME_BOOLEAN_PROPERTY, true);
* updater.update(SOME_INT_PROPERTY, null); // reset to default
* });
* }</pre>
*
* @since 2.9.0
*/
@FunctionalInterface
public interface BatchPropertyUpdater {
/**
* Queues an update for the given property within the current batch.
* <p>
* If {@code value} is {@code null}, the property will be reset to its default value
* as declared when the property was registered during the {@link GeyserDefineEntityPropertiesEvent}.
*
* @param property a {@link GeyserEntityProperty} registered for the target entity type
* @param value the new value, or {@code null} to reset to the default
* @param <T> the property's value type
*
* @since 2.9.0
*/
<T> void update(@NonNull GeyserEntityProperty<T> property, @Nullable T value);
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 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
* 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.api.entity.property;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.util.Identifier;
/**
* Represents a property that can be attached to an entity.
* <p>
* Entity properties are used to describe metadata about an entity, such as
* integers, floats, booleans, or enums.
* @see <a href="https://learn.microsoft.com/en-us/minecraft/creator/documents/introductiontoentityproperties?view=minecraft-bedrock-stable#number-of-entity-properties-per-entity-type">
* Official documentation for info</a>
*
* @param <T> the type of value stored by this property
*
* @since 2.9.0
*/
public interface GeyserEntityProperty<T> {
/**
* Gets the unique name of this property.
* Custom properties cannot use the vanilla namespace
* to avoid collisions with vanilla entity properties.
*
* @return the property identifier
* @since 2.9.0
*/
@NonNull
Identifier identifier();
/**
* Gets the default value of this property which
* is set upon spawning entities.
*
* @return the default value of this property
* @since 2.9.0
*/
@NonNull
T defaultValue();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 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
* 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.api.entity.property.type;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
/**
* Represents a boolean entity property.
* @since 2.9.0
*/
public interface GeyserBooleanEntityProperty extends GeyserEntityProperty<Boolean> {
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 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
* 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.api.entity.property.type;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
/**
* Represents a Java enum-backed enum property.
* There are a few key limitations:
* <ul>
* <li>There cannot be more than 16 values</li>
* <li>Enum names cannot be longer than 32 chars, must start with a letter, and may contain numbers and underscores</li>
* </ul>
*
* @param <E> the enum type
* @since 2.9.0
*/
public interface GeyserEnumEntityProperty<E extends Enum<E>> extends GeyserEntityProperty<E> {
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 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
* 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.api.entity.property.type;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
import org.geysermc.geyser.api.util.Identifier;
/**
* Represents a float-backed entity property with inclusive bounds.
* Values associated with this property must be always within the {@code [min(), max()]} bounds.
*
* @see GeyserDefineEntityPropertiesEvent#registerFloatProperty(Identifier, Identifier, float, float, Float)
* @since 2.9.0
*/
public interface GeyserFloatEntityProperty extends GeyserEntityProperty<Float> {
/**
* @return the inclusive lower bound for this property
* @since 2.9.0
*/
float min();
/**
* @return the inclusive upper bound for this property
* @since 2.9.0
*/
float max();
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 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
* 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.api.entity.property.type;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
import org.geysermc.geyser.api.util.Identifier;
/**
* Represents an int-backed entity property with inclusive bounds.
* There are a few key limitations:
* <ul>
* <li>Values must be always within the {@code [min(), max()]} bounds</li>
* <li>Molang evaluation uses floats under the hood; very large integers can lose precision.
* Prefer keeping values in a practical range to avoid rounding issues.</li>
* </ul>
*
* @see GeyserDefineEntityPropertiesEvent#registerIntegerProperty(Identifier, Identifier, int, int, Integer)
* @since 2.9.0
*/
public interface GeyserIntEntityProperty extends GeyserEntityProperty<Integer> {
/**
* @return the inclusive lower bound for this property
* @since 2.9.0
*/
int min();
/**
* @return the inclusive upper bound for this property
* @since 2.9.0
*/
int max();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 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
* 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.api.entity.property.type;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
import java.util.List;
/**
* Represents a string-backed enum property.
* There are a few key limitations:
* <ul>
* <li>There cannot be more than 16 values</li>
* <li>The values' names cannot be longer than 32 chars, must start with a letter, and may contain numbers and underscores</li>
* </ul>
*
* @since 2.9.0
*/
public interface GeyserStringEnumProperty extends GeyserEntityProperty<String> {
/**
* @return an unmodifiable list of all registered values
* @since 2.9.0
*/
List<String> values();
}

View File

@@ -26,9 +26,17 @@
package org.geysermc.geyser.api.entity.type;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.api.entity.property.BatchPropertyUpdater;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineEntityPropertiesEvent;
import java.util.function.Consumer;
/**
* Represents a unique instance of an entity. Each {@link org.geysermc.geyser.api.connection.GeyserConnection}
* Represents a unique instance of an entity. Each {@link GeyserConnection}
* have their own sets of entities - no two instances will share the same GeyserEntity instance.
*/
public interface GeyserEntity {
@@ -37,4 +45,24 @@ public interface GeyserEntity {
*/
@NonNegative
int javaId();
/**
* Updates an entity property with a new value.
* If the new value is null, the property is reset to the default value.
*
* @param property a {@link GeyserEntityProperty} registered for this type in the {@link GeyserDefineEntityPropertiesEvent}
* @param value the new property value
* @param <T> the type of the value
* @since 2.9.0
*/
default <T> void updateProperty(@NonNull GeyserEntityProperty<T> property, @Nullable T value) {
this.updatePropertiesBatched(consumer -> consumer.update(property, value));
}
/**
* Updates multiple properties with just one update packet.
* @see BatchPropertyUpdater
* @since 2.9.0
*/
void updatePropertiesBatched(Consumer<BatchPropertyUpdater> consumer);
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright (c) 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
* 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.api.event.lifecycle;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.event.Event;
import org.geysermc.geyser.api.entity.EntityData;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
import org.geysermc.geyser.api.entity.property.type.GeyserBooleanEntityProperty;
import org.geysermc.geyser.api.entity.property.type.GeyserEnumEntityProperty;
import org.geysermc.geyser.api.entity.property.type.GeyserFloatEntityProperty;
import org.geysermc.geyser.api.entity.property.type.GeyserIntEntityProperty;
import org.geysermc.geyser.api.entity.property.type.GeyserStringEnumProperty;
import org.geysermc.geyser.api.entity.type.GeyserEntity;
import org.geysermc.geyser.api.util.Identifier;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
/**
* Lifecycle event fired during Geyser's startup to allow custom entity properties
* to be registered for a specific entity type.
* <p>
* Listeners can add new properties for any entity by passing the target entity's
* identifier (e.g., {@code Identifier.of("player")}) to the registration methods.
* The returned {@link GeyserEntityProperty} is used to identify the properties and to
* update the value of a specific entity instance.
*
* <h2>Example usage</h2>
* <pre>{@code
* public void onDefine(GeyserDefineEntityPropertiesEvent event) {
* Identifier player = Identifier.of("player");
* GeyserFloatEntityProperty ANIMATION_SPEED =
* event.registerFloatProperty(player, Identifier.of("my_group:animation_speed"), 0.0f, 1.0f, 0.1f);
* GeyserBooleanEntityProperty SHOW_SHORTS =
* event.registerBooleanProperty(player, Identifier.of("my_group:show_shorts"), false);
* }
* }</pre>
*
* Retrieving entity instances is possible with the {@link EntityData#entityByJavaId(int)} method, or
* {@link EntityData#playerEntity()} for the connection player entity.
* To update the value of a property on a specific entity, use {@link GeyserEntity#updateProperty(GeyserEntityProperty, Object)},
* or {@link GeyserEntity#updatePropertiesBatched(Consumer)} to update multiple properties efficiently at once.
*
* <p><b>Notes:</b>
* <ul>
* <li>Default values must fall within the provided bounds.</li>
* <li>There cannot be more than 32 properties registered per entity type in total</li>
* <li>{@link #properties(Identifier)} returns properties registered for the given entity
* (including those added earlier in the same callback), including vanilla properties.</li>
* </ul>
*
* @since 2.9.0
*/
public interface GeyserDefineEntityPropertiesEvent extends Event {
/**
* Returns an <em>unmodifiable</em> view of all properties that have been registered
* so far for the given entity type. This includes entity properties used for vanilla gameplay,
* such as those used for creaking animations.
*
* @param entityType the Java edition entity type identifier
* @return an unmodifiable collection of registered properties
*
* @since 2.9.0
*/
Collection<GeyserEntityProperty<?>> properties(@NonNull Identifier entityType);
/**
* Registers a {@code float}-backed entity property.
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param min the minimum allowed value (inclusive)
* @param max the maximum allowed value (inclusive)
* @param defaultValue the default value assigned initially on entity spawn - if null, it will be the minimum value
* @return the created float property
*
* @since 2.9.0
*/
GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max, @Nullable Float defaultValue);
/**
* Registers a {@code float}-backed entity property with a default value set to the minimum value.
* @see #registerFloatProperty(Identifier, Identifier, float, float, Float)
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param min the minimum allowed value (inclusive)
* @param max the maximum allowed value (inclusive)
* @return the created float property
*
* @since 2.9.0
*/
default GeyserFloatEntityProperty registerFloatProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, float min, float max) {
return registerFloatProperty(entityType, propertyIdentifier, min, max, null);
}
/**
* Registers an {@code int}-backed entity property.
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param min the minimum allowed value (inclusive)
* @param max the maximum allowed value (inclusive)
* @param defaultValue the default value assigned initially on entity spawn - if null, it will be the minimum value
* @return the created int property
*
* @since 2.9.0
*/
GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max, @Nullable Integer defaultValue);
/**
* Registers an {@code int}-backed entity property with a default value set to the minimum value.
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param min the minimum allowed value (inclusive)
* @param max the maximum allowed value (inclusive)
* @return the created int property
*
* @since 2.9.0
*/
default GeyserIntEntityProperty registerIntegerProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, int min, int max) {
return registerIntegerProperty(entityType, propertyIdentifier, min, max, null);
}
/**
* Registers a {@code boolean}-backed entity property.
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param defaultValue the default boolean value
* @return the created boolean property handle
*
* @since 2.9.0
*/
GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, boolean defaultValue);
/**
* Registers a {@code boolean}-backed entity property with a default of {@code false}.
* @see #registerBooleanProperty(Identifier, Identifier, boolean)
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @return the created boolean property
* @since 2.9.0
*/
default GeyserBooleanEntityProperty registerBooleanProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier) {
return registerBooleanProperty(entityType, propertyIdentifier, false);
}
/**
* Registers a typed {@linkplain Enum enum}-backed entity property.
* <p>
* The enum constants define the allowed values. If {@code defaultValue} is {@code null},
* the first enum value is set as the default.
* @see GeyserEnumEntityProperty for further limitations
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param enumClass the enum class that defines allowed values
* @param defaultValue the default enum value, or {@code null} for the first enum value to be the default
* @param <E> the enum type
* @return the created enum property
*
* @since 2.9.0
*/
<E extends Enum<E>> GeyserEnumEntityProperty<E> registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class<E> enumClass, @Nullable E defaultValue);
/**
* Registers a typed {@linkplain Enum enum}-backed entity property with the first value set as the default.
* @see #registerEnumProperty(Identifier, Identifier, Class, Enum)
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param enumClass the enum class that defines allowed values
* @param <E> the enum type
* @return the created enum property
*
* @since 2.9.0
*/
default <E extends Enum<E>> GeyserEnumEntityProperty<E> registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull Class<E> enumClass) {
return registerEnumProperty(entityType, propertyIdentifier, enumClass, null);
}
/**
* Registers a string-backed "enum-like" entity property where the set of allowed values
* is defined by the provided list. If {@code defaultValue} is {@code null}, the first value is used as the default
* on entity spawn. The default must be one of the values in {@code values}.
* @see GeyserStringEnumProperty
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param values the allowed string values
* @param defaultValue the default string value, or {@code null} for the first value to be used
* @return the created string-enum property
*
* @since 2.9.0
*/
GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List<String> values, @Nullable String defaultValue);
/**
* Registers a string-backed "enum-like" entity property with the first value as the default.
* @see #registerEnumProperty(Identifier, Identifier, List, String)
*
* @param entityType the Java edition entity type identifier
* @param propertyIdentifier the unique property identifier
* @param values the allowed string values
* @return the created string-enum property handle
*
* @since 2.9.0
*/
default GeyserStringEnumProperty registerEnumProperty(@NonNull Identifier entityType, @NonNull Identifier propertyIdentifier, @NonNull List<String> values) {
return registerEnumProperty(entityType, propertyIdentifier, values, null);
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2024-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
* 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.api.util;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.GeyserApi;
/**
* An identifying object for representing unique objects.
* This identifier consists of two parts:
* <ul>
* <li>
* a namespace, which is usually a name identifying your work
* </li>
* <li>
* a path, which holds a value.
* </li>
* </ul>
*
* Examples of identifiers:
* <ul>
* <li>{@code minecraft:fox}</li>
* <li>{@code geysermc:one_fun_example}</li>
* </ul>
*
* If this identifier is referencing anything not in the
* vanilla Minecraft game, the namespace cannot be "minecraft".
* Further, paths cannot contain colons ({@code :}).
*
* @since 2.9.0
*/
public interface Identifier {
/**
* The namespace for Minecraft.
* @since 2.9.0
*/
String DEFAULT_NAMESPACE = "minecraft";
/**
* Attempts to create a new identifier from a namespace and path.
*
* @return the identifier for this namespace and path
* @throws IllegalArgumentException if either namespace or path are invalid.
* @since 2.9.0
*/
static Identifier of(@NonNull String namespace, @NonNull String path) {
return GeyserApi.api().provider(Identifier.class, namespace, path);
}
/**
* Attempts to create a new identifier from a string representation.
*
* @return the identifier for this namespace and path
* @throws IllegalArgumentException if either the namespace or path are invalid
* @since 2.9.0
*/
static Identifier of(String identifier) {
String[] split = identifier.split(":");
String namespace;
String path;
if (split.length == 1) {
namespace = DEFAULT_NAMESPACE;
path = split[0];
} else if (split.length == 2) {
namespace = split[0];
path = split[1];
} else {
throw new IllegalArgumentException("':' in identifier path: " + identifier);
}
return of(namespace, path);
}
/**
* @return the namespace of this identifier.
* @since 2.9.0
*/
String namespace();
/**
* @return the path of this identifier.
* @since 2.9.0
*/
String path();
/**
* Checks whether this identifier is using the "minecraft" namespace.
* @since 2.9.0
*/
default boolean vanilla() {
return namespace().equals(DEFAULT_NAMESPACE);
}
}