AttachedRegistry refactors, and several adjacent ones

This commit is contained in:
TropheusJ 2025-02-12 08:08:19 -05:00
parent 888d175695
commit 41d725878e
36 changed files with 568 additions and 992 deletions

View file

@ -3,8 +3,8 @@ package com.simibubi.create;
import static com.simibubi.create.AllInteractionBehaviours.interactionBehaviour;
import static com.simibubi.create.AllMovementBehaviours.movementBehaviour;
import static com.simibubi.create.Create.REGISTRATE;
import static com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry.mountedFluidStorage;
import static com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry.mountedItemStorage;
import static com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries.mountedFluidStorage;
import static com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries.mountedItemStorage;
import static com.simibubi.create.content.redstone.displayLink.AllDisplayBehaviours.assignDataBehaviour;
import static com.simibubi.create.foundation.data.BlockStateGen.axisBlock;
import static com.simibubi.create.foundation.data.BlockStateGen.simpleCubeAll;
@ -768,7 +768,7 @@ public class AllBlocks {
.loot((lt, block) -> lt.add(block, BlazeBurnerBlock.buildLootTable()))
.blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p)))
.onRegister(movementBehaviour(new BlazeBurnerMovementBehaviour()))
.onRegister(block -> TrainConductorHandler.registerBlazeBurner())
.onRegister(TrainConductorHandler::registerBlazeBurner)
.item(BlazeBurnerBlockItem::withBlaze)
.model(AssetLookup.customBlockItemModel("blaze_burner", "block_with_blaze"))
.build()

View file

@ -1,94 +1,39 @@
package com.simibubi.create;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.content.contraptions.behaviour.DoorMovingInteraction;
import com.simibubi.create.content.contraptions.behaviour.FenceGateMovingInteraction;
import com.simibubi.create.content.contraptions.behaviour.LeverMovingInteraction;
import com.simibubi.create.content.contraptions.behaviour.MovingInteractionBehaviour;
import com.simibubi.create.content.contraptions.behaviour.TrapdoorMovingInteraction;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.tterrag.registrate.util.nullness.NonNullConsumer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.registries.ForgeRegistries;
public class AllInteractionBehaviours {
private static final AttachedRegistry<Block, MovingInteractionBehaviour> BLOCK_BEHAVIOURS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final List<BehaviourProvider> GLOBAL_BEHAVIOURS = new ArrayList<>();
public static void registerBehaviour(ResourceLocation block, MovingInteractionBehaviour provider) {
BLOCK_BEHAVIOURS.register(block, provider);
}
public static void registerBehaviour(Block block, MovingInteractionBehaviour provider) {
BLOCK_BEHAVIOURS.register(block, provider);
}
public static void registerBehaviourProvider(BehaviourProvider provider) {
GLOBAL_BEHAVIOURS.add(provider);
}
public static final AttachedRegistry<Block, MovingInteractionBehaviour> REGISTRY = AttachedRegistry.create();
@Nullable
public static MovingInteractionBehaviour getBehaviour(BlockState state) {
MovingInteractionBehaviour behaviour = BLOCK_BEHAVIOURS.get(state.getBlock());
if (behaviour != null) {
return behaviour;
return REGISTRY.get(state.getBlock());
}
for (BehaviourProvider provider : GLOBAL_BEHAVIOURS) {
behaviour = provider.getBehaviour(state);
if (behaviour != null) {
return behaviour;
}
}
return null;
}
public static <B extends Block> NonNullConsumer<? super B> interactionBehaviour(
MovingInteractionBehaviour behaviour) {
return b -> registerBehaviour(b, behaviour);
/**
* Creates a consumer that will register a behavior to a block. Useful for Registrate.
*/
public static <B extends Block> NonNullConsumer<? super B> interactionBehaviour(MovingInteractionBehaviour behaviour) {
return b -> REGISTRY.register(b, behaviour);
}
static void registerDefaults() {
registerBehaviour(Blocks.LEVER, new LeverMovingInteraction());
REGISTRY.register(Blocks.LEVER, new LeverMovingInteraction());
DoorMovingInteraction doorBehaviour = new DoorMovingInteraction();
registerBehaviourProvider(state -> {
if (state.is(BlockTags.WOODEN_DOORS)) {
return doorBehaviour;
}
return null;
});
TrapdoorMovingInteraction trapdoorBehaviour = new TrapdoorMovingInteraction();
registerBehaviourProvider(state -> {
if (state.is(BlockTags.WOODEN_TRAPDOORS)) {
return trapdoorBehaviour;
}
return null;
});
FenceGateMovingInteraction fenceGateBehavior = new FenceGateMovingInteraction();
registerBehaviourProvider(state -> {
if (state.is(BlockTags.FENCE_GATES)) {
return fenceGateBehavior;
}
return null;
});
}
public interface BehaviourProvider {
@Nullable
MovingInteractionBehaviour getBehaviour(BlockState state);
REGISTRY.registerProvider(AttachedRegistry.Provider.forBlockTag(BlockTags.WOODEN_DOORS, new DoorMovingInteraction()));
REGISTRY.registerProvider(AttachedRegistry.Provider.forBlockTag(BlockTags.WOODEN_TRAPDOORS, new TrapdoorMovingInteraction()));
REGISTRY.registerProvider(AttachedRegistry.Provider.forBlockTag(BlockTags.FENCE_GATES, new TrapdoorMovingInteraction()));
}
}

View file

@ -1,74 +1,41 @@
package com.simibubi.create;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.content.contraptions.behaviour.BellMovementBehaviour;
import com.simibubi.create.content.contraptions.behaviour.CampfireMovementBehaviour;
import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour;
import com.simibubi.create.content.contraptions.behaviour.dispenser.DispenserMovementBehaviour;
import com.simibubi.create.content.contraptions.behaviour.dispenser.DropperMovementBehaviour;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.tterrag.registrate.util.nullness.NonNullConsumer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.registries.ForgeRegistries;
public class AllMovementBehaviours {
private static final AttachedRegistry<Block, MovementBehaviour> BLOCK_BEHAVIOURS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final List<BehaviourProvider> GLOBAL_BEHAVIOURS = new ArrayList<>();
public static void registerBehaviour(ResourceLocation block, MovementBehaviour behaviour) {
BLOCK_BEHAVIOURS.register(block, behaviour);
}
public static void registerBehaviour(Block block, MovementBehaviour behaviour) {
BLOCK_BEHAVIOURS.register(block, behaviour);
}
public static void registerBehaviourProvider(BehaviourProvider provider) {
GLOBAL_BEHAVIOURS.add(provider);
}
public static final AttachedRegistry<Block, MovementBehaviour> REGISTRY = AttachedRegistry.create();
@Nullable
public static MovementBehaviour getBehaviour(BlockState state) {
MovementBehaviour behaviour = BLOCK_BEHAVIOURS.get(state.getBlock());
if (behaviour != null) {
return behaviour;
return REGISTRY.get(state.getBlock());
}
for (BehaviourProvider provider : GLOBAL_BEHAVIOURS) {
behaviour = provider.getBehaviour(state);
if (behaviour != null) {
return behaviour;
}
}
return null;
}
public static <B extends Block> NonNullConsumer<? super B> movementBehaviour(
MovementBehaviour behaviour) {
return b -> registerBehaviour(b, behaviour);
/**
* Creates a consumer that will register a behavior to a block. Useful for Registrate.
*/
public static <B extends Block> NonNullConsumer<? super B> movementBehaviour(MovementBehaviour behaviour) {
return b -> REGISTRY.register(b, behaviour);
}
static void registerDefaults() {
registerBehaviour(Blocks.BELL, new BellMovementBehaviour());
registerBehaviour(Blocks.CAMPFIRE, new CampfireMovementBehaviour());
registerBehaviour(Blocks.SOUL_CAMPFIRE, new CampfireMovementBehaviour());
REGISTRY.register(Blocks.BELL, new BellMovementBehaviour());
REGISTRY.register(Blocks.CAMPFIRE, new CampfireMovementBehaviour());
REGISTRY.register(Blocks.SOUL_CAMPFIRE, new CampfireMovementBehaviour());
DispenserMovementBehaviour.gatherMovedDispenseItemBehaviours();
registerBehaviour(Blocks.DISPENSER, new DispenserMovementBehaviour());
registerBehaviour(Blocks.DROPPER, new DropperMovementBehaviour());
}
public interface BehaviourProvider {
@Nullable
MovementBehaviour getBehaviour(BlockState state);
REGISTRY.register(Blocks.DISPENSER, new DispenserMovementBehaviour());
REGISTRY.register(Blocks.DROPPER, new DropperMovementBehaviour());
}
}

View file

@ -33,7 +33,6 @@ import com.simibubi.create.foundation.data.CreateRegistrate;
import com.simibubi.create.foundation.item.ItemDescription;
import com.simibubi.create.foundation.item.KineticStats;
import com.simibubi.create.foundation.item.TooltipModifier;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.simibubi.create.foundation.utility.CreateNBTProcessors;
import com.simibubi.create.infrastructure.command.ServerLagger;
import com.simibubi.create.infrastructure.config.AllConfigs;
@ -48,6 +47,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.MinecraftForge;
@ -140,8 +140,6 @@ public class Create {
// FIXME: some of these registrations are not thread-safe
AllMovementBehaviours.registerDefaults();
AllInteractionBehaviours.registerDefaults();
AllPortalTracks.registerDefaults();
AllDisplayBehaviours.registerDefaults();
ContraptionMovementSetting.registerDefaults();
BogeySizes.init();
AllBogeyStyles.init();
@ -173,9 +171,10 @@ public class Create {
// These registrations use Create's registered objects directly so they must run after registration has finished.
BuiltinPotatoProjectileTypes.register();
BoilerHeaters.registerDefaults();
AllPortalTracks.registerDefaults();
AllDisplayBehaviours.registerDefaults();
// --
AttachedRegistry.unwrapAll();
AllAdvancements.register();
AllTriggers.register();
});

View file

@ -3,7 +3,7 @@ package com.simibubi.create.api.contraption.storage;
import com.simibubi.create.Create;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.lookup.BlockLookup;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.impl.contraption.storage.MountedStorageTypeRegistryImpl;
import com.tterrag.registrate.builders.BlockBuilder;
import com.tterrag.registrate.util.entry.RegistryEntry;
@ -12,9 +12,10 @@ import com.tterrag.registrate.util.nullness.NonNullUnaryOperator;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.IForgeRegistry;
public class MountedStorageTypeRegistry {
public class MountedStorageTypeRegistries {
public static final ResourceKey<Registry<MountedItemStorageType<?>>> ITEMS = ResourceKey.createRegistryKey(
Create.asResource("mounted_item_storage_type")
);
@ -22,16 +23,8 @@ public class MountedStorageTypeRegistry {
Create.asResource("mounted_fluid_storage_type")
);
/**
* Lookup used for finding the item storage type associated with a block.
* @see BlockLookup
*/
public static final BlockLookup<MountedItemStorageType<?>> ITEM_LOOKUP = MountedStorageTypeRegistryImpl.ITEM_LOOKUP;
/**
* Lookup used for finding the fluid storage type associated with a block.
* @see BlockLookup
*/
public static final BlockLookup<MountedFluidStorageType<?>> FLUID_LOOKUP = MountedStorageTypeRegistryImpl.FLUID_LOOKUP;
public static final AttachedRegistry<Block, MountedItemStorageType<?>> ITEM_STORAGES = MountedStorageTypeRegistryImpl.ITEM_STORAGES;
public static final AttachedRegistry<Block, MountedFluidStorageType<?>> FLUID_STORAGES = MountedStorageTypeRegistryImpl.FLUID_STORAGES;
/**
* @throws NullPointerException if called before registry registration
@ -52,7 +45,7 @@ public class MountedStorageTypeRegistry {
* that will register the given MountedItemStorageType to a block when ready.
*/
public static <B extends Block, P> NonNullUnaryOperator<BlockBuilder<B, P>> mountedItemStorage(RegistryEntry<? extends MountedItemStorageType<?>> type) {
return builder -> builder.onRegisterAfter(ITEMS, block -> ITEM_LOOKUP.register(block, type.get()));
return builder -> builder.onRegisterAfter(ITEMS, block -> ITEM_STORAGES.register(block, type.get()));
}
/**
@ -60,6 +53,6 @@ public class MountedStorageTypeRegistry {
* that will register the given MountedFluidStorageType to a block when ready.
*/
public static <B extends Block, P> NonNullUnaryOperator<BlockBuilder<B, P>> mountedFluidStorage(RegistryEntry<? extends MountedFluidStorageType<?>> type) {
return builder -> builder.onRegisterAfter(ITEMS, block -> FLUID_LOOKUP.register(block, type.get()));
return builder -> builder.onRegisterAfter(ITEMS, block -> FLUID_STORAGES.register(block, type.get()));
}
}

View file

@ -3,7 +3,7 @@ package com.simibubi.create.api.contraption.storage.fluid;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
@ -13,7 +13,7 @@ import net.minecraft.world.level.block.state.BlockState;
public abstract class MountedFluidStorageType<T extends MountedFluidStorage> {
public static final Codec<MountedFluidStorageType<?>> CODEC = ExtraCodecs.lazyInitializedCodec(
() -> MountedStorageTypeRegistry.getFluidsRegistry().getCodec()
() -> MountedStorageTypeRegistries.getFluidsRegistry().getCodec()
);
public final Codec<? extends T> codec;

View file

@ -1,7 +1,8 @@
package com.simibubi.create.api.contraption.storage.fluid.registrate;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.tterrag.registrate.AbstractRegistrate;
import com.tterrag.registrate.builders.AbstractBuilder;
import com.tterrag.registrate.builders.BuilderCallback;
@ -14,17 +15,17 @@ public class MountedFluidStorageTypeBuilder<T extends MountedFluidStorageType<?>
private final T type;
public MountedFluidStorageTypeBuilder(AbstractRegistrate<?> owner, P parent, String name, BuilderCallback callback, T type) {
super(owner, parent, name, callback, MountedStorageTypeRegistry.FLUIDS);
super(owner, parent, name, callback, MountedStorageTypeRegistries.FLUIDS);
this.type = type;
}
public MountedFluidStorageTypeBuilder<T, P> registerTo(Block block) {
MountedStorageTypeRegistry.FLUID_LOOKUP.register(block, this.type);
MountedStorageTypeRegistries.FLUID_STORAGES.register(block, this.type);
return this;
}
public MountedFluidStorageTypeBuilder<T, P> registerTo(TagKey<Block> tag) {
MountedStorageTypeRegistry.FLUID_LOOKUP.registerTag(tag, this.type);
MountedStorageTypeRegistries.FLUID_STORAGES.registerProvider(AttachedRegistry.Provider.forBlockTag(tag, this.type));
return this;
}

View file

@ -3,7 +3,7 @@ package com.simibubi.create.api.contraption.storage.item;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
@ -13,7 +13,7 @@ import net.minecraft.world.level.block.state.BlockState;
public abstract class MountedItemStorageType<T extends MountedItemStorage> {
public static final Codec<MountedItemStorageType<?>> CODEC = ExtraCodecs.lazyInitializedCodec(
() -> MountedStorageTypeRegistry.getItemsRegistry().getCodec()
() -> MountedStorageTypeRegistries.getItemsRegistry().getCodec()
);
public final Codec<? extends T> codec;

View file

@ -1,7 +1,8 @@
package com.simibubi.create.api.contraption.storage.item.registrate;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.tterrag.registrate.AbstractRegistrate;
import com.tterrag.registrate.builders.AbstractBuilder;
import com.tterrag.registrate.builders.BuilderCallback;
@ -14,17 +15,17 @@ public class MountedItemStorageTypeBuilder<T extends MountedItemStorageType<?>,
private final T type;
public MountedItemStorageTypeBuilder(AbstractRegistrate<?> owner, P parent, String name, BuilderCallback callback, T type) {
super(owner, parent, name, callback, MountedStorageTypeRegistry.ITEMS);
super(owner, parent, name, callback, MountedStorageTypeRegistries.ITEMS);
this.type = type;
}
public MountedItemStorageTypeBuilder<T, P> registerTo(Block block) {
MountedStorageTypeRegistry.ITEM_LOOKUP.register(block, this.type);
MountedStorageTypeRegistries.ITEM_STORAGES.register(block, this.type);
return this;
}
public MountedItemStorageTypeBuilder<T, P> registerTo(TagKey<Block> tag) {
MountedStorageTypeRegistry.ITEM_LOOKUP.registerTag(tag, this.type);
MountedStorageTypeRegistries.ITEM_STORAGES.registerProvider(AttachedRegistry.Provider.forBlockTag(tag, this.type));
return this;
}

View file

@ -1,52 +1,49 @@
package com.simibubi.create.api.contraption.train;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllInteractionBehaviours;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.processing.burner.BlockBasedTrainConductorInteractionBehaviour;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllInteractionBehaviours;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.processing.burner.BlockBasedTrainConductorInteractionBehaviour;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
/**
* All required methods to make your block a train conductor similar to the blaze burner
*/
@FunctionalInterface
public interface TrainConductorHandler {
@ApiStatus.Internal
List<TrainConductorHandler> CONDUCTOR_HANDLERS = new ArrayList<>();
boolean isValidConductor(BlockState state);
private static void registerHandler(TrainConductorHandler handler) {
CONDUCTOR_HANDLERS.add(handler);
}
static void registerConductor(ResourceLocation blockRl, Predicate<BlockState> isValidConductor, UpdateScheduleCallback updateScheduleCallback) {
AllInteractionBehaviours.registerBehaviour(blockRl, new BlockBasedTrainConductorInteractionBehaviour(isValidConductor, updateScheduleCallback));
static void registerConductor(Block block, Predicate<BlockState> isValidConductor, UpdateScheduleCallback updateScheduleCallback) {
BlockBasedTrainConductorInteractionBehaviour behavior = new BlockBasedTrainConductorInteractionBehaviour(isValidConductor, updateScheduleCallback);
AllInteractionBehaviours.REGISTRY.register(block, behavior);
registerHandler(isValidConductor::test);
}
@ApiStatus.Internal
static void registerBlazeBurner() {
registerConductor(AllBlocks.BLAZE_BURNER.getId(), blockState -> AllBlocks.BLAZE_BURNER.has(blockState)
static void registerBlazeBurner(Block block) {
registerConductor(block, blockState -> AllBlocks.BLAZE_BURNER.has(blockState)
&& blockState.getValue(BlazeBurnerBlock.HEAT_LEVEL) != BlazeBurnerBlock.HeatLevel.NONE, UpdateScheduleCallback.EMPTY);
}
@FunctionalInterface
interface UpdateScheduleCallback {
UpdateScheduleCallback EMPTY = (hasSchedule, blockState, blockStateSetter) -> {};
void update(boolean hasSchedule, BlockState currentBlockState, Consumer<BlockState> blockStateSetter);

View file

@ -1,49 +0,0 @@
package com.simibubi.create.api.contraption.transformable;
import com.simibubi.create.content.contraptions.StructureTransform;
import com.simibubi.create.impl.contraption.transformable.ContraptionTransformableRegistryImpl;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
/**
* Registry for registering new contraption transformations
* to properly place blocks when disassembled after being part of a contraption
*/
public class ContraptionTransformableRegistry {
/**
* Register a new transform for a provided block
*
* @param block The block you want to register a new {@link TransformableBlock} for
* @param transformableBlock The transform that should be applied whenever this block is being placed from
* contraption disassembly
*/
public static void registerForBlock(Block block, TransformableBlock transformableBlock) {
ContraptionTransformableRegistryImpl.registerForBlock(block, transformableBlock);
}
/**
* Register a new transform for a provided block entity type
*
* @param blockEntityType The blockEntityType you want to register a new {@link TransformableBlockEntity} for
* @param transformableBlockEntity The transform that should be applied whenever this block entity type is
* being placed from contraption disassembly
*/
public static void registerForBlockEntity(BlockEntityType<?> blockEntityType, TransformableBlockEntity transformableBlockEntity) {
ContraptionTransformableRegistryImpl.registerForBlockEntity(blockEntityType, transformableBlockEntity);
}
// --- Interfaces that provide the context that would be accessible if you implemented the ITransformable* interfaces ---
@FunctionalInterface
public interface TransformableBlock {
BlockState transform(Block block, BlockState state, StructureTransform transform);
}
@FunctionalInterface
public interface TransformableBlockEntity {
void transform(BlockEntity blockEntity, StructureTransform transform);
}
}

View file

@ -0,0 +1,28 @@
package com.simibubi.create.api.contraption.transformable;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.content.contraptions.StructureTransform;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
/**
* Registry for custom transformations to apply to blocks after they've been moved by a contraption.
* These interfaces are alternatives to the {@link ITransformableBlock} and {@link ITransformableBlockEntity} interfaces.
*/
public class MovedBlockTransformerRegistries {
public static final AttachedRegistry<Block, BlockTransformer> BLOCK_TRANSFORMERS = AttachedRegistry.create();
public static final AttachedRegistry<BlockEntityType<?>, BlockEntityTransformer> BLOCK_ENTITY_TRANSFORMERS = AttachedRegistry.create();
@FunctionalInterface
public interface BlockTransformer {
BlockState transform(Block block, BlockState state, StructureTransform transform);
}
@FunctionalInterface
public interface BlockEntityTransformer {
void transform(BlockEntity be, StructureTransform transform);
}
}

View file

@ -1,66 +0,0 @@
package com.simibubi.create.api.lookup;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.impl.lookup.BlockLookupImpl;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
/**
* Lookup for objects provided by blocks. Values can either be registered directly
* or found lazily through providers. Providers are only queried once per block.
* If they return a value, that value is cached. If they don't, that block is recorded
* as not having a corresponding value.
* <p>
* Provided values are reset on resource reloads and will be re-queried and re-cached the
* next time a block is queried.
* <p>
* All providers are expected to be registered synchronously during game init.
* Adding new ones late is not supported.
*/
@ApiStatus.NonExtendable
public interface BlockLookup<T> {
@Nullable
T find(Block block);
/**
* Shortcut to avoid calling getBlock() on a BlockState.
*/
@Nullable
T find(BlockState state);
/**
* Register a value to one block.
*/
void register(Block block, T value);
/**
* Register a value to all entries of a tag.
*/
void registerTag(TagKey<Block> tag, T value);
/**
* Register a new provider that will be queried.
* Providers are queried in reverse-registration order.
*/
void registerProvider(Provider<T> provider);
static <T> BlockLookup<T> create() {
return new BlockLookupImpl<>();
}
static <T> BlockLookup<T> create(Provider<T> initialProvider) {
BlockLookup<T> lookup = new BlockLookupImpl<>();
lookup.registerProvider(initialProvider);
return lookup;
}
@FunctionalInterface
interface Provider<T> {
@Nullable
T get(Block block);
}
}

View file

@ -0,0 +1,92 @@
package com.simibubi.create.api.registry;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.impl.registry.AttachedRegistryImpl;
import com.simibubi.create.impl.registry.TagProviderImpl;
import net.minecraft.core.Holder;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
/**
* A mapping of registered objects to something else.
* This class is thread-safe, and may be safely used during parallel mod init.
*/
public interface AttachedRegistry<K, V> {
/**
* Register an association between a key and a value.
* @throws IllegalArgumentException if the object already has an associated value
*/
void register(K object, V value);
/**
* Add a new provider to this registry. For information on providers, see {@link Provider}.
* @throws IllegalArgumentException if the provider has already been registered to this registry
*/
void registerProvider(Provider<K, V> provider);
/**
* Invalidate the cached values provided by the given provider, so they get re-computed on the next query.
* @throws IllegalArgumentException if the provider is not registered to this registry
*/
void invalidateProvider(Provider<K, V> provider);
/**
* Query the value associated with the given object. May be null if no association is present.
*/
@Nullable
V get(K object);
static <K, V> AttachedRegistry<K, V> create() {
return new AttachedRegistryImpl<>();
}
/**
* A provider can provide values to the registry in a lazy fashion. When a key does not have an
* associated value, all providers will be queried in reverse-registration order.
* <p>
* The values returned by providers are cached so that repeated queries always return the same value.
* To invalidate the cache of a provider, call {@link AttachedRegistry#invalidateProvider(Provider)}.
*/
@FunctionalInterface
interface Provider<K, V> {
@Nullable
V get(K object);
/**
* Called by the AttachedRegistry this provider is registered to after it's registered.
* This is useful for behavior that should only happen if a provider is actually registered,
* such as registering event listeners.
*/
default void onRegister(AttachedRegistry<K, V> registry) {
}
/**
* Create a provider that will return the same value for all entries in a tag.
* The Provider will invalidate itself when tags are reloaded.
*/
static <K, V> Provider<K, V> forTag(TagKey<K> tag, Function<K, Holder<K>> holderGetter, V value) {
return new TagProviderImpl<>(tag, holderGetter, value);
}
/**
* Shortcut for {@link #forTag} when the registry's type is Block.
*/
@SuppressWarnings("deprecation")
static <V> Provider<Block, V> forBlockTag(TagKey<Block> tag, V value) {
return new TagProviderImpl<>(tag, Block::builtInRegistryHolder, value);
}
/**
* Shortcut for {@link #forTag} when the registry's type is Item.
*/
@SuppressWarnings("deprecation")
static <V> Provider<Item, V> forItemTag(TagKey<Item> tag, V value) {
return new TagProviderImpl<>(tag, Item::builtInRegistryHolder, value);
}
}
}

View file

@ -0,0 +1,27 @@
package com.simibubi.create.api.schematic.nbt;
import com.simibubi.create.api.registry.AttachedRegistry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
/**
* Registry for safe NBT writers, used for filtering unsafe BlockEntity data out of schematics.
* <p>
* This is used to exclude specific tags that would result in exploits, ex. signs that execute commands when clicked.
* <p>
* This is provided as an alternative to {@link IPartialSafeNBT}.
*/
public class SafeNbtWriterRegistry {
public static final AttachedRegistry<BlockEntityType<?>, SafeNbtWriter> REGISTRY = AttachedRegistry.create();
@FunctionalInterface
public interface SafeNbtWriter {
/**
* Write filtered, safe NBT to the given tag. This is always called on the logical server.
* @param tag the NBT tag to write to
*/
void writeSafe(BlockEntity be, CompoundTag tag);
}
}

View file

@ -1,37 +0,0 @@
package com.simibubi.create.api.schematic.nbt;
import com.simibubi.create.impl.schematic.nbt.SchematicSafeNBTRegistryImpl;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
/**
* Registry for modifying the data of BlockEntities when being placed with the schematic system.
* </br>
* Mostly used to exclude specific tags that would result in exploits from being written.
*/
public class SchematicSafeNBTRegistry {
/**
* Register a new partial safe nbt provider for a specific blockEntityType
*
* @param blockEntityType The block entity type you would like to register this for
* @param safeNBT The custom PartialSafeNBT provider you would like to register for this blockEntityType,
* your {@link ContextProvidingPartialSafeNBT#writeSafe(BlockEntity, CompoundTag)} method will be
* called on the passed {@link ContextProvidingPartialSafeNBT}
* when the block entities data is being prepared for placement.
*/
public static void register(BlockEntityType<? extends BlockEntity> blockEntityType, ContextProvidingPartialSafeNBT safeNBT) {
SchematicSafeNBTRegistryImpl.register(blockEntityType, safeNBT);
}
// --- Interface that provides the context that would be available if you were to implement IPartialSafeNBT instead ---
@FunctionalInterface
public interface ContextProvidingPartialSafeNBT {
/**
* This will always be called from the logical server
*/
void writeSafe(BlockEntity blockEntity, CompoundTag tag);
}
}

View file

@ -0,0 +1,45 @@
package com.simibubi.create.api.schematic.requirement;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.content.schematics.requirement.ItemRequirement;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
/**
* Registries for custom schematic requirements for blocks, block entities, and entities. These requirements determine
* the items that are needed for placement into the world through schematics.
* <p>
* This is provided as an alternative to the following interfaces:
* <ul>
* <li>{@link ISpecialBlockItemRequirement}</li>
* <li>{@link ISpecialBlockEntityItemRequirement}</li>
* <li>{@link ISpecialEntityItemRequirement}</li>
* </ul>
*/
public class SchematicRequirementRegistries {
public static final AttachedRegistry<Block, BlockRequirement> BLOCKS = AttachedRegistry.create();
public static final AttachedRegistry<BlockEntityType<?>, BlockEntityRequirement> BLOCK_ENTITIES = AttachedRegistry.create();
public static final AttachedRegistry<EntityType<?>, EntityRequirement> ENTITIES = AttachedRegistry.create();
@FunctionalInterface
public interface BlockRequirement {
ItemRequirement getRequiredItems(Block block, BlockState state, @Nullable BlockEntity blockEntity);
}
@FunctionalInterface
public interface BlockEntityRequirement {
ItemRequirement getRequiredItems(BlockEntity blockEntity, BlockState state);
}
@FunctionalInterface
public interface EntityRequirement {
ItemRequirement getRequiredItems(Entity entity);
}
}

View file

@ -1,115 +0,0 @@
package com.simibubi.create.api.schematic.requirement;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.content.schematics.requirement.ItemRequirement;
import com.simibubi.create.impl.schematic.requirement.SchematicRequirementsRegistryImpl;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
/**
* Registry for schematic requirements for blocks, block entities, and entities.
*/
public class SchematicRequirementsRegistry {
/**
* Register a new special requirement for a specified block
*
* @param block The block you want to register a {@link BlockRequirement} for
* @param requirement The requirement you would like to add to this block,
* the {@link BlockRequirement#getRequiredItems(Block, BlockState, BlockEntity)}
* method will be called on the {@link BlockRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForBlock(Block block, BlockRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForBlock(block, requirement);
}
/**
* Register a new special requirement for a specified block
*
* @param block The id of the block you want to register a {@link BlockRequirement} for
* @param requirement The requirement you would like to add to this block,
* the {@link BlockRequirement#getRequiredItems(Block, BlockState, BlockEntity)}
* method will be called on the {@link BlockRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForBlock(ResourceLocation block, BlockRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForBlock(block, requirement);
}
/**
* Register a new special requirement for a specified block entity type
*
* @param blockEntityType The blockEntityType you want to register a {@link BlockEntityRequirement} for
* @param requirement The requirement you would like to add to this block entity type,
* the {@link BlockEntityRequirement#getRequiredItems(BlockEntity, BlockState)}
* method will be called on the {@link BlockEntityRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForBlockEntity(BlockEntityType<BlockEntity> blockEntityType, BlockEntityRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForBlockEntity(blockEntityType, requirement);
}
/**
* Register a new special requirement for a specified block entity type
*
* @param blockEntityType The id of the blockEntityType you want to register a {@link BlockEntityRequirement} for
* @param requirement The requirement you would like to add to this block entity type,
* the {@link BlockEntityRequirement#getRequiredItems(BlockEntity, BlockState)}
* method will be called on the {@link BlockEntityRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForBlockEntity(ResourceLocation blockEntityType, BlockEntityRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForBlockEntity(blockEntityType, requirement);
}
/**
* Register a new special requirement for a specified entity type
*
* @param entityType The entityType you want to register a {@link EntityRequirement} for
* @param requirement The requirement you would like to add to this entity type,
* the {@link EntityRequirement#getRequiredItems(Entity)}
* method will be called on the {@link EntityRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForEntity(EntityType<Entity> entityType, EntityRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForEntity(entityType, requirement);
}
/**
* Register a new special requirement for a specified entity type
*
* @param entityType The id of the entityType you want to register a {@link EntityRequirement} for
* @param requirement The requirement you would like to add to this entity type,
* the {@link EntityRequirement#getRequiredItems(Entity)}
* method will be called on the {@link EntityRequirement} you have provided,
* and you will be able to insert requirements based off the context that is given
*/
public static void registerForEntity(ResourceLocation entityType, EntityRequirement requirement) {
SchematicRequirementsRegistryImpl.registerForEntity(entityType, requirement);
}
// --- Interfaces that provide the context that would be accessible if you implemented the ISpecial* interfaces ---
@FunctionalInterface
public interface BlockRequirement {
ItemRequirement getRequiredItems(Block block, BlockState state, @Nullable BlockEntity blockEntity);
}
@FunctionalInterface
public interface BlockEntityRequirement {
ItemRequirement getRequiredItems(BlockEntity blockEntity, BlockState state);
}
@FunctionalInterface
public interface EntityRequirement {
ItemRequirement getRequiredItems(Entity entity);
}
}

View file

@ -1,39 +1,30 @@
package com.simibubi.create.content.contraptions;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import java.util.Collection;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.common.extensions.IForgeBlock;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.function.Supplier;
import net.minecraftforge.common.extensions.IForgeBlock;
public enum ContraptionMovementSetting {
MOVABLE, NO_PICKUP, UNMOVABLE;
private static final AttachedRegistry<Block, Supplier<ContraptionMovementSetting>> SETTING_SUPPLIERS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
public static void register(ResourceLocation block, Supplier<ContraptionMovementSetting> settingSupplier) {
SETTING_SUPPLIERS.register(block, settingSupplier);
}
public static void register(Block block, Supplier<ContraptionMovementSetting> settingSupplier) {
SETTING_SUPPLIERS.register(block, settingSupplier);
}
public static final AttachedRegistry<Block, Supplier<ContraptionMovementSetting>> REGISTRY = AttachedRegistry.create();
@Nullable
public static ContraptionMovementSetting get(Block block) {
if (block instanceof IMovementSettingProvider provider)
return provider.getContraptionMovementSetting();
Supplier<ContraptionMovementSetting> supplier = SETTING_SUPPLIERS.get(block);
if (supplier == null)
return null;
return supplier.get();
Supplier<ContraptionMovementSetting> supplier = REGISTRY.get(block);
return supplier == null ? null : supplier.get();
}
public static boolean allAre(Collection<StructureTemplate.StructureBlockInfo> blocks, ContraptionMovementSetting are) {
@ -45,12 +36,12 @@ public enum ContraptionMovementSetting {
}
public static void registerDefaults() {
register(Blocks.SPAWNER, () -> AllConfigs.server().kinetics.spawnerMovement.get());
register(Blocks.BUDDING_AMETHYST, () -> AllConfigs.server().kinetics.amethystMovement.get());
register(Blocks.OBSIDIAN, () -> AllConfigs.server().kinetics.obsidianMovement.get());
register(Blocks.CRYING_OBSIDIAN, () -> AllConfigs.server().kinetics.obsidianMovement.get());
register(Blocks.RESPAWN_ANCHOR, () -> AllConfigs.server().kinetics.obsidianMovement.get());
register(Blocks.REINFORCED_DEEPSLATE, () -> AllConfigs.server().kinetics.reinforcedDeepslateMovement.get());
REGISTRY.register(Blocks.SPAWNER, () -> AllConfigs.server().kinetics.spawnerMovement.get());
REGISTRY.register(Blocks.BUDDING_AMETHYST, () -> AllConfigs.server().kinetics.amethystMovement.get());
REGISTRY.register(Blocks.OBSIDIAN, () -> AllConfigs.server().kinetics.obsidianMovement.get());
REGISTRY.register(Blocks.CRYING_OBSIDIAN, () -> AllConfigs.server().kinetics.obsidianMovement.get());
REGISTRY.register(Blocks.RESPAWN_ANCHOR, () -> AllConfigs.server().kinetics.obsidianMovement.get());
REGISTRY.register(Blocks.REINFORCED_DEEPSLATE, () -> AllConfigs.server().kinetics.reinforcedDeepslateMovement.get());
}
public interface IMovementSettingProvider extends IForgeBlock {

View file

@ -17,7 +17,7 @@ import com.google.common.collect.Sets.SetView;
import com.mojang.datafixers.util.Pair;
import com.simibubi.create.AllPackets;
import com.simibubi.create.Create;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import com.simibubi.create.api.contraption.storage.SyncedMountedStorage;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
@ -144,7 +144,7 @@ public class MountedStorageManager {
}
public void addBlock(Level level, BlockState state, BlockPos globalPos, BlockPos localPos, @Nullable BlockEntity be) {
MountedItemStorageType<?> itemType = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state);
MountedItemStorageType<?> itemType = MountedStorageTypeRegistries.ITEM_STORAGES.get(state.getBlock());
if (itemType != null) {
MountedItemStorage storage = itemType.mount(level, state, globalPos, be);
if (storage != null) {
@ -152,7 +152,7 @@ public class MountedStorageManager {
}
}
MountedFluidStorageType<?> fluidType = MountedStorageTypeRegistry.FLUID_LOOKUP.find(state);
MountedFluidStorageType<?> fluidType = MountedStorageTypeRegistries.FLUID_STORAGES.get(state.getBlock());
if (fluidType != null) {
MountedFluidStorage storage = fluidType.mount(level, state, globalPos, be);
if (storage != null) {
@ -167,7 +167,7 @@ public class MountedStorageManager {
MountedItemStorage itemStorage = this.getAllItemStorages().get(localPos);
if (itemStorage != null) {
MountedItemStorageType<?> expectedType = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state);
MountedItemStorageType<?> expectedType = MountedStorageTypeRegistries.ITEM_STORAGES.get(state.getBlock());
if (itemStorage.type == expectedType) {
itemStorage.unmount(level, state, globalPos, be);
}
@ -175,7 +175,7 @@ public class MountedStorageManager {
MountedFluidStorage fluidStorage = this.getFluids().storages.get(localPos);
if (fluidStorage != null) {
MountedFluidStorageType<?> expectedType = MountedStorageTypeRegistry.FLUID_LOOKUP.find(state);
MountedFluidStorageType<?> expectedType = MountedStorageTypeRegistries.FLUID_STORAGES.get(state.getBlock());
if (fluidStorage.type == expectedType) {
fluidStorage.unmount(level, state, globalPos, be);
}

View file

@ -4,10 +4,11 @@ import static net.minecraft.world.level.block.state.properties.BlockStatePropert
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.FACING;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING;
import com.simibubi.create.api.contraption.transformable.ContraptionTransformableRegistry;
import com.simibubi.create.api.contraption.transformable.ITransformableBlock;
import com.simibubi.create.api.contraption.transformable.ITransformableBlockEntity;
import com.simibubi.create.impl.contraption.transformable.ContraptionTransformableRegistryImpl;
import com.simibubi.create.api.contraption.transformable.MovedBlockTransformerRegistries;
import com.simibubi.create.api.contraption.transformable.MovedBlockTransformerRegistries.BlockEntityTransformer;
import com.simibubi.create.api.contraption.transformable.MovedBlockTransformerRegistries.BlockTransformer;
import net.createmod.catnip.math.VecHelper;
import net.minecraft.core.BlockPos;
@ -133,9 +134,9 @@ public class StructureTransform {
}
public void apply(BlockEntity be) {
ContraptionTransformableRegistry.TransformableBlockEntity transformableBlockEntity = ContraptionTransformableRegistryImpl.get(be.getType());
if (transformableBlockEntity != null) {
transformableBlockEntity.transform(be, this);
BlockEntityTransformer transformer = MovedBlockTransformerRegistries.BLOCK_ENTITY_TRANSFORMERS.get(be.getType());
if (transformer != null) {
transformer.transform(be, this);
} else if (be instanceof ITransformableBlockEntity itbe) {
itbe.transform(this);
}
@ -148,9 +149,9 @@ public class StructureTransform {
*/
public BlockState apply(BlockState state) {
Block block = state.getBlock();
ContraptionTransformableRegistry.TransformableBlock transformableBlock = ContraptionTransformableRegistryImpl.get(block);
if (transformableBlock != null) {
return transformableBlock.transform(block, state, this);
BlockTransformer transformer = MovedBlockTransformerRegistries.BLOCK_TRANSFORMERS.get(block);
if (transformer != null) {
return transformer.transform(block, state, this);
} else if (block instanceof ITransformableBlock transformable) {
return transformable.transform(state, this);
}

View file

@ -1,66 +1,39 @@
package com.simibubi.create.content.fluids.tank;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.simibubi.create.foundation.utility.BlockHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.registries.ForgeRegistries;
public class BoilerHeaters {
private static final AttachedRegistry<Block, Heater> BLOCK_HEATERS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final List<HeaterProvider> GLOBAL_HEATERS = new ArrayList<>();
public static final int PASSIVE_HEAT = 0;
public static final int NO_HEAT = -1;
public static void registerHeater(ResourceLocation block, Heater heater) {
BLOCK_HEATERS.register(block, heater);
}
public static final AttachedRegistry<Block, Heater> REGISTRY = AttachedRegistry.create();
public static void registerHeater(Block block, Heater heater) {
BLOCK_HEATERS.register(block, heater);
}
public static void registerHeaterProvider(HeaterProvider provider) {
GLOBAL_HEATERS.add(provider);
}
public static final Heater PASSIVE_HEATER = (level, pos, state) -> BlockHelper.isNotUnheated(state) ? PASSIVE_HEAT : NO_HEAT;
/**
* A return value of {@code -1} represents no heat.
* A return value of {@code 0} represents passive heat.
* All other positive values are used as the amount of active heat.
* Gets the heat at the given location. If a heater is present, queries it for heat. If not, returns {@link #NO_HEAT}.
* @see Heater#getActiveHeat(Level, BlockPos, BlockState)
*/
public static float getActiveHeat(Level level, BlockPos pos, BlockState state) {
Heater heater = BLOCK_HEATERS.get(state.getBlock());
if (heater != null) {
return heater.getActiveHeat(level, pos, state);
}
for (HeaterProvider provider : GLOBAL_HEATERS) {
heater = provider.getHeater(level, pos, state);
if (heater != null) {
return heater.getActiveHeat(level, pos, state);
}
}
return -1;
Heater heater = REGISTRY.get(state.getBlock());
return heater != null ? heater.getActiveHeat(level, pos, state) : NO_HEAT;
}
public static void registerDefaults() {
registerHeater(AllBlocks.BLAZE_BURNER.get(), (level, pos, state) -> {
REGISTRY.register(AllBlocks.BLAZE_BURNER.get(), (level, pos, state) -> {
HeatLevel value = state.getValue(BlazeBurnerBlock.HEAT_LEVEL);
if (value == HeatLevel.NONE) {
return -1;
return NO_HEAT;
}
if (value == HeatLevel.SEETHING) {
return 2;
@ -68,28 +41,19 @@ public class BoilerHeaters {
if (value.isAtLeast(HeatLevel.FADING)) {
return 1;
}
return 0;
return PASSIVE_HEAT;
});
registerHeaterProvider((level, pos, state) -> {
if (AllBlockTags.PASSIVE_BOILER_HEATERS.matches(state) && BlockHelper.isNotUnheated(state)) {
return (level1, pos1, state1) -> 0;
}
return null;
});
REGISTRY.registerProvider(AttachedRegistry.Provider.forBlockTag(AllBlockTags.PASSIVE_BOILER_HEATERS.tag, PASSIVE_HEATER));
}
@FunctionalInterface
public interface Heater {
/**
* A return value of {@code -1} represents no heat.
* A return value of {@code 0} represents passive heat.
* All other positive values are used as the amount of active heat.
* @return the amount of heat to provide.
* @see #NO_HEAT
* @see #PASSIVE_HEAT
*/
float getActiveHeat(Level level, BlockPos pos, BlockState state);
}
public interface HeaterProvider {
@Nullable
Heater getHeater(Level level, BlockPos pos, BlockState state);
}
}

View file

@ -9,6 +9,7 @@ import java.util.Map;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.content.redstone.displayLink.source.ComputerDisplaySource;
import com.simibubi.create.content.redstone.displayLink.source.DeathCounterDisplaySource;
@ -19,7 +20,6 @@ import com.simibubi.create.content.redstone.displayLink.source.ScoreboardDisplay
import com.simibubi.create.content.redstone.displayLink.target.DisplayTarget;
import com.simibubi.create.content.redstone.displayLink.target.LecternDisplayTarget;
import com.simibubi.create.content.redstone.displayLink.target.SignDisplayTarget;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.tterrag.registrate.util.nullness.NonNullConsumer;
import net.createmod.catnip.platform.CatnipServices;
@ -32,16 +32,17 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.registries.ForgeRegistries;
public class AllDisplayBehaviours {
public static final Map<ResourceLocation, DisplayBehaviour> GATHERER_BEHAVIOURS = new HashMap<>();
private static final AttachedRegistry<Block, List<DisplaySource>> SOURCES_BY_BLOCK = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final AttachedRegistry<BlockEntityType<?>, List<DisplaySource>> SOURCES_BY_BLOCK_ENTITY = new AttachedRegistry<>(ForgeRegistries.BLOCK_ENTITY_TYPES);
private static final AttachedRegistry<Block, List<DisplaySource>> SOURCES_BY_BLOCK = AttachedRegistry.create();
private static final AttachedRegistry<BlockEntityType<?>, List<DisplaySource>> SOURCES_BY_BLOCK_ENTITY = AttachedRegistry.create();
private static final AttachedRegistry<Block, DisplayTarget> TARGETS_BY_BLOCK = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final AttachedRegistry<BlockEntityType<?>, DisplayTarget> TARGETS_BY_BLOCK_ENTITY = new AttachedRegistry<>(ForgeRegistries.BLOCK_ENTITY_TYPES);
private static final AttachedRegistry<Block, DisplayTarget> TARGETS_BY_BLOCK = AttachedRegistry.create();
private static final AttachedRegistry<BlockEntityType<?>, DisplayTarget> TARGETS_BY_BLOCK_ENTITY = AttachedRegistry.create();
public static DisplayBehaviour register(ResourceLocation id, DisplayBehaviour behaviour) {
behaviour.id = id;
@ -49,34 +50,6 @@ public class AllDisplayBehaviours {
return behaviour;
}
public static void assignBlock(DisplayBehaviour behaviour, ResourceLocation block) {
if (behaviour instanceof DisplaySource source) {
List<DisplaySource> sources = SOURCES_BY_BLOCK.get(block);
if (sources == null) {
sources = new ArrayList<>();
SOURCES_BY_BLOCK.register(block, sources);
}
sources.add(source);
}
if (behaviour instanceof DisplayTarget target) {
TARGETS_BY_BLOCK.register(block, target);
}
}
public static void assignBlockEntity(DisplayBehaviour behaviour, ResourceLocation beType) {
if (behaviour instanceof DisplaySource source) {
List<DisplaySource> sources = SOURCES_BY_BLOCK_ENTITY.get(beType);
if (sources == null) {
sources = new ArrayList<>();
SOURCES_BY_BLOCK_ENTITY.register(beType, sources);
}
sources.add(source);
}
if (behaviour instanceof DisplayTarget target) {
TARGETS_BY_BLOCK_ENTITY.register(beType, target);
}
}
public static void assignBlock(DisplayBehaviour behaviour, Block block) {
if (behaviour instanceof DisplaySource source) {
List<DisplaySource> sources = SOURCES_BY_BLOCK.get(block);
@ -105,6 +78,15 @@ public class AllDisplayBehaviours {
}
}
public static void tryAssignBlockEntity(DisplayBehaviour behaviour, ResourceLocation id) {
if (ForgeRegistries.BLOCK_ENTITY_TYPES.containsKey(id)) {
BlockEntityType<?> type = ForgeRegistries.BLOCK_ENTITY_TYPES.getValue(id);
assignBlockEntity(behaviour, type);
} else {
Create.LOGGER.warn("Block entity for display behavior wasn't found: {}. Outdated compat?", id);
}
}
public static <B extends Block> NonNullConsumer<? super B> assignDataBehaviour(DisplayBehaviour behaviour,
String... suffix) {
return b -> {
@ -113,7 +95,7 @@ public class AllDisplayBehaviours {
if (suffix.length > 0)
idSuffix += "_" + suffix[0];
assignBlock(register(new ResourceLocation(registryName.getNamespace(), registryName.getPath() + idSuffix),
behaviour), registryName);
behaviour), b);
};
}
@ -127,7 +109,7 @@ public class AllDisplayBehaviours {
assignBlockEntity(
register(new ResourceLocation(registryName.getNamespace(), registryName.getPath() + idSuffix),
behaviour),
registryName);
b);
};
}
@ -237,10 +219,10 @@ public class AllDisplayBehaviours {
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
DisplayBehaviour computerDisplaySource = register(Create.asResource("computer_display_source"), new ComputerDisplaySource());
assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("wired_modem_full"));
assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_normal"));
assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_advanced"));
assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_command"));
tryAssignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("wired_modem_full"));
tryAssignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_normal"));
tryAssignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_advanced"));
tryAssignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_command"));
});
}
}

View file

@ -6,15 +6,15 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.schematic.requirement.ISpecialBlockEntityItemRequirement;
import com.simibubi.create.api.schematic.requirement.ISpecialBlockItemRequirement;
import com.simibubi.create.api.schematic.requirement.ISpecialEntityItemRequirement;
import com.simibubi.create.api.schematic.requirement.SchematicRequirementsRegistry;
import com.simibubi.create.api.schematic.requirement.SchematicRequirementRegistries;
import com.simibubi.create.compat.framedblocks.FramedBlocksInSchematics;
import com.simibubi.create.foundation.data.recipe.Mods;
import com.simibubi.create.impl.schematic.requirement.SchematicRequirementsRegistryImpl;
import net.createmod.catnip.nbt.NBTProcessors;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.ArmorStand;
@ -35,9 +35,8 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.Nullable;
import net.minecraftforge.registries.ForgeRegistries;
public class ItemRequirement {
public static final ItemRequirement NONE = new ItemRequirement(Collections.emptyList());
@ -71,9 +70,9 @@ public class ItemRequirement {
Block block = state.getBlock();
ItemRequirement requirement;
SchematicRequirementsRegistry.BlockRequirement blockItemRequirement = SchematicRequirementsRegistryImpl.getRequirementForBlock(block);
if (blockItemRequirement != null) {
requirement = blockItemRequirement.getRequiredItems(block, state, be);
SchematicRequirementRegistries.BlockRequirement blockRequirement = SchematicRequirementRegistries.BLOCKS.get(block);
if (blockRequirement != null) {
requirement = blockRequirement.getRequiredItems(block, state, be);
} else if (block instanceof ISpecialBlockItemRequirement specialBlock) {
requirement = specialBlock.getRequiredItems(state, be);
} else {
@ -81,9 +80,9 @@ public class ItemRequirement {
}
if (be != null) {
SchematicRequirementsRegistry.BlockEntityRequirement blockEntityItemRequirement = SchematicRequirementsRegistryImpl.getRequirementForBlockEntityType(be.getType());
if (blockEntityItemRequirement != null) {
requirement = requirement.union(blockEntityItemRequirement.getRequiredItems(be, state));
SchematicRequirementRegistries.BlockEntityRequirement beRequirement = SchematicRequirementRegistries.BLOCK_ENTITIES.get(be.getType());
if (beRequirement != null) {
requirement = requirement.union(beRequirement.getRequiredItems(be, state));
} else if (be instanceof ISpecialBlockEntityItemRequirement specialBE) {
requirement = requirement.union(specialBE.getRequiredItems(state));
} else if (com.simibubi.create.compat.Mods.FRAMEDBLOCKS.contains(block)) {
@ -134,9 +133,9 @@ public class ItemRequirement {
}
public static ItemRequirement of(Entity entity) {
SchematicRequirementsRegistry.EntityRequirement entityItemRequirement = SchematicRequirementsRegistryImpl.getRequirementForEntityType(entity.getType());
if (entityItemRequirement != null) {
return entityItemRequirement.getRequiredItems(entity);
SchematicRequirementRegistries.EntityRequirement requirement = SchematicRequirementRegistries.ENTITIES.get(entity.getType());
if (requirement != null) {
return requirement.getRequiredItems(entity);
} else if (entity instanceof ISpecialEntityItemRequirement specialEntity) {
return specialEntity.getRequiredItems();
}

View file

@ -5,13 +5,13 @@ import java.util.function.Function;
import java.util.function.UnaryOperator;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.AttachedRegistry;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.betterend.BetterEndPortalCompat;
import com.simibubi.create.content.contraptions.glue.SuperGlueEntity;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import net.createmod.catnip.math.BlockFace;
import net.createmod.catnip.data.Pair;
import net.createmod.catnip.math.BlockFace;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
@ -26,6 +26,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.util.ITeleporter;
import net.minecraftforge.registries.ForgeRegistries;
@ -48,27 +49,22 @@ public class AllPortalTracks {
/**
* Registry mapping portal blocks to their respective {@link PortalTrackProvider}s.
*/
private static final AttachedRegistry<Block, PortalTrackProvider> PORTAL_BEHAVIOURS =
new AttachedRegistry<>(ForgeRegistries.BLOCKS);
public static final AttachedRegistry<Block, PortalTrackProvider> REGISTRY = AttachedRegistry.create();
/**
* Registers a portal track integration for a given block identified by its {@link ResourceLocation}.
* Registers a portal track integration for a given block identified by its {@link ResourceLocation}, if it exists.
* If it does not, a warning will be logged.
*
* @param block The resource location of the portal block.
* @param id The resource location of the portal block.
* @param provider The portal track provider for the block.
*/
public static void registerIntegration(ResourceLocation block, PortalTrackProvider provider) {
PORTAL_BEHAVIOURS.register(block, provider);
public static void tryRegisterIntegration(ResourceLocation id, PortalTrackProvider provider) {
if (ForgeRegistries.BLOCKS.containsKey(id)) {
Block block = ForgeRegistries.BLOCKS.getValue(id);
REGISTRY.register(block, provider);
} else {
Create.LOGGER.warn("Portal for integration wasn't found: {}. Compat outdated?", id);
}
/**
* Registers a portal track integration for a given {@link Block}.
*
* @param block The portal block.
* @param provider The portal track provider for the block.
*/
public static void registerIntegration(Block block, PortalTrackProvider provider) {
PORTAL_BEHAVIOURS.register(block, provider);
}
/**
@ -78,7 +74,7 @@ public class AllPortalTracks {
* @return {@code true} if the block state represents a supported portal; {@code false} otherwise.
*/
public static boolean isSupportedPortal(BlockState state) {
return PORTAL_BEHAVIOURS.get(state.getBlock()) != null;
return REGISTRY.get(state.getBlock()) != null;
}
/**
@ -92,7 +88,7 @@ public class AllPortalTracks {
public static Pair<ServerLevel, BlockFace> getOtherSide(ServerLevel level, BlockFace inboundTrack) {
BlockPos portalPos = inboundTrack.getConnectedPos();
BlockState portalState = level.getBlockState(portalPos);
PortalTrackProvider provider = PORTAL_BEHAVIOURS.get(portalState.getBlock());
PortalTrackProvider provider = REGISTRY.get(portalState.getBlock());
return provider == null ? null : provider.apply(Pair.of(level, inboundTrack));
}
@ -103,11 +99,15 @@ public class AllPortalTracks {
* This includes the Nether and the Aether (if loaded).
*/
public static void registerDefaults() {
registerIntegration(Blocks.NETHER_PORTAL, AllPortalTracks::nether);
if (Mods.AETHER.isLoaded())
registerIntegration(Mods.AETHER.rl("aether_portal"), AllPortalTracks::aether);
if (Mods.BETTEREND.isLoaded())
registerIntegration(Mods.BETTEREND.rl("end_portal_block"), AllPortalTracks::betterend);
REGISTRY.register(Blocks.NETHER_PORTAL, AllPortalTracks::nether);
if (Mods.AETHER.isLoaded()) {
tryRegisterIntegration(Mods.AETHER.rl("aether_portal"), AllPortalTracks::aether);
}
if (Mods.BETTEREND.isLoaded()) {
tryRegisterIntegration(Mods.BETTEREND.rl("end_portal_block"), AllPortalTracks::betterend);
}
}
/**

View file

@ -47,6 +47,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour.Properties;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions;
@ -106,10 +107,13 @@ public class CreateRegistrate extends AbstractRegistrate<CreateRegistrate> {
Builder<R, T, ?, ?> builder, NonNullSupplier<? extends T> creator,
NonNullFunction<RegistryObject<T>, ? extends RegistryEntry<T>> entryFactory) {
RegistryEntry<T> entry = super.accept(name, type, builder, creator, entryFactory);
if (type.equals(Registries.ITEM)) {
if (currentTooltipModifierFactory != null) {
TooltipModifier.REGISTRY.registerDeferred(entry.getId(), currentTooltipModifierFactory);
}
if (type.equals(Registries.ITEM) && currentTooltipModifierFactory != null) {
// grab the factory here for the lambda, it can change between now and registration
Function<Item, TooltipModifier> factory = currentTooltipModifierFactory;
this.addRegisterCallback(name, Registries.ITEM, item -> {
TooltipModifier modifier = factory.apply(item);
TooltipModifier.REGISTRY.register(item, modifier);
});
}
if (currentTab != null) {
TAB_LOOKUP.put(entry, currentTab);

View file

@ -2,14 +2,15 @@ package com.simibubi.create.foundation.item;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import com.simibubi.create.api.registry.AttachedRegistry;
import net.minecraft.world.item.Item;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
@FunctionalInterface
public interface TooltipModifier {
AttachedRegistry<Item, TooltipModifier> REGISTRY = new AttachedRegistry<>(ForgeRegistries.ITEMS);
AttachedRegistry<Item, TooltipModifier> REGISTRY = AttachedRegistry.create();
TooltipModifier EMPTY = new TooltipModifier() {
@Override

View file

@ -1,146 +0,0 @@
package com.simibubi.create.foundation.utility;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.Create;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
public class AttachedRegistry<K, V> {
private static final List<AttachedRegistry<?, ?>> ALL = new ArrayList<>();
protected final IForgeRegistry<K> objectRegistry;
protected final Map<ResourceLocation, V> idMap = new HashMap<>();
protected final Map<K, V> objectMap = new IdentityHashMap<>();
protected final Map<ResourceLocation, Function<K, V>> deferredRegistrations = new HashMap<>();
protected boolean unwrapped = false;
public AttachedRegistry(IForgeRegistry<K> objectRegistry) {
this.objectRegistry = objectRegistry;
ALL.add(this);
}
public void register(ResourceLocation id, V value) {
if (!unwrapped) {
idMap.put(id, value);
} else {
K object = objectRegistry.getValue(id);
if (object != null) {
objectMap.put(object, value);
} else {
Create.LOGGER.warn("Could not get object for id '" + id + "' in AttachedRegistry after unwrapping!");
}
}
}
public void register(K object, V value) {
if (unwrapped) {
objectMap.put(object, value);
} else {
ResourceLocation id = objectRegistry.getKey(object);
if (id != null) {
idMap.put(id, value);
} else {
Create.LOGGER.warn("Could not get id of object '" + object + "' in AttachedRegistry before unwrapping!");
}
}
}
public void registerDeferred(ResourceLocation id, Function<K, V> func) {
if (!unwrapped) {
deferredRegistrations.put(id, func);
} else {
K object = objectRegistry.getValue(id);
if (object != null) {
objectMap.put(object, func.apply(object));
} else {
Create.LOGGER.warn("Could not get object for id '" + id + "' in AttachedRegistry after unwrapping!");
}
}
}
public void registerDeferred(K object, Function<K, V> func) {
if (unwrapped) {
objectMap.put(object, func.apply(object));
} else {
ResourceLocation id = objectRegistry.getKey(object);
if (id != null) {
deferredRegistrations.put(id, func);
} else {
Create.LOGGER.warn("Could not get id of object '" + object + "' in AttachedRegistry before unwrapping!");
}
}
}
@Nullable
public V get(ResourceLocation id) {
if (!unwrapped) {
return idMap.get(id);
} else {
K object = objectRegistry.getValue(id);
if (object != null) {
return objectMap.get(object);
} else {
Create.LOGGER.warn("Could not get object for id '" + id + "' in AttachedRegistry after unwrapping!");
return null;
}
}
}
@Nullable
public V get(K object) {
if (unwrapped) {
return objectMap.get(object);
} else {
ResourceLocation id = objectRegistry.getKey(object);
if (id != null) {
return idMap.get(id);
} else {
Create.LOGGER.warn("Could not get id of object '" + object + "' in AttachedRegistry before unwrapping!");
return null;
}
}
}
public boolean isUnwrapped() {
return unwrapped;
}
protected void unwrap() {
deferredRegistrations.forEach((id, func) -> {
K object = objectRegistry.getValue(id);
if (object != null) {
objectMap.put(object, func.apply(object));
} else {
Create.LOGGER.warn("Could not get object for id '" + id + "' in AttachedRegistry during unwrapping!");
}
});
idMap.forEach((id, value) -> {
K object = objectRegistry.getValue(id);
if (object != null) {
objectMap.put(object, value);
} else {
Create.LOGGER.warn("Could not get object for id '" + id + "' in AttachedRegistry during unwrapping!");
}
});
deferredRegistrations.clear();
idMap.clear();
unwrapped = true;
}
public static void unwrapAll() {
for (AttachedRegistry<?, ?> registry : ALL) {
registry.unwrap();
}
}
}

View file

@ -8,7 +8,8 @@ import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.api.schematic.nbt.IPartialSafeNBT;
import com.simibubi.create.api.schematic.nbt.SchematicSafeNBTRegistry;
import com.simibubi.create.api.schematic.nbt.SafeNbtWriterRegistry;
import com.simibubi.create.api.schematic.nbt.SafeNbtWriterRegistry.SafeNbtWriter;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.framedblocks.FramedBlocksInSchematics;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
@ -16,7 +17,6 @@ import com.simibubi.create.content.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.blockEntity.IMergeableBE;
import com.simibubi.create.foundation.blockEntity.IMultiBlockEntityContainer;
import com.simibubi.create.impl.schematic.nbt.SchematicSafeNBTRegistryImpl;
import net.createmod.catnip.nbt.NBTProcessors;
import net.minecraft.core.BlockPos;
@ -57,6 +57,7 @@ import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.material.FluidState;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.level.BlockEvent;
@ -279,18 +280,19 @@ public class BlockHelper {
if (blockEntity == null)
return null;
SchematicSafeNBTRegistry.ContextProvidingPartialSafeNBT safeNBT = SchematicSafeNBTRegistryImpl.getPartialSafeNBT(blockEntity.getType());
SafeNbtWriter writer = SafeNbtWriterRegistry.REGISTRY.get(blockEntity.getType());
if (AllBlockTags.SAFE_NBT.matches(blockState)) {
data = blockEntity.saveWithFullMetadata();
} else if (safeNBT != null) {
} else if (writer != null) {
data = new CompoundTag();
safeNBT.writeSafe(blockEntity, data);
writer.writeSafe(blockEntity, data);
} else if (blockEntity instanceof IPartialSafeNBT safeNbtBE) {
data = new CompoundTag();
safeNbtBE.writeSafe(data);
} else if (Mods.FRAMEDBLOCKS.contains(blockState.getBlock())) {
data = FramedBlocksInSchematics.prepareBlockEntityData(blockState, blockEntity);
}
return NBTProcessors.process(blockState, blockEntity, data, true);
}

View file

@ -3,15 +3,20 @@ package com.simibubi.create.impl.contraption.storage;
import java.util.Objects;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllMountedStorageTypes;
import com.simibubi.create.AllTags;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistries;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.lookup.BlockLookup;
import com.simibubi.create.api.registry.AttachedRegistry;
import net.minecraft.Util;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TagsUpdatedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.IForgeRegistry;
@ -21,8 +26,12 @@ import net.minecraftforge.registries.RegistryBuilder;
@ApiStatus.Internal
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class MountedStorageTypeRegistryImpl {
public static final BlockLookup<MountedItemStorageType<?>> ITEM_LOOKUP = BlockLookup.create(MountedStorageTypeRegistryImpl::itemFallback);
public static final BlockLookup<MountedFluidStorageType<?>> FLUID_LOOKUP = BlockLookup.create();
public static final AttachedRegistry<Block, MountedItemStorageType<?>> ITEM_STORAGES = Util.make(() -> {
AttachedRegistry<Block, MountedItemStorageType<?>> registry = AttachedRegistry.create();
registry.registerProvider(ItemFallbackProvider.INSTANCE);
return registry;
});
public static final AttachedRegistry<Block, MountedFluidStorageType<?>> FLUID_STORAGES = AttachedRegistry.create();
private static IForgeRegistry<MountedItemStorageType<?>> itemsRegistry;
private static IForgeRegistry<MountedFluidStorageType<?>> fluidsRegistry;
@ -39,19 +48,34 @@ public class MountedStorageTypeRegistryImpl {
public static void registerRegistries(NewRegistryEvent event) {
event.create(
new RegistryBuilder<MountedItemStorageType<?>>()
.setName(MountedStorageTypeRegistry.ITEMS.location()),
.setName(MountedStorageTypeRegistries.ITEMS.location()),
registry -> itemsRegistry = registry
);
event.create(
new RegistryBuilder<MountedFluidStorageType<?>>()
.setName(MountedStorageTypeRegistry.FLUIDS.location()),
.setName(MountedStorageTypeRegistries.FLUIDS.location()),
registry -> fluidsRegistry = registry
);
}
private static MountedItemStorageType<?> itemFallback(Block block) {
private enum ItemFallbackProvider implements AttachedRegistry.Provider<Block, MountedItemStorageType<?>> {
INSTANCE;
@Override
@Nullable
public MountedItemStorageType<?> get(Block block) {
return AllTags.AllBlockTags.FALLBACK_MOUNTED_STORAGE_BLACKLIST.matches(block)
? null
: AllMountedStorageTypes.FALLBACK.get();
}
@Override
public void onRegister(AttachedRegistry<Block, MountedItemStorageType<?>> registry) {
MinecraftForge.EVENT_BUS.addListener((TagsUpdatedEvent event) -> {
if (event.shouldUpdateStaticData()) {
registry.invalidateProvider(this);
}
});
}
}
}

View file

@ -1,33 +0,0 @@
package com.simibubi.create.impl.contraption.transformable;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.api.contraption.transformable.ContraptionTransformableRegistry.TransformableBlock;
import com.simibubi.create.api.contraption.transformable.ContraptionTransformableRegistry.TransformableBlockEntity;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.ForgeRegistries;
@ApiStatus.Internal
public class ContraptionTransformableRegistryImpl {
private static final AttachedRegistry<Block, TransformableBlock> TRANSFORMABLE_BLOCKS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final AttachedRegistry<BlockEntityType<?>, TransformableBlockEntity> TRANSFORMABLE_BLOCK_ENTITIES = new AttachedRegistry<>(ForgeRegistries.BLOCK_ENTITY_TYPES);
public static void registerForBlock(Block block, TransformableBlock transformableBlock) {
TRANSFORMABLE_BLOCKS.register(block, transformableBlock);
}
public static void registerForBlockEntity(BlockEntityType<?> blockEntityType, TransformableBlockEntity transformableBlockEntity) {
TRANSFORMABLE_BLOCK_ENTITIES.register(blockEntityType, transformableBlockEntity);
}
public static TransformableBlock get(Block block) {
return TRANSFORMABLE_BLOCKS.get(block);
}
public static TransformableBlockEntity get(BlockEntityType<?> blockEntityType) {
return TRANSFORMABLE_BLOCK_ENTITIES.get(blockEntityType);
}
}

View file

@ -1,103 +0,0 @@
package com.simibubi.create.impl.lookup;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.simibubi.create.api.lookup.BlockLookup;
import net.minecraftforge.event.TagsUpdatedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
@ApiStatus.Internal
@Mod.EventBusSubscriber
public class BlockLookupImpl<T> implements BlockLookup<T> {
private static final List<BlockLookupImpl<?>> allLookups = new ArrayList<>();
private final Map<Block, T> map;
private final Map<Block, T> providedValues;
private final Set<Block> providedNull;
private final List<Provider<T>> providers;
public BlockLookupImpl() {
this.map = new IdentityHashMap<>();
this.providedValues = new IdentityHashMap<>();
this.providedNull = new HashSet<>();
this.providers = new ArrayList<>();
allLookups.add(this);
}
@Nullable
@Override
public T find(BlockState state) {
return this.find(state.getBlock());
}
@Nullable
@Override
public T find(Block block) {
T registered = this.map.get(block);
if (registered != null)
return registered;
if (this.providedNull.contains(block))
return null;
return this.providedValues.computeIfAbsent(block, $ -> {
for (Provider<T> provider : this.providers) {
T value = provider.get(block);
if (value != null) {
return value;
}
}
this.providedNull.add(block);
return null;
});
}
@Override
public void register(Block block, T value) {
this.map.put(block, value);
}
@Override
public void registerTag(TagKey<Block> tag, T value) {
this.registerProvider(new TagProvider<>(tag, value));
}
@Override
public void registerProvider(Provider<T> provider) {
this.providers.add(0, provider);
}
@SubscribeEvent
public static void onTagsReloaded(TagsUpdatedEvent event) {
if (event.getUpdateCause() == TagsUpdatedEvent.UpdateCause.SERVER_DATA_LOAD) {
for (BlockLookupImpl<?> lookup : allLookups) {
lookup.providedValues.clear();
lookup.providedNull.clear();
}
}
}
private record TagProvider<T>(TagKey<Block> tag, T value) implements Provider<T> {
@Override
@Nullable
@SuppressWarnings("deprecation")
public T get(Block block) {
return block.builtInRegistryHolder().is(this.tag) ? this.value : null;
}
}
}

View file

@ -0,0 +1,103 @@
package com.simibubi.create.impl.registry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
// methods are synchronized since registrations can happen during parallel mod loading
public class AttachedRegistryImpl<K, V> implements AttachedRegistry<K, V> {
private static final Object nullMarker = new Object();
// all of these have identity semantics
private final Map<K, V> registrations = new IdentityHashMap<>();
private final List<Provider<K, V>> providers = new ArrayList<>();
private final Map<Provider<K, V>, Set<K>> providedKeys = new IdentityHashMap<>();
private final Map<K, V> providedValues = new IdentityHashMap<>();
@Override
public synchronized void register(K object, V value) {
Objects.requireNonNull(object, "object");
Objects.requireNonNull(value, "value");
V existing = this.registrations.get(object);
if (existing != null) {
String message = String.format("Tried to register duplicate values for object %s: old=%s, new=%s", object, existing, value);
throw new IllegalArgumentException(message);
}
this.registrations.put(object, value);
}
@Override
public synchronized void registerProvider(Provider<K, V> provider) {
Objects.requireNonNull(provider);
if (this.providers.contains(provider)) {
throw new IllegalArgumentException("Tried to register provider twice: " + provider);
}
// add to start of list so it's queried first
this.providers.add(0, provider);
provider.onRegister(this);
}
@Override
public synchronized void invalidateProvider(Provider<K, V> provider) {
Objects.requireNonNull(provider);
if (!this.providers.contains(provider)) {
throw new IllegalArgumentException("Cannot invalidate non-registered provider: " + provider);
}
// discard all the values the provider has provided
Set<K> keys = providedKeys.remove(provider);
if (keys != null) {
keys.forEach(providedValues::remove);
}
// when a provider is invalidated, we need to clear all cached values that evaluated to null, so they can be re-queried
this.providedValues.values().removeIf(value -> value == nullMarker);
}
@Override
@Nullable
public synchronized V get(K object) {
if (this.registrations.containsKey(object)) {
return this.registrations.get(object);
} else if (this.providedValues.containsKey(object)) {
V provided = this.providedValues.get(object);
return provided == nullMarker ? null : provided;
}
// no value known, check providers
// descendingSet: go in reverse-registration order
for (Provider<K, V> provider : this.providers) {
V value = provider.get(object);
if (value != null) {
this.providedValues.put(object, value);
// track which provider provided the value for invalidation
this.providedKeys.computeIfAbsent(provider, $ -> identityHashSet()).add(object);
return value;
}
}
// no provider returned non-null
this.providedValues.put(object, nullMarker());
return null;
}
@SuppressWarnings("unchecked")
private static <T> T nullMarker() {
return (T) nullMarker;
}
private static <T> Set<T> identityHashSet() {
return Collections.newSetFromMap(new IdentityHashMap<>());
}
}

View file

@ -0,0 +1,41 @@
package com.simibubi.create.impl.registry;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.registry.AttachedRegistry;
import net.minecraft.core.Holder;
import net.minecraft.tags.TagKey;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TagsUpdatedEvent;
public class TagProviderImpl<K, V> implements AttachedRegistry.Provider<K, V> {
private final TagKey<K> tag;
private final Function<K, Holder<K>> holderGetter;
private final V value;
public TagProviderImpl(TagKey<K> tag, Function<K, Holder<K>> holderGetter, V value) {
this.tag = tag;
this.holderGetter = holderGetter;
this.value = value;
}
@Override
@Nullable
public V get(K object) {
Holder<K> holder = this.holderGetter.apply(object);
return holder.is(this.tag) ? this.value : null;
}
@Override
public void onRegister(AttachedRegistry<K, V> registry) {
MinecraftForge.EVENT_BUS.addListener((TagsUpdatedEvent event) -> {
if (event.shouldUpdateStaticData()) {
registry.invalidateProvider(this);
}
});
}
}

View file

@ -1,23 +0,0 @@
package com.simibubi.create.impl.schematic.nbt;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.api.schematic.nbt.SchematicSafeNBTRegistry;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.ForgeRegistries;
@ApiStatus.Internal
public class SchematicSafeNBTRegistryImpl {
private static final AttachedRegistry<BlockEntityType<?>, SchematicSafeNBTRegistry.ContextProvidingPartialSafeNBT> BLOCK_ENTITY_PARTIAL_SAFE_NBT = new AttachedRegistry<>(ForgeRegistries.BLOCK_ENTITY_TYPES);
public static void register(BlockEntityType<? extends BlockEntity> blockEntityType, SchematicSafeNBTRegistry.ContextProvidingPartialSafeNBT safeNBT) {
BLOCK_ENTITY_PARTIAL_SAFE_NBT.register(blockEntityType, safeNBT);
}
public static SchematicSafeNBTRegistry.ContextProvidingPartialSafeNBT getPartialSafeNBT(BlockEntityType<? extends BlockEntity> blockEntityType) {
return BLOCK_ENTITY_PARTIAL_SAFE_NBT.get(blockEntityType);
}
}

View file

@ -1,59 +0,0 @@
package com.simibubi.create.impl.schematic.requirement;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.api.schematic.requirement.SchematicRequirementsRegistry;
import com.simibubi.create.foundation.utility.AttachedRegistry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.ForgeRegistries;
@ApiStatus.Internal
public class SchematicRequirementsRegistryImpl {
private static final AttachedRegistry<Block, SchematicRequirementsRegistry.BlockRequirement> BLOCK_REQUIREMENTS = new AttachedRegistry<>(ForgeRegistries.BLOCKS);
private static final AttachedRegistry<BlockEntityType<?>, SchematicRequirementsRegistry.BlockEntityRequirement> BLOCK_ENTITY_REQUIREMENTS = new AttachedRegistry<>(ForgeRegistries.BLOCK_ENTITY_TYPES);
private static final AttachedRegistry<EntityType<?>, SchematicRequirementsRegistry.EntityRequirement> ENTITY_REQUIREMENTS = new AttachedRegistry<>(ForgeRegistries.ENTITY_TYPES);
public static void registerForBlock(Block block, SchematicRequirementsRegistry.BlockRequirement requirement) {
BLOCK_REQUIREMENTS.register(block, requirement);
}
public static void registerForBlock(ResourceLocation block, SchematicRequirementsRegistry.BlockRequirement requirement) {
BLOCK_REQUIREMENTS.register(block, requirement);
}
public static void registerForBlockEntity(BlockEntityType<BlockEntity> blockEntityType, SchematicRequirementsRegistry.BlockEntityRequirement requirement) {
BLOCK_ENTITY_REQUIREMENTS.register(blockEntityType, requirement);
}
public static void registerForBlockEntity(ResourceLocation blockEntityType, SchematicRequirementsRegistry.BlockEntityRequirement requirement) {
BLOCK_ENTITY_REQUIREMENTS.register(blockEntityType, requirement);
}
public static void registerForEntity(EntityType<Entity> entityType, SchematicRequirementsRegistry.EntityRequirement requirement) {
ENTITY_REQUIREMENTS.register(entityType, requirement);
}
// ---
public static void registerForEntity(ResourceLocation entityType, SchematicRequirementsRegistry.EntityRequirement requirement) {
ENTITY_REQUIREMENTS.register(entityType, requirement);
}
public static SchematicRequirementsRegistry.BlockRequirement getRequirementForBlock(Block block) {
return BLOCK_REQUIREMENTS.get(block);
}
public static SchematicRequirementsRegistry.BlockEntityRequirement getRequirementForBlockEntityType(BlockEntityType<? extends BlockEntity> blockEntityType) {
return BLOCK_ENTITY_REQUIREMENTS.get(blockEntityType);
}
public static SchematicRequirementsRegistry.EntityRequirement getRequirementForEntityType(EntityType<? extends Entity> entityType) {
return ENTITY_REQUIREMENTS.get(entityType);
}
}