Skip to content

Commit

Permalink
Avoid codecs as much as possible for syncing fluid tank contents, as …
Browse files Browse the repository at this point in the history
…there is a chance that there are a lot of fluid tanks in a close vicinity
  • Loading branch information
pupnewfster committed Aug 25, 2024
1 parent c1a6935 commit b353206
Showing 1 changed file with 94 additions and 4 deletions.
98 changes: 94 additions & 4 deletions src/main/java/mekanism/common/tile/TileEntityFluidTank.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package mekanism.common.tile;

import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import mekanism.api.Action;
import mekanism.api.IConfigurable;
import mekanism.api.IContentsListener;
import mekanism.api.SerializationConstants;
import mekanism.api.fluid.IExtendedFluidTank;
import mekanism.api.functions.ConstantPredicates;
import mekanism.api.providers.IBlockProvider;
import mekanism.common.Mekanism;
import mekanism.common.attachments.containers.ContainerType;
import mekanism.common.block.attribute.Attribute;
import mekanism.common.capabilities.Capabilities;
Expand Down Expand Up @@ -42,9 +47,16 @@
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
Expand All @@ -53,6 +65,7 @@
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.material.Fluid;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
Expand Down Expand Up @@ -141,6 +154,7 @@ protected boolean onUpdateServer() {

float scale = MekanismUtils.getScale(prevScale, fluidTank);
//TODO - 1.21: Figure out handling of stacked tanks where it may be going back and forth between being full and not?
// or even just empty and not
if (MekanismUtils.scaleChanged(scale, prevScale)) {
if (prevScale == 0 || scale == 0) {
//If it was empty and no longer is, or wasn't empty and now is empty we want to recheck the block lighting
Expand Down Expand Up @@ -315,17 +329,46 @@ public void addContainerTrackers(MekanismContainer container) {
@Override
public CompoundTag getReducedUpdateTag(@NotNull HolderLookup.Provider provider) {
CompoundTag updateTag = super.getReducedUpdateTag(provider);
updateTag.put(SerializationConstants.FLUID, fluidTank.getFluid().saveOptional(provider));
updateTag.put(SerializationConstants.VALVE, valveFluid.saveOptional(provider));
//updateTag.put(SerializationConstants.FLUID, fluidTank.getFluid().saveOptional(provider));
//updateTag.put(SerializationConstants.VALVE, valveFluid.saveOptional(provider));
updateTag.putFloat(SerializationConstants.SCALE, prevScale);
//TODO - 1.21: Re-evaluate this alternate encoding further
CompoundTag fluidData = new CompoundTag();
FluidStack fluid = fluidTank.getFluid();
if (fluid.isEmpty()) {
fluid = valveFluid;
} else {
fluidData.putInt(SerializationConstants.AMOUNT, fluid.getAmount());
}
if (!fluid.isEmpty()) {
//Note: This should never be null as it returns a reference holder
// We throw if it is, so that we can find the bug if it gets introduced during porting
ResourceKey<Fluid> key = Objects.requireNonNull(fluid.getFluidHolder().getKey());

This comment has been minimized.

Copy link
@thiakil

thiakil Aug 26, 2024

Member

please use the Object,String variant of requireNonNull here to specify that "reference holder should never be null"

fluidData.putString(SerializationConstants.ID, key.location().toString());
if (!fluid.isComponentsPatchEmpty()) {
//Note: This isn't necessarily optimal, but it does mean in general we can avoid codecs unless it happens to be a fluid that
// does have component data
DataResult<Tag> componentData = DataComponentPatch.CODEC.encodeStart(provider.createSerializationContext(NbtOps.INSTANCE), fluid.getComponentsPatch());
if (componentData.isError()) {
componentData.mapError(error -> {
Mekanism.logger.error("Failed to encode fluid stack component data: {}", error);
return error;
});
} else {
fluidData.put(SerializationConstants.DATA, componentData.getOrThrow());
}
}
fluidData.putBoolean(SerializationConstants.VALVE, !valveFluid.isEmpty());
updateTag.put(SerializationConstants.FLUID, fluidData);
}
return updateTag;
}

@Override
public void handleUpdateTag(@NotNull CompoundTag tag, @NotNull HolderLookup.Provider provider) {
super.handleUpdateTag(tag, provider);
NBTUtils.setFluidStackIfPresent(provider, tag, SerializationConstants.FLUID, fluid -> fluidTank.setStack(fluid));
NBTUtils.setFluidStackIfPresent(provider, tag, SerializationConstants.VALVE, fluid -> valveFluid = fluid);
//NBTUtils.setFluidStackIfPresent(provider, tag, SerializationConstants.FLUID, fluid -> fluidTank.setStack(fluid));
//NBTUtils.setFluidStackIfPresent(provider, tag, SerializationConstants.VALVE, fluid -> valveFluid = fluid);
NBTUtils.setFloatIfPresent(tag, SerializationConstants.SCALE, scale -> {
if (MekanismUtils.scaleChanged(prevScale, scale)) {
if (prevScale == 0 || scale == 0) {
Expand All @@ -336,6 +379,53 @@ public void handleUpdateTag(@NotNull CompoundTag tag, @NotNull HolderLookup.Prov
}
prevScale = scale;
});

boolean unsetFluid = true;
if (tag.contains(SerializationConstants.FLUID, Tag.TAG_COMPOUND)) {
CompoundTag fluidData = tag.getCompound(SerializationConstants.FLUID);
if (!fluidData.isEmpty()) {
String fluidId = fluidData.getString(SerializationConstants.ID);
Optional<Reference<Fluid>> holder = BuiltInRegistries.FLUID.getHolder(ResourceLocation.parse(fluidId));
if (holder.isEmpty()) {
Mekanism.logger.info("Received update packet for a fluid tank for an unregistered fluid with expected id: {}", fluidId);
} else {
Reference<Fluid> fluidType = holder.get();
DataComponentPatch patch = DataComponentPatch.EMPTY;
int amount = fluidData.getInt(SerializationConstants.AMOUNT);
if (fluidData.contains(SerializationConstants.DATA)) {
DataResult<Pair<DataComponentPatch, Tag>> componentPatch = DataComponentPatch.CODEC.decode(
provider.createSerializationContext(NbtOps.INSTANCE),
fluidData.get(SerializationConstants.DATA)
);
if (componentPatch.isSuccess()) {
patch = componentPatch.getOrThrow().getFirst();
} else {
componentPatch.mapError(error -> {
Mekanism.logger.info("Received update packet for a fluid tank storing {}, and could not decode the data component patch: {}",
fluidId, error);
return error;
});
}
}
//We actually have something to set, so mark that we shouldn't reset the stored fluid data
unsetFluid = false;
if (amount == 0) {
fluidTank.setEmpty();
} else {
fluidTank.setStack(new FluidStack(fluidType, amount, patch));
}
if (fluidData.getBoolean(SerializationConstants.VALVE)) {
valveFluid = new FluidStack(fluidType, 1, patch);
} else {
valveFluid = FluidStack.EMPTY;
}
}
}
}
if (unsetFluid) {
fluidTank.setEmpty();
valveFluid = FluidStack.EMPTY;
}
}

//Methods relating to IComputerTile
Expand Down

0 comments on commit b353206

Please sign in to comment.