mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
rework synced storage
- ported depot support
This commit is contained in:
parent
4c5a350d2b
commit
2b91d7cd0c
21 changed files with 497 additions and 441 deletions
|
@ -332,6 +332,7 @@ import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
|
|||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider;
|
||||
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||
|
||||
import net.minecraftforge.client.model.generators.ConfiguredModel;
|
||||
import net.minecraftforge.client.model.generators.ModelFile;
|
||||
import net.minecraftforge.common.Tags;
|
||||
|
@ -799,6 +800,7 @@ public class AllBlocks {
|
|||
.blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p)))
|
||||
.onRegister(assignDataBehaviour(new ItemNameDisplaySource(), "combine_item_names"))
|
||||
.onRegister(interactionBehaviour(new MountedDepotInteractionBehaviour()))
|
||||
.transform(mountedItemStorage(AllMountedStorageTypes.DEPOT))
|
||||
.item()
|
||||
.transform(customItemModel("_", "block"))
|
||||
.register();
|
||||
|
|
|
@ -13,6 +13,7 @@ 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.depot.storage.DepotMountedStorageType;
|
||||
import com.simibubi.create.content.logistics.vault.ItemVaultMountedStorageType;
|
||||
import com.simibubi.create.impl.contraption.storage.FallbackMountedStorageType;
|
||||
import com.tterrag.registrate.util.entry.RegistryEntry;
|
||||
|
@ -27,6 +28,7 @@ public class AllMountedStorageTypes {
|
|||
public static final RegistryEntry<CreativeCrateMountedStorageType> CREATIVE_CRATE = simpleItem("creative_crate", CreativeCrateMountedStorageType::new);
|
||||
public static final RegistryEntry<ItemVaultMountedStorageType> VAULT = simpleItem("vault", ItemVaultMountedStorageType::new);
|
||||
public static final RegistryEntry<ToolboxMountedStorageType> TOOLBOX = simpleItem("toolbox", ToolboxMountedStorageType::new);
|
||||
public static final RegistryEntry<DepotMountedStorageType> DEPOT = simpleItem("depot", DepotMountedStorageType::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);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.simibubi.create.content.contraptions.ContraptionColliderLockPacket.Co
|
|||
import com.simibubi.create.content.contraptions.ContraptionDisassemblyPacket;
|
||||
import com.simibubi.create.content.contraptions.ContraptionRelocationPacket;
|
||||
import com.simibubi.create.content.contraptions.ContraptionStallPacket;
|
||||
import com.simibubi.create.content.contraptions.MountedStorageSyncPacket;
|
||||
import com.simibubi.create.content.contraptions.TrainCollisionPacket;
|
||||
import com.simibubi.create.content.contraptions.actors.contraptionControls.ContraptionDisableActorPacket;
|
||||
import com.simibubi.create.content.contraptions.actors.trainControls.ControlsInputPacket;
|
||||
|
@ -30,9 +31,7 @@ import com.simibubi.create.content.contraptions.glue.SuperGlueSelectionPacket;
|
|||
import com.simibubi.create.content.contraptions.minecart.CouplingCreationPacket;
|
||||
import com.simibubi.create.content.contraptions.minecart.capability.MinecartControllerUpdatePacket;
|
||||
import com.simibubi.create.content.contraptions.sync.ClientMotionPacket;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionInteractionPacket;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionItemPacket;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionSeatMappingPacket;
|
||||
import com.simibubi.create.content.contraptions.sync.LimbSwingUpdatePacket;
|
||||
import com.simibubi.create.content.contraptions.wrench.RadialWrenchMenuSubmitPacket;
|
||||
|
@ -123,6 +122,7 @@ import net.minecraft.core.BlockPos;
|
|||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import net.minecraftforge.network.NetworkEvent.Context;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
|
@ -218,8 +218,7 @@ public enum AllPackets {
|
|||
LIMBSWING_UPDATE(LimbSwingUpdatePacket.class, LimbSwingUpdatePacket::new, PLAY_TO_CLIENT),
|
||||
MINECART_CONTROLLER(MinecartControllerUpdatePacket.class, MinecartControllerUpdatePacket::new, PLAY_TO_CLIENT),
|
||||
FLUID_SPLASH(FluidSplashPacket.class, FluidSplashPacket::new, PLAY_TO_CLIENT),
|
||||
CONTRAPTION_FLUID(ContraptionFluidPacket.class, ContraptionFluidPacket::new, PLAY_TO_CLIENT),
|
||||
CONTRAPTION_ITEM(ContraptionItemPacket.class, ContraptionItemPacket::new, PLAY_TO_CLIENT),
|
||||
MOUNTED_STORAGE_SYNC(MountedStorageSyncPacket.class, MountedStorageSyncPacket::new, PLAY_TO_CLIENT),
|
||||
GANTRY_UPDATE(GantryContraptionUpdatePacket.class, GantryContraptionUpdatePacket::new, PLAY_TO_CLIENT),
|
||||
BLOCK_HIGHLIGHT(HighlightPacket.class, HighlightPacket::new, PLAY_TO_CLIENT),
|
||||
TUNNEL_FLAP(TunnelFlapPacket.class, TunnelFlapPacket::new, PLAY_TO_CLIENT),
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package com.simibubi.create.api.contraption.storage;
|
||||
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
/**
|
||||
* Optional interface for mounted storage that is synced with the client.
|
||||
*/
|
||||
public interface SyncedMountedStorage {
|
||||
/**
|
||||
* @return true if this storage needs to be synced.
|
||||
*/
|
||||
boolean isDirty();
|
||||
|
||||
/**
|
||||
* Called after this storage has been synced.
|
||||
*/
|
||||
void markClean();
|
||||
|
||||
/**
|
||||
* Called on the client side after this storage has been synced from the server.
|
||||
*/
|
||||
void afterSync(Contraption contraption, BlockPos localPos);
|
||||
}
|
|
@ -376,7 +376,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
|
|||
if (!initialized)
|
||||
contraptionInitialize();
|
||||
|
||||
contraption.tick(this);
|
||||
contraption.tickStorage(this);
|
||||
tickContraption();
|
||||
super.tick();
|
||||
|
||||
|
|
|
@ -732,7 +732,7 @@ public abstract class Contraption {
|
|||
});
|
||||
});
|
||||
|
||||
storage.read(nbt);
|
||||
storage.read(nbt, spawnData, this);
|
||||
|
||||
actors.clear();
|
||||
nbt.getList("Actors", Tag.TAG_COMPOUND)
|
||||
|
@ -1447,10 +1447,6 @@ public abstract class Contraption {
|
|||
return this.storage;
|
||||
}
|
||||
|
||||
public MountedStorageManager getStorageManager() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public RenderedBlocks getRenderedBlocks() {
|
||||
return new RenderedBlocks(pos -> {
|
||||
StructureBlockInfo info = blocks.get(pos);
|
||||
|
@ -1473,7 +1469,8 @@ public abstract class Contraption {
|
|||
return simplifiedEntityColliders;
|
||||
}
|
||||
|
||||
public void tick(AbstractContraptionEntity entity) {
|
||||
public void tickStorage(AbstractContraptionEntity entity) {
|
||||
this.storage.tick(entity);
|
||||
}
|
||||
|
||||
public boolean containsBlockBreakers() {
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions;
|
||||
|
||||
import com.simibubi.create.AllPackets;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket;
|
||||
import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity;
|
||||
import com.simibubi.create.content.fluids.tank.CreativeFluidTankBlockEntity.CreativeSmartFluidTank;
|
||||
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
|
||||
import com.simibubi.create.foundation.fluid.SmartFluidTank;
|
||||
|
||||
import net.createmod.catnip.animation.LerpedFloat;
|
||||
import net.createmod.catnip.animation.LerpedFloat.Chaser;
|
||||
import net.createmod.catnip.nbt.NBTHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.IFluidTank;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
|
||||
public class MountedFluidStorage {
|
||||
|
||||
SmartFluidTank tank;
|
||||
private boolean valid;
|
||||
private BlockEntity blockEntity;
|
||||
|
||||
private int packetCooldown = 0;
|
||||
private boolean sendPacket = false;
|
||||
|
||||
public static boolean canUseAsStorage(BlockEntity be) {
|
||||
if (be instanceof FluidTankBlockEntity)
|
||||
return ((FluidTankBlockEntity) be).isController();
|
||||
return false;
|
||||
}
|
||||
|
||||
public MountedFluidStorage(BlockEntity be) {
|
||||
assignBlockEntity(be);
|
||||
}
|
||||
|
||||
public void assignBlockEntity(BlockEntity be) {
|
||||
this.blockEntity = be;
|
||||
tank = createMountedTank(be);
|
||||
}
|
||||
|
||||
private SmartFluidTank createMountedTank(BlockEntity be) {
|
||||
if (be instanceof CreativeFluidTankBlockEntity)
|
||||
return new CreativeSmartFluidTank(
|
||||
((FluidTankBlockEntity) be).getTotalTankSize() * FluidTankBlockEntity.getCapacityMultiplier(), $ -> {
|
||||
});
|
||||
if (be instanceof FluidTankBlockEntity)
|
||||
return new SmartFluidTank(
|
||||
((FluidTankBlockEntity) be).getTotalTankSize() * FluidTankBlockEntity.getCapacityMultiplier(),
|
||||
this::onFluidStackChanged);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void tick(Entity entity, BlockPos pos, boolean isRemote) {
|
||||
if (!isRemote) {
|
||||
if (packetCooldown > 0)
|
||||
packetCooldown--;
|
||||
else if (sendPacket) {
|
||||
sendPacket = false;
|
||||
AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity),
|
||||
new ContraptionFluidPacket(entity.getId(), pos, tank.getFluid()));
|
||||
packetCooldown = 8;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(blockEntity instanceof FluidTankBlockEntity))
|
||||
return;
|
||||
FluidTankBlockEntity tank = (FluidTankBlockEntity) blockEntity;
|
||||
tank.getFluidLevel()
|
||||
.tickChaser();
|
||||
}
|
||||
|
||||
public void updateFluid(FluidStack fluid) {
|
||||
tank.setFluid(fluid);
|
||||
if (!(blockEntity instanceof FluidTankBlockEntity))
|
||||
return;
|
||||
float fillState = tank.getFluidAmount() / (float) tank.getCapacity();
|
||||
FluidTankBlockEntity tank = (FluidTankBlockEntity) blockEntity;
|
||||
if (tank.getFluidLevel() == null)
|
||||
tank.setFluidLevel(LerpedFloat.linear()
|
||||
.startWithValue(fillState));
|
||||
tank.getFluidLevel()
|
||||
.chase(fillState, 0.5, Chaser.EXP);
|
||||
IFluidTank tankInventory = tank.getTankInventory();
|
||||
if (tankInventory instanceof SmartFluidTank)
|
||||
((SmartFluidTank) tankInventory).setFluid(fluid);
|
||||
}
|
||||
|
||||
public void removeStorageFromWorld() {
|
||||
valid = false;
|
||||
if (blockEntity == null)
|
||||
return;
|
||||
|
||||
IFluidHandler teHandler = blockEntity.getCapability(ForgeCapabilities.FLUID_HANDLER)
|
||||
.orElse(null);
|
||||
if (!(teHandler instanceof SmartFluidTank))
|
||||
return;
|
||||
SmartFluidTank smartTank = (SmartFluidTank) teHandler;
|
||||
tank.setFluid(smartTank.getFluid());
|
||||
sendPacket = false;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
private void onFluidStackChanged(FluidStack fs) {
|
||||
sendPacket = true;
|
||||
}
|
||||
|
||||
public void addStorageToWorld(BlockEntity be) {
|
||||
if (tank instanceof CreativeSmartFluidTank)
|
||||
return;
|
||||
|
||||
LazyOptional<IFluidHandler> capability = be.getCapability(ForgeCapabilities.FLUID_HANDLER);
|
||||
IFluidHandler teHandler = capability.orElse(null);
|
||||
if (!(teHandler instanceof SmartFluidTank))
|
||||
return;
|
||||
|
||||
SmartFluidTank inv = (SmartFluidTank) teHandler;
|
||||
inv.setFluid(tank.getFluid()
|
||||
.copy());
|
||||
}
|
||||
|
||||
public IFluidHandler getFluidHandler() {
|
||||
return tank;
|
||||
}
|
||||
|
||||
public CompoundTag serialize() {
|
||||
if (!valid)
|
||||
return null;
|
||||
CompoundTag tag = tank.writeToNBT(new CompoundTag());
|
||||
tag.putInt("Capacity", tank.getCapacity());
|
||||
|
||||
if (tank instanceof CreativeSmartFluidTank) {
|
||||
NBTHelper.putMarker(tag, "Bottomless");
|
||||
tag.put("ProvidedStack", tank.getFluid()
|
||||
.writeToNBT(new CompoundTag()));
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static MountedFluidStorage deserialize(CompoundTag nbt) {
|
||||
MountedFluidStorage storage = new MountedFluidStorage(null);
|
||||
if (nbt == null)
|
||||
return storage;
|
||||
|
||||
int capacity = nbt.getInt("Capacity");
|
||||
storage.tank = new SmartFluidTank(capacity, storage::onFluidStackChanged);
|
||||
storage.valid = true;
|
||||
|
||||
if (nbt.contains("Bottomless")) {
|
||||
FluidStack providedStack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("ProvidedStack"));
|
||||
CreativeSmartFluidTank creativeSmartFluidTank = new CreativeSmartFluidTank(capacity, $ -> {
|
||||
});
|
||||
creativeSmartFluidTank.setContainedFluid(providedStack);
|
||||
storage.tank = creativeSmartFluidTank;
|
||||
return storage;
|
||||
}
|
||||
|
||||
storage.tank.readFromNBT(nbt);
|
||||
return storage;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,24 @@
|
|||
package com.simibubi.create.content.contraptions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Sets;
|
||||
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.SyncedMountedStorage;
|
||||
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;
|
||||
|
@ -24,7 +32,7 @@ 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.nbt.NBTHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
|
@ -38,25 +46,39 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
|
||||
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
import net.minecraftforge.items.ItemStackHandler;
|
||||
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
|
||||
public class MountedStorageManager {
|
||||
// builders used during assembly, null afterward
|
||||
private ImmutableMap.Builder<BlockPos, MountedItemStorage> itemsBuilder;
|
||||
private ImmutableMap.Builder<BlockPos, MountedFluidStorage> fluidsBuilder;
|
||||
// ImmutableMap.Builder is not used because it will throw with duplicate keys, not override them
|
||||
private Map<BlockPos, MountedItemStorage> itemsBuilder;
|
||||
private Map<BlockPos, MountedFluidStorage> fluidsBuilder;
|
||||
private Map<BlockPos, SyncedMountedStorage> syncedItemsBuilder;
|
||||
private Map<BlockPos, SyncedMountedStorage> syncedFluidsBuilder;
|
||||
|
||||
// built data structures after assembly, null before
|
||||
protected ImmutableMap<BlockPos, MountedItemStorage> allItemStorages;
|
||||
private ImmutableMap<BlockPos, MountedItemStorage> allItemStorages;
|
||||
// different from allItemStorages, does not contain internal ones
|
||||
protected MountedItemStorageWrapper items;
|
||||
@Nullable
|
||||
protected MountedItemStorageWrapper fuelItems;
|
||||
protected MountedFluidStorageWrapper fluids;
|
||||
|
||||
private ImmutableMap<BlockPos, SyncedMountedStorage> syncedItems;
|
||||
private ImmutableMap<BlockPos, SyncedMountedStorage> syncedFluids;
|
||||
|
||||
private List<IItemHandlerModifiable> externalHandlers;
|
||||
protected CombinedInvWrapper allItems;
|
||||
private CombinedInvWrapper allItems;
|
||||
|
||||
// ticks until storage can sync again
|
||||
private int syncCooldown;
|
||||
|
||||
// client-side: not all storages are synced, this determines which interactions are valid
|
||||
private Set<BlockPos> interactablePositions;
|
||||
|
||||
public MountedStorageManager() {
|
||||
this.reset();
|
||||
|
@ -67,7 +89,7 @@ public class MountedStorageManager {
|
|||
throw new IllegalStateException("Mounted storage has already been initialized");
|
||||
}
|
||||
|
||||
this.allItemStorages = this.itemsBuilder.build();
|
||||
this.allItemStorages = ImmutableMap.copyOf(this.itemsBuilder);
|
||||
|
||||
this.items = new MountedItemStorageWrapper(subMap(
|
||||
this.allItemStorages, storage -> !storage.isInternal()
|
||||
|
@ -81,9 +103,14 @@ public class MountedStorageManager {
|
|||
);
|
||||
this.fuelItems = fuelMap.isEmpty() ? null : new MountedItemStorageWrapper(fuelMap);
|
||||
|
||||
ImmutableMap<BlockPos, MountedFluidStorage> fluids = this.fluidsBuilder.build();
|
||||
ImmutableMap<BlockPos, MountedFluidStorage> fluids = ImmutableMap.copyOf(this.fluidsBuilder);
|
||||
this.fluids = new MountedFluidStorageWrapper(fluids);
|
||||
this.fluidsBuilder = null;
|
||||
|
||||
this.syncedItems = ImmutableMap.copyOf(this.syncedItemsBuilder);
|
||||
this.syncedItemsBuilder = null;
|
||||
this.syncedFluids = ImmutableMap.copyOf(this.syncedFluidsBuilder);
|
||||
this.syncedFluidsBuilder = null;
|
||||
}
|
||||
|
||||
private boolean isInitialized() {
|
||||
|
@ -103,8 +130,11 @@ public class MountedStorageManager {
|
|||
this.fluids = null;
|
||||
this.externalHandlers = new ArrayList<>();
|
||||
this.allItems = null;
|
||||
this.itemsBuilder = ImmutableMap.builder();
|
||||
this.fluidsBuilder = ImmutableMap.builder();
|
||||
this.itemsBuilder = new HashMap<>();
|
||||
this.fluidsBuilder = new HashMap<>();
|
||||
this.syncedItemsBuilder = new HashMap<>();
|
||||
this.syncedFluidsBuilder = new HashMap<>();
|
||||
// interactablePositions intentionally not reset
|
||||
}
|
||||
|
||||
public void addBlock(Level level, BlockState state, BlockPos globalPos, BlockPos localPos, @Nullable BlockEntity be) {
|
||||
|
@ -113,6 +143,9 @@ public class MountedStorageManager {
|
|||
MountedItemStorage storage = itemType.mount(level, state, globalPos, be);
|
||||
if (storage != null) {
|
||||
this.itemsBuilder.put(localPos, storage);
|
||||
if (storage instanceof SyncedMountedStorage synced) {
|
||||
this.syncedItemsBuilder.put(localPos, synced);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +154,9 @@ public class MountedStorageManager {
|
|||
MountedFluidStorage storage = fluidType.mount(level, state, globalPos, be);
|
||||
if (storage != null) {
|
||||
this.fluidsBuilder.put(localPos, storage);
|
||||
if (storage instanceof SyncedMountedStorage synced) {
|
||||
this.syncedFluidsBuilder.put(localPos, synced);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +182,70 @@ public class MountedStorageManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void read(CompoundTag nbt) {
|
||||
public void tick(AbstractContraptionEntity entity) {
|
||||
if (this.syncCooldown > 0) {
|
||||
this.syncCooldown--;
|
||||
return;
|
||||
}
|
||||
|
||||
Map<BlockPos, MountedItemStorage> items = new HashMap<>();
|
||||
Map<BlockPos, MountedFluidStorage> fluids = new HashMap<>();
|
||||
this.syncedItems.forEach((pos, storage) -> {
|
||||
if (storage.isDirty()) {
|
||||
items.put(pos, (MountedItemStorage) storage);
|
||||
storage.markClean();
|
||||
}
|
||||
});
|
||||
this.syncedFluids.forEach((pos, storage) -> {
|
||||
if (storage.isDirty()) {
|
||||
fluids.put(pos, (MountedFluidStorage) storage);
|
||||
storage.markClean();
|
||||
}
|
||||
});
|
||||
|
||||
if (!items.isEmpty() || !fluids.isEmpty()) {
|
||||
MountedStorageSyncPacket packet = new MountedStorageSyncPacket(entity.getId(), items, fluids);
|
||||
AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), packet);
|
||||
this.syncCooldown = 8;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleSync(MountedStorageSyncPacket packet, AbstractContraptionEntity entity) {
|
||||
// packet only contains changed storages, grab existing ones before resetting
|
||||
ImmutableMap<BlockPos, MountedItemStorage> items = this.getAllItemStorages();
|
||||
MountedFluidStorageWrapper fluids = this.getFluids();
|
||||
this.reset();
|
||||
|
||||
// track freshly synced storages
|
||||
Map<SyncedMountedStorage, BlockPos> syncedStorages = new IdentityHashMap<>();
|
||||
|
||||
try {
|
||||
// re-add existing ones
|
||||
this.itemsBuilder.putAll(items);
|
||||
this.fluidsBuilder.putAll(fluids.storages);
|
||||
// add newly synced ones, overriding existing ones if present
|
||||
packet.items.forEach((pos, storage) -> {
|
||||
this.itemsBuilder.put(pos, storage);
|
||||
syncedStorages.put((SyncedMountedStorage) storage, pos);
|
||||
});
|
||||
packet.fluids.forEach((pos, storage) -> {
|
||||
this.fluidsBuilder.put(pos, storage);
|
||||
syncedStorages.put((SyncedMountedStorage) storage, pos);
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
// an exception will leave the manager in an invalid state
|
||||
Create.LOGGER.error("An error occurred while syncing a MountedStorageManager", t);
|
||||
}
|
||||
|
||||
this.initialize();
|
||||
|
||||
// call all afterSync methods
|
||||
Contraption contraption = entity.getContraption();
|
||||
syncedStorages.forEach((storage, pos) -> storage.afterSync(contraption, pos));
|
||||
}
|
||||
|
||||
// contraption is provided on the client for initial afterSync storage callbacks
|
||||
public void read(CompoundTag nbt, boolean clientPacket, @Nullable Contraption contraption) {
|
||||
this.reset();
|
||||
|
||||
try {
|
||||
|
@ -169,42 +268,79 @@ public class MountedStorageManager {
|
|||
});
|
||||
|
||||
this.readLegacy(nbt);
|
||||
|
||||
if (nbt.contains("interactable_positions")) {
|
||||
this.interactablePositions = new HashSet<>();
|
||||
NBTHelper.iterateCompoundList(nbt.getList("interactable_positions", Tag.TAG_COMPOUND), tag -> {
|
||||
BlockPos pos = NbtUtils.readBlockPos(tag);
|
||||
this.interactablePositions.add(pos);
|
||||
});
|
||||
}
|
||||
} 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();
|
||||
|
||||
// for client sync, run initial afterSync callbacks
|
||||
if (!clientPacket || contraption == null)
|
||||
return;
|
||||
|
||||
this.getAllItemStorages().forEach((pos, storage) -> {
|
||||
if (storage instanceof SyncedMountedStorage synced) {
|
||||
synced.afterSync(contraption, pos);
|
||||
}
|
||||
});
|
||||
this.getFluids().storages.forEach((pos, storage) -> {
|
||||
if (storage instanceof SyncedMountedStorage synced) {
|
||||
synced.afterSync(contraption, pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void write(CompoundTag nbt, boolean clientPacket) {
|
||||
if (!this.getAllItemStorages().isEmpty()) {
|
||||
ListTag items = new ListTag();
|
||||
this.getAllItemStorages().forEach(
|
||||
(pos, storage) -> MountedItemStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage)
|
||||
.result().ifPresent(encoded -> {
|
||||
ListTag items = new ListTag();
|
||||
this.getAllItemStorages().forEach((pos, storage) -> {
|
||||
if (!clientPacket || storage instanceof SyncedMountedStorage) {
|
||||
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);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
if (!items.isEmpty()) {
|
||||
nbt.put("items", items);
|
||||
}
|
||||
|
||||
if (!this.getFluids().storages.isEmpty()) {
|
||||
ListTag fluids = new ListTag();
|
||||
this.getFluids().storages.forEach(
|
||||
(pos, storage) -> MountedFluidStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage)
|
||||
.result().ifPresent(encoded -> {
|
||||
ListTag fluids = new ListTag();
|
||||
this.getFluids().storages.forEach((pos, storage) -> {
|
||||
if (!clientPacket || storage instanceof SyncedMountedStorage) {
|
||||
MountedFluidStorage.CODEC.encodeStart(NbtOps.INSTANCE, storage).result().ifPresent(encoded -> {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.put("pos", NbtUtils.writeBlockPos(pos));
|
||||
tag.put("storage", encoded);
|
||||
fluids.add(tag);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
if (!fluids.isEmpty()) {
|
||||
nbt.put("fluids", fluids);
|
||||
}
|
||||
|
||||
if (clientPacket) {
|
||||
// let the client know of all non-synced ones too
|
||||
SetView<BlockPos> positions = Sets.union(this.getAllItemStorages().keySet(), this.getFluids().storages.keySet());
|
||||
ListTag list = new ListTag();
|
||||
for (BlockPos pos : positions) {
|
||||
list.add(NbtUtils.writeBlockPos(pos));
|
||||
}
|
||||
nbt.put("interactable_positions", list);
|
||||
}
|
||||
}
|
||||
|
||||
public void attachExternal(IItemHandlerModifiable externalStorage) {
|
||||
|
@ -218,9 +354,18 @@ public class MountedStorageManager {
|
|||
this.allItems = new CombinedInvWrapper(all);
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary way to access a contraption's inventory. Includes all
|
||||
* non-internal mounted storages as well as all external storage.
|
||||
*/
|
||||
public CombinedInvWrapper getAllItems() {
|
||||
this.assertInitialized();
|
||||
return this.allItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map of all MountedItemStorages in the contraption, irrelevant of them
|
||||
* being internal or providing fuel. The methods below are likely more useful.
|
||||
* being internal or providing fuel.
|
||||
* @see MountedItemStorage#isInternal()
|
||||
* @see MountedItemStorage#providesFuel()
|
||||
*/
|
||||
|
@ -250,22 +395,18 @@ public class MountedStorageManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets an item handler wrapping all non-internal mounted storages and all external storages.
|
||||
* Non-internal storages are mounted storages that are intended to be exposed to the entire
|
||||
* contraption. External storages are non-mounted storages that are still part of a contraption's
|
||||
* inventory, such as the inventories of chest minecarts.
|
||||
* Gets a fluid handler wrapping all mounted fluid storages.
|
||||
*/
|
||||
public CombinedInvWrapper getAllItems() {
|
||||
this.assertInitialized();
|
||||
return this.allItems;
|
||||
}
|
||||
|
||||
public MountedFluidStorageWrapper getFluids() {
|
||||
this.assertInitialized();
|
||||
return this.fluids;
|
||||
}
|
||||
|
||||
public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) {
|
||||
if (!(player instanceof ServerPlayer serverPlayer)) {
|
||||
return this.interactablePositions != null && this.interactablePositions.contains(localPos);
|
||||
}
|
||||
|
||||
StructureBlockInfo info = contraption.getBlocks().get(localPos);
|
||||
if (info == null)
|
||||
return false;
|
||||
|
@ -274,7 +415,7 @@ public class MountedStorageManager {
|
|||
MountedItemStorage storage = storageManager.getAllItemStorages().get(localPos);
|
||||
|
||||
if (storage != null) {
|
||||
return !(player instanceof ServerPlayer serverPlayer) || storage.handleInteraction(serverPlayer, contraption, info);
|
||||
return storage.handleInteraction(serverPlayer, contraption, info);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package com.simibubi.create.content.contraptions;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
import net.minecraftforge.network.NetworkEvent.Context;
|
||||
|
||||
public class MountedStorageSyncPacket extends SimplePacketBase {
|
||||
public final int contraptionId;
|
||||
public final Map<BlockPos, MountedItemStorage> items;
|
||||
public final Map<BlockPos, MountedFluidStorage> fluids;
|
||||
|
||||
public MountedStorageSyncPacket(int contraptionId, Map<BlockPos, MountedItemStorage> items, Map<BlockPos, MountedFluidStorage> fluids) {
|
||||
this.contraptionId = contraptionId;
|
||||
this.items = items;
|
||||
this.fluids = fluids;
|
||||
}
|
||||
|
||||
public MountedStorageSyncPacket(FriendlyByteBuf buf) {
|
||||
this.contraptionId = buf.readVarInt();
|
||||
this.items = read(buf, MountedItemStorage.CODEC);
|
||||
this.fluids = read(buf, MountedFluidStorage.CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer) {
|
||||
buffer.writeVarInt(this.contraptionId);
|
||||
write(buffer, this.items, MountedItemStorage.CODEC);
|
||||
write(buffer, this.fluids, MountedFluidStorage.CODEC);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static <T> void write(FriendlyByteBuf buf, Map<BlockPos, T> map, Codec<T> codec) {
|
||||
buf.writeMap(map, FriendlyByteBuf::writeBlockPos, (b, t) -> b.writeWithCodec(NbtOps.INSTANCE, codec, t));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static <T> Map<BlockPos, T> read(FriendlyByteBuf buf, Codec<T> codec) {
|
||||
return buf.readMap(FriendlyByteBuf::readBlockPos, (b) -> b.readWithCodec(NbtOps.INSTANCE, codec));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Context context) {
|
||||
context.enqueueWork(() -> {
|
||||
Entity entity = Minecraft.getInstance().level.getEntity(this.contraptionId);
|
||||
if (!(entity instanceof AbstractContraptionEntity contraption))
|
||||
return;
|
||||
|
||||
contraption.getContraption().getStorage().handleSync(this, contraption);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -56,8 +56,8 @@ public class ElevatorContraption extends PulleyContraption {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void tick(AbstractContraptionEntity entity) {
|
||||
super.tick(entity);
|
||||
public void tickStorage(AbstractContraptionEntity entity) {
|
||||
super.tickStorage(entity);
|
||||
|
||||
if (entity.tickCount % 10 != 0)
|
||||
return;
|
||||
|
|
|
@ -2,14 +2,17 @@ package com.simibubi.create.content.contraptions.minecart;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageWrapper;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageWrapper;
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
import com.simibubi.create.content.contraptions.MountedStorageManager;
|
||||
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
|
||||
public class TrainCargoManager extends MountedStorageManager {
|
||||
|
||||
|
@ -28,6 +31,7 @@ public class TrainCargoManager extends MountedStorageManager {
|
|||
if (this.fuelItems != null) {
|
||||
this.fuelItems = new CargoInvWrapper(this.fuelItems);
|
||||
}
|
||||
this.fluids = new CargoTankWrapper(this.fluids);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,8 +41,8 @@ public class TrainCargoManager extends MountedStorageManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundTag nbt) {
|
||||
super.read(nbt);
|
||||
public void read(CompoundTag nbt, boolean clientPacket, @Nullable Contraption contraption) {
|
||||
super.read(nbt, clientPacket, contraption);
|
||||
ticksSinceLastExchange = nbt.getInt("TicksSinceLastExchange");
|
||||
}
|
||||
|
||||
|
@ -64,7 +68,7 @@ public class TrainCargoManager extends MountedStorageManager {
|
|||
}
|
||||
|
||||
class CargoInvWrapper extends MountedItemStorageWrapper {
|
||||
public CargoInvWrapper(MountedItemStorageWrapper wrapped) {
|
||||
CargoInvWrapper(MountedItemStorageWrapper wrapped) {
|
||||
super(wrapped.storages);
|
||||
}
|
||||
|
||||
|
@ -93,10 +97,9 @@ public class TrainCargoManager extends MountedStorageManager {
|
|||
|
||||
}
|
||||
|
||||
class CargoTankWrapper extends CombinedTankWrapper {
|
||||
|
||||
public CargoTankWrapper(IFluidHandler... fluidHandler) {
|
||||
super(fluidHandler);
|
||||
class CargoTankWrapper extends MountedFluidStorageWrapper {
|
||||
CargoTankWrapper(MountedFluidStorageWrapper wrapped) {
|
||||
super(wrapped.storages);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.sync;
|
||||
|
||||
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
|
||||
import com.simibubi.create.content.fluids.tank.FluidTankMovementBehavior;
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.network.NetworkEvent.Context;
|
||||
|
||||
public class ContraptionFluidPacket extends SimplePacketBase {
|
||||
|
||||
private int entityId;
|
||||
private BlockPos localPos;
|
||||
private FluidStack containedFluid;
|
||||
|
||||
public ContraptionFluidPacket(int entityId, BlockPos localPos, FluidStack containedFluid) {
|
||||
this.entityId = entityId;
|
||||
this.localPos = localPos;
|
||||
this.containedFluid = containedFluid;
|
||||
}
|
||||
|
||||
public ContraptionFluidPacket(FriendlyByteBuf buffer) {
|
||||
entityId = buffer.readInt();
|
||||
localPos = buffer.readBlockPos();
|
||||
containedFluid = FluidStack.readFromPacket(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer) {
|
||||
buffer.writeInt(entityId);
|
||||
buffer.writeBlockPos(localPos);
|
||||
containedFluid.writeToPacket(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Context context) {
|
||||
context.enqueueWork(() -> {
|
||||
Entity entityByID = Minecraft.getInstance().level.getEntity(entityId);
|
||||
if (entityByID instanceof AbstractContraptionEntity entity) {
|
||||
FluidTankMovementBehavior.handlePacket(entity, this.localPos, this.containedFluid);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package com.simibubi.create.content.contraptions.sync;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
|
||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.items.ItemStackHandler;
|
||||
import net.minecraftforge.network.NetworkEvent.Context;
|
||||
|
||||
public class ContraptionItemPacket extends SimplePacketBase {
|
||||
|
||||
private int entityId;
|
||||
private BlockPos localPos;
|
||||
private List<ItemStack> containedItems;
|
||||
|
||||
public ContraptionItemPacket(int entityId, BlockPos localPos, ItemStackHandler handler) {
|
||||
this.entityId = entityId;
|
||||
this.localPos = localPos;
|
||||
this.containedItems = new ArrayList<>(handler.getSlots());
|
||||
for (int i = 0; i < handler.getSlots(); i++)
|
||||
containedItems.add(handler.getStackInSlot(i));
|
||||
}
|
||||
|
||||
public ContraptionItemPacket(FriendlyByteBuf buffer) {
|
||||
entityId = buffer.readInt();
|
||||
localPos = buffer.readBlockPos();
|
||||
int count = buffer.readVarInt();
|
||||
containedItems = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++)
|
||||
containedItems.add(buffer.readItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buffer) {
|
||||
buffer.writeInt(entityId);
|
||||
buffer.writeBlockPos(localPos);
|
||||
buffer.writeVarInt(containedItems.size());
|
||||
for (ItemStack stack : containedItems)
|
||||
buffer.writeItem(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Context context) {
|
||||
context.enqueueWork(() -> {
|
||||
Entity entityByID = Minecraft.getInstance().level.getEntity(entityId);
|
||||
if (!(entityByID instanceof AbstractContraptionEntity))
|
||||
return;
|
||||
AbstractContraptionEntity contraptionEntity = (AbstractContraptionEntity) entityByID;
|
||||
contraptionEntity.getContraption().handleContraptionItemPacket(localPos, containedItems);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,21 +1,11 @@
|
|||
package com.simibubi.create.content.fluids.tank;
|
||||
|
||||
import com.simibubi.create.AllPackets;
|
||||
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorage;
|
||||
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovementContext;
|
||||
import com.simibubi.create.content.contraptions.sync.ContraptionFluidPacket;
|
||||
import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorage;
|
||||
|
||||
import net.createmod.catnip.utility.animation.LerpedFloat;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidTank;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
|
||||
// the contents of the mounted storage need to be synced to the block entity for rendering
|
||||
// The fluid level needs to be ticked to animate smoothly
|
||||
public class FluidTankMovementBehavior implements MovementBehaviour {
|
||||
@Override
|
||||
public boolean mustTickWhileDisabled() {
|
||||
|
@ -24,51 +14,11 @@ public class FluidTankMovementBehavior implements MovementBehaviour {
|
|||
|
||||
@Override
|
||||
public void tick(MovementContext context) {
|
||||
if (!context.world.isClientSide) {
|
||||
MountedFluidStorage storage = context.getFluidStorage();
|
||||
if (!(storage instanceof FluidTankMountedStorage tank))
|
||||
return;
|
||||
|
||||
FluidStack fluid = tank.getTank().getFluid();
|
||||
if (!(context.temporaryData instanceof SyncState))
|
||||
context.temporaryData = new SyncState();
|
||||
|
||||
SyncState state = (SyncState) context.temporaryData;
|
||||
if (state.cooldown > 0) {
|
||||
state.cooldown--;
|
||||
} else if (!fluid.isFluidStackIdentical(state.stack)) {
|
||||
// fluid has changed, sync it
|
||||
state.cooldown = 8;
|
||||
state.stack = fluid.copy();
|
||||
|
||||
AbstractContraptionEntity entity = context.contraption.entity;
|
||||
ContraptionFluidPacket packet = new ContraptionFluidPacket(entity.getId(), context.localPos, fluid);
|
||||
AllPackets.getChannel().send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), packet);
|
||||
}
|
||||
} else {
|
||||
if (context.world.isClientSide) {
|
||||
BlockEntity be = context.contraption.presentBlockEntities.get(context.localPos);
|
||||
if (be instanceof FluidTankBlockEntity tank) {
|
||||
tank.getFluidLevel().tickChaser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handlePacket(AbstractContraptionEntity entity, BlockPos localPos, FluidStack fluid) {
|
||||
BlockEntity be = entity.getContraption().presentBlockEntities.get(localPos);
|
||||
if (!(be instanceof FluidTankBlockEntity tank))
|
||||
return;
|
||||
|
||||
FluidTank inv = tank.getTankInventory();
|
||||
inv.setFluid(fluid);
|
||||
float fillLevel = inv.getFluidAmount() / (float) inv.getCapacity();
|
||||
if (tank.getFluidLevel() == null) {
|
||||
tank.setFluidLevel(LerpedFloat.linear().startWithValue(fillLevel));
|
||||
}
|
||||
tank.getFluidLevel().chase(fillLevel, 0.5, LerpedFloat.Chaser.EXP);
|
||||
}
|
||||
|
||||
private static final class SyncState {
|
||||
private int cooldown = 0;
|
||||
private FluidStack stack = FluidStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,30 +3,41 @@ package com.simibubi.create.content.fluids.tank.storage;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import com.simibubi.create.AllMountedStorageTypes;
|
||||
import com.simibubi.create.api.contraption.storage.SyncedMountedStorage;
|
||||
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
|
||||
import com.simibubi.create.api.contraption.storage.fluid.WrapperMountedFluidStorage;
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
|
||||
import com.simibubi.create.foundation.utility.CreateCodecs;
|
||||
import com.simibubi.create.content.fluids.tank.storage.FluidTankMountedStorage.Handler;
|
||||
|
||||
import net.createmod.catnip.animation.LerpedFloat;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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;
|
||||
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
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
|
||||
);
|
||||
public class FluidTankMountedStorage extends WrapperMountedFluidStorage<Handler> implements SyncedMountedStorage {
|
||||
public static final Codec<FluidTankMountedStorage> CODEC = RecordCodecBuilder.create(i -> i.group(
|
||||
ExtraCodecs.NON_NEGATIVE_INT.fieldOf("capacity").forGetter(FluidTankMountedStorage::getCapacity),
|
||||
FluidStack.CODEC.fieldOf("fluid").forGetter(FluidTankMountedStorage::getFluid)
|
||||
).apply(i, FluidTankMountedStorage::new));
|
||||
|
||||
protected FluidTankMountedStorage(MountedFluidStorageType<?> type, FluidTank wrapped) {
|
||||
super(type, wrapped);
|
||||
private boolean dirty;
|
||||
|
||||
protected FluidTankMountedStorage(MountedFluidStorageType<?> type, int capacity, FluidStack stack) {
|
||||
super(type, new Handler(capacity, stack));
|
||||
this.wrapped.onChange = () -> this.dirty = true;
|
||||
}
|
||||
|
||||
protected FluidTankMountedStorage(FluidTank wrapped) {
|
||||
this(AllMountedStorageTypes.FLUID_TANK.get(), wrapped);
|
||||
protected FluidTankMountedStorage(int capacity, FluidStack stack) {
|
||||
this(AllMountedStorageTypes.FLUID_TANK.get(), capacity, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,22 +49,62 @@ public class FluidTankMountedStorage extends WrapperMountedFluidStorage<FluidTan
|
|||
}
|
||||
}
|
||||
|
||||
public FluidTank getTank() {
|
||||
return this.wrapped;
|
||||
public FluidStack getFluid() {
|
||||
return this.wrapped.getFluid();
|
||||
}
|
||||
|
||||
public int getCapacity() {
|
||||
return this.wrapped.getCapacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return this.dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markClean() {
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSync(Contraption contraption, BlockPos localPos) {
|
||||
BlockEntity be = contraption.presentBlockEntities.get(localPos);
|
||||
if (!(be instanceof FluidTankBlockEntity tank))
|
||||
return;
|
||||
|
||||
FluidTank inv = tank.getTankInventory();
|
||||
inv.setFluid(this.getFluid());
|
||||
float fillLevel = inv.getFluidAmount() / (float) inv.getCapacity();
|
||||
if (tank.getFluidLevel() == null) {
|
||||
tank.setFluidLevel(LerpedFloat.linear().startWithValue(fillLevel));
|
||||
}
|
||||
tank.getFluidLevel().chase(fillLevel, 0.5, LerpedFloat.Chaser.EXP);
|
||||
}
|
||||
|
||||
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);
|
||||
return new FluidTankMountedStorage(inventory.getCapacity(), inventory.getFluid().copy());
|
||||
}
|
||||
|
||||
public static FluidTankMountedStorage fromLegacy(CompoundTag nbt) {
|
||||
int capacity = nbt.getInt("Capacity");
|
||||
FluidTank tank = new FluidTank(capacity);
|
||||
tank.readFromNBT(nbt);
|
||||
return new FluidTankMountedStorage(tank);
|
||||
FluidStack fluid = FluidStack.loadFluidStackFromNBT(nbt);
|
||||
return new FluidTankMountedStorage(capacity, fluid);
|
||||
}
|
||||
|
||||
public static final class Handler extends FluidTank {
|
||||
private Runnable onChange = () -> {};
|
||||
|
||||
public Handler(int capacity, FluidStack stack) {
|
||||
super(capacity);
|
||||
this.setFluid(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContentsChanged() {
|
||||
this.onChange.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package com.simibubi.create.content.logistics.depot;
|
||||
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorage;
|
||||
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
|
||||
import com.simibubi.create.content.contraptions.MountedStorage;
|
||||
import com.simibubi.create.content.contraptions.MountedStorageManager;
|
||||
import com.simibubi.create.content.contraptions.behaviour.MovingInteractionBehaviour;
|
||||
import com.simibubi.create.content.logistics.depot.storage.DepotMountedStorage;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
|
||||
public class MountedDepotInteractionBehaviour extends MovingInteractionBehaviour {
|
||||
|
||||
|
@ -24,23 +24,17 @@ public class MountedDepotInteractionBehaviour extends MovingInteractionBehaviour
|
|||
if (player.level().isClientSide)
|
||||
return true;
|
||||
|
||||
MountedStorageManager storageManager = contraptionEntity.getContraption()
|
||||
.getStorageManager();
|
||||
if (storageManager == null)
|
||||
MountedStorageManager manager = contraptionEntity.getContraption().getStorage();
|
||||
|
||||
MountedItemStorage storage = manager.getAllItemStorages().get(localPos);
|
||||
if (!(storage instanceof DepotMountedStorage depot))
|
||||
return false;
|
||||
|
||||
MountedStorage mountedStorage = storageManager.getMountedItemStorage()
|
||||
.get(localPos);
|
||||
if (mountedStorage == null)
|
||||
return false;
|
||||
|
||||
IItemHandlerModifiable itemHandler = mountedStorage.getItemHandler();
|
||||
ItemStack itemOnDepot = itemHandler.getStackInSlot(0);
|
||||
|
||||
ItemStack itemOnDepot = depot.getItem();
|
||||
if (itemOnDepot.isEmpty() && itemInHand.isEmpty())
|
||||
return true;
|
||||
|
||||
itemHandler.setStackInSlot(0, itemInHand.copy());
|
||||
depot.setItem(itemInHand.copy());
|
||||
player.setItemInHand(activeHand, itemOnDepot.copy());
|
||||
AllSoundEvents.DEPOT_PLOP.playOnServer(player.level(),
|
||||
BlockPos.containing(contraptionEntity.toGlobalVector(Vec3.atCenterOf(localPos), 0)));
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.simibubi.create.content.logistics.depot.storage;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.simibubi.create.AllMountedStorageTypes;
|
||||
import com.simibubi.create.api.contraption.storage.SyncedMountedStorage;
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
|
||||
import com.simibubi.create.api.contraption.storage.item.WrapperMountedItemStorage;
|
||||
import com.simibubi.create.content.contraptions.Contraption;
|
||||
import com.simibubi.create.content.logistics.depot.DepotBlockEntity;
|
||||
import com.simibubi.create.content.logistics.depot.storage.DepotMountedStorage.Handler;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
|
||||
|
||||
import net.minecraftforge.items.ItemStackHandler;
|
||||
|
||||
public class DepotMountedStorage extends WrapperMountedItemStorage<Handler> implements SyncedMountedStorage {
|
||||
public static final Codec<DepotMountedStorage> CODEC = ItemStack.CODEC.xmap(
|
||||
DepotMountedStorage::new, DepotMountedStorage::getItem
|
||||
);
|
||||
|
||||
private boolean dirty;
|
||||
|
||||
protected DepotMountedStorage(ItemStack stack) {
|
||||
this(AllMountedStorageTypes.DEPOT.get(), stack);
|
||||
}
|
||||
|
||||
protected DepotMountedStorage(MountedItemStorageType<?> type, ItemStack stack) {
|
||||
super(type, new Handler(stack));
|
||||
this.wrapped.onChange = () -> this.dirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unmount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
|
||||
if (be instanceof DepotBlockEntity depot) {
|
||||
depot.setHeldItem(this.getStackInSlot(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleInteraction(ServerPlayer player, Contraption contraption, StructureBlockInfo info) {
|
||||
// interaction is handled in the Interaction Behavior, swaps items with the player
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return this.dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markClean() {
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSync(Contraption contraption, BlockPos localPos) {
|
||||
BlockEntity be = contraption.presentBlockEntities.get(localPos);
|
||||
if (be instanceof DepotBlockEntity depot) {
|
||||
depot.setHeldItem(this.getItem());
|
||||
}
|
||||
}
|
||||
|
||||
public void setItem(ItemStack stack) {
|
||||
this.setStackInSlot(0, stack);
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
return this.getStackInSlot(0);
|
||||
}
|
||||
|
||||
public static DepotMountedStorage fromDepot(DepotBlockEntity depot) {
|
||||
ItemStack held = depot.getHeldItem();
|
||||
return new DepotMountedStorage(held.copy());
|
||||
}
|
||||
|
||||
public static final class Handler extends ItemStackHandler {
|
||||
private Runnable onChange = () -> {};
|
||||
|
||||
private Handler(ItemStack stack) {
|
||||
super(1);
|
||||
this.setStackInSlot(0, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContentsChanged(int slot) {
|
||||
this.onChange.run();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.simibubi.create.content.logistics.depot.storage;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
|
||||
import com.simibubi.create.content.logistics.depot.DepotBlockEntity;
|
||||
|
||||
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 DepotMountedStorageType extends MountedItemStorageType<DepotMountedStorage> {
|
||||
public DepotMountedStorageType() {
|
||||
super(DepotMountedStorage.CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DepotMountedStorage mount(Level level, BlockState state, BlockPos pos, @Nullable BlockEntity be) {
|
||||
if (be instanceof DepotBlockEntity depot) {
|
||||
return DepotMountedStorage.fromDepot(depot);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -30,9 +30,9 @@ import com.simibubi.create.foundation.advancement.AllAdvancements;
|
|||
|
||||
import net.createmod.catnip.data.Couple;
|
||||
import net.createmod.catnip.data.Iterate;
|
||||
import net.createmod.catnip.nbt.NBTHelper;
|
||||
import net.createmod.catnip.data.Pair;
|
||||
import net.createmod.catnip.math.VecHelper;
|
||||
import net.createmod.catnip.nbt.NBTHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
@ -46,6 +46,7 @@ import net.minecraft.world.entity.EntityType;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
|
@ -647,7 +648,7 @@ public class Carriage {
|
|||
public void read(CompoundTag tag) {
|
||||
cutoff = tag.getFloat("Cutoff");
|
||||
discardTicks = tag.getInt("DiscardTicks");
|
||||
storage.read(tag);
|
||||
storage.read(tag, false, null);
|
||||
if (tag.contains("Pivot"))
|
||||
pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null);
|
||||
if (positionAnchor != null)
|
||||
|
|
|
@ -27,6 +27,7 @@ import net.minecraft.network.chat.Component;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
|
||||
public class DeliverPackagesInstruction extends ScheduleInstruction {
|
||||
|
@ -78,7 +79,7 @@ public class DeliverPackagesInstruction extends ScheduleInstruction {
|
|||
}
|
||||
|
||||
for (Carriage carriage : train.carriages) {
|
||||
IItemHandlerModifiable carriageInventory = carriage.storage.getItems();
|
||||
IItemHandlerModifiable carriageInventory = carriage.storage.getAllItems();
|
||||
if (carriageInventory == null)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@ package com.simibubi.create.foundation.utility;
|
|||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import com.simibubi.create.foundation.item.ItemSlots;
|
||||
|
||||
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 {
|
||||
|
@ -26,18 +24,6 @@ public class CreateCodecs {
|
|||
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) {
|
||||
return ExtraCodecs.validate(
|
||||
INT_STR,
|
||||
|
|
Loading…
Add table
Reference in a new issue