mirror of
https://github.com/GeyserMC/Geyser.git
synced 2026-01-06 15:41:50 +00:00
Implement JavaRegistry(Provider) for game tests, get basic test for component hashing working
This commit is contained in:
@@ -40,7 +40,10 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.util.HashOps;
|
||||
import org.geysermc.geyser.gametest.registries.GameTestJavaRegistryProvider;
|
||||
import org.geysermc.geyser.item.hashing.DataComponentHashers;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
|
||||
@@ -48,39 +51,42 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GeyserComponentHashTestInstance<T> extends GameTestInstance {
|
||||
public class GeyserComponentHashTestInstance extends GameTestInstance {
|
||||
|
||||
private static final MapCodec<TypedDataComponent<?>> TYPED_COMPONENT_CODEC = DataComponentType.PERSISTENT_CODEC
|
||||
.dispatchMap("component", TypedDataComponent::type, GeyserComponentHashTestInstance::typedComponentCodec);
|
||||
public static final MapCodec<GeyserComponentHashTestInstance<?>> CODEC = TYPED_COMPONENT_CODEC.xmap(GeyserComponentHashTestInstance::new,
|
||||
instance -> instance.testValue);
|
||||
private static final MapCodec<List<TypedDataComponent<?>>> TYPED_COMPONENT_CODEC = DataComponentType.PERSISTENT_CODEC
|
||||
.dispatchMap("component", list -> list.getFirst().type(), GeyserComponentHashTestInstance::typedComponentListCodec);
|
||||
public static final MapCodec<GeyserComponentHashTestInstance> CODEC = TYPED_COMPONENT_CODEC.xmap(GeyserComponentHashTestInstance::new,
|
||||
instance -> instance.testCases);
|
||||
|
||||
private final TypedDataComponent<T> testValue;
|
||||
private final List<TypedDataComponent<?>> testCases;
|
||||
|
||||
public GeyserComponentHashTestInstance(TypedDataComponent<T> testValue) {
|
||||
public GeyserComponentHashTestInstance(List<TypedDataComponent<?>> testCases) {
|
||||
// TODO use default vanilla test environment
|
||||
super(new TestData<>(Holder.direct(new TestEnvironmentDefinition.AllOf(List.of())),
|
||||
ResourceLocation.withDefaultNamespace("empty"), 1, 1, true));
|
||||
this.testValue = testValue;
|
||||
this.testCases = testCases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(@NotNull GameTestHelper helper) {
|
||||
// Encode vanilla component to buffer
|
||||
RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), helper.getLevel().registryAccess());
|
||||
TypedDataComponent.STREAM_CODEC.encode(buffer, testValue);
|
||||
|
||||
// Read with MCPL
|
||||
int id = MinecraftTypes.readVarInt(buffer);
|
||||
DataComponent<?, ?> mcplComponent = DataComponentTypes.from(id).readDataComponent(buffer);
|
||||
|
||||
// Hash both and compare
|
||||
RegistryOps<HashCode> ops = RegistryOps.create(HashOps.CRC32C_INSTANCE, helper.getLevel().registryAccess());
|
||||
int expected = testValue.encodeValue(ops).getOrThrow().asInt();
|
||||
//int geyser = DataComponentHashers.hash(session, mcplComponent).asInt();
|
||||
int geyser = 0;
|
||||
|
||||
helper.assertValueEqual(expected, geyser, Component.literal("Hash for component " + testValue));
|
||||
for (TypedDataComponent<?> testCase : testCases) {
|
||||
// Encode vanilla component to buffer
|
||||
RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), helper.getLevel().registryAccess());
|
||||
TypedDataComponent.STREAM_CODEC.encode(buffer, testCase);
|
||||
|
||||
// Read with MCPL
|
||||
int id = MinecraftTypes.readVarInt(buffer);
|
||||
DataComponent<?, ?> mcplComponent = DataComponentTypes.from(id).readDataComponent(buffer);
|
||||
|
||||
// Hash both and compare
|
||||
int expected = testCase.encodeValue(ops).getOrThrow().asInt();
|
||||
GameTestJavaRegistryProvider registries = new GameTestJavaRegistryProvider(helper.getLevel().registryAccess());
|
||||
int geyser = DataComponentHashers.hash(registries, mcplComponent).asInt();
|
||||
|
||||
helper.assertValueEqual(expected, geyser, Component.literal("Hash for component " + testCase));
|
||||
}
|
||||
|
||||
// Succeed if nothing was thrown
|
||||
helper.succeed();
|
||||
@@ -97,7 +103,17 @@ public class GeyserComponentHashTestInstance<T> extends GameTestInstance {
|
||||
return Component.literal("Geyser Data Component Hash Test");
|
||||
}
|
||||
|
||||
private static <T> MapCodec<TypedDataComponent<T>> typedComponentCodec(DataComponentType<T> component) {
|
||||
return component.codecOrThrow().fieldOf("value").xmap(value -> new TypedDataComponent<>(component, value), TypedDataComponent::value);
|
||||
// Generics are NOT friendly!!!
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private static MapCodec<List<TypedDataComponent<?>>> typedComponentListCodec(DataComponentType component) {
|
||||
return ExtraCodecs.compactListCodec(component.codecOrThrow()).fieldOf("value")
|
||||
.xmap(
|
||||
values -> ((List<?>) values).stream()
|
||||
.map(testCase -> new TypedDataComponent(component, testCase))
|
||||
.map(testCase -> (TypedDataComponent<?>) testCase)
|
||||
.toList(),
|
||||
typedComponents -> ((List<?>) typedComponents).stream()
|
||||
.map(testCase -> ((TypedDataComponent<?>) testCase).value())
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package org.geysermc.geyser.gametest.registries;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
||||
import org.cloudburstmc.nbt.NbtList;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// Stolen from mappings-gen: is there a better way to do this?
|
||||
public class CloudburstNbtOps implements DynamicOps<Object> {
|
||||
public static final CloudburstNbtOps INSTANCE = new CloudburstNbtOps();
|
||||
|
||||
@Override
|
||||
public Object empty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object emptyMap() {
|
||||
return NbtMap.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object emptyList() {
|
||||
return NbtList.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> U convertTo(DynamicOps<U> outOps, Object input) {
|
||||
if (input == empty()) {
|
||||
return outOps.empty();
|
||||
}
|
||||
NbtType<?> type = NbtType.byClass(input.getClass());
|
||||
return switch (type.getEnum()) {
|
||||
case END -> outOps.empty();
|
||||
case BYTE -> outOps.createByte((Byte) input);
|
||||
case SHORT -> outOps.createShort((Short) input);
|
||||
case INT -> outOps.createInt((Integer) input);
|
||||
case LONG -> outOps.createLong((Long) input);
|
||||
case FLOAT -> outOps.createFloat((Float) input);
|
||||
case DOUBLE -> outOps.createDouble((Double) input);
|
||||
case BYTE_ARRAY -> outOps.createByteList(ByteBuffer.wrap((byte[]) input));
|
||||
case STRING -> outOps.createString((String) input);
|
||||
case LIST -> this.convertList(outOps, input);
|
||||
case COMPOUND -> this.convertMap(outOps, input);
|
||||
case INT_ARRAY -> outOps.createIntList(Arrays.stream((int[]) input));
|
||||
case LONG_ARRAY -> outOps.createLongList(Arrays.stream((long[]) input));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<Number> getNumberValue(Object input) {
|
||||
if (input instanceof Number) {
|
||||
return DataResult.success((Number) input);
|
||||
}
|
||||
return DataResult.error(() -> "Input is not a number: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getNumberValue(Object input, Number defaultValue) {
|
||||
return input instanceof Number ? (Number) input : defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createNumeric(Number i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<String> getStringValue(Object input) {
|
||||
if (input instanceof String) {
|
||||
return DataResult.success((String) input);
|
||||
}
|
||||
return DataResult.error(() -> "Input is not a string: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createString(String value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public DataResult<Object> mergeToList(Object list, Object value) {
|
||||
if (list == empty()) {
|
||||
NbtType<?> type = NbtType.byClass(value.getClass());
|
||||
return DataResult.success(new NbtList(type, value));
|
||||
}
|
||||
if (list instanceof NbtList<?> nbtList) {
|
||||
List listBuilder = new ArrayList<>(nbtList);
|
||||
listBuilder.add(value);
|
||||
return DataResult.success(new NbtList(nbtList.getType(), listBuilder));
|
||||
}
|
||||
return DataResult.error(() -> "mergeToList was not called with a list: " + list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<Object> mergeToList(Object list, List<Object> values) {
|
||||
if (list == empty()) {
|
||||
if (values.isEmpty()) {
|
||||
return DataResult.success(emptyList());
|
||||
}
|
||||
NbtType<?> type = NbtType.byClass(values.get(0).getClass());
|
||||
return DataResult.success(new NbtList(type, values));
|
||||
}
|
||||
if (list instanceof NbtList<?> nbtList) {
|
||||
if (values.isEmpty()) {
|
||||
return DataResult.success(nbtList);
|
||||
}
|
||||
if (nbtList.isEmpty()) {
|
||||
return DataResult.success(new NbtList(NbtType.byClass(values.get(0).getClass()), values));
|
||||
}
|
||||
List listBuilder = new ArrayList<>(nbtList);
|
||||
listBuilder.addAll(values);
|
||||
return DataResult.success(new NbtList(nbtList.getType(), listBuilder));
|
||||
}
|
||||
return DataResult.error(() -> "mergeToList was not called with a list: " + list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<Object> mergeToMap(Object map, Object key, Object value) {
|
||||
if (!(map instanceof NbtMap) && map != null) {
|
||||
return DataResult.error(() -> "mergeToMap called with not a map: " + map, map);
|
||||
} else if (!(key instanceof String)) {
|
||||
return DataResult.error(() -> "key is not a string: " + key, map);
|
||||
} else {
|
||||
NbtMapBuilder builder;
|
||||
if (map instanceof NbtMap nbtMap) {
|
||||
builder = nbtMap.toBuilder();
|
||||
} else {
|
||||
builder = NbtMap.builder();
|
||||
}
|
||||
|
||||
builder.put((String) key, value);
|
||||
return DataResult.success(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<Stream<Pair<Object, Object>>> getMapValues(Object input) {
|
||||
if (input instanceof NbtMap nbt) {
|
||||
return DataResult.success(nbt.entrySet().stream().map(entry -> Pair.of(entry.getKey(), entry.getValue())));
|
||||
} else {
|
||||
return DataResult.error(() -> "Input was not NbtMap");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createMap(Stream<Pair<Object, Object>> map) {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
map.forEach(pair -> builder.put((String) pair.getFirst(), pair.getSecond()));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public DataResult<Stream<Object>> getStream(Object input) {
|
||||
if (input instanceof NbtList<?> list) {
|
||||
return DataResult.success((Stream<Object>) list.stream());
|
||||
}
|
||||
if (input instanceof int[] ints) {
|
||||
return DataResult.success(Arrays.stream(ints).mapToObj(Integer::valueOf));
|
||||
}
|
||||
if (input instanceof long[] longs) {
|
||||
return DataResult.success(Arrays.stream(longs).mapToObj(Long::valueOf));
|
||||
}
|
||||
if (input instanceof byte[] bytes) {
|
||||
ByteList byteList = new ByteArrayList(bytes);
|
||||
return DataResult.success((Stream) byteList.stream());
|
||||
}
|
||||
return DataResult.error(() -> "Was not a list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<IntStream> getIntStream(Object input) {
|
||||
if (input instanceof int[] ints) {
|
||||
return DataResult.success(Arrays.stream(ints));
|
||||
} else {
|
||||
return DynamicOps.super.getIntStream(input);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createIntList(IntStream input) {
|
||||
return input.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResult<LongStream> getLongStream(Object input) {
|
||||
if (input instanceof long[] longs) {
|
||||
return DataResult.success(Arrays.stream(longs));
|
||||
} else {
|
||||
return DynamicOps.super.getLongStream(input);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createLongList(LongStream input) {
|
||||
return input.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createList(Stream<Object> input) {
|
||||
final List<?> list = input.toList();
|
||||
if (list.isEmpty()) {
|
||||
return emptyList();
|
||||
}
|
||||
NbtType<?> type = NbtType.byClass(list.getFirst().getClass());
|
||||
return new NbtList(type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(Object input, String key) {
|
||||
if (input instanceof NbtMap map) {
|
||||
NbtMapBuilder builder = map.toBuilder();
|
||||
builder.remove(key);
|
||||
return builder.build();
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.gametest.registries;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.RegistryDataLoader;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.session.cache.RegistryCache;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryData;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
public class GameTestJavaRegistry<T> implements JavaRegistry<T> {
|
||||
private final List<RegistryEntryData<T>> entries;
|
||||
|
||||
public GameTestJavaRegistry(RegistryAccess registries, JavaRegistryKey<T> registryKey) {
|
||||
Registry<?> registry = registries.lookupOrThrow(geyserKeyToMojangKey(registryKey));
|
||||
entries = convertRegistryData(registryKey, registries, registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegistryEntryData<T>> entries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static <Mojang, Geyser> List<RegistryEntryData<Geyser>> convertRegistryData(JavaRegistryKey<Geyser> registryKey, RegistryAccess registries, Registry<Mojang> registry) {
|
||||
DynamicOps<Object> nbtOps = registries.createSerializationContext(CloudburstNbtOps.INSTANCE);
|
||||
Codec<Mojang> codec = getSyncedRegistryData(registry.key()).elementCodec();
|
||||
//noinspection unchecked
|
||||
RegistryCache.RegistryReader<Geyser> reader = (RegistryCache.RegistryReader<Geyser>) RegistryCache.READERS.get(registryKey);
|
||||
|
||||
ToIntFunction<Key> keyIdFunction = key -> registry.getId(registry.getValue(keyToResourceLocation(key)));
|
||||
List<RegistryEntryData<Geyser>> entries = new ArrayList<>();
|
||||
for (Mojang entry : registry) {
|
||||
int id = registry.getIdOrThrow(entry);
|
||||
Key key = resourceLocationToKey(Objects.requireNonNull(registry.getKey(entry)));
|
||||
NbtMap encoded = (NbtMap) codec.encodeStart(nbtOps, entry).getOrThrow();
|
||||
Geyser mapped = reader.read(new RegistryEntryContext(new RegistryEntry(key, encoded), keyIdFunction, Optional.empty()));
|
||||
entries.add(new RegistryEntryData<>(id, key, mapped));
|
||||
}
|
||||
return List.copyOf(entries);
|
||||
}
|
||||
|
||||
private static <T> RegistryDataLoader.RegistryData<T> getSyncedRegistryData(ResourceKey<? extends Registry<T>> registry) {
|
||||
//noinspection unchecked
|
||||
return RegistryDataLoader.SYNCHRONIZED_REGISTRIES.stream()
|
||||
.filter(data -> data.key() == registry)
|
||||
.map(data -> (RegistryDataLoader.RegistryData<T>) data)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException(registry + " is not a network synced registry"));
|
||||
}
|
||||
|
||||
private static ResourceKey<? extends Registry<?>> geyserKeyToMojangKey(JavaRegistryKey<?> key) {
|
||||
return ResourceKey.createRegistryKey(keyToResourceLocation(key.registryKey()));
|
||||
}
|
||||
|
||||
private static ResourceLocation keyToResourceLocation(Key key) {
|
||||
return ResourceLocation.fromNamespaceAndPath(key.namespace(), key.value());
|
||||
}
|
||||
|
||||
private static Key resourceLocationToKey(ResourceLocation location) {
|
||||
//noinspection PatternValidation
|
||||
return Key.key(location.getNamespace(), location.getPath());
|
||||
}
|
||||
}
|
||||
@@ -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.gametest.registries;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GameTestJavaRegistryProvider implements JavaRegistryProvider {
|
||||
private final RegistryAccess registries;
|
||||
private final Map<JavaRegistryKey<?>, GameTestJavaRegistry<?>> registryCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public GameTestJavaRegistryProvider(RegistryAccess registries) {
|
||||
this.registries = registries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JavaRegistry<T> registry(JavaRegistryKey<T> registryKey) {
|
||||
//noinspection unchecked
|
||||
return (JavaRegistry<T>) registryCache.computeIfAbsent(registryKey, key -> new GameTestJavaRegistry<>(registries, key));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "geyser:component_hash",
|
||||
"component": "minecraft:custom_data",
|
||||
"value": [
|
||||
{
|
||||
"hello": "g'day",
|
||||
"nice?": false,
|
||||
"coolness": 100,
|
||||
"geyser": {
|
||||
"is": "very cool"
|
||||
},
|
||||
"a list": [
|
||||
["in a list"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"example_key": "second test case!"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"type": "geyser:component_hash",
|
||||
"component": "minecraft:custom_data",
|
||||
"value": {
|
||||
"hello": "g'day",
|
||||
"nice?": false,
|
||||
"coolness": 100,
|
||||
"geyser": {
|
||||
"is": "very cool"
|
||||
},
|
||||
"a list": [
|
||||
["in a list"]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "org.geysermc.geyser.platform.gametest.mixin",
|
||||
"package": "org.geysermc.geyser.gametest.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [],
|
||||
"server": [],
|
||||
|
||||
@@ -58,7 +58,7 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
|
||||
this.setServer(server);
|
||||
onGeyserEnable();
|
||||
});
|
||||
} else {
|
||||
} else if (!GeyserFabricPlatform.isGameTestServer()) {
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(($)-> {
|
||||
onGeyserShutdown();
|
||||
});
|
||||
|
||||
@@ -701,7 +701,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
||||
runIfNonNull(erosionUnixListener, UnixSocketClientListener::close);
|
||||
|
||||
ResourcePackLoader.clear();
|
||||
CodeOfConductManager.getInstance().save();
|
||||
if (platformType != InternalPlatformType.GAMETEST) {
|
||||
CodeOfConductManager.getInstance().save();
|
||||
}
|
||||
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.geysermc.geyser.item.components.Rarity;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistryProvider;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
|
||||
@@ -307,9 +308,9 @@ public class DataComponentHashers {
|
||||
return hasher;
|
||||
}
|
||||
|
||||
public static <T> HashCode hash(GeyserSession session, DataComponentType<T> component, T value) {
|
||||
public static <T> HashCode hash(JavaRegistryProvider registries, DataComponentType<T> component, T value) {
|
||||
try {
|
||||
return hasher(component).hash(value, new MinecraftHashEncoder(session.getRegistryCache()));
|
||||
return hasher(component).hash(value, new MinecraftHashEncoder(registries));
|
||||
} catch (Exception exception) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to hash item data component " + component.getKey() + " with value " + value + "!");
|
||||
GeyserImpl.getInstance().getLogger().error("This is a Geyser bug, please report this!");
|
||||
@@ -317,8 +318,8 @@ public class DataComponentHashers {
|
||||
}
|
||||
}
|
||||
|
||||
public static <V, T extends DataComponentType<V>> HashCode hash(GeyserSession session, DataComponent<V, T> component) {
|
||||
return hash(session, component.getType(), component.getValue());
|
||||
public static <V, T extends DataComponentType<V>> HashCode hash(JavaRegistryProvider registries, DataComponent<V, T> component) {
|
||||
return hash(registries, component.getType(), component.getValue());
|
||||
}
|
||||
|
||||
public static HashedStack hashStack(GeyserSession session, ItemStack stack) {
|
||||
@@ -339,7 +340,7 @@ public class DataComponentHashers {
|
||||
} else if (component.getValue().getValue() == null) {
|
||||
removals.add(component.getKey());
|
||||
} else {
|
||||
hashedAdditions.put(component.getKey(), hash(session, (DataComponentType) component.getKey(), component.getValue().getValue()).asInt());
|
||||
hashedAdditions.put(component.getKey(), hash(session.getRegistryCache(), (DataComponentType) component.getKey(), component.getValue().getValue()).asInt());
|
||||
}
|
||||
}
|
||||
return new HashedStack(stack.getId(), stack.getAmount(), hashedAdditions, removals);
|
||||
@@ -540,7 +541,7 @@ public class DataComponentHashers {
|
||||
}
|
||||
|
||||
private static <T> void testHash(GeyserSession session, DataComponentType<T> component, T value, int expected) {
|
||||
int got = hash(session, component, value).asInt();
|
||||
int got = hash(session.getRegistryCache(), component, value).asInt();
|
||||
System.out.println("Testing hashing component " + component.getKey() + ", expected " + expected + ", got " + got + " " + (got == expected ? "PASS" : "ERROR"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ public final class RegistryCache implements JavaRegistryProvider {
|
||||
entry = new RegistryEntry(entry.getId(), localRegistry.get(entry.getId()));
|
||||
}
|
||||
|
||||
RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, Optional.of(session));
|
||||
RegistryEntryContext context = new RegistryEntryContext(entry, key -> entryIdMap.getOrDefault(key, -1), Optional.of(session));
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
T cacheEntry = reader.read(context);
|
||||
if (cacheEntry == null) {
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.session.cache.registry;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import java.util.Optional;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
@@ -38,16 +38,16 @@ import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry;
|
||||
* Used to store context around a single registry entry when reading said entry's NBT.
|
||||
*
|
||||
* @param entry the registry entry being read.
|
||||
* @param keyIdMap a map for each of the resource location's in the registry and their respective network IDs.
|
||||
* @param keyIdFunction a function that turns a resource location in the registry to its respective network ID.
|
||||
* @param session the Geyser session. Only empty during testing.
|
||||
*/
|
||||
public record RegistryEntryContext(RegistryEntry entry, Object2IntMap<Key> keyIdMap, Optional<GeyserSession> session) {
|
||||
public record RegistryEntryContext(RegistryEntry entry, ToIntFunction<Key> keyIdFunction, Optional<GeyserSession> session) {
|
||||
|
||||
// TODO: not a fan of this. With JavaRegistryKey#key now being a thing, I'd rather have that always used, so that registry readers won't have to worry
|
||||
// about using the right method. This would require pre-populating all data-driven registries with default (probably null) values before actually decoding the data from the registy packet.
|
||||
// This could also be helpful in the feature when a data-driven registry reader needs to use an element from another data-driven registry
|
||||
public int getNetworkId(Key registryKey) {
|
||||
return keyIdMap.getOrDefault(registryKey, -1);
|
||||
return keyIdFunction.applyAsInt(registryKey);
|
||||
}
|
||||
|
||||
public Key id() {
|
||||
|
||||
Reference in New Issue
Block a user