mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
rework BlockSpoutingBehaviour and add new behaviors
This commit is contained in:
parent
6edf7ec68e
commit
e966455143
10 changed files with 250 additions and 106 deletions
|
@ -7,7 +7,7 @@ import org.slf4j.Logger;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.api.behaviour.spouting.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.compat.Mods;
|
||||
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||
import com.simibubi.create.compat.curios.Curios;
|
||||
|
@ -135,7 +135,6 @@ public class Create {
|
|||
AllArmInteractionPointTypes.register(modEventBus);
|
||||
AllFanProcessingTypes.register(modEventBus);
|
||||
AllItemAttributeTypes.register(modEventBus);
|
||||
BlockSpoutingBehaviour.registerDefaults();
|
||||
|
||||
// FIXME: some of these registrations are not thread-safe
|
||||
AllMovementBehaviours.registerDefaults();
|
||||
|
@ -173,6 +172,7 @@ public class Create {
|
|||
BoilerHeaters.registerDefaults();
|
||||
AllPortalTracks.registerDefaults();
|
||||
AllDisplayBehaviours.registerDefaults();
|
||||
BlockSpoutingBehaviour.registerDefaults();
|
||||
// --
|
||||
|
||||
AllAdvancements.register();
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package com.simibubi.create.api.behaviour;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.compat.tconstruct.SpoutCasting;
|
||||
import com.simibubi.create.content.fluids.spout.SpoutBlockEntity;
|
||||
import com.simibubi.create.impl.behaviour.BlockSpoutingBehaviourImpl;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
public abstract class BlockSpoutingBehaviour {
|
||||
/**
|
||||
* Register a new custom spout interaction
|
||||
*
|
||||
* @param resourceLocation The interaction id
|
||||
* @param spoutingBehaviour An instance of your behaviour class
|
||||
*/
|
||||
public static void addCustomSpoutInteraction(ResourceLocation resourceLocation, BlockSpoutingBehaviour spoutingBehaviour) {
|
||||
BlockSpoutingBehaviourImpl.addCustomSpoutInteraction(resourceLocation, spoutingBehaviour);
|
||||
}
|
||||
|
||||
public static void registerDefaults() {
|
||||
addCustomSpoutInteraction(Create.asResource("ticon_casting"), new SpoutCasting());
|
||||
}
|
||||
|
||||
/**
|
||||
* While idle, Spouts will call this every tick with simulate == true <br>
|
||||
* When fillBlock returns > 0, the Spout will start its animation cycle <br>
|
||||
* <br>
|
||||
* During this animation cycle, fillBlock is called once again with simulate == false but only on the relevant SpoutingBehaviour <br>
|
||||
* When fillBlock returns > 0 once again, the Spout will drain its content by the returned amount of units <br>
|
||||
* Perform any other side effects in this method <br>
|
||||
* This method is called server-side only (except in ponder) <br>
|
||||
*
|
||||
* @param level The current level
|
||||
* @param pos The position of the affected block
|
||||
* @param spout The spout block entity that is calling this
|
||||
* @param availableFluid A copy of the fluidStack that is available, modifying this will do nothing, return the amount to be subtracted instead
|
||||
* @param simulate Whether the spout is testing or actually performing this behaviour
|
||||
* @return The amount filled into the block, 0 to idle/cancel
|
||||
*/
|
||||
public abstract int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid, boolean simulate);
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package com.simibubi.create.api.behaviour.spouting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.api.registry.SimpleRegistry;
|
||||
import com.simibubi.create.compat.Mods;
|
||||
import com.simibubi.create.compat.tconstruct.SpoutCasting;
|
||||
import com.simibubi.create.content.fluids.spout.SpoutBlockEntity;
|
||||
|
||||
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.Blocks;
|
||||
import net.minecraft.world.level.block.FarmBlock;
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
/**
|
||||
* Interface for custom block-filling behavior for spouts.
|
||||
* <p>
|
||||
* Behaviors are queried by block first, through {@link #BY_BLOCK}. If no behavior was provided,
|
||||
* they are then queried by block entity type, through {@link #BY_BLOCK_ENTITY}.
|
||||
* @see StateChangingBehavior
|
||||
* @see CauldronSpoutingBehavior
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BlockSpoutingBehaviour {
|
||||
SimpleRegistry<Block, BlockSpoutingBehaviour> BY_BLOCK = SimpleRegistry.create();
|
||||
SimpleRegistry<BlockEntityType<?>, BlockSpoutingBehaviour> BY_BLOCK_ENTITY = SimpleRegistry.create();
|
||||
|
||||
/**
|
||||
* Get the behavior that should be used for the block at the given location.
|
||||
* Queries both the block and the block entity if needed.
|
||||
*/
|
||||
@Nullable
|
||||
static BlockSpoutingBehaviour get(Level level, BlockPos pos) {
|
||||
BlockState state = level.getBlockState(pos);
|
||||
BlockSpoutingBehaviour byBlock = BY_BLOCK.get(state.getBlock());
|
||||
if (byBlock != null)
|
||||
return byBlock;
|
||||
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be == null)
|
||||
return null;
|
||||
|
||||
return BY_BLOCK_ENTITY.get(be.getType());
|
||||
}
|
||||
|
||||
static void registerDefaults() {
|
||||
Predicate<Fluid> isWater = fluid -> fluid.isSame(Fluids.WATER);
|
||||
BlockSpoutingBehaviour toMud = StateChangingBehavior.setTo(250, isWater, Blocks.MUD);
|
||||
|
||||
for (Block dirt : List.of(Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.ROOTED_DIRT)) {
|
||||
BY_BLOCK.register(dirt, toMud);
|
||||
}
|
||||
|
||||
BY_BLOCK.register(Blocks.FARMLAND, StateChangingBehavior.incrementingState(100, isWater, FarmBlock.MOISTURE));
|
||||
BY_BLOCK.register(Blocks.WATER_CAULDRON, StateChangingBehavior.incrementingState(250, isWater, LayeredCauldronBlock.LEVEL));
|
||||
BY_BLOCK.register(Blocks.CAULDRON, CauldronSpoutingBehavior.INSTANCE);
|
||||
|
||||
if (!Mods.TCONSTRUCT.isLoaded())
|
||||
return;
|
||||
|
||||
for (String name : List.of("table", "basin")) {
|
||||
ResourceLocation id = Mods.TCONSTRUCT.rl(name);
|
||||
if (ForgeRegistries.BLOCK_ENTITY_TYPES.containsKey(id)) {
|
||||
BlockEntityType<?> table = ForgeRegistries.BLOCK_ENTITY_TYPES.getValue(id);
|
||||
BY_BLOCK_ENTITY.register(table, SpoutCasting.INSTANCE);
|
||||
} else {
|
||||
Create.LOGGER.warn("Block entity {} wasn't found. Outdated compat?", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* While idle, spouts will query the behavior provided by the block below it.
|
||||
* If one is present, this method will be called every tick with simulate == true.
|
||||
* <p>
|
||||
* When a value greater than 0 is returned, the spout will begin processing. It will call this method again
|
||||
* with simulate == false, which is when any filling behavior should actually occur.
|
||||
* <p>
|
||||
* This method is only called on the server side, except for in Ponder.
|
||||
* @param level The current level
|
||||
* @param pos The position of the affected block
|
||||
* @param spout The spout block entity that is calling this
|
||||
* @param availableFluid A copy of the fluidStack that is available, modifying this will do nothing, return the amount to be subtracted instead
|
||||
* @param simulate Whether the spout is testing or actually performing this behaviour
|
||||
* @return The amount filled into the block, 0 to idle/cancel
|
||||
*/
|
||||
int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid, boolean simulate);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.simibubi.create.api.behaviour.spouting;
|
||||
|
||||
import com.simibubi.create.api.registry.SimpleRegistry;
|
||||
import com.simibubi.create.content.fluids.spout.SpoutBlockEntity;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
/**
|
||||
* {@link BlockSpoutingBehaviour} for empty cauldrons. Mods can register their fluids
|
||||
* to {@link #CAULDRON_INFO} to allow spouts to fill empty cauldrons with their fluids.
|
||||
*/
|
||||
public enum CauldronSpoutingBehavior implements BlockSpoutingBehaviour {
|
||||
INSTANCE;
|
||||
|
||||
public static final SimpleRegistry<Fluid, CauldronInfo> CAULDRON_INFO = Util.make(() -> {
|
||||
SimpleRegistry<Fluid, CauldronInfo> registry = SimpleRegistry.create();
|
||||
registry.register(Fluids.WATER, new CauldronInfo(250, Blocks.WATER_CAULDRON));
|
||||
registry.register(Fluids.LAVA, new CauldronInfo(1000, Blocks.LAVA_CAULDRON));
|
||||
return registry;
|
||||
});
|
||||
|
||||
@Override
|
||||
public int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid, boolean simulate) {
|
||||
CauldronInfo info = CAULDRON_INFO.get(availableFluid.getFluid());
|
||||
if (info == null)
|
||||
return 0;
|
||||
|
||||
if (availableFluid.getAmount() < info.amount)
|
||||
return 0;
|
||||
|
||||
if (!simulate) {
|
||||
level.setBlockAndUpdate(pos, info.cauldron);
|
||||
}
|
||||
|
||||
return info.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param amount the amount of fluid that must be inserted into an empty cauldron
|
||||
* @param cauldron the BlockState to set after filling an empty cauldron with the given amount of fluid
|
||||
*/
|
||||
public record CauldronInfo(int amount, BlockState cauldron) {
|
||||
public CauldronInfo(int amount, Block block) {
|
||||
this(amount, block.defaultBlockState());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.simibubi.create.api.behaviour.spouting;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import com.simibubi.create.content.fluids.spout.SpoutBlockEntity;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
/**
|
||||
* An implementation of {@link BlockSpoutingBehaviour} that allows for easily modifying a BlockState through spouting.
|
||||
* @param amount the amount of fluid consumed when filling
|
||||
* @param fluidTest a predicate for fluids that can be used to fill the target block
|
||||
* @param canFill a predicate that must match the target BlockState to fill it
|
||||
* @param fillFunction a function that converts the current state into the filled one
|
||||
*/
|
||||
public record StateChangingBehavior(int amount, Predicate<Fluid> fluidTest, Predicate<BlockState> canFill,
|
||||
UnaryOperator<BlockState> fillFunction) implements BlockSpoutingBehaviour {
|
||||
@Override
|
||||
public int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid, boolean simulate) {
|
||||
if (availableFluid.getAmount() < this.amount || !this.fluidTest.test(availableFluid.getFluid()))
|
||||
return 0;
|
||||
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (!this.canFill.test(state))
|
||||
return 0;
|
||||
|
||||
if (!simulate) {
|
||||
BlockState newState = this.fillFunction.apply(state);
|
||||
level.setBlockAndUpdate(pos, newState);
|
||||
}
|
||||
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link #setTo(int, Predicate, BlockState)} that uses the Block's default state.
|
||||
*/
|
||||
public static BlockSpoutingBehaviour setTo(int amount, Predicate<Fluid> fluidTest, Block block) {
|
||||
return setTo(amount, fluidTest, block.defaultBlockState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BlockSpoutingBehaviour} that will simply convert the target block to the given state.
|
||||
* @param newState the state that will be set after filling
|
||||
*/
|
||||
public static BlockSpoutingBehaviour setTo(int amount, Predicate<Fluid> fluidTest, BlockState newState) {
|
||||
return new StateChangingBehavior(amount, fluidTest, state -> true, state -> newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BlockSpoutingBehaviour} that will increment the given {@link IntegerProperty} until it reaches
|
||||
* its maximum value, consuming {@code amount} each time fluid is filled.
|
||||
* @param property the property that will be incremented by one on each fill
|
||||
*/
|
||||
public static BlockSpoutingBehaviour incrementingState(int amount, Predicate<Fluid> fluidTest, IntegerProperty property) {
|
||||
int max = property.getPossibleValues().stream().max(Integer::compareTo).orElseThrow();
|
||||
|
||||
Predicate<BlockState> canFill = state -> state.getValue(property) < max;
|
||||
UnaryOperator<BlockState> fillFunction = state -> state.setValue(property, state.getValue(property) + 1);
|
||||
|
||||
return new StateChangingBehavior(amount, fluidTest, canFill, fillFunction);
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
|||
* <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}.
|
||||
* This is provided as an alternative to {@link PartialSafeNBT}.
|
||||
*/
|
||||
public class SafeNbtWriterRegistry {
|
||||
public static final SimpleRegistry<BlockEntityType<?>, SafeNbtWriter> REGISTRY = SimpleRegistry.create();
|
||||
|
|
|
@ -18,9 +18,9 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
* <p>
|
||||
* This is provided as an alternative to the following interfaces:
|
||||
* <ul>
|
||||
* <li>{@link ISpecialBlockItemRequirement}</li>
|
||||
* <li>{@link ISpecialBlockEntityItemRequirement}</li>
|
||||
* <li>{@link ISpecialEntityItemRequirement}</li>
|
||||
* <li>{@link SpecialBlockItemRequirement}</li>
|
||||
* <li>{@link SpecialBlockEntityItemRequirement}</li>
|
||||
* <li>{@link SpecialEntityItemRequirement}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SchematicRequirementRegistries {
|
||||
|
|
|
@ -1,31 +1,25 @@
|
|||
package com.simibubi.create.compat.tconstruct;
|
||||
|
||||
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.compat.Mods;
|
||||
import com.simibubi.create.api.behaviour.spouting.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.content.fluids.spout.SpoutBlockEntity;
|
||||
import com.simibubi.create.foundation.fluid.FluidHelper;
|
||||
import com.simibubi.create.infrastructure.config.AllConfigs;
|
||||
import net.createmod.catnip.platform.CatnipServices;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||
|
||||
public class SpoutCasting extends BlockSpoutingBehaviour {
|
||||
|
||||
private static final boolean TICON_PRESENT = Mods.TCONSTRUCT.isLoaded();
|
||||
|
||||
ResourceLocation TABLE = new ResourceLocation("tconstruct", "table");
|
||||
ResourceLocation BASIN = new ResourceLocation("tconstruct", "basin");
|
||||
public enum SpoutCasting implements BlockSpoutingBehaviour {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid,
|
||||
boolean simulate) {
|
||||
public int fillBlock(Level level, BlockPos pos, SpoutBlockEntity spout, FluidStack availableFluid, boolean simulate) {
|
||||
if (!enabled())
|
||||
return 0;
|
||||
|
||||
|
@ -40,9 +34,6 @@ public class SpoutCasting extends BlockSpoutingBehaviour {
|
|||
if (handler.getTanks() != 1)
|
||||
return 0;
|
||||
|
||||
ResourceLocation registryName = CatnipServices.REGISTRIES.getKeyOrThrow(blockEntity.getType());
|
||||
if (!registryName.equals(TABLE) && !registryName.equals(BASIN))
|
||||
return 0;
|
||||
if (!handler.isFluidValid(0, availableFluid))
|
||||
return 0;
|
||||
|
||||
|
@ -61,9 +52,6 @@ public class SpoutCasting extends BlockSpoutingBehaviour {
|
|||
}
|
||||
|
||||
private boolean enabled() {
|
||||
if (!TICON_PRESENT)
|
||||
return false;
|
||||
return AllConfigs.server().recipes.allowCastingBySpout.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
|||
|
||||
import com.simibubi.create.AllItems;
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.api.behaviour.spouting.BlockSpoutingBehaviour;
|
||||
import com.simibubi.create.api.equipment.goggles.IHaveGoggleInformation;
|
||||
import com.simibubi.create.content.fluids.FluidFX;
|
||||
import com.simibubi.create.content.kinetics.belt.behaviour.BeltProcessingBehaviour;
|
||||
|
@ -22,7 +22,6 @@ import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
|
|||
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
|
||||
import com.simibubi.create.foundation.blockEntity.behaviour.fluid.SmartFluidTankBehaviour;
|
||||
import com.simibubi.create.foundation.fluid.FluidHelper;
|
||||
import com.simibubi.create.impl.behaviour.BlockSpoutingBehaviourImpl;
|
||||
|
||||
import net.createmod.catnip.math.VecHelper;
|
||||
import net.createmod.catnip.nbt.NBTHelper;
|
||||
|
@ -199,15 +198,13 @@ public class SpoutBlockEntity extends SmartBlockEntity implements IHaveGoggleInf
|
|||
|
||||
FluidStack currentFluidInTank = getCurrentFluidInTank();
|
||||
if (processingTicks == -1 && (isVirtual() || !level.isClientSide()) && !currentFluidInTank.isEmpty()) {
|
||||
BlockSpoutingBehaviourImpl.forEach(behaviour -> {
|
||||
if (customProcess != null)
|
||||
return;
|
||||
if (behaviour.fillBlock(level, worldPosition.below(2), this, currentFluidInTank.copy(), true) > 0) {
|
||||
processingTicks = FILLING_TIME;
|
||||
customProcess = behaviour;
|
||||
notifyUpdate();
|
||||
}
|
||||
});
|
||||
BlockPos filling = this.worldPosition.below(2);
|
||||
BlockSpoutingBehaviour behavior = BlockSpoutingBehaviour.get(this.level, filling);
|
||||
if (behavior != null && behavior.fillBlock(this.level, filling, this, currentFluidInTank.copy(), true) > 0) {
|
||||
processingTicks = FILLING_TIME;
|
||||
customProcess = behavior;
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
if (processingTicks >= 0) {
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package com.simibubi.create.impl.behaviour;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
// TODO - Make this use AttachedRegistry later
|
||||
@ApiStatus.Internal
|
||||
public class BlockSpoutingBehaviourImpl {
|
||||
private static final Map<ResourceLocation, BlockSpoutingBehaviour> BLOCK_SPOUTING_BEHAVIOURS = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addCustomSpoutInteraction(ResourceLocation resourceLocation, BlockSpoutingBehaviour spoutingBehaviour) {
|
||||
BLOCK_SPOUTING_BEHAVIOURS.put(resourceLocation, spoutingBehaviour);
|
||||
}
|
||||
|
||||
public static void forEach(Consumer<? super BlockSpoutingBehaviour> accept) {
|
||||
BLOCK_SPOUTING_BEHAVIOURS.values().forEach(accept);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue