Document the registry system (Closes #2387)

This commit is contained in:
Redned 2021-07-21 21:14:00 -05:00 committed by RednedEpic
parent 8f98162c69
commit 4a8e598c62
23 changed files with 394 additions and 16 deletions

View File

@ -27,21 +27,54 @@ package org.geysermc.connector.registry;
import org.geysermc.connector.registry.loader.RegistryLoader;
import javax.annotation.Nullable;
import java.util.Map;
public abstract class AbstractMappedRegistry<K, V, C extends Map<K, V>> extends Registry<C> {
protected <I> AbstractMappedRegistry(I input, RegistryLoader<I, C> registryLoader) {
/**
* An abstract registry holding a map of various registrations as defined by {@link M}.
* The M represents the map class, which can be anything that extends {@link Map}. The
* {@link K} and {@link V} generics are the key and value respectively.
*
* @param <K> the key
* @param <V> the value
* @param <M> the map
*/
public abstract class AbstractMappedRegistry<K, V, M extends Map<K, V>> extends Registry<M> {
protected <I> AbstractMappedRegistry(I input, RegistryLoader<I, M> registryLoader) {
super(input, registryLoader);
}
/**
* Returns the value registered by the given key.
*
* @param key the key
* @return the value registered by the given key.
*/
@Nullable
public V get(K key) {
return this.mappings.get(key);
}
/**
* Returns the value registered by the given key or the default value
* specified if null.
*
* @param key the key
* @param defaultValue the default value
* @return the value registered by the given key or the default value
* specified if null.
*/
public V getOrDefault(K key, V defaultValue) {
return this.mappings.getOrDefault(key, defaultValue);
}
/**
* Registers a new value into this registry with the given key.
*
* @param key the key
* @param value the value
* @return a new value into this registry with the given key.
*/
public V register(K key, V value) {
return this.mappings.put(key, value);
}

View File

@ -36,6 +36,9 @@ import org.geysermc.connector.registry.populator.BlockRegistryPopulator;
import org.geysermc.connector.registry.type.BlockMapping;
import org.geysermc.connector.registry.type.BlockMappings;
/**
* Holds all the block registries in Geyser.
*/
public class BlockRegistries {
public static final VersionedRegistry<BlockMappings> BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));

View File

@ -30,28 +30,78 @@ import org.geysermc.connector.registry.loader.RegistryLoader;
import java.util.Map;
import java.util.function.Supplier;
public class MappedRegistry<K, V, C extends Map<K, V>> extends AbstractMappedRegistry<K, V, C> {
protected <I> MappedRegistry(I input, RegistryLoader<I, C> registryLoader) {
/**
* An public registry holding a map of various registrations as defined by {@link M}.
* The M represents the map class, which can be anything that extends {@link Map}. The
* {@link K} and {@link V} generics are the key and value respectively.
*
* @param <K> the key
* @param <V> the value
* @param <M> the map
*/
public class MappedRegistry<K, V, M extends Map<K, V>> extends AbstractMappedRegistry<K, V, M> {
protected <I> MappedRegistry(I input, RegistryLoader<I, M> registryLoader) {
super(input, registryLoader);
}
public static <I, K, V, C extends Map<K, V>> MappedRegistry<K, V, C> createEmpty() {
return new MappedRegistry<>(null, input -> null);
}
public static <I, K, V, C extends Map<K, V>> MappedRegistry<K, V, C> create(RegistryLoader<I, C> registryLoader) {
/**
* Creates a new mapped registry with the given {@link RegistryLoader}. The
* input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @param <M> the returned mappings type, a map in this case
* @return a new registry with the given RegistryLoader
*/
public static <I, K, V, M extends Map<K, V>> MappedRegistry<K, V, M> create(RegistryLoader<I, M> registryLoader) {
return new MappedRegistry<>(null, registryLoader);
}
public static <I, K, V, C extends Map<K, V>> MappedRegistry<K, V, C> create(I input, RegistryLoader<I, C> registryLoader) {
/**
* Creates a new mapped registry with the given {@link RegistryLoader} and input.
*
* @param input the input
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @param <M> the returned mappings type, a map in this case
* @return a new registry with the given RegistryLoader
*/
public static <I, K, V, M extends Map<K, V>> MappedRegistry<K, V, M> create(I input, RegistryLoader<I, M> registryLoader) {
return new MappedRegistry<>(input, registryLoader);
}
public static <I, K, V, C extends Map<K, V>> MappedRegistry<K, V, C> create(Supplier<RegistryLoader<I, C>> registryLoader) {
/**
* Creates a new mapped registry with the given {@link RegistryLoader} supplier.
* The input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader supplier
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @param <M> the returned mappings type, a map in this case
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, K, V, M extends Map<K, V>> MappedRegistry<K, V, M> create(Supplier<RegistryLoader<I, M>> registryLoader) {
return new MappedRegistry<>(null, registryLoader.get());
}
public static <I, K, V, C extends Map<K, V>> MappedRegistry<K, V, C> create(I input, Supplier<RegistryLoader<I, C>> registryLoader) {
/**
* Creates a new mapped registry with the given {@link RegistryLoader} and input.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @param <M> the returned mappings type, a map in this case
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, K, V, M extends Map<K, V>> MappedRegistry<K, V, M> create(I input, Supplier<RegistryLoader<I, M>> registryLoader) {
return new MappedRegistry<>(input, registryLoader.get());
}
}

View File

@ -52,6 +52,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Holds all the common registries in Geyser.
*/
public class Registries {
public static final SimpleRegistry<NbtMap> BIOMES = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT);

View File

@ -29,17 +29,71 @@ import org.geysermc.connector.registry.loader.RegistryLoader;
import java.util.function.Consumer;
/**
* A wrapper around a value which is loaded based on the output from the provided
* {@link RegistryLoader}. This class is primarily designed to hold a registration
* of some kind, however no limits are set on what it can hold, as long as the
* specified RegistryLoader returns the same value type that is specified in the
* generic.
*
* <p>
* Below, a RegistryLoader is taken in the constructor. RegistryLoaders have two
* generic types: the input, and the output. The input is what it takes in, whether
* it be a string which references to a file, or nothing more than an integer. The
* output is what it generates based on the input, and should be the same type as
* the {@link M} generic specified in the registry.
*
* <p>
* Registries can be very simple to create. Here is an example that simply parses a
* number given a string:
*
* <pre>
* {@code
* public static final SimpleRegistry<Integer> STRING_TO_INT = SimpleRegistry.create("5", Integer::parseInt);
* }
* </pre>
*
* <p>
* This is a simple example which really wouldn't have much of a practical use,
* however it demonstrates a fairly basic use case of how this system works. Typically
* though, the first parameter would be a location of some sort, such as a file path
* where the loader will load the mappings from. The NBT registry is a good reference
* point for something both simple and practical. See {@link Registries#BIOMES} and
* {@link org.geysermc.connector.registry.loader.NbtRegistryLoader}.
*
* @param <M> the value being held by the registry
*/
public abstract class Registry<M> {
protected final M mappings;
/**
* Creates a new instance of this class with the given input and
* {@link RegistryLoader}. The input specified is what the registry
* loader needs to take in.
*
* @param input the input
* @param registryLoader the registry loader
* @param <I> the input type
*/
protected <I> Registry(I input, RegistryLoader<I, M> registryLoader) {
this.mappings = registryLoader.load(input);
}
/**
* Gets the underlying value held by this registry.
*
* @return the underlying value held by this registry.
*/
public M get() {
return this.mappings;
}
/**
* Registers what is specified in the given
* {@link Consumer} into the underlying value.
*
* @param consumer the consumer
*/
public void register(Consumer<M> consumer) {
consumer.accept(this.mappings);
}

View File

@ -30,23 +30,72 @@ import org.geysermc.connector.registry.loader.RegistryLoader;
import java.util.Map;
import java.util.function.Supplier;
/**
* A variant of {@link AbstractMappedRegistry} with {@link Map} as the defined type. Unlike
* {@link MappedRegistry}, this registry does not support specifying your own Map class,
* and only permits operations the {@link Map} interface does, unless you manually cast.
*
* @param <K> the key
* @param <V> the value
*/
public class SimpleMappedRegistry<K, V> extends AbstractMappedRegistry<K, V, Map<K, V>> {
protected <I> SimpleMappedRegistry(I input, RegistryLoader<I, Map<K, V>> registryLoader) {
super(input, registryLoader);
}
/**
* Creates a new mapped registry with the given {@link RegistryLoader}. The
* input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @return a new registry with the given RegistryLoader
*/
public static <I, K, V> SimpleMappedRegistry<K, V> create(RegistryLoader<I, Map<K, V>> registryLoader) {
return new SimpleMappedRegistry<>(null, registryLoader);
}
/**
* Creates a new mapped registry with the given {@link RegistryLoader} and input.
*
* @param input the input
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @return a new registry with the given RegistryLoader
*/
public static <I, K, V> SimpleMappedRegistry<K, V> create(I input, RegistryLoader<I, Map<K, V>> registryLoader) {
return new SimpleMappedRegistry<>(input, registryLoader);
}
/**
* Creates a new mapped registry with the given {@link RegistryLoader} supplier.
* The input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader supplier
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, K, V> SimpleMappedRegistry<K, V> create(Supplier<RegistryLoader<I, Map<K, V>>> registryLoader) {
return new SimpleMappedRegistry<>(null, registryLoader.get());
}
/**
* Creates a new mapped registry with the given {@link RegistryLoader} and input.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <K> the map key
* @param <V> the map value
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, K, V> SimpleMappedRegistry<K, V> create(I input, Supplier<RegistryLoader<I, Map<K, V>>> registryLoader) {
return new SimpleMappedRegistry<>(input, registryLoader.get());
}

View File

@ -29,23 +29,69 @@ import org.geysermc.connector.registry.loader.RegistryLoader;
import java.util.function.Supplier;
/**
* A simple registry with no defined mapping or input type. Designed to allow
* for simple registrations of any given type without restrictions on what
* the input or output can be.
*
* @param <M> the value being held by the registry
*/
public class SimpleRegistry<M> extends Registry<M> {
private <I> SimpleRegistry(I input, RegistryLoader<I, M> registryLoader) {
super(input, registryLoader);
}
/**
* Creates a new registry with the given {@link RegistryLoader} supplier. The
* input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader supplier
* @param <I> the input type
* @param <M> the returned mappings type
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> SimpleRegistry<M> create(Supplier<RegistryLoader<I, M>> registryLoader) {
return new SimpleRegistry<>(null, registryLoader.get());
}
/**
* Creates a new registry with the given {@link RegistryLoader} supplier
* and input.
*
* @param input the input
* @param registryLoader the registry loader supplier
* @param <I> the input type
* @param <M> the returned mappings type
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> SimpleRegistry<M> create(I input, Supplier<RegistryLoader<I, M>> registryLoader) {
return new SimpleRegistry<>(input, registryLoader.get());
}
/**
* Creates a new registry with the given {@link RegistryLoader}. The
* input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader
* @param <I> the input type
* @param <M> the returned mappings type
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> SimpleRegistry<M> create(RegistryLoader<I, M> registryLoader) {
return new SimpleRegistry<>(null, registryLoader);
}
/**
* Creates a new registry with the given {@link RegistryLoader} and input.
*
* @param input the input
* @param registryLoader the registry loader
* @param <I> the input type
* @param <M> the returned mappings type
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, M> SimpleRegistry<M> create(I input, RegistryLoader<I, M> registryLoader) {
return new SimpleRegistry<>(input, registryLoader);
}

View File

@ -28,13 +28,32 @@ package org.geysermc.connector.registry;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import org.geysermc.connector.registry.loader.RegistryLoader;
import java.util.Map;
import java.util.function.Supplier;
/**
* A versioned, mapped registry. Like {@link SimpleMappedRegistry}, the {@link Map} interface is
* not able to be specified here, but unlike it, it does not have support for specialized
* instances, and ONLY supports {@link Int2ObjectMap} for optimal performance to prevent boxing
* of integers.
*
* @param <V> the value
*/
public class VersionedRegistry<V> extends AbstractMappedRegistry<Integer, V, Int2ObjectMap<V>> {
protected <I> VersionedRegistry(I input, RegistryLoader<I, Int2ObjectMap<V>> registryLoader) {
super(input, registryLoader);
}
/**
* Gets the closest value for the specified version. Only
* returns versions higher up than the specified if one
* does not exist for the given one. Useful in the event
* that you want to get a resource which is guaranteed for
* older versions, but not on newer ones.
*
* @param version the version
* @return the closest value for the specified version
*/
public V forVersion(int version) {
V value = null;
for (Int2ObjectMap.Entry<V> entry : this.mappings.int2ObjectEntrySet()) {
@ -49,18 +68,54 @@ public class VersionedRegistry<V> extends AbstractMappedRegistry<Integer, V, Int
return value;
}
/**
* Creates a new versioned registry with the given {@link RegistryLoader}. The
* input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <V> the map value
* @return a new registry with the given RegistryLoader
*/
public static <I, V> VersionedRegistry<V> create(RegistryLoader<I, Int2ObjectMap<V>> registryLoader) {
return new VersionedRegistry<>(null, registryLoader);
}
/**
* Creates a new versioned registry with the given {@link RegistryLoader} and input.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <V> the map value
* @return a new registry with the given RegistryLoader
*/
public static <I, V> VersionedRegistry<V> create(I input, RegistryLoader<I, Int2ObjectMap<V>> registryLoader) {
return new VersionedRegistry<>(input, registryLoader);
}
/**
* Creates a new versioned registry with the given {@link RegistryLoader} supplier.
* The input type is not specified here, meaning the loader return type is either
* predefined, or the registry is populated at a later point.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <V> the map value
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, V> VersionedRegistry< V> create(Supplier<RegistryLoader<I, Int2ObjectMap<V>>> registryLoader) {
return new VersionedRegistry<>(null, registryLoader.get());
}
/**
* Creates a new versioned registry with the given {@link RegistryLoader} supplier and input.
*
* @param registryLoader the registry loader
* @param <I> the input
* @param <V> the map value
* @return a new registry with the given RegistryLoader supplier
*/
public static <I, V> VersionedRegistry< V> create(I input, Supplier<RegistryLoader<I, Int2ObjectMap<V>>> registryLoader) {
return new VersionedRegistry<>(input, registryLoader.get());
}

View File

@ -34,6 +34,26 @@ import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.Function;
/**
* A mapped registry loader which takes in a {@link String} and returns a transformed
* {@link Annotation} as the value. The {@link R} represents the final result as mapped
* by the {@link A}, the annotation. This function exists in this registry loader for
* the purpose of annotations not often being used as a map key. The {@link V} generic
* represents the actual map value of what is expected. The function transformation done
* is used for transforming the key, however the value is not expected to be transformed.
*
* <p>
* Keep in mind that this annotation transforming does NOT need to be done, and can be
* replaced with a simple <code>Function.identity()</code> if not desired.
*
* <p>
* See {@link BlockEntityRegistryLoader} and {@link SoundHandlerRegistryLoader} as a
* good example of these registry loaders in use.
*
* @param <R> the final result as transformed by the function
* @param <A> the raw annotation itself can be transformed
* @param <V> the value
*/
public class AnnotatedRegistryLoader<R, A extends Annotation, V> implements RegistryLoader<String, Map<R, V>> {
private final Class<A> annotation;
private final Function<A, R> mapper;

View File

@ -28,6 +28,9 @@ package org.geysermc.connector.registry.loader;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntity;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
/**
* Loads block entities from the given classpath.
*/
public class BlockEntityRegistryLoader extends AnnotatedRegistryLoader<String, BlockEntity, BlockEntityTranslator> {
public BlockEntityRegistryLoader() {
super(BlockEntity.class, BlockEntity::name);

View File

@ -48,6 +48,9 @@ import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Loads collision data from the given resource path.
*/
public class CollisionRegistryLoader extends MultiResourceRegistryLoader<String, Map<Integer, BlockCollision>> {
@Override

View File

@ -33,6 +33,11 @@ import java.io.InputStream;
import java.util.Map;
import java.util.WeakHashMap;
/**
* An abstract registry loader for loading effects from a resource path.
*
* @param <T> the value
*/
public abstract class EffectRegistryLoader<T> implements RegistryLoader<String, T> {
private static final Map<String, JsonNode> loadedFiles = new WeakHashMap<>();

View File

@ -27,5 +27,11 @@ package org.geysermc.connector.registry.loader;
import it.unimi.dsi.fastutil.Pair;
/**
* A RegistryLoader that loads data from two different locations, yet with the same input type.
*
* @param <I> the input type
* @param <V> the value
*/
public abstract class MultiResourceRegistryLoader<I, V> implements RegistryLoader<Pair<I, I>, V> {
}

View File

@ -32,6 +32,9 @@ import org.geysermc.connector.utils.FileUtils;
import java.io.InputStream;
/**
* Loads NBT data from the given resource path.
*/
public class NbtRegistryLoader implements RegistryLoader<String, NbtMap> {
@Override

View File

@ -34,6 +34,9 @@ import org.geysermc.connector.registry.type.ParticleMapping;
import java.util.Iterator;
import java.util.Map;
/**
* Loads particle types from the given resource path.
*/
public class ParticleTypesRegistryLoader extends EffectRegistryLoader<Map<ParticleType, ParticleMapping>> {
@Override

View File

@ -26,12 +26,22 @@
package org.geysermc.connector.registry.loader;
/**
* Represents a registry loader.
* Represents a registry loader. {@link I} is the input value, which can be anything,
* but is commonly a file path or something similar. {@link O} represents the output
* type returned by this, which can also be anything. See {@link NbtRegistryLoader}
* as a good and simple example of how this system works.
*
* @param <I> the input to load the registry from
* @param <O> the output of the registry
*/
@FunctionalInterface
public interface RegistryLoader<I, O> {
/**
* Loads an output from the given input.
*
* @param input the input
* @return the output
*/
O load(I input);
}

View File

@ -27,10 +27,24 @@ package org.geysermc.connector.registry.loader;
import java.util.function.Supplier;
/**
* Holds common {@link RegistryLoader}s or utility methods surrounding them.
*/
public class RegistryLoaders {
/**
* The {@link RegistryLoader} responsible for loading NBT.
*/
public static NbtRegistryLoader NBT = new NbtRegistryLoader();
public static <V> RegistryLoader<Object, V> empty(Supplier<V> value) {
return input -> value.get();
/**
* Wraps the surrounding {@link Supplier} in a {@link RegistryLoader} which does
* not take in any input value.
*
* @param supplier the supplier
* @param <V> the value
* @return a RegistryLoader wrapping the given Supplier
*/
public static <V> RegistryLoader<Object, V> empty(Supplier<V> supplier) {
return input -> supplier.get();
}
}

View File

@ -39,6 +39,9 @@ import org.geysermc.connector.network.translators.effect.SoundLevelEffect;
import java.util.Iterator;
import java.util.Map;
/**
* Loads sound effects from the given resource path.
*/
public class SoundEffectsRegistryLoader extends EffectRegistryLoader<Map<SoundEffect, Effect>> {
@Override

View File

@ -30,6 +30,9 @@ import org.geysermc.connector.network.translators.sound.SoundInteractionHandler;
import java.util.function.Function;
/**
* Loads sound handlers from the given classpath.
*/
public class SoundHandlerRegistryLoader extends AnnotatedRegistryLoader<SoundHandler, SoundHandler, SoundInteractionHandler<?>> {
public SoundHandlerRegistryLoader() {
super(SoundHandler.class, Function.identity());

View File

@ -36,11 +36,14 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Loads sounds from the given input.
*/
public class SoundRegistryLoader implements RegistryLoader<String, Map<String, SoundMapping>> {
@Override
public Map<String, SoundMapping> load(String input) {
InputStream stream = FileUtils.getResource("mappings/sounds.json");
InputStream stream = FileUtils.getResource(input);
JsonNode soundsTree;
try {
soundsTree = GeyserConnector.JSON_MAPPER.readTree(stream);

View File

@ -52,6 +52,9 @@ import java.util.Map;
import java.util.function.BiFunction;
import java.util.zip.GZIPInputStream;
/**
* Populates the block registries.
*/
public class BlockRegistryPopulator {
private static final ImmutableMap<String, BiFunction<String, NbtMapBuilder, String>> STATE_MAPPER;

View File

@ -57,6 +57,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Populates the item registries.
*/
public class ItemRegistryPopulator {
private static final Map<String, PaletteVersion> PALETTE_VERSIONS;

View File

@ -54,6 +54,9 @@ import java.util.*;
import static org.geysermc.connector.utils.InventoryUtils.LAST_RECIPE_NET_ID;
/**
* Populates the recipe registry.
*/
public class RecipeRegistryPopulator {
public static void populate() {