Skip to content

Instantly share code, notes, and snippets.

@Commoble
Last active March 17, 2021 02:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Commoble/cd22566fbf381c5fa060195cc9fbc30f to your computer and use it in GitHub Desktop.
Save Commoble/cd22566fbf381c5fa060195cc9fbc30f to your computer and use it in GitHub Desktop.
Using Dispatch Codecs with Forge Registries
// json for a StatePredicate that a data loader could load
// this will be parsed into a BlockPredicate instance that returns true
// if a blockstate's block is the Grass Block
{
"type": "sandbox:block",
"block": "minecraft:grass_block"
}
package commoble.sandbox;
import java.util.function.Supplier;
import com.mojang.serialization.Codec;
import commoble.sandbox.StatePredicateSerializer.StatePredicate;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.registry.Registry;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
// some basic state predicates
public class StatePredicates
{
@SuppressWarnings("unchecked")
public static final DeferredRegister<StatePredicateSerializer<?>> STATE_PREDICATE_SERIALIZERS = DeferredRegister.create(StatePredicateSerializer.class, "modid");
public static final RegistryObject<StatePredicateSerializer<ConstantPredicate>> ALWAYS_TRUE = STATE_PREDICATE_SERIALIZERS.register("always_true", () -> new StatePredicateSerializer<>(StatePredicates.ConstantPredicate.ALWAYS_TRUE_CODEC));
public static final RegistryObject<StatePredicateSerializer<ConstantPredicate>> ALWAYS_FALSE = STATE_PREDICATE_SERIALIZERS.register("always_false", () -> new StatePredicateSerializer<>(StatePredicates.ConstantPredicate.ALWAYS_FALSE_CODEC));
public static final RegistryObject<StatePredicateSerializer<BlockPredicate>> BLOCK_SERIALIZER = STATE_PREDICATE_SERIALIZERS.register("block", () -> new StatePredicateSerializer<>(StatePredicates.BlockPredicate.CODEC));
/** Predicates that always return either true or false **/
public static class ConstantPredicate extends StatePredicate<ConstantPredicate>
{
public static final Codec<ConstantPredicate> ALWAYS_FALSE_CODEC = Codec.unit(new ConstantPredicate(ALWAYS_FALSE, false));
public static final Codec<ConstantPredicate> ALWAYS_TRUE_CODEC = Codec.unit(new ConstantPredicate(ALWAYS_TRUE, true));
private final boolean value;
public ConstantPredicate(Supplier<StatePredicateSerializer<ConstantPredicate>> serializer, boolean value)
{
super(serializer);
this.value = value;
}
@Override
public boolean test(BlockState state)
{
return this.value;
}
}
/** Predicate that returns true if a state belongs to a given block instance **/
public static class BlockPredicate extends StatePredicate<BlockPredicate>
{
public static final Codec<BlockPredicate> CODEC = Registry.BLOCK.xmap(BlockPredicate::new, BlockPredicate::getBlock).fieldOf("block").codec();
private final Block block;
public Block getBlock() { return this.block; }
public BlockPredicate(Block block)
{
super(BLOCK_SERIALIZER);
this.block = block;
}
@Override
public boolean test(BlockState state)
{
return state.isIn(this.block);
}
}
}
package commoble.sandbox;
import java.util.function.Predicate;
import java.util.function.Supplier;
import com.mojang.serialization.Codec;
import commoble.sandbox.StatePredicateSerializer.StatePredicate;
import net.minecraft.block.BlockState;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.registries.ForgeRegistryEntry;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryBuilder;
/** Example of using dispatch codecs to make a registry of serializers for blockstate predicates **/
@EventBusSubscriber(modid="sandbox", bus=Bus.MOD)
public class StatePredicateSerializer<LOGIC extends StatePredicate<LOGIC>> extends ForgeRegistryEntry<StatePredicateSerializer<LOGIC>>
{
// forge hack to build generic-param-class registries
@SuppressWarnings("unchecked")
private static <T> Class<T> genericClass(Class<?> cls) { return (Class<T>)cls; }
// get a forge registry registered
@SubscribeEvent
static void onRegisterRegistries(RegistryEvent.NewRegistry event)
{
Class<StatePredicateSerializer<?>> registryClass = genericClass(StatePredicateSerializer.class);
REGISTRY = new RegistryBuilder<StatePredicateSerializer<?>>()
.setName(new ResourceLocation("sandbox:statepredicate"))
.setType(registryClass)
.disableSaving()
// disable sync if you're not going to use the data loader on the client
.disableSync()
.create();
}
// use DeferredRegister.create(StatePredicateSerializer.class, modid) to register stuff to this
public static IForgeRegistry<StatePredicateSerializer<?>> REGISTRY = null;
/**
* This is the registry dispatch codec;
* this will read jsons of the format:
{
"type": "modid:serializer-name",
// extra fields definable by the sub-codecs
}
* this codec can be used to read jsons in data loaders, etc
* Fields in the subcodecs will get read from the same root jsonobject as the "type" field.
* If the subcodec isn't a map codec, the dispatcher will read the subcodec from a "value" field instead.
*/
public static final Codec<StatePredicateSerializer<?>> CODEC = ResourceLocation.CODEC.xmap(id -> REGISTRY.getValue(id), StatePredicateSerializer::getRegistryName);
private final Codec<LOGIC> subCodec;
public Codec<LOGIC> getSubCodec() { return this.subCodec; }
/**
*
* @param subCodec The codec to use to read extra data for the StatePredicate instances
*/
public StatePredicateSerializer(Codec<LOGIC> subCodec)
{
this.subCodec = subCodec;
}
// the self-referential generic param here lets us have a reference to a properly generic'd serializer
public static abstract class StatePredicate<T extends StatePredicate<T>> implements Predicate<BlockState>
{
public static final Codec<StatePredicate<?>> CODEC = StatePredicateSerializer.CODEC.dispatch(StatePredicate::getSerializer, StatePredicateSerializer::getSubCodec);
private final Supplier<StatePredicateSerializer<T>> serializerGetter;
public StatePredicateSerializer<T> getSerializer() { return this.serializerGetter.get(); }
/**
* @param serializerGetter Supplier for a state predicate serializer (since they're forge registry entries, you can use a RegistryObject here)
*/
public StatePredicate(Supplier<StatePredicateSerializer<T>> serializerGetter)
{
this.serializerGetter = serializerGetter;
}
/**
* @param state A blockstate to test
* @return true if the state passes the test
*/
@Override
public abstract boolean test(BlockState state);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment