mirror of
https://github.com/GeyserMC/Geyser.git
synced 2026-01-04 15:31:36 +00:00
Finish documentation on hashing classes, correctly hash data component removals in item stacks, some cleanups
This commit is contained in:
@@ -41,6 +41,9 @@ import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This interface contains various {@link MinecraftHasher}s used to encode properties of {@link Component}s. Usually, you'll only need {@link ComponentHasher#COMPONENT}.
|
||||
*/
|
||||
public interface ComponentHasher {
|
||||
|
||||
MinecraftHasher<Component> COMPONENT = MinecraftHasher.lazyInitialize(new Supplier<>() {
|
||||
|
||||
@@ -182,7 +182,7 @@ public class DataComponentHashers {
|
||||
register(DataComponentTypes.MAP_DECORATIONS, MinecraftHasher.NBT_MAP);
|
||||
|
||||
register(DataComponentTypes.CHARGED_PROJECTILES, RegistryHasher.ITEM_STACK.list());
|
||||
register(DataComponentTypes.BUNDLE_CONTENTS, RegistryHasher.ITEM_STACK.list());
|
||||
register(DataComponentTypes.BUNDLE_CONTENTS, RegistryHasher.ITEM_STACK.list()); // TODO test data component removal
|
||||
|
||||
registerMap(DataComponentTypes.POTION_CONTENTS, builder -> builder
|
||||
.optional("potion", RegistryHasher.POTION, PotionContents::getPotionId, -1)
|
||||
@@ -285,20 +285,16 @@ public class DataComponentHashers {
|
||||
hashers.put(component, hasher);
|
||||
}
|
||||
|
||||
public static <T> MinecraftHasher<T> hasherOrEmpty(DataComponentType<T> component) {
|
||||
public static <T> MinecraftHasher<T> hasher(DataComponentType<T> component) {
|
||||
MinecraftHasher<T> hasher = (MinecraftHasher<T>) hashers.get(component);
|
||||
if (hasher == null) {
|
||||
return MinecraftHasher.UNIT.cast(value -> Unit.INSTANCE);
|
||||
throw new IllegalStateException("Unregistered hasher for component " + component + "!");
|
||||
}
|
||||
return hasher;
|
||||
}
|
||||
|
||||
public static <T> HashCode hash(GeyserSession session, DataComponentType<T> component, T value) {
|
||||
MinecraftHasher<T> hasher = (MinecraftHasher<T>) hashers.get(component);
|
||||
if (hasher == null) {
|
||||
throw new IllegalStateException("Unregistered hasher for component " + component + "!");
|
||||
}
|
||||
return hasher.hash(value, new MinecraftHashEncoder(session));
|
||||
return hasher(component).hash(value, new MinecraftHashEncoder(session));
|
||||
}
|
||||
|
||||
public static HashedStack hashStack(GeyserSession session, ItemStack stack) {
|
||||
|
||||
@@ -27,14 +27,29 @@ package org.geysermc.geyser.item.hashing;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* {@link MapBuilder}s can be used to define map-like structures to encode a {@link Type} using a {@link MapHasher}.
|
||||
*
|
||||
* @param <Type> the type to encode.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MapBuilder<T> extends UnaryOperator<MapHasher<T>> {
|
||||
public interface MapBuilder<Type> extends UnaryOperator<MapHasher<Type>> {
|
||||
|
||||
default <C> MapBuilder<C> cast() {
|
||||
return builder -> builder.accept(this, casted -> (T) casted);
|
||||
/**
|
||||
* Casts this map builder to a {@link Casted}. This cast is done unsafely, only use this if you are sure the object being encoded is of the type being cast to!
|
||||
*
|
||||
* @param <Casted> the type to cast to.
|
||||
*/
|
||||
default <Casted> MapBuilder<Casted> cast() {
|
||||
return builder -> builder.accept(this, casted -> (Type) casted);
|
||||
}
|
||||
|
||||
static <T> MapBuilder<T> empty() {
|
||||
/**
|
||||
* Returns a map builder that doesn't contain anything.
|
||||
*
|
||||
* @param <Type> the type to encode.
|
||||
*/
|
||||
static <Type> MapBuilder<Type> empty() {
|
||||
return builder -> builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,88 +33,168 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* {@link MapHasher}s are used to encode a {@link Type} to a map-like structure, which is then hashed using a {@link MinecraftHashEncoder}.
|
||||
*
|
||||
* <p>{@link MapHasher}s store the {@link Type} they are encoding, but it isn't directly accessible. Instead, extractor functions are used to extract specific properties of the {@link Type}.</p>
|
||||
*
|
||||
* @param <Type> the type this {@link MapHasher} encodes.
|
||||
*/
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class MapHasher<T> {
|
||||
public class MapHasher<Type> {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final MinecraftHashEncoder encoder;
|
||||
private final T object;
|
||||
private final Type object;
|
||||
private final Map<HashCode, HashCode> map;
|
||||
private final Map<String, Object> unhashed;
|
||||
|
||||
MapHasher(T object, MinecraftHashEncoder encoder) {
|
||||
MapHasher(Type object, MinecraftHashEncoder encoder) {
|
||||
this(object, encoder, new HashMap<>(), DEBUG ? new HashMap<>() : null);
|
||||
}
|
||||
|
||||
private MapHasher(T object, MinecraftHashEncoder encoder, Map<HashCode, HashCode> map, Map<String, Object> unhashed) {
|
||||
private MapHasher(Type object, MinecraftHashEncoder encoder, Map<HashCode, HashCode> map, Map<String, Object> unhashed) {
|
||||
this.encoder = encoder;
|
||||
this.object = object;
|
||||
this.map = map;
|
||||
this.unhashed = unhashed;
|
||||
}
|
||||
|
||||
public MapHasher<T> accept(String key, HashCode hash) {
|
||||
private MapHasher<Type> accept(String key, HashCode hash) {
|
||||
map.put(encoder.string(key), hash);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> acceptConstant(String key, MinecraftHasher<V> hasher, V value) {
|
||||
/**
|
||||
* Adds a constant {@link Value} to the map.
|
||||
*
|
||||
* @param key the key to put the constant in.
|
||||
* @param hasher the hasher used to hash a {@link Value}.
|
||||
* @param value the {@link Value}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> acceptConstant(String key, MinecraftHasher<Value> hasher, Value value) {
|
||||
if (unhashed != null) {
|
||||
unhashed.put(key, value);
|
||||
}
|
||||
return accept(key, hasher.hash(value, encoder));
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> accept(String key, MinecraftHasher<V> hasher, Function<T, V> extractor) {
|
||||
/**
|
||||
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map.
|
||||
*
|
||||
* @param key the key to put the {@link Value} in.
|
||||
* @param hasher the hasher used to hash a {@link Value}.
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> accept(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor) {
|
||||
return acceptConstant(key, hasher, extractor.apply(object));
|
||||
}
|
||||
|
||||
// Adds keys and values from the builder directly to this map (document me properly)
|
||||
public MapHasher<T> accept(MapBuilder<T> builder) {
|
||||
/**
|
||||
* Applies the {@link MapBuilder} to this {@link MapHasher}, essentially adding all the keys it defines here.
|
||||
*/
|
||||
public MapHasher<Type> accept(MapBuilder<Type> builder) {
|
||||
builder.apply(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Adds keys and values from the builder directly to this map (document me properly)
|
||||
public <V> MapHasher<T> accept(MapBuilder<V> builder, Function<T, V> extractor) {
|
||||
/**
|
||||
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and applies the given {@link MapBuilder} for it to this {@link MapHasher},
|
||||
* essentially adding the keys it defines here.
|
||||
*
|
||||
* @param builder the {@link MapBuilder} that encodes a {@link Value}.
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> accept(MapBuilder<Value> builder, Function<Type, Value> extractor) {
|
||||
builder.apply(new MapHasher<>(extractor.apply(object), encoder, map, unhashed));
|
||||
return this;
|
||||
}
|
||||
|
||||
// Adds keys and values from the builder directly to this map (document me properly)
|
||||
public <V> MapHasher<T> accept(Function<V, MapBuilder<T>> builderExtractor, Function<T, V> extractor) {
|
||||
builderExtractor.apply(extractor.apply(object)).apply(this);
|
||||
/**
|
||||
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, then dispatches a {@link MapBuilder} from the {@link Value} using the {@code builderDispatcher},
|
||||
* and applies it to this {@link MapHasher}, essentially adding the keys it defines here.
|
||||
*
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param builderDispatcher the function that dispatches a {@link MapBuilder} from a {@link Value}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> accept(Function<Type, Value> extractor, Function<Value, MapBuilder<Type>> builderDispatcher) {
|
||||
builderDispatcher.apply(extractor.apply(object)).apply(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> optionalNullable(String key, MinecraftHasher<V> hasher, Function<T, V> extractor) {
|
||||
V value = extractor.apply(object);
|
||||
/**
|
||||
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map if it is not null.
|
||||
*
|
||||
* @param key the key to put the {@link Value} in.
|
||||
* @param hasher the hasher used to hash a {@link Value}.
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> optionalNullable(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor) {
|
||||
Value value = extractor.apply(object);
|
||||
if (value != null) {
|
||||
acceptConstant(key, hasher, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> optional(String key, MinecraftHasher<V> hasher, Function<T, Optional<V>> extractor) {
|
||||
Optional<V> value = extractor.apply(object);
|
||||
/**
|
||||
* Extracts an {@link Optional} of a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map if it is present.
|
||||
*
|
||||
* @param key the key to put the {@link Value} in.
|
||||
* @param hasher the hasher used to hash a {@link Value}.
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> optional(String key, MinecraftHasher<Value> hasher, Function<Type, Optional<Value>> extractor) {
|
||||
Optional<Value> value = extractor.apply(object);
|
||||
value.ifPresent(v -> acceptConstant(key, hasher, v));
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> optional(String key, MinecraftHasher<V> hasher, Function<T, V> extractor, V defaultValue) {
|
||||
V value = extractor.apply(object);
|
||||
/**
|
||||
* Extracts a {@link Value} from a {@link Type} using the {@code extractor}, and adds it to the map if it's not equal to {@code defaultValue}.
|
||||
*
|
||||
* @param key the key to put the {@link Value} in.
|
||||
* @param hasher the hasher used to hash a {@link Value}.
|
||||
* @param extractor the function that extracts a {@link Value} from a {@link Type}.
|
||||
* @param defaultValue the default {@link Value}. The {@link Value} won't be added to the map if it equals the default.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> optional(String key, MinecraftHasher<Value> hasher, Function<Type, Value> extractor, Value defaultValue) {
|
||||
Value value = extractor.apply(object);
|
||||
if (!value.equals(defaultValue)) {
|
||||
acceptConstant(key, hasher, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> acceptList(String key, MinecraftHasher<V> valueHasher, Function<T, List<V>> extractor) {
|
||||
/**
|
||||
* Extracts a list of {@link Value}s from a {@link Type}, and adds it to the map.
|
||||
*
|
||||
* @param key the key to put the list of {@link Value}s in.
|
||||
* @param valueHasher the hasher used to hash a single {@link Value}.
|
||||
* @param extractor the function that extracts a list of {@link Value}s from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> acceptList(String key, MinecraftHasher<Value> valueHasher, Function<Type, List<Value>> extractor) {
|
||||
return acceptConstant(key, valueHasher.list(), extractor.apply(object));
|
||||
}
|
||||
|
||||
public <V> MapHasher<T> optionalList(String key, MinecraftHasher<V> valueHasher, Function<T, List<V>> extractor) {
|
||||
List<V> list = extractor.apply(object);
|
||||
/**
|
||||
* Extracts a list of {@link Value}s from a {@link Type}, and adds it to the map if it is not empty.
|
||||
*
|
||||
* @param key the key to put the list of {@link Value}s in.
|
||||
* @param valueHasher the hasher used to hash a single {@link Value}.
|
||||
* @param extractor the function that extracts a list of {@link Value}s from a {@link Type}.
|
||||
* @param <Value> the type of the value.
|
||||
*/
|
||||
public <Value> MapHasher<Type> optionalList(String key, MinecraftHasher<Value> valueHasher, Function<Type, List<Value>> extractor) {
|
||||
List<Value> list = extractor.apply(object);
|
||||
if (!list.isEmpty()) {
|
||||
acceptConstant(key, valueHasher.list(), list);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.Filterable;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -48,7 +49,6 @@ import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@@ -69,7 +69,7 @@ import java.util.stream.IntStream;
|
||||
* </ul>
|
||||
*
|
||||
* <p>On top of these there are more methods, as well as static helper methods in this class, to create hashers.
|
||||
* One worth pointing out is {@link MinecraftHasher#mapBuilder(MapBuilder)}, which hashes an object into a map-like structure. Read the documentation there and on {@link MapBuilder} on how to use it.</p>
|
||||
* One worth pointing out is {@link MinecraftHasher#mapBuilder(MapBuilder)}, which hashes an object into a map-like structure. Read the documentation there, on {@link MapBuilder}, and on {@link MapHasher} on how to use it.</p>
|
||||
*
|
||||
* <p>As of right now, hashers are used to create hash codes for data components, which Minecraft requires to be sent in inventory transactions. You'll find all the hashers for data components in
|
||||
* {@link DataComponentHashers}. In the future, hashers may be used elsewhere as well. If necessary, this system can even be refactored to write to different data structures, such as NBT or JSON files, as well.</p>
|
||||
@@ -116,7 +116,9 @@ public interface MinecraftHasher<Type> {
|
||||
|
||||
MinecraftHasher<Key> KEY = STRING.cast(Key::asString);
|
||||
|
||||
MinecraftHasher<Key> TAG = STRING.cast(key -> "#" + key.asString());
|
||||
MinecraftHasher<Key> TAG = STRING.cast(key -> '#' + key.asString());
|
||||
|
||||
MinecraftHasher<Key> KEY_REMOVAL = STRING.cast(key -> '!' + key.asString());
|
||||
|
||||
MinecraftHasher<UUID> UUID = INT_ARRAY.cast(uuid -> {
|
||||
long mostSignificant = uuid.getMostSignificantBits();
|
||||
@@ -223,7 +225,7 @@ public interface MinecraftHasher<Type> {
|
||||
default <Dispatched> MinecraftHasher<Dispatched> dispatch(String typeKey, Function<Dispatched, Type> typeExtractor, Function<Type, MapBuilder<Dispatched>> mapDispatch) {
|
||||
return mapBuilder(builder -> builder
|
||||
.accept(typeKey, this, typeExtractor)
|
||||
.accept(mapDispatch, typeExtractor));
|
||||
.accept(typeExtractor, mapDispatch));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,17 +245,14 @@ public interface MinecraftHasher<Type> {
|
||||
return (value, encoder) -> memoized.get().hash(value, encoder);
|
||||
}
|
||||
|
||||
// TODO currently unused, has to be used for mob effects, check
|
||||
static <Type> MinecraftHasher<Type> recursive(UnaryOperator<MinecraftHasher<Type>> delegate) {
|
||||
return new Recursive<>(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses {@link Enum#name()} (lowercased) as {@code toName} function in {@link MinecraftHasher#fromIdEnum(Enum[], Function)}.
|
||||
*
|
||||
* <p>Please be aware that you are using literal enum constants as string values here, meaning that if there is a typo in a constant, or a constant changes name, things
|
||||
* may break. Use cautiously.</p>
|
||||
*
|
||||
* @param values the array of {@link EnumConstant}s.
|
||||
* @param <EnumConstant> the enum.
|
||||
* @see MinecraftHasher#fromIdEnum(Enum[], Function)
|
||||
*/
|
||||
static <EnumConstant extends Enum<EnumConstant>> MinecraftHasher<Integer> fromIdEnum(EnumConstant[] values) {
|
||||
@@ -278,6 +277,7 @@ public interface MinecraftHasher<Type> {
|
||||
* <p>Please be aware that you are using literal enum constants as string values here, meaning that if there is a typo in a constant, or a constant changes name, things
|
||||
* may break. Use cautiously.</p>
|
||||
*
|
||||
* @param <EnumConstant> the enum.
|
||||
* @see MinecraftHasher#fromEnum(Function)
|
||||
*/
|
||||
static <EnumConstant extends Enum<EnumConstant>> MinecraftHasher<EnumConstant> fromEnum() {
|
||||
@@ -296,7 +296,7 @@ public interface MinecraftHasher<Type> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that uses a {@link MapBuilder} to hash a {@link Type} into a map.
|
||||
* Creates a hasher that uses a {@link MapBuilder}, which uses a {@link MapHasher} to hash a {@link Type} into a map.
|
||||
*
|
||||
* @param builder the builder that creates a map from a {@link Type}.
|
||||
* @param <Type> the type to hash.
|
||||
@@ -306,20 +306,33 @@ public interface MinecraftHasher<Type> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that hashes {@link K} keys and {@link V} values into a map, using the {@code keyHasher} and {@code valueHasher} respectively.
|
||||
*
|
||||
* <p>Note that the key hasher is usually expected to hash into a string, but this is not necessarily a requirement. It may be once different encoders are used to encode to NBT or JSON,
|
||||
* but this is not the case yet.</p>
|
||||
* Creates a hasher that uses {@link MinecraftHasher#mapSet(MinecraftHasher, MinecraftHasher)} to hash {@link K} keys and {@link V} values into a map,
|
||||
* using the {@code keyHasher} and {@code valueHasher} respectively.
|
||||
*
|
||||
* @param keyHasher the hasher that hashes objects of type {@link K}.
|
||||
* @param valueHasher the hasher that hashes objects of type {@link V}.
|
||||
* @param <K> the key type.
|
||||
* @param <V> the value type.
|
||||
* @see MinecraftHasher#mapSet(MinecraftHasher, MinecraftHasher)
|
||||
*/
|
||||
static <K, V> MinecraftHasher<Map<K, V>> map(MinecraftHasher<K> keyHasher, MinecraftHasher<V> valueHasher) {
|
||||
return (map, encoder) -> encoder.map(map.entrySet().stream()
|
||||
.map(entry -> Map.entry(keyHasher.hash(entry.getKey(), encoder), valueHasher.hash(entry.getValue(), encoder)))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
return MinecraftHasher.<Map.Entry<K, V>>mapSet(keyHasher.cast(Map.Entry::getKey), valueHasher.cast(Map.Entry::getValue)).cast(Map::entrySet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that hashes a set of {@link Type} entries into a map of keys and values, using {@code keyHasher} and {@code valueHasher} respectively.
|
||||
*
|
||||
* <p>Note that the key hasher is usually expected to hash into a string, but this is not necessarily a requirement. It may be once different encoders are used to encode to NBT or JSON,
|
||||
* but this is not the case yet.</p>
|
||||
*
|
||||
* @param keyHasher the hasher that hashes a {@link Type} into a key to be used in the map.
|
||||
* @param valueHasher the hasher that hashes a {@link Type} into a value to be used in the map.
|
||||
* @param <Type> the entry type.
|
||||
* @see MinecraftHasher#map(MinecraftHasher, MinecraftHasher)
|
||||
*/
|
||||
static <Type> MinecraftHasher<Collection<Type>> mapSet(MinecraftHasher<Type> keyHasher, MinecraftHasher<Type> valueHasher) {
|
||||
return (set, encoder) -> encoder.map(set.stream()
|
||||
.collect(Collectors.toMap(value -> keyHasher.hash(value, encoder), value -> valueHasher.hash(value, encoder))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,17 +373,4 @@ public interface MinecraftHasher<Type> {
|
||||
static <Type> MinecraftHasher<Type> dispatch(Function<Type, MinecraftHasher<Type>> hashDispatch) {
|
||||
return (value, encoder) -> hashDispatch.apply(value).hash(value, encoder);
|
||||
}
|
||||
|
||||
class Recursive<T> implements MinecraftHasher<T> {
|
||||
private final Supplier<MinecraftHasher<T>> delegate;
|
||||
|
||||
public Recursive(UnaryOperator<MinecraftHasher<T>> delegate) {
|
||||
this.delegate = Suppliers.memoize(() -> delegate.apply(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashCode hash(T value, MinecraftHashEncoder encoder) {
|
||||
return delegate.get().hash(value, encoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +69,12 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentCo
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttributeModifiers;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemEnchantments;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.JukeboxPlayable;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectDetails;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ProvidesTrimMaterial;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.SuspiciousStewEffect;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ToolData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Unit;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.CustomSound;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.Sound;
|
||||
@@ -86,20 +88,20 @@ import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* {@link RegistryHasher}s are hashers that hash a network, integer ID to an identifier. {@link RegistryHasher}s can be created using static utility methods in this class, and all registry hashers should be kept in here.
|
||||
* {@link RegistryHasher}s are hashers that hash a network integer ID to a namespaced identifier. {@link RegistryHasher}s can be created using static utility methods in this class, and all registry hashers should be kept in here.
|
||||
*
|
||||
* <p>The {@link Type} parameter is only used for registry hashers that are able to encode {@link Holder}s, and must be left as a {@code ?} if this functionality is not in use. This makes it clear the hasher is not
|
||||
* <p>The {@link DirectType} parameter is only used for registry hashers that are able to encode {@link Holder}s, and must be left as a {@code ?} if this functionality is not in use. This makes it clear the hasher is not
|
||||
* supposed to be able to encode holders.</p>
|
||||
*
|
||||
* <p>To create a hasher that can encode a {@link Holder}, a direct hasher should be created that directly hashes a {@link Type} (in case of a custom holder), and {@link RegistryHasher#registry(JavaRegistryKey, MinecraftHasher)}
|
||||
* should be used to create the registry hasher. {@link RegistryHasher#holder()} can then be used to obtain a hasher that encodes a holder of {@link Type}.</p>
|
||||
* <p>To create a hasher that can encode a {@link Holder}, a direct hasher should be created that hashes a {@link DirectType} (in case of a custom holder), and {@link RegistryHasher#registry(JavaRegistryKey, MinecraftHasher)}
|
||||
* should be used to create the registry hasher. {@link RegistryHasher#holder()} can then be used to obtain a hasher that encodes a holder of {@link DirectType}.</p>
|
||||
*
|
||||
* <p>Along with {@link RegistryHasher}s, this class also contains a bunch of hashers for various Minecraft objects. For organisational purposes, these are grouped in various sections with comments.</p>
|
||||
*
|
||||
* @param <Type> the type this hasher hashes. Only used for registry hashers that can hash holders.
|
||||
* @param <DirectType> the type this hasher hashes. Only used for registry hashers that can hash holders.
|
||||
*/
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
public interface RegistryHasher<DirectType> extends MinecraftHasher<Integer> {
|
||||
|
||||
// Java registries
|
||||
|
||||
@@ -122,6 +124,8 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
|
||||
RegistryHasher<?> POTION = enumIdRegistry(Potion.values());
|
||||
|
||||
RegistryHasher<?> VILLAGER_TYPE = enumIdRegistry(VillagerVariant.values());
|
||||
|
||||
// Java data-driven registries
|
||||
|
||||
MinecraftHasher<BuiltinSound> BUILTIN_SOUND = KEY.cast(sound -> MinecraftKey.key(sound.getName()));
|
||||
@@ -175,8 +179,6 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
|
||||
RegistryHasher<BannerPatternLayer.BannerPattern> BANNER_PATTERN = registry(JavaRegistries.BANNER_PATTERN, DIRECT_BANNER_PATTERN);
|
||||
|
||||
RegistryHasher<?> VILLAGER_TYPE = enumIdRegistry(VillagerVariant.values());
|
||||
|
||||
RegistryHasher<?> WOLF_VARIANT = registry(JavaRegistries.WOLF_VARIANT);
|
||||
|
||||
RegistryHasher<?> WOLF_SOUND_VARIANT = registry(JavaRegistries.WOLF_SOUND_VARIANT);
|
||||
@@ -221,26 +223,36 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
|
||||
// Widely used Minecraft types
|
||||
|
||||
MinecraftHasher<DataComponent<?, ?>> DATA_COMPONENT_KEY = MinecraftHasher.either(KEY,
|
||||
component -> component.getValue() == null ? null : component.getType().getKey(), KEY_REMOVAL, component -> component.getType().getKey());
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // Java generics :(
|
||||
MinecraftHasher<DataComponent<?, ?>> DATA_COMPONENT = (component, encoder) -> {
|
||||
MinecraftHasher hasher = DataComponentHashers.hasherOrEmpty(component.getType());
|
||||
MinecraftHasher<DataComponent<?, ?>> DATA_COMPONENT_VALUE = (component, encoder) -> {
|
||||
if (component.getValue() == null) {
|
||||
return UNIT.hash(Unit.INSTANCE, encoder);
|
||||
}
|
||||
MinecraftHasher hasher = DataComponentHashers.hasher(component.getType());
|
||||
return hasher.hash(component.getValue(), encoder);
|
||||
};
|
||||
|
||||
MinecraftHasher<DataComponents> DATA_COMPONENTS = MinecraftHasher.map(RegistryHasher.DATA_COMPONENT_TYPE, DATA_COMPONENT).cast(DataComponents::getDataComponents); // TODO component removals (needs unit value and ! component prefix)
|
||||
MinecraftHasher<DataComponents> DATA_COMPONENTS = MinecraftHasher.mapSet(DATA_COMPONENT_KEY, DATA_COMPONENT_VALUE).cast(components -> components.getDataComponents().values());
|
||||
|
||||
MinecraftHasher<ItemStack> ITEM_STACK = MinecraftHasher.mapBuilder(builder -> builder
|
||||
.accept("id", ITEM, ItemStack::getId)
|
||||
.accept("count", INT, ItemStack::getAmount)
|
||||
.optionalNullable("components", DATA_COMPONENTS, ItemStack::getDataComponentsPatch));
|
||||
|
||||
// Encoding of hidden effects is unfortunately not possible
|
||||
MapBuilder<MobEffectDetails> MOB_EFFECT_DETAILS = builder -> builder
|
||||
.optional("amplifier", BYTE, instance -> (byte) instance.getAmplifier(), (byte) 0)
|
||||
.optional("duration", INT, MobEffectDetails::getDuration, 0)
|
||||
.optional("ambient", BOOL, MobEffectDetails::isAmbient, false)
|
||||
.optional("show_particles", BOOL, MobEffectDetails::isShowParticles, true)
|
||||
.accept("show_icon", BOOL, MobEffectDetails::isShowIcon); // Yes, this is not an optional. I checked. Maybe it will be in the future and break everything!
|
||||
|
||||
MinecraftHasher<MobEffectInstance> MOB_EFFECT_INSTANCE = MinecraftHasher.mapBuilder(builder -> builder
|
||||
.accept("id", RegistryHasher.EFFECT, MobEffectInstance::getEffect)
|
||||
.optional("amplifier", BYTE, instance -> (byte) instance.getDetails().getAmplifier(), (byte) 0)
|
||||
.optional("duration", INT, instance -> instance.getDetails().getDuration(), 0)
|
||||
.optional("ambient", BOOL, instance -> instance.getDetails().isAmbient(), false)
|
||||
.optional("show_particles", BOOL, instance -> instance.getDetails().isShowParticles(), true)
|
||||
.accept("show_icon", BOOL, instance -> instance.getDetails().isShowIcon())); // TODO check this, also hidden effect but is recursive
|
||||
.accept(MOB_EFFECT_DETAILS, MobEffectInstance::getDetails));
|
||||
|
||||
MinecraftHasher<ModifierOperation> ATTRIBUTE_MODIFIER_OPERATION = MinecraftHasher.fromEnum(operation -> switch (operation) {
|
||||
case ADD -> "add_value";
|
||||
@@ -344,7 +356,7 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that using {@link RegistryHasher#registry(JavaRegistryKey)} that is also able to encode {@link Holder}s by using the {@code directHasher}.
|
||||
* Creates a hasher that encodes network IDs using {@link RegistryHasher#registry(JavaRegistryKey)}, and is also able to encode {@link Holder}s by using the {@code directHasher}.
|
||||
*
|
||||
* <p>A hasher that encodes {@link Holder}s can be obtained by using {@link RegistryHasher#holder()}</p>
|
||||
*
|
||||
@@ -358,13 +370,27 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
return new RegistryHasherWithDirectHasher<>(registry(registry), directHasher);
|
||||
}
|
||||
|
||||
default MinecraftHasher<Holder<Type>> holder() {
|
||||
if (this instanceof RegistryHasher.RegistryHasherWithDirectHasher<Type> withDirect) {
|
||||
/**
|
||||
* Creates a hasher that encodes a {@link Holder} of {@link DirectType}. If the holder has an ID, the {@link RegistryHasher} is used to encode it. If the holder is custom,
|
||||
* a direct hasher specified in {@link RegistryHasher#registry(JavaRegistryKey, MinecraftHasher)} is used to encode it.
|
||||
*
|
||||
* <p>This method can only be used if this hasher has a direct hasher attached to it. That is only the case if {@link DirectType} is not {@code ?}. If this hasher doesn't have
|
||||
* a direct hasher, a {@link IllegalStateException} will be thrown upon use.</p>
|
||||
*
|
||||
* @throws IllegalStateException when this hasher does not have a direct hasher attached to it.
|
||||
*/
|
||||
default MinecraftHasher<Holder<DirectType>> holder() {
|
||||
if (this instanceof RegistryHasher.RegistryHasherWithDirectHasher<DirectType> withDirect) {
|
||||
return withDirect.holderHasher;
|
||||
}
|
||||
throw new IllegalStateException("Tried to create a holder hasher on a registry hasher that does not have a direct hasher specified");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that hashes a {@link HolderSet} of the registry. {@link HolderSet}s can encode as a hash-prefixed tag, a single namespaced ID, or a list of namespaced IDs.
|
||||
*
|
||||
* <p>The hasher throws a {@link IllegalStateException} if the holder set does not have a tag nor a list of IDs. This should never happen.</p>
|
||||
*/
|
||||
default MinecraftHasher<HolderSet> holderSet() {
|
||||
return (holder, encoder) -> {
|
||||
if (holder.getLocation() != null) {
|
||||
@@ -379,15 +405,41 @@ public interface RegistryHasher<Type> extends MinecraftHasher<Integer> {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO note that this only works if the enum constants match
|
||||
/**
|
||||
* Creates a hasher that uses {@link Enum#name()} (lowercased) to create a key in the {@code minecraft} namespace, and then hashes it.
|
||||
*
|
||||
* <p>Please be aware that you are using literal enum constants as key paths here, meaning that if there is a typo in a constant, or a constant changes name, things
|
||||
* may break. Use cautiously.</p>
|
||||
*
|
||||
* @param <EnumConstant> the enum.
|
||||
*/
|
||||
static <EnumConstant extends Enum<EnumConstant>> MinecraftHasher<EnumConstant> enumRegistry() {
|
||||
return KEY.cast(constant -> MinecraftKey.key(constant.name().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses {@link Enum#name()} (lowercased) to create a function that creates a {@link Key} from a {@link EnumConstant}, and uses this as {@code toKey}
|
||||
* function in {@link RegistryHasher#enumIdRegistry(Enum[], Function)}.
|
||||
*
|
||||
* <p>Please be aware that you are using literal enum constants as key paths here, meaning that if there is a typo in a constant, or a constant changes name, things
|
||||
* may break. Use cautiously.</p>
|
||||
*
|
||||
* @param values the array of {@link EnumConstant}s.
|
||||
* @param <EnumConstant> the enum.
|
||||
* @see RegistryHasher#enumIdRegistry(Enum[], Function)
|
||||
*/
|
||||
static <EnumConstant extends Enum<EnumConstant>> RegistryHasher<?> enumIdRegistry(EnumConstant[] values) {
|
||||
return enumIdRegistry(values, constant -> MinecraftKey.key(constant.name().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hasher that looks up a network ID in the array of {@link EnumConstant}s, and then uses {@code toKey} to turn the constant into a key, which it then hashes.
|
||||
*
|
||||
* @param values the array of {@link EnumConstant}s.
|
||||
* @param toKey the function that turns a {@link EnumConstant} into a {@link Key}.
|
||||
* @param <EnumConstant> the enum.
|
||||
* @see MinecraftHasher#fromIdEnum(Enum[])
|
||||
*/
|
||||
static <EnumConstant extends Enum<EnumConstant>> RegistryHasher<?> enumIdRegistry(EnumConstant[] values, Function<EnumConstant, Key> toKey) {
|
||||
MinecraftHasher<Integer> hasher = KEY.cast(i -> toKey.apply(values[i]));
|
||||
return hasher::hash;
|
||||
|
||||
@@ -84,7 +84,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
|
||||
entity.updateOwnRotation(entity.getYaw(), entity.getPitch(), entity.getHeadYaw());
|
||||
|
||||
session.setSpawned(true);
|
||||
DataComponentHashers.testHashing(session);
|
||||
// DataComponentHashers.testHashing(session); // TODO remove me
|
||||
|
||||
// Make sure the player moves away from (0, 32767, 0) before accepting movement packets
|
||||
session.setUnconfirmedTeleport(new TeleportCache(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ(), packet.getXRot(), packet.getYRot(), packet.getId())); // TODO
|
||||
|
||||
Reference in New Issue
Block a user