mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Implement hashers for new object component type (#5935)
* Implement hashers for new object component type * Remove extra whitespace
This commit is contained in:
@@ -28,6 +28,7 @@ package org.geysermc.geyser.item.hashing;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.KeybindComponent;
|
import net.kyori.adventure.text.KeybindComponent;
|
||||||
import net.kyori.adventure.text.NBTComponent;
|
import net.kyori.adventure.text.NBTComponent;
|
||||||
|
import net.kyori.adventure.text.ObjectComponent;
|
||||||
import net.kyori.adventure.text.ScoreComponent;
|
import net.kyori.adventure.text.ScoreComponent;
|
||||||
import net.kyori.adventure.text.SelectorComponent;
|
import net.kyori.adventure.text.SelectorComponent;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
@@ -39,6 +40,9 @@ import net.kyori.adventure.text.format.ShadowColor;
|
|||||||
import net.kyori.adventure.text.format.Style;
|
import net.kyori.adventure.text.format.Style;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import net.kyori.adventure.text.object.ObjectContents;
|
||||||
|
import net.kyori.adventure.text.object.PlayerHeadObjectContents;
|
||||||
|
import org.geysermc.geyser.item.hashing.data.ObjectContentsType;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -54,6 +58,17 @@ public interface ComponentHasher {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
MinecraftHasher<PlayerHeadObjectContents.ProfileProperty> PROFILE_PROPERTY = MinecraftHasher.mapBuilder(builder -> builder
|
||||||
|
.accept("name", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::name)
|
||||||
|
.accept("value", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::value)
|
||||||
|
.accept("signature", MinecraftHasher.STRING, PlayerHeadObjectContents.ProfileProperty::signature));
|
||||||
|
|
||||||
|
MinecraftHasher<PlayerHeadObjectContents> RESOLVABLE_PROFILE = MinecraftHasher.mapBuilder(builder -> builder
|
||||||
|
.optionalNullable("name", MinecraftHasher.STRING, PlayerHeadObjectContents::name)
|
||||||
|
.optionalNullable("id", MinecraftHasher.UUID, PlayerHeadObjectContents::id)
|
||||||
|
.optionalList("properties", PROFILE_PROPERTY, PlayerHeadObjectContents::profileProperties)
|
||||||
|
.optionalNullable("texture", MinecraftHasher.KEY, PlayerHeadObjectContents::texture));
|
||||||
|
|
||||||
MinecraftHasher<NamedTextColor> NAMED_COLOR = MinecraftHasher.STRING.cast(NamedTextColor::toString);
|
MinecraftHasher<NamedTextColor> NAMED_COLOR = MinecraftHasher.STRING.cast(NamedTextColor::toString);
|
||||||
|
|
||||||
MinecraftHasher<TextColor> DIRECT_COLOR = MinecraftHasher.STRING.cast(TextColor::asHexString);
|
MinecraftHasher<TextColor> DIRECT_COLOR = MinecraftHasher.STRING.cast(TextColor::asHexString);
|
||||||
@@ -151,6 +166,13 @@ public interface ComponentHasher {
|
|||||||
.optional("interpret", MinecraftHasher.BOOL, NBTComponent::interpret, false)
|
.optional("interpret", MinecraftHasher.BOOL, NBTComponent::interpret, false)
|
||||||
.optionalNullable("separator", COMPONENT, NBTComponent::separator)); // TODO source key, needs kyori update?
|
.optionalNullable("separator", COMPONENT, NBTComponent::separator)); // TODO source key, needs kyori update?
|
||||||
|
|
||||||
|
MinecraftHasher<ObjectContentsType> OBJECT_CONTENTS_TYPE = MinecraftHasher.fromEnum(ObjectContentsType::getName);
|
||||||
|
|
||||||
|
MapBuilder<ObjectContents> OBJECT_CONTENTS = MapBuilder.dispatch("object", OBJECT_CONTENTS_TYPE, ObjectContentsType::fromContents, ObjectContentsType::mapBuilder);
|
||||||
|
|
||||||
|
MinecraftHasher<ObjectComponent> OBJECT_COMPONENT = component(builder -> builder
|
||||||
|
.accept(OBJECT_CONTENTS, ObjectComponent::contents));
|
||||||
|
|
||||||
MinecraftHasher<Component> ACTUAL_COMPONENT = (component, encoder) -> {
|
MinecraftHasher<Component> ACTUAL_COMPONENT = (component, encoder) -> {
|
||||||
if (component instanceof TextComponent text) {
|
if (component instanceof TextComponent text) {
|
||||||
return TEXT_COMPONENT.hash(text, encoder);
|
return TEXT_COMPONENT.hash(text, encoder);
|
||||||
@@ -164,9 +186,10 @@ public interface ComponentHasher {
|
|||||||
return SELECTOR_COMPONENT.hash(selector, encoder);
|
return SELECTOR_COMPONENT.hash(selector, encoder);
|
||||||
} else if (component instanceof NBTComponent<?,?> nbt) {
|
} else if (component instanceof NBTComponent<?,?> nbt) {
|
||||||
return NBT_COMPONENT.hash(nbt, encoder);
|
return NBT_COMPONENT.hash(nbt, encoder);
|
||||||
|
} else if (component instanceof ObjectComponent object) {
|
||||||
|
return OBJECT_COMPONENT.hash(object, encoder);
|
||||||
}
|
}
|
||||||
// TODO support object component hashing!
|
throw new UnsupportedOperationException("Unimplemented component hasher: " + component);
|
||||||
throw new IllegalStateException("Unimplemented component hasher: " + component);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static <T extends Component> MinecraftHasher<T> component(MapBuilder<T> componentBuilder) {
|
private static <T extends Component> MinecraftHasher<T> component(MapBuilder<T> componentBuilder) {
|
||||||
|
|||||||
@@ -57,6 +57,34 @@ public interface MapBuilder<Type> extends UnaryOperator<MapHasher<Type>> {
|
|||||||
return builder -> builder;
|
return builder -> builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link MapBuilder#dispatch(String, MinecraftHasher, Function, Function)}, uses {@code "type"} as the {@code typeKey}.
|
||||||
|
*
|
||||||
|
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||||
|
*/
|
||||||
|
static <Type, Dispatched> MapBuilder<Dispatched> dispatch(MinecraftHasher<Type> typeHasher, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> hashDispatch) {
|
||||||
|
return dispatch("type", typeHasher, typeExtractor, hashDispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a map builder that dispatches a {@link Type} from a {@link Dispatched} using {@code typeExtractor}, puts this as the {@code typeKey} key in the map using the given {@code typeHasher},
|
||||||
|
* and uses a {@link MapBuilder} provided by {@code mapDispatch} to build the rest of the map.
|
||||||
|
*
|
||||||
|
* <p>This can be used to create map builders that build an abstract type or interface into a map with different keys depending on the type.</p>
|
||||||
|
*
|
||||||
|
* @param typeKey the key to store the {@link Type} in.
|
||||||
|
* @param typeHasher the hasher used to encode the {@link Type}.
|
||||||
|
* @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}.
|
||||||
|
* @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}.
|
||||||
|
* @param <Type> the type of the {@code typeKey}.
|
||||||
|
* @param <Dispatched> the type of the new map builder.
|
||||||
|
*/
|
||||||
|
static <Type, Dispatched> MapBuilder<Dispatched> dispatch(String typeKey, MinecraftHasher<Type> typeHasher, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
||||||
|
return builder -> builder
|
||||||
|
.accept(typeKey, typeHasher, typeExtractor)
|
||||||
|
.accept(typeExtractor, mapDispatch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that creates a map builder from an NBT map. The builder simply adds all keys from the NBT map.
|
* Returns a function that creates a map builder from an NBT map. The builder simply adds all keys from the NBT map.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ public interface MinecraftHasher<Type> {
|
|||||||
* Delegates to {@link MinecraftHasher#dispatch(String, Function, Function)}, uses {@code "type"} as the {@code typeKey}.
|
* Delegates to {@link MinecraftHasher#dispatch(String, Function, Function)}, uses {@code "type"} as the {@code typeKey}.
|
||||||
*
|
*
|
||||||
* @see MinecraftHasher#dispatch(String, Function, Function)
|
* @see MinecraftHasher#dispatch(String, Function, Function)
|
||||||
|
* @see MapBuilder#dispatch(MinecraftHasher, Function, Function)
|
||||||
|
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||||
*/
|
*/
|
||||||
default <Dispatched> MinecraftHasher<Dispatched> dispatch(Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> hashDispatch) {
|
default <Dispatched> MinecraftHasher<Dispatched> dispatch(Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> hashDispatch) {
|
||||||
return dispatch("type", typeExtractor, hashDispatch);
|
return dispatch("type", typeExtractor, hashDispatch);
|
||||||
@@ -227,15 +229,17 @@ public interface MinecraftHasher<Type> {
|
|||||||
*
|
*
|
||||||
* <p>This can be used to create hashers that hash an abstract type or interface into a map with different keys depending on the type.</p>
|
* <p>This can be used to create hashers that hash an abstract type or interface into a map with different keys depending on the type.</p>
|
||||||
*
|
*
|
||||||
|
* <p>Internally this simply delegates to and wraps {@link MapBuilder#dispatch(String, MinecraftHasher, Function, Function)} in {@link MinecraftHasher#mapBuilder(MapBuilder)},
|
||||||
|
* using {@code this} as {@code typeHasher}.</p>
|
||||||
|
*
|
||||||
* @param typeKey the key to store the {@link Type} in.
|
* @param typeKey the key to store the {@link Type} in.
|
||||||
* @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}.
|
* @param typeExtractor the function that extracts a {@link Type} from a {@link Dispatched}.
|
||||||
* @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}.
|
* @param mapDispatch the function that provides a {@link MapBuilder} based on a {@link Type}.
|
||||||
* @param <Dispatched> the type of the new hasher.
|
* @param <Dispatched> the type of the new hasher.
|
||||||
|
* @see MapBuilder#dispatch(String, MinecraftHasher, Function, Function)
|
||||||
*/
|
*/
|
||||||
default <Dispatched> MinecraftHasher<Dispatched> dispatch(String typeKey, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
default <Dispatched> MinecraftHasher<Dispatched> dispatch(String typeKey, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
||||||
return mapBuilder(builder -> builder
|
return mapBuilder(MapBuilder.dispatch(typeKey, this, typeExtractor, mapDispatch));
|
||||||
.accept(typeKey, this, typeExtractor)
|
|
||||||
.accept(typeExtractor, mapDispatch));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
|
|||||||
|
|
||||||
MinecraftHasher<ConsumeEffectType> CONSUME_EFFECT_TYPE = enumRegistry();
|
MinecraftHasher<ConsumeEffectType> CONSUME_EFFECT_TYPE = enumRegistry();
|
||||||
|
|
||||||
MinecraftHasher<ConsumeEffect> CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, type -> type.getBuilder().cast());
|
MinecraftHasher<ConsumeEffect> CONSUME_EFFECT = CONSUME_EFFECT_TYPE.dispatch(ConsumeEffectType::fromEffect, ConsumeEffectType::mapBuilder);
|
||||||
|
|
||||||
MinecraftHasher<SuspiciousStewEffect> SUSPICIOUS_STEW_EFFECT = MinecraftHasher.mapBuilder(builder -> builder
|
MinecraftHasher<SuspiciousStewEffect> SUSPICIOUS_STEW_EFFECT = MinecraftHasher.mapBuilder(builder -> builder
|
||||||
.accept("id", EFFECT_ID, SuspiciousStewEffect::getMobEffectId)
|
.accept("id", EFFECT_ID, SuspiciousStewEffect::getMobEffectId)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.item.hashing.data;
|
package org.geysermc.geyser.item.hashing.data;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.geysermc.geyser.item.hashing.MapBuilder;
|
import org.geysermc.geyser.item.hashing.MapBuilder;
|
||||||
import org.geysermc.geyser.item.hashing.MinecraftHasher;
|
import org.geysermc.geyser.item.hashing.MinecraftHasher;
|
||||||
import org.geysermc.geyser.item.hashing.RegistryHasher;
|
import org.geysermc.geyser.item.hashing.RegistryHasher;
|
||||||
@@ -44,7 +43,6 @@ public enum ConsumeEffectType {
|
|||||||
.accept("sound", RegistryHasher.SOUND_EVENT, ConsumeEffect.PlaySound::sound));
|
.accept("sound", RegistryHasher.SOUND_EVENT, ConsumeEffect.PlaySound::sound));
|
||||||
|
|
||||||
private final Class<? extends ConsumeEffect> clazz;
|
private final Class<? extends ConsumeEffect> clazz;
|
||||||
@Getter
|
|
||||||
private final MapBuilder<? extends ConsumeEffect> builder;
|
private final MapBuilder<? extends ConsumeEffect> builder;
|
||||||
|
|
||||||
<T extends ConsumeEffect> ConsumeEffectType(Class<T> clazz) {
|
<T extends ConsumeEffect> ConsumeEffectType(Class<T> clazz) {
|
||||||
@@ -57,6 +55,10 @@ public enum ConsumeEffectType {
|
|||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MapBuilder<ConsumeEffect> mapBuilder() {
|
||||||
|
return builder.cast();
|
||||||
|
}
|
||||||
|
|
||||||
public static ConsumeEffectType fromEffect(ConsumeEffect effect) {
|
public static ConsumeEffectType fromEffect(ConsumeEffect effect) {
|
||||||
Class<? extends ConsumeEffect> clazz = effect.getClass();
|
Class<? extends ConsumeEffect> clazz = effect.getClass();
|
||||||
for (ConsumeEffectType type : values()) {
|
for (ConsumeEffectType type : values()) {
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.hashing.data;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.kyori.adventure.text.object.ObjectContents;
|
||||||
|
import net.kyori.adventure.text.object.PlayerHeadObjectContents;
|
||||||
|
import net.kyori.adventure.text.object.SpriteObjectContents;
|
||||||
|
import org.geysermc.geyser.item.hashing.ComponentHasher;
|
||||||
|
import org.geysermc.geyser.item.hashing.MapBuilder;
|
||||||
|
import org.geysermc.geyser.item.hashing.MinecraftHasher;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public enum ObjectContentsType {
|
||||||
|
ATLAS("atlas", SpriteObjectContents.class,
|
||||||
|
builder -> builder
|
||||||
|
.optional("atlas", MinecraftHasher.KEY, SpriteObjectContents::atlas, SpriteObjectContents.DEFAULT_ATLAS)
|
||||||
|
.accept("sprite", MinecraftHasher.KEY, SpriteObjectContents::sprite)),
|
||||||
|
PLAYER("player", PlayerHeadObjectContents.class,
|
||||||
|
builder -> builder
|
||||||
|
.accept("player", ComponentHasher.RESOLVABLE_PROFILE, Function.identity())
|
||||||
|
.optional("hat", MinecraftHasher.BOOL, PlayerHeadObjectContents::hat, true));
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
private final MapBuilder<? extends ObjectContents> builder;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // So Java knows what T we are talking about
|
||||||
|
<T extends ObjectContents> ObjectContentsType(String name, Class<T> clazz, MapBuilder<T> builder) {
|
||||||
|
this.name = name;
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapBuilder<ObjectContents> mapBuilder() {
|
||||||
|
return builder.cast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObjectContentsType fromContents(ObjectContents contents) {
|
||||||
|
if (contents instanceof SpriteObjectContents) {
|
||||||
|
return ATLAS;
|
||||||
|
} else if (contents instanceof PlayerHeadObjectContents) {
|
||||||
|
return PLAYER;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("Don't know how to hash object contents of type " + contents.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user