double it and give it to the next person

- fluid mounted storage
- handling for old data format
- a few more docs
This commit is contained in:
TropheusJ 2025-01-24 23:07:07 -05:00
parent 5c0b012357
commit 595263e0df
25 changed files with 597 additions and 71 deletions

View file

@ -3,6 +3,7 @@ package com.simibubi.create;
import static com.simibubi.create.AllInteractionBehaviours.interactionBehaviour; import static com.simibubi.create.AllInteractionBehaviours.interactionBehaviour;
import static com.simibubi.create.AllMovementBehaviours.movementBehaviour; import static com.simibubi.create.AllMovementBehaviours.movementBehaviour;
import static com.simibubi.create.Create.REGISTRATE; 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.MountedStorageTypeRegistry.mountedItemStorage;
import static com.simibubi.create.content.redstone.displayLink.AllDisplayBehaviours.assignDataBehaviour; 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.axisBlock;
@ -989,6 +990,7 @@ public class AllBlocks {
.blockstate(new FluidTankGenerator()::generate) .blockstate(new FluidTankGenerator()::generate)
.onRegister(CreateRegistrate.blockModel(() -> FluidTankModel::standard)) .onRegister(CreateRegistrate.blockModel(() -> FluidTankModel::standard))
.onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status")) .onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status"))
.transform(mountedFluidStorage(AllMountedStorageTypes.FLUID_TANK))
.addLayer(() -> RenderType::cutoutMipped) .addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new) .item(FluidTankItem::new)
.model(AssetLookup.customBlockItemModel("_", "block_single_window")) .model(AssetLookup.customBlockItemModel("_", "block_single_window"))
@ -1004,6 +1006,7 @@ public class AllBlocks {
.tag(AllBlockTags.SAFE_NBT.tag) .tag(AllBlockTags.SAFE_NBT.tag)
.blockstate(new FluidTankGenerator("creative_")::generate) .blockstate(new FluidTankGenerator("creative_")::generate)
.onRegister(CreateRegistrate.blockModel(() -> FluidTankModel::creative)) .onRegister(CreateRegistrate.blockModel(() -> FluidTankModel::creative))
.transform(mountedFluidStorage(AllMountedStorageTypes.CREATIVE_FLUID_TANK))
.addLayer(() -> RenderType::cutoutMipped) .addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new) .item(FluidTankItem::new)
.properties(p -> p.rarity(Rarity.EPIC)) .properties(p -> p.rarity(Rarity.EPIC))

View file

@ -4,29 +4,31 @@ import static com.simibubi.create.Create.REGISTRATE;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.contraption.storage.item.chest.ChestMountedStorageType; import com.simibubi.create.api.contraption.storage.item.chest.ChestMountedStorageType;
import com.simibubi.create.api.contraption.storage.item.simple.SimpleMountedStorageType;
import com.simibubi.create.content.contraptions.behaviour.dispenser.storage.DispenserMountedStorageType; import com.simibubi.create.content.contraptions.behaviour.dispenser.storage.DispenserMountedStorageType;
import com.simibubi.create.content.equipment.toolbox.ToolboxMountedStorageType; import com.simibubi.create.content.equipment.toolbox.ToolboxMountedStorageType;
import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorageType;
import com.simibubi.create.content.fluids.tank.storage.creative.CreativeFluidTankMountedStorageType;
import com.simibubi.create.content.logistics.crate.CreativeCrateMountedStorageType; import com.simibubi.create.content.logistics.crate.CreativeCrateMountedStorageType;
import com.simibubi.create.api.contraption.storage.item.simple.SimpleMountedStorageType;
import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorageType; import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorageType;
import com.simibubi.create.impl.contraption.storage.FallbackMountedStorageType; import com.simibubi.create.impl.contraption.storage.FallbackMountedStorageType;
import com.tterrag.registrate.util.entry.RegistryEntry; import com.tterrag.registrate.util.entry.RegistryEntry;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
public class AllMountedStorageTypes { public class AllMountedStorageTypes {
// fallback is special, provider is registered on lookup creation so it's always last // fallback is special, provider is registered on lookup creation so it's always last
public static final RegistryEntry<FallbackMountedStorageType> FALLBACK = simple("fallback", FallbackMountedStorageType::new); public static final RegistryEntry<FallbackMountedStorageType> FALLBACK = simpleItem("fallback", FallbackMountedStorageType::new);
// registrations for these are handled by the blocks, not the types // registrations for these are handled by the blocks, not the types
public static final RegistryEntry<CreativeCrateMountedStorageType> CREATIVE_CRATE = simple("creative_crate", CreativeCrateMountedStorageType::new); public static final RegistryEntry<CreativeCrateMountedStorageType> CREATIVE_CRATE = simpleItem("creative_crate", CreativeCrateMountedStorageType::new);
public static final RegistryEntry<ItemVaultMountedStorageType> VAULT = simple("vault", ItemVaultMountedStorageType::new); public static final RegistryEntry<ItemVaultMountedStorageType> VAULT = simpleItem("vault", ItemVaultMountedStorageType::new);
public static final RegistryEntry<ToolboxMountedStorageType> TOOLBOX = simple("toolbox", ToolboxMountedStorageType::new); public static final RegistryEntry<ToolboxMountedStorageType> TOOLBOX = simpleItem("toolbox", ToolboxMountedStorageType::new);
public static final RegistryEntry<FluidTankMountedStorageType> FLUID_TANK = simpleFluid("fluid_tank", FluidTankMountedStorageType::new);
public static final RegistryEntry<CreativeFluidTankMountedStorageType> CREATIVE_FLUID_TANK = simpleFluid("creative_fluid_tank", CreativeFluidTankMountedStorageType::new);
// these are for external blocks, register associations here // these are for external blocks, register associations here
public static final RegistryEntry<SimpleMountedStorageType.Impl> SIMPLE = REGISTRATE.mountedItemStorage("simple", SimpleMountedStorageType.Impl::new) public static final RegistryEntry<SimpleMountedStorageType.Impl> SIMPLE = REGISTRATE.mountedItemStorage("simple", SimpleMountedStorageType.Impl::new)
@ -40,10 +42,14 @@ public class AllMountedStorageTypes {
.registerTo(Blocks.DROPPER) .registerTo(Blocks.DROPPER)
.register(); .register();
private static <T extends MountedItemStorageType<?>> RegistryEntry<T> simple(String name, Supplier<T> supplier) { private static <T extends MountedItemStorageType<?>> RegistryEntry<T> simpleItem(String name, Supplier<T> supplier) {
return REGISTRATE.mountedItemStorage(name, supplier).register(); return REGISTRATE.mountedItemStorage(name, supplier).register();
} }
private static <T extends MountedFluidStorageType<?>> RegistryEntry<T> simpleFluid(String name, Supplier<T> supplier) {
return REGISTRATE.mountedFluidStorage(name, supplier).register();
}
public static void register() { public static void register() {
} }
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.api.contraption.storage; package com.simibubi.create.api.contraption.storage;
import com.simibubi.create.Create; 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.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.lookup.BlockLookup; import com.simibubi.create.api.lookup.BlockLookup;
import com.simibubi.create.impl.contraption.storage.MountedStorageTypeRegistryImpl; import com.simibubi.create.impl.contraption.storage.MountedStorageTypeRegistryImpl;
@ -8,22 +9,29 @@ import com.tterrag.registrate.builders.BlockBuilder;
import com.tterrag.registrate.util.entry.RegistryEntry; import com.tterrag.registrate.util.entry.RegistryEntry;
import com.tterrag.registrate.util.nullness.NonNullUnaryOperator; import com.tterrag.registrate.util.nullness.NonNullUnaryOperator;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.IForgeRegistry;
public class MountedStorageTypeRegistry { public class MountedStorageTypeRegistry {
public static final ResourceKey<Registry<MountedItemStorageType<?>>> ITEMS = ResourceKey.createRegistryKey( public static final ResourceKey<Registry<MountedItemStorageType<?>>> ITEMS = ResourceKey.createRegistryKey(
Create.asResource("mounted_item_storage_type") Create.asResource("mounted_item_storage_type")
); );
public static final ResourceKey<Registry<MountedFluidStorageType<?>>> FLUIDS = ResourceKey.createRegistryKey(
Create.asResource("mounted_fluid_storage_type")
);
/** /**
* Lookup used for finding the item storage type associated with a block. * Lookup used for finding the item storage type associated with a block.
* @see BlockLookup * @see BlockLookup
*/ */
public static final BlockLookup<MountedItemStorageType<?>> ITEM_LOOKUP = MountedStorageTypeRegistryImpl.ITEM_LOOKUP; 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;
/** /**
* @throws NullPointerException if called before registry registration * @throws NullPointerException if called before registry registration
@ -32,6 +40,13 @@ public class MountedStorageTypeRegistry {
return MountedStorageTypeRegistryImpl.getItemsRegistry(); return MountedStorageTypeRegistryImpl.getItemsRegistry();
} }
/**
* @throws NullPointerException if called before registry registration
*/
public static IForgeRegistry<MountedFluidStorageType<?>> getFluidsRegistry() {
return MountedStorageTypeRegistryImpl.getFluidsRegistry();
}
/** /**
* Utility for use with Registrate builders. Creates a builder transformer * Utility for use with Registrate builders. Creates a builder transformer
* that will register the given MountedItemStorageType to a block when ready. * that will register the given MountedItemStorageType to a block when ready.
@ -39,4 +54,12 @@ public class MountedStorageTypeRegistry {
public static <B extends Block, P> NonNullUnaryOperator<BlockBuilder<B, P>> mountedItemStorage(RegistryEntry<? extends MountedItemStorageType<?>> type) { 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_LOOKUP.register(block, type.get()));
} }
/**
* Utility for use with Registrate builders. Creates a builder transformer
* 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()));
}
} }

View file

@ -0,0 +1,31 @@
package com.simibubi.create.api.contraption.storage.fluid;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fluids.capability.IFluidHandler;
public abstract class MountedFluidStorage implements IFluidHandler {
public static final Codec<MountedFluidStorage> CODEC = MountedFluidStorageType.CODEC.dispatch(
storage -> storage.type, type -> type.codec
);
public final MountedFluidStorageType<? extends MountedFluidStorage> type;
protected MountedFluidStorage(MountedFluidStorageType<?> type) {
this.type = Objects.requireNonNull(type);
}
/**
* Un-mount this storage back into the world. The expected storage type of the target
* block has already been checked to make sure it matches this storage's type.
*/
public abstract void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be);
}

View file

@ -0,0 +1,27 @@
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 net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
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()
);
public final Codec<? extends T> codec;
protected MountedFluidStorageType(Codec<? extends T> codec) {
this.codec = codec;
}
@Nullable
public abstract T mount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be);
}

View file

@ -0,0 +1,20 @@
package com.simibubi.create.api.contraption.storage.fluid;
import com.google.common.collect.ImmutableMap;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import net.minecraft.core.BlockPos;
import net.minecraftforge.fluids.capability.IFluidHandler;
/**
* Wrapper around many MountedFluidStorages, providing access to all of them as one storage.
* They can still be accessed individually through the map.
*/
public class MountedFluidStorageWrapper extends CombinedTankWrapper {
public final ImmutableMap<BlockPos, MountedFluidStorage> storages;
public MountedFluidStorageWrapper(ImmutableMap<BlockPos, MountedFluidStorage> storages) {
super(storages.values().toArray(IFluidHandler[]::new));
this.storages = storages;
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.api.contraption.storage.fluid;
import org.jetbrains.annotations.NotNull;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
/**
* Partial implementation of a MountedFluidStorage that wraps a fluid handler.
*/
public abstract class WrapperMountedFluidStorage<T extends IFluidHandler> extends MountedFluidStorage {
protected final T wrapped;
protected WrapperMountedFluidStorage(MountedFluidStorageType<?> type, T wrapped) {
super(type);
this.wrapped = wrapped;
}
@Override
public int getTanks() {
return this.wrapped.getTanks();
}
@Override
@NotNull
public FluidStack getFluidInTank(int tank) {
return this.wrapped.getFluidInTank(tank);
}
@Override
public int getTankCapacity(int tank) {
return this.wrapped.getTankCapacity(tank);
}
@Override
public boolean isFluidValid(int tank, @NotNull FluidStack stack) {
return this.wrapped.isFluidValid(tank, stack);
}
@Override
public int fill(FluidStack resource, FluidAction action) {
return this.wrapped.fill(resource, action);
}
@Override
@NotNull
public FluidStack drain(FluidStack resource, FluidAction action) {
return this.wrapped.drain(resource, action);
}
@Override
@NotNull
public FluidStack drain(int maxDrain, FluidAction action) {
return this.wrapped.drain(maxDrain, action);
}
}

View file

@ -0,0 +1,36 @@
package com.simibubi.create.api.contraption.storage.fluid.registrate;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.tterrag.registrate.AbstractRegistrate;
import com.tterrag.registrate.builders.AbstractBuilder;
import com.tterrag.registrate.builders.BuilderCallback;
import com.tterrag.registrate.util.nullness.NonnullType;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
public class MountedFluidStorageTypeBuilder<T extends MountedFluidStorageType<?>, P> extends AbstractBuilder<MountedFluidStorageType<?>, T, P, MountedFluidStorageTypeBuilder<T, P>> {
private final T type;
public MountedFluidStorageTypeBuilder(AbstractRegistrate<?> owner, P parent, String name, BuilderCallback callback, T type) {
super(owner, parent, name, callback, MountedStorageTypeRegistry.FLUIDS);
this.type = type;
}
public MountedFluidStorageTypeBuilder<T, P> registerTo(Block block) {
MountedStorageTypeRegistry.FLUID_LOOKUP.register(block, this.type);
return this;
}
public MountedFluidStorageTypeBuilder<T, P> registerTo(TagKey<Block> tag) {
MountedStorageTypeRegistry.FLUID_LOOKUP.registerTag(tag, this.type);
return this;
}
@Override
@NonnullType
protected T createEntry() {
return this.type;
}
}

View file

@ -2,11 +2,14 @@ package com.simibubi.create.api.contraption.storage.item;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import net.minecraft.core.BlockPos;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.CombinedInvWrapper;
import net.minecraft.core.BlockPos; /**
* Wrapper around many MountedItemStorages, providing access to all of them as one storage.
* They can still be accessed individually through the map.
*/
public class MountedItemStorageWrapper extends CombinedInvWrapper { public class MountedItemStorageWrapper extends CombinedInvWrapper {
public final ImmutableMap<BlockPos, MountedItemStorage> storages; public final ImmutableMap<BlockPos, MountedItemStorage> storages;

View file

@ -7,6 +7,9 @@ import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemStackHandler; import net.minecraftforge.items.ItemStackHandler;
/**
* Partial implementation of a MountedItemStorage that wraps an item handler.
*/
public abstract class WrapperMountedItemStorage<T extends IItemHandlerModifiable> extends MountedItemStorage { public abstract class WrapperMountedItemStorage<T extends IItemHandlerModifiable> extends MountedItemStorage {
protected final T wrapped; protected final T wrapped;

View file

@ -24,7 +24,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.CombinedInvWrapper;
@ -33,7 +32,6 @@ import net.minecraftforge.items.wrapper.InvWrapper;
/** /**
* Mounted storage that handles opening a combined GUI for double chests. * Mounted storage that handles opening a combined GUI for double chests.
*/ */
@Mod.EventBusSubscriber
public class ChestMountedStorage extends SimpleMountedStorage { public class ChestMountedStorage extends SimpleMountedStorage {
public static final Codec<ChestMountedStorage> CODEC = SimpleMountedStorage.codec(ChestMountedStorage::new); public static final Codec<ChestMountedStorage> CODEC = SimpleMountedStorage.codec(ChestMountedStorage::new);

View file

@ -1,10 +1,10 @@
package com.simibubi.create.api.lookup; package com.simibubi.create.api.lookup;
import com.simibubi.create.impl.lookup.BlockLookupImpl;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.simibubi.create.impl.lookup.BlockLookupImpl;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -14,7 +14,10 @@ import net.minecraft.world.level.block.state.BlockState;
* or found lazily through providers. Providers are only queried once per block. * 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 * If they return a value, that value is cached. If they don't, that block is recorded
* as not having a corresponding value. * as not having a corresponding value.
* <br> * <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. * All providers are expected to be registered synchronously during game init.
* Adding new ones late is not supported. * Adding new ones late is not supported.
*/ */

View file

@ -9,11 +9,20 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.simibubi.create.Create;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry; import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageWrapper;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage; import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageWrapper; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageWrapper;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper; import com.simibubi.create.content.equipment.toolbox.ToolboxMountedStorage;
import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorage;
import com.simibubi.create.content.fluids.tank.storage.creative.CreativeFluidTankMountedStorage;
import com.simibubi.create.content.logistics.crate.CreativeCrateMountedStorage;
import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorage;
import com.simibubi.create.impl.contraption.storage.FallbackMountedStorage;
import net.createmod.catnip.utility.NBTHelper; import net.createmod.catnip.utility.NBTHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -24,23 +33,27 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.CombinedInvWrapper; import net.minecraftforge.items.wrapper.CombinedInvWrapper;
public class MountedStorageManager { public class MountedStorageManager {
// builders used during assembly, null afterward
private ImmutableMap.Builder<BlockPos, MountedItemStorage> itemsBuilder; private ImmutableMap.Builder<BlockPos, MountedItemStorage> itemsBuilder;
private ImmutableMap.Builder<BlockPos, MountedFluidStorage> fluidsBuilder;
// built data structures after assembly, null before
protected ImmutableMap<BlockPos, MountedItemStorage> allItemStorages; protected ImmutableMap<BlockPos, MountedItemStorage> allItemStorages;
// different from allItemStorages, does not contain internal ones
protected MountedItemStorageWrapper items; protected MountedItemStorageWrapper items;
@Nullable @Nullable
protected MountedItemStorageWrapper fuelItems; protected MountedItemStorageWrapper fuelItems;
protected MountedFluidStorageWrapper fluids;
private List<IItemHandlerModifiable> externalHandlers; private List<IItemHandlerModifiable> externalHandlers;
protected CombinedInvWrapper allItems; protected CombinedInvWrapper allItems;
@ -67,6 +80,10 @@ public class MountedStorageManager {
this.allItemStorages, storage -> !storage.isInternal() && storage.providesFuel() this.allItemStorages, storage -> !storage.isInternal() && storage.providesFuel()
); );
this.fuelItems = fuelMap.isEmpty() ? null : new MountedItemStorageWrapper(fuelMap); this.fuelItems = fuelMap.isEmpty() ? null : new MountedItemStorageWrapper(fuelMap);
ImmutableMap<BlockPos, MountedFluidStorage> fluids = this.fluidsBuilder.build();
this.fluids = new MountedFluidStorageWrapper(fluids);
this.fluidsBuilder = null;
} }
private boolean isInitialized() { private boolean isInitialized() {
@ -83,30 +100,48 @@ public class MountedStorageManager {
this.allItemStorages = null; this.allItemStorages = null;
this.items = null; this.items = null;
this.fuelItems = null; this.fuelItems = null;
this.fluids = null;
this.externalHandlers = new ArrayList<>(); this.externalHandlers = new ArrayList<>();
this.allItems = null; this.allItems = null;
this.itemsBuilder = ImmutableMap.builder(); this.itemsBuilder = ImmutableMap.builder();
this.fluidsBuilder = ImmutableMap.builder();
} }
public void addBlock(Level level, BlockState state, BlockPos globalPos, BlockPos localPos, @Nullable BlockEntity be) { public void addBlock(Level level, BlockState state, BlockPos globalPos, BlockPos localPos, @Nullable BlockEntity be) {
MountedItemStorageType<?> type = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state); MountedItemStorageType<?> itemType = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state);
if (type == null) if (itemType != null) {
return; MountedItemStorage storage = itemType.mount(level, state, globalPos, be);
if (storage != null) {
this.itemsBuilder.put(localPos, storage);
}
}
MountedItemStorage storage = type.mount(level, state, globalPos, be); MountedFluidStorageType<?> fluidType = MountedStorageTypeRegistry.FLUID_LOOKUP.find(state);
if (storage != null) { if (fluidType != null) {
this.itemsBuilder.put(localPos, storage); MountedFluidStorage storage = fluidType.mount(level, state, globalPos, be);
if (storage != null) {
this.fluidsBuilder.put(localPos, storage);
}
} }
} }
public void unmount(Level level, StructureBlockInfo info, BlockPos globalPos, @Nullable BlockEntity be) { public void unmount(Level level, StructureBlockInfo info, BlockPos globalPos, @Nullable BlockEntity be) {
BlockPos localPos = info.pos(); BlockPos localPos = info.pos();
MountedItemStorage storage = this.getAllItemStorages().get(localPos); BlockState state = info.state();
if (storage != null) {
BlockState state = info.state(); MountedItemStorage itemStorage = this.getAllItemStorages().get(localPos);
if (itemStorage != null) {
MountedItemStorageType<?> expectedType = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state); MountedItemStorageType<?> expectedType = MountedStorageTypeRegistry.ITEM_LOOKUP.find(state);
if (storage.type == expectedType) { if (itemStorage.type == expectedType) {
storage.unmount(level, state, globalPos, be); itemStorage.unmount(level, state, globalPos, be);
}
}
MountedFluidStorage fluidStorage = this.getFluids().storages.get(localPos);
if (fluidStorage != null) {
MountedFluidStorageType<?> expectedType = MountedStorageTypeRegistry.FLUID_LOOKUP.find(state);
if (fluidStorage.type == expectedType) {
fluidStorage.unmount(level, state, globalPos, be);
} }
} }
} }
@ -114,35 +149,62 @@ public class MountedStorageManager {
public void read(CompoundTag nbt) { public void read(CompoundTag nbt) {
this.reset(); this.reset();
NBTHelper.iterateCompoundList(nbt.getList("items", Tag.TAG_COMPOUND), tag -> { try {
BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("pos")); NBTHelper.iterateCompoundList(nbt.getList("items", Tag.TAG_COMPOUND), tag -> {
CompoundTag data = tag.getCompound("storage"); BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("pos"));
MountedItemStorage.CODEC.decode(NbtOps.INSTANCE, data) CompoundTag data = tag.getCompound("storage");
.result() MountedItemStorage.CODEC.decode(NbtOps.INSTANCE, data)
.map(Pair::getFirst) .result()
// .or(() -> Optional.ofNullable(parseLegacy(data))) .map(Pair::getFirst)
.ifPresent(storage -> this.itemsBuilder.put(pos, storage)); .ifPresent(storage -> this.itemsBuilder.put(pos, storage));
}); });
// NBTHelper.iterateCompoundList(nbt.getList("FluidStorage", Tag.TAG_COMPOUND), c -> fluidStorage NBTHelper.iterateCompoundList(nbt.getList("fluids", Tag.TAG_COMPOUND), tag -> {
// .put(NbtUtils.readBlockPos(c.getCompound("Pos")), MountedFluidStorage.deserialize(c.getCompound("Data")))); BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("pos"));
CompoundTag data = tag.getCompound("storage");
MountedFluidStorage.CODEC.decode(NbtOps.INSTANCE, data)
.result()
.map(Pair::getFirst)
.ifPresent(storage -> this.fluidsBuilder.put(pos, storage));
});
this.readLegacy(nbt);
} catch (Throwable t) {
Create.LOGGER.error("Error deserializing mounted storage", t);
// an exception will leave the manager in an invalid state, initialize must be called
}
this.initialize(); this.initialize();
} }
public void write(CompoundTag nbt, boolean clientPacket) { public void write(CompoundTag nbt, boolean clientPacket) {
ListTag items = new ListTag(); if (!this.getAllItemStorages().isEmpty()) {
nbt.put("items", items); ListTag items = new ListTag();
this.getAllItemStorages().forEach(
(pos, storage) -> MountedItemStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage)
.result().ifPresent(encoded -> {
CompoundTag tag = new CompoundTag();
tag.put("pos", NbtUtils.writeBlockPos(pos));
tag.put("storage", encoded);
items.add(tag);
})
);
nbt.put("items", items);
}
this.getAllItemStorages().forEach( if (!this.getFluids().storages.isEmpty()) {
(pos, storage) -> MountedItemStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage) ListTag fluids = new ListTag();
.result().ifPresent(encoded -> { this.getFluids().storages.forEach(
CompoundTag tag = new CompoundTag(); (pos, storage) -> MountedFluidStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage)
tag.put("pos", NbtUtils.writeBlockPos(pos)); .result().ifPresent(encoded -> {
tag.put("storage", encoded); CompoundTag tag = new CompoundTag();
items.add(tag); tag.put("pos", NbtUtils.writeBlockPos(pos));
}) tag.put("storage", encoded);
); fluids.add(tag);
})
);
nbt.put("fluids", fluids);
}
} }
public void attachExternal(IItemHandlerModifiable externalStorage) { public void attachExternal(IItemHandlerModifiable externalStorage) {
@ -198,8 +260,9 @@ public class MountedStorageManager {
return this.allItems; return this.allItems;
} }
public IFluidHandler getFluids() { public MountedFluidStorageWrapper getFluids() {
return new CombinedTankWrapper(); this.assertInitialized();
return this.fluids;
} }
public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) { public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) {
@ -217,6 +280,39 @@ public class MountedStorageManager {
} }
} }
private void readLegacy(CompoundTag nbt) {
NBTHelper.iterateCompoundList(nbt.getList("Storage", Tag.TAG_COMPOUND), tag -> {
BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("Pos"));
CompoundTag data = tag.getCompound("Data");
// note: Synced is ignored, since all are synced
if (data.contains("Toolbox")) {
this.itemsBuilder.put(pos, ToolboxMountedStorage.fromLegacy(data));
} else if (data.contains("NoFuel")) {
this.itemsBuilder.put(pos, ItemVaultMountedStorage.fromLegacy(data));
} else if (data.contains("Bottomless")) {
ItemStack supplied = ItemStack.of(data.getCompound("ProvidedStack"));
this.itemsBuilder.put(pos, new CreativeCrateMountedStorage(supplied));
} else {
// we can create a fallback storage safely, it will be validated before unmounting
ItemStackHandler handler = new ItemStackHandler();
handler.deserializeNBT(data);
this.itemsBuilder.put(pos, new FallbackMountedStorage(handler));
}
});
NBTHelper.iterateCompoundList(nbt.getList("FluidStorage", Tag.TAG_COMPOUND), tag -> {
BlockPos pos = NbtUtils.readBlockPos(tag.getCompound("Pos"));
CompoundTag data = tag.getCompound("Data");
if (data.contains("Bottomless")) {
this.fluidsBuilder.put(pos, CreativeFluidTankMountedStorage.fromLegacy(data));
} else {
this.fluidsBuilder.put(pos, FluidTankMountedStorage.fromLegacy(data));
}
});
}
private static <K, V> ImmutableMap<K, V> subMap(Map<K, V> map, Predicate<V> predicate) { private static <K, V> ImmutableMap<K, V> subMap(Map<K, V> map, Predicate<V> predicate) {
ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
map.forEach((key, value) -> { map.forEach((key, value) -> {

View file

@ -10,6 +10,7 @@ import com.simibubi.create.content.contraptions.Contraption;
import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.item.ItemHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -50,4 +51,10 @@ public class ToolboxMountedStorage extends WrapperMountedItemStorage<ToolboxInve
copy.filters = toolbox.inventory.filters.stream().map(ItemStack::copy).toList(); copy.filters = toolbox.inventory.filters.stream().map(ItemStack::copy).toList();
return new ToolboxMountedStorage(copy); return new ToolboxMountedStorage(copy);
} }
public static ToolboxMountedStorage fromLegacy(CompoundTag nbt) {
ToolboxInventory inv = new ToolboxInventory(null);
inv.deserializeNBT(nbt);
return new ToolboxMountedStorage(inv);
}
} }

View file

@ -3,13 +3,17 @@ package com.simibubi.create.content.fluids.tank;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.foundation.fluid.SmartFluidTank; import com.simibubi.create.foundation.fluid.SmartFluidTank;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.templates.FluidTank;
public class CreativeFluidTankBlockEntity extends FluidTankBlockEntity { public class CreativeFluidTankBlockEntity extends FluidTankBlockEntity {
@ -21,26 +25,34 @@ public class CreativeFluidTankBlockEntity extends FluidTankBlockEntity {
protected SmartFluidTank createInventory() { protected SmartFluidTank createInventory() {
return new CreativeSmartFluidTank(getCapacityMultiplier(), this::onFluidStackChanged); return new CreativeSmartFluidTank(getCapacityMultiplier(), this::onFluidStackChanged);
} }
@Override @Override
public boolean addToGoggleTooltip(List<Component> tooltip, boolean isPlayerSneaking) { public boolean addToGoggleTooltip(List<Component> tooltip, boolean isPlayerSneaking) {
return false; return false;
} }
public static class CreativeSmartFluidTank extends SmartFluidTank { public static class CreativeSmartFluidTank extends SmartFluidTank {
public static final Codec<CreativeSmartFluidTank> CODEC = RecordCodecBuilder.create(i -> i.group(
FluidStack.CODEC.fieldOf("fluid").forGetter(FluidTank::getFluid),
ExtraCodecs.NON_NEGATIVE_INT.fieldOf("capacity").forGetter(FluidTank::getCapacity)
).apply(i, (fluid, capacity) -> {
CreativeSmartFluidTank tank = new CreativeSmartFluidTank(capacity, $ -> {});
tank.setFluid(fluid);
return tank;
}));
public CreativeSmartFluidTank(int capacity, Consumer<FluidStack> updateCallback) { public CreativeSmartFluidTank(int capacity, Consumer<FluidStack> updateCallback) {
super(capacity, updateCallback); super(capacity, updateCallback);
} }
@Override @Override
public int getFluidAmount() { public int getFluidAmount() {
return getFluid().isEmpty() ? 0 : getTankCapacity(0); return getFluid().isEmpty() ? 0 : getTankCapacity(0);
} }
public void setContainedFluid(FluidStack fluidStack) { public void setContainedFluid(FluidStack fluidStack) {
fluid = fluidStack.copy(); fluid = fluidStack.copy();
if (!fluidStack.isEmpty()) if (!fluidStack.isEmpty())
fluid.setAmount(getTankCapacity(0)); fluid.setAmount(getTankCapacity(0));
onContentsChanged(); onContentsChanged();
} }

View file

@ -502,7 +502,7 @@ public class FluidTankBlockEntity extends SmartBlockEntity implements IHaveGoggl
registerAwardables(behaviours, AllAdvancements.STEAM_ENGINE_MAXED, AllAdvancements.PIPE_ORGAN); registerAwardables(behaviours, AllAdvancements.STEAM_ENGINE_MAXED, AllAdvancements.PIPE_ORGAN);
} }
public IFluidTank getTankInventory() { public FluidTank getTankInventory() {
return tankInventory; return tankInventory;
} }

View file

@ -0,0 +1,55 @@
package com.simibubi.create.content.fluids.tank.storage;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.simibubi.create.AllMountedStorageTypes;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.fluid.WrapperMountedFluidStorage;
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
import com.simibubi.create.foundation.utility.CreateCodecs;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fluids.capability.templates.FluidTank;
public class FluidTankMountedStorage extends WrapperMountedFluidStorage<FluidTank> {
public static final Codec<FluidTankMountedStorage> CODEC = CreateCodecs.FLUID_TANK.xmap(
FluidTankMountedStorage::new, storage -> storage.wrapped
);
protected FluidTankMountedStorage(MountedFluidStorageType<?> type, FluidTank wrapped) {
super(type, wrapped);
}
protected FluidTankMountedStorage(FluidTank wrapped) {
this(AllMountedStorageTypes.FLUID_TANK.get(), wrapped);
}
@Override
public void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
if (be instanceof FluidTankBlockEntity tank && tank.isController()) {
FluidTank inventory = tank.getTankInventory();
// capacity shouldn't change, leave it
inventory.setFluid(this.wrapped.getFluid());
}
}
public static FluidTankMountedStorage fromTank(FluidTankBlockEntity tank) {
// tank has update callbacks, make an isolated copy
FluidTank inventory = tank.getTankInventory();
FluidTank copy = new FluidTank(inventory.getCapacity());
copy.setFluid(inventory.getFluid());
return new FluidTankMountedStorage(copy);
}
public static FluidTankMountedStorage fromLegacy(CompoundTag nbt) {
int capacity = nbt.getInt("Capacity");
FluidTank tank = new FluidTank(capacity);
tank.readFromNBT(nbt);
return new FluidTankMountedStorage(tank);
}
}

View file

@ -0,0 +1,27 @@
package com.simibubi.create.content.fluids.tank.storage;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class FluidTankMountedStorageType extends MountedFluidStorageType<FluidTankMountedStorage> {
public FluidTankMountedStorageType() {
super(FluidTankMountedStorage.CODEC);
}
@Override
@Nullable
public FluidTankMountedStorage mount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
if (be instanceof FluidTankBlockEntity tank && tank.isController()) {
return FluidTankMountedStorage.fromTank(tank);
}
return null;
}
}

View file

@ -0,0 +1,53 @@
package com.simibubi.create.content.fluids.tank.storage.creative;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.simibubi.create.AllMountedStorageTypes;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.fluid.WrapperMountedFluidStorage;
import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity;
import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity.CreativeSmartFluidTank;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.templates.FluidTank;
public class CreativeFluidTankMountedStorage extends WrapperMountedFluidStorage<CreativeSmartFluidTank> {
public static final Codec<CreativeFluidTankMountedStorage> CODEC = CreativeSmartFluidTank.CODEC.xmap(
CreativeFluidTankMountedStorage::new, storage -> storage.wrapped
);
protected CreativeFluidTankMountedStorage(MountedFluidStorageType<?> type, CreativeSmartFluidTank tank) {
super(type, tank);
}
protected CreativeFluidTankMountedStorage(CreativeSmartFluidTank tank) {
this(AllMountedStorageTypes.CREATIVE_FLUID_TANK.get(), tank);
}
@Override
public void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
// no need to do anything, supplied stack can't change while mounted
}
public static CreativeFluidTankMountedStorage fromTank(CreativeFluidTankBlockEntity tank) {
// make an isolated copy
FluidTank inv = tank.getTankInventory();
CreativeSmartFluidTank copy = new CreativeSmartFluidTank(inv.getCapacity(), $ -> {});
copy.setContainedFluid(inv.getFluid());
return new CreativeFluidTankMountedStorage(copy);
}
public static CreativeFluidTankMountedStorage fromLegacy(CompoundTag nbt) {
int capacity = nbt.getInt("Capacity");
FluidStack fluid = FluidStack.loadFluidStackFromNBT(nbt.getCompound("ProvidedStack"));
CreativeSmartFluidTank tank = new CreativeSmartFluidTank(capacity, $ -> {});
tank.setContainedFluid(fluid);
return new CreativeFluidTankMountedStorage(tank);
}
}

View file

@ -0,0 +1,27 @@
package com.simibubi.create.content.fluids.tank.storage.creative;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class CreativeFluidTankMountedStorageType extends MountedFluidStorageType<CreativeFluidTankMountedStorage> {
public CreativeFluidTankMountedStorageType() {
super(CreativeFluidTankMountedStorage.CODEC);
}
@Override
@Nullable
public CreativeFluidTankMountedStorage mount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
if (be instanceof CreativeFluidTankBlockEntity tank) {
return CreativeFluidTankMountedStorage.fromTank(tank);
}
return null;
}
}

View file

@ -9,6 +9,7 @@ import com.simibubi.create.api.contraption.storage.item.WrapperMountedItemStorag
import com.simibubi.create.foundation.utility.CreateCodecs; import com.simibubi.create.foundation.utility.CreateCodecs;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -43,4 +44,10 @@ public class ItemVaultMountedStorage extends WrapperMountedItemStorage<ItemStack
// Vault inventories have a world-affecting onContentsChanged, copy to a safe one // Vault inventories have a world-affecting onContentsChanged, copy to a safe one
return new ItemVaultMountedStorage(copyToItemStackHandler(vault.getInventoryOfBlock())); return new ItemVaultMountedStorage(copyToItemStackHandler(vault.getInventoryOfBlock()));
} }
public static ItemVaultMountedStorage fromLegacy(CompoundTag nbt) {
ItemStackHandler handler = new ItemStackHandler();
handler.deserializeNBT(nbt);
return new ItemVaultMountedStorage(handler);
}
} }

View file

@ -10,13 +10,13 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.contraption.storage.item.registrate.MountedItemStorageTypeBuilder;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.fluid.registrate.MountedFluidStorageTypeBuilder;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.contraption.storage.item.registrate.MountedItemStorageTypeBuilder;
import com.simibubi.create.content.decoration.encasing.CasingConnectivity; import com.simibubi.create.content.decoration.encasing.CasingConnectivity;
import com.simibubi.create.content.fluids.VirtualFluid; import com.simibubi.create.content.fluids.VirtualFluid;
import com.simibubi.create.foundation.block.connected.CTModel; import com.simibubi.create.foundation.block.connected.CTModel;
@ -148,6 +148,10 @@ public class CreateRegistrate extends AbstractRegistrate<CreateRegistrate> {
return this.entry(name, callback -> new MountedItemStorageTypeBuilder<>(this, this, name, callback, supplier.get())); return this.entry(name, callback -> new MountedItemStorageTypeBuilder<>(this, this, name, callback, supplier.get()));
} }
public <T extends MountedFluidStorageType<?>> MountedFluidStorageTypeBuilder<T, CreateRegistrate> mountedFluidStorage(String name, Supplier<T> supplier) {
return this.entry(name, callback -> new MountedFluidStorageTypeBuilder<>(this, this, name, callback, supplier.get()));
}
/* Palettes */ /* Palettes */
public <T extends Block> BlockBuilder<T, CreateRegistrate> paletteStoneBlock(String name, public <T extends Block> BlockBuilder<T, CreateRegistrate> paletteStoneBlock(String name,

View file

@ -11,11 +11,14 @@ import com.simibubi.create.foundation.utility.CreateCodecs;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraft.util.ExtraCodecs; import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
/**
* Utility class representing non-empty slots in an item inventory.
*/
public class ItemSlots { public class ItemSlots {
public static final Codec<ItemSlots> CODEC = RecordCodecBuilder.create(instance -> instance.group( public static final Codec<ItemSlots> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.unboundedMap(CreateCodecs.boundedIntStr(0), ItemStack.CODEC).fieldOf("items").forGetter(ItemSlots::toBoxedMap), Codec.unboundedMap(CreateCodecs.boundedIntStr(0), ItemStack.CODEC).fieldOf("items").forGetter(ItemSlots::toBoxedMap),

View file

@ -2,11 +2,13 @@ package com.simibubi.create.foundation.utility;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult; import com.mojang.serialization.DataResult;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.foundation.item.ItemSlots; import com.simibubi.create.foundation.item.ItemSlots;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraft.util.ExtraCodecs; import net.minecraft.util.ExtraCodecs;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.templates.FluidTank;
import net.minecraftforge.items.ItemStackHandler;
public class CreateCodecs { public class CreateCodecs {
public static final Codec<Integer> INT_STR = Codec.STRING.comapFlatMap( public static final Codec<Integer> INT_STR = Codec.STRING.comapFlatMap(
@ -24,6 +26,18 @@ public class CreateCodecs {
slots -> slots.toHandler(ItemStackHandler::new), ItemSlots::fromHandler slots -> slots.toHandler(ItemStackHandler::new), ItemSlots::fromHandler
); );
/**
* Codec for a simple FluidTank with no validator support.
*/
public static final Codec<FluidTank> FLUID_TANK = RecordCodecBuilder.create(i -> i.group(
FluidStack.CODEC.fieldOf("fluid").forGetter(FluidTank::getFluid),
ExtraCodecs.NON_NEGATIVE_INT.fieldOf("capacity").forGetter(FluidTank::getCapacity)
).apply(i, (fluid, capacity) -> {
FluidTank tank = new FluidTank(capacity);
tank.setFluid(fluid);
return tank;
}));
public static Codec<Integer> boundedIntStr(int min) { public static Codec<Integer> boundedIntStr(int min) {
return ExtraCodecs.validate( return ExtraCodecs.validate(
INT_STR, INT_STR,

View file

@ -7,6 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.AllMountedStorageTypes; import com.simibubi.create.AllMountedStorageTypes;
import com.simibubi.create.AllTags; import com.simibubi.create.AllTags;
import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry; import com.simibubi.create.api.contraption.storage.MountedStorageTypeRegistry;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType; import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.lookup.BlockLookup; import com.simibubi.create.api.lookup.BlockLookup;
@ -21,13 +22,19 @@ import net.minecraftforge.registries.RegistryBuilder;
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class MountedStorageTypeRegistryImpl { public class MountedStorageTypeRegistryImpl {
public static final BlockLookup<MountedItemStorageType<?>> ITEM_LOOKUP = BlockLookup.create(MountedStorageTypeRegistryImpl::itemFallback); public static final BlockLookup<MountedItemStorageType<?>> ITEM_LOOKUP = BlockLookup.create(MountedStorageTypeRegistryImpl::itemFallback);
public static final BlockLookup<MountedFluidStorageType<?>> FLUID_LOOKUP = BlockLookup.create();
private static IForgeRegistry<MountedItemStorageType<?>> itemsRegistry; private static IForgeRegistry<MountedItemStorageType<?>> itemsRegistry;
private static IForgeRegistry<MountedFluidStorageType<?>> fluidsRegistry;
public static IForgeRegistry<MountedItemStorageType<?>> getItemsRegistry() { public static IForgeRegistry<MountedItemStorageType<?>> getItemsRegistry() {
return Objects.requireNonNull(itemsRegistry, "Registry accessed too early"); return Objects.requireNonNull(itemsRegistry, "Registry accessed too early");
} }
public static IForgeRegistry<MountedFluidStorageType<?>> getFluidsRegistry() {
return Objects.requireNonNull(fluidsRegistry, "Registry accessed too early");
}
@SubscribeEvent @SubscribeEvent
public static void registerRegistries(NewRegistryEvent event) { public static void registerRegistries(NewRegistryEvent event) {
event.create( event.create(
@ -35,9 +42,14 @@ public class MountedStorageTypeRegistryImpl {
.setName(MountedStorageTypeRegistry.ITEMS.location()), .setName(MountedStorageTypeRegistry.ITEMS.location()),
registry -> itemsRegistry = registry registry -> itemsRegistry = registry
); );
event.create(
new RegistryBuilder<MountedFluidStorageType<?>>()
.setName(MountedStorageTypeRegistry.FLUIDS.location()),
registry -> fluidsRegistry = registry
);
} }
public static MountedItemStorageType<?> itemFallback(Block block) { private static MountedItemStorageType<?> itemFallback(Block block) {
return AllTags.AllBlockTags.FALLBACK_MOUNTED_STORAGE_BLACKLIST.matches(block) return AllTags.AllBlockTags.FALLBACK_MOUNTED_STORAGE_BLACKLIST.matches(block)
? null ? null
: AllMountedStorageTypes.FALLBACK.get(); : AllMountedStorageTypes.FALLBACK.get();