Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for WorldSaveData dimension handling (#1398) and fluid in pipes… #1399

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/main/java/gregtech/api/pipenet/PipeNet.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

public abstract class PipeNet<NodeDataType> implements INBTSerializable<NBTTagCompound> {

protected final WorldPipeNet<NodeDataType, PipeNet<NodeDataType>> worldData;
protected WorldPipeNet<NodeDataType, PipeNet<NodeDataType>> worldData;
private final Map<BlockPos, Node<NodeDataType>> nodeByBlockPos = new HashMap<>();
private final Map<BlockPos, Node<NodeDataType>> unmodifiableNodeByBlockPos = Collections.unmodifiableMap(nodeByBlockPos);
private final Map<ChunkPos, Integer> ownedChunks = new HashMap<>();
Expand All @@ -31,6 +31,12 @@ public PipeNet(WorldPipeNet<NodeDataType, ? extends PipeNet> world) {
this.worldData = (WorldPipeNet<NodeDataType, PipeNet<NodeDataType>>) world;
}

void setWorldData(WorldPipeNet<NodeDataType, ? extends PipeNet> worldData) {
this.worldData = (WorldPipeNet<NodeDataType, PipeNet<NodeDataType>>) worldData;
// Duplicated from WorldPipeNet.onWorldSet()
onConnectionsUpdate();
}

public Set<ChunkPos> getContainedChunks() {
return Collections.unmodifiableSet(ownedChunks.keySet());
}
Expand Down
63 changes: 57 additions & 6 deletions src/main/java/gregtech/api/pipenet/WorldPipeNet.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,38 @@
import net.minecraft.world.storage.WorldSavedData;
import net.minecraftforge.common.util.Constants.NBT;

import java.lang.ref.WeakReference;
import java.util.*;

import gregtech.api.util.GTLog;

public abstract class WorldPipeNet<NodeDataType, T extends PipeNet<NodeDataType>> extends WorldSavedData {

private World world;
protected boolean isFirstTick = true;
private WeakReference<World> worldRef = new WeakReference<>(null);
protected List<T> pipeNets = new ArrayList<>();
protected Map<ChunkPos, List<T>> pipeNetsByChunk = new HashMap<>();
protected WorldPipeNet<NodeDataType, T> oldData = null;
protected boolean checkedForOldData = false;

public static String getDataID(final String baseID, final World world) {
if (world == null || world.isRemote)
throw new RuntimeException("WorldPipeNet should only be created on the server!");
final int dimension = world.provider.getDimension();
return baseID + '.' + dimension;
}

public WorldPipeNet(String name) {
super(name);
}

public World getWorld() {
return world;
return this.worldRef.get();
}

protected void setWorldAndInit(World world) {
if (isFirstTick) {
this.world = world;
this.isFirstTick = false;
// Reset the world as the dimensions are loaded/unloaded
if (world != this.worldRef.get()) {
this.worldRef = new WeakReference<World>(world);
onWorldSet();
}
}
Expand Down Expand Up @@ -122,6 +133,46 @@ protected void removePipeNet(T pipeNet) {

protected abstract T createNetInstance();

/*
* This method is invoked during tile loading
*
* It's purpose is to move pipenets from the old data file to new one based on matching block position
*/
public void checkForOldData(final BlockPos blockPos) {
// No old data
if (this.oldData == null || this.oldData.pipeNets.isEmpty())
return;

// We have new data at this position so don't try to fix
if (getNetFromPos(blockPos) != null)
return;

// See if we have a pipenet for this block pos in the old data
T foundOldData = null;
final List<T> oldPipeNets = this.oldData.pipeNetsByChunk.getOrDefault(new ChunkPos(blockPos), Collections.emptyList());
for (T pipeNet : oldPipeNets) {
if (pipeNet.containsNode(blockPos)) {
if (foundOldData != null)
{
// We have 2 pipenets at this position?
GTLog.logger.warn("Found duplicate pipenets in old data at " + blockPos + " [" + foundOldData + ',' + pipeNet + ']');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use logging formatting instead of string concatenation here.

return;
}
foundOldData = pipeNet;
}
}
// Nothing found
if (foundOldData == null)
return;
// Move the old data into the new data
GTLog.logger.info("Fixing old data for " + foundOldData + " found at " + blockPos);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing.

this.oldData.removePipeNet(foundOldData);
this.oldData.markDirty();
this.addPipeNetSilently(foundOldData);
this.markDirty();
foundOldData.setWorldData(this);
}

@Override
public void readFromNBT(NBTTagCompound nbt) {
this.pipeNets = new ArrayList<>();
Expand Down
15 changes: 12 additions & 3 deletions src/main/java/gregtech/api/pipenet/tickable/TickableWorldPipeNetEventHandler.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,25 @@ public static void registerTickablePipeNet(Function<World, TickableWorldPipeNet<

@SubscribeEvent
public static void onWorldTick(WorldTickEvent event) {
getPipeNetsForWorld(event.world).forEach(TickableWorldPipeNet::update);
final World world = event.world;
if (world == null || world.isRemote)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't check world for null here, these events simply should never have it null.
Might be a good idea to refactor if-not-return to if-then statements, too.

return;
getPipeNetsForWorld(world).forEach(TickableWorldPipeNet::update);
}

@SubscribeEvent
public static void onChunkLoad(ChunkEvent.Load event) {
getPipeNetsForWorld(event.getWorld()).forEach(it -> it.onChunkLoaded(event.getChunk()));
final World world = event.getWorld();
if (world == null || world.isRemote)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

return;
getPipeNetsForWorld(world).forEach(it -> it.onChunkLoaded(event.getChunk()));
}

@SubscribeEvent
public static void onChunkUnload(ChunkEvent.Unload event) {
getPipeNetsForWorld(event.getWorld()).forEach(it -> it.onChunkUnloaded(event.getChunk()));
final World world = event.getWorld();
if (world == null || world.isRemote)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing again.

return;
getPipeNetsForWorld(world).forEach(it -> it.onChunkUnloaded(event.getChunk()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ public void readFromNBT(NBTTagCompound compound) {
@Override
public void onLoad() {
super.onLoad();
final World world = getWorld();
final BlockPipe<PipeType, NodeDataType, ?> pipeBlock = getPipeBlock();
if (world != null && !world.isRemote && pipeBlock != null) {
pipeBlock.getWorldPipeNet(world).checkForOldData(getPos());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is a good place to check for legacy data. Since it will be called pretty often (actually for every pipe-like tile entity loaded), it might incur some overhead, especially taking into account that usually pipe tile entities don't perform any work themselves and don't even tick unless they have to.

Besides, I'm not really sure if you can obtain the block object in question at that point. You probably can, though, because it's saved in NBT. Although it was not always the case, but I think we shouldn't care too much about ancient versions where pipe block was actually retrieved from the world object and never saved.

It might be a good idea to move these checks to some kind of place which is not called that often, or at least not for all pipelike tile entities in the world. Some places that come to mind quickly: "active" fluid pipe tile entities, "source" type cable tile entities, onBlockAdded which checks for neighbor networks.
Although it leaves logic split across multiple places, so it's not the best solution there.

Alternatively we can try to move migration logic to getNetFromPos, e.g. if pipe net is not found, it will try to check for it by digging through old data and restoring it from there. Since getNetFromPos is mostly used only in 2 cases (performing actual transfer logic and joining neighbor nets), and on case 1, which is also the most common one, it will always have a valid network, it shouldn't incur any kind of noticeable performance impact. On case 2 it shouldn't really matter, since that logic is only caused by players placing/breaking blocks, and these actions just don't happen every tick, and even then, hash map lookup is pretty fast.

I personally think last suggestion is the best one to implement, but I'm always open for a civil discussion, especially from @LAGIdiot.

}
this.coverableImplementation.onLoad();
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/gregtech/common/pipelike/cable/BlockCable.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public int getActiveNodeConnections(IBlockAccess world, BlockPos nodePos, IPipeT

@Override
public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn) {
if (worldIn.isRemote)
return;
Insulation insulation = getPipeTileEntity(worldIn, pos).getPipeType();
boolean damageOnLossless = ConfigHolder.doLosslessWiresDamage;
if (!worldIn.isRemote && insulation.insulationLevel == -1 && entityIn instanceof EntityLivingBase) {
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/gregtech/common/pipelike/cable/net/WorldENet.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@

public class WorldENet extends WorldPipeNet<WireProperties, EnergyNet> {

private static final String DATA_ID = "gregtech.e_net";
private static final String DATA_ID_BASE = "gregtech.e_net";

public static WorldENet getWorldENet(World world) {
final String DATA_ID = getDataID(DATA_ID_BASE, world);
// First look for saved data
WorldENet eNetWorldData = (WorldENet) world.loadData(WorldENet.class, DATA_ID);
// No saved data, create it and queue it to be saved
if (eNetWorldData == null) {
eNetWorldData = new WorldENet(DATA_ID);
world.setData(DATA_ID, eNetWorldData);
}
// See if we have old data
if (!eNetWorldData.checkedForOldData) {
eNetWorldData.oldData = (WorldENet) world.loadData(WorldENet.class, DATA_ID_BASE);
eNetWorldData.checkedForOldData = true;
}
// Initialise
eNetWorldData.setWorldAndInit(world);
return eNetWorldData;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/gregtech/common/pipelike/fluidpipe/BlockFluidPipe.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ public boolean canPushIntoFluidHandler(IPipeTile<FluidPipeType, FluidPipePropert

@Override
public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn) {
if (worldIn.isRemote)
return;
if (entityIn instanceof EntityLivingBase && entityIn.world.getWorldTime() % 20 == 0L) {
EntityLivingBase entityLiving = (EntityLivingBase) entityIn;
FluidPipeNet pipeNet = getWorldPipeNet(worldIn).getNetFromPos(pos);
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/gregtech/common/pipelike/fluidpipe/net/FluidPipeNet.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,19 @@ protected FluidPipeProperties readNodeData(NBTTagCompound tagCompound) {
return new FluidPipeProperties(maxTemperature, throughput, gasProof);
}

@Override
public NBTTagCompound serializeNBT() {
final NBTTagCompound nbt = super.serializeNBT();
nbt.setTag("FluidNetTank", this.fluidNetTank.writeToNBT(new NBTTagCompound()));
return nbt;
}

@Override
public void deserializeNBT(final NBTTagCompound nbt) {
super.deserializeNBT(nbt);
final NBTTagCompound tankData = nbt.getCompoundTag("FluidNetTank");
// Guard against old data that didn't have this tag
if (tankData != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use if (nbt.hasTag("FluidNetTank")) here instead.

this.fluidNetTank.readFromNBT(tankData);
}
}
11 changes: 10 additions & 1 deletion src/main/java/gregtech/common/pipelike/fluidpipe/net/WorldFluidPipeNet.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@

public class WorldFluidPipeNet extends WorldPipeNet<FluidPipeProperties, FluidPipeNet> {

private static final String DATA_ID = "gregtech.fluid_pipe_net";
private static final String DATA_ID_BASE = "gregtech.fluid_pipe_net";

public static WorldFluidPipeNet getWorldPipeNet(World world) {
final String DATA_ID = getDataID(DATA_ID_BASE, world);
// First look for saved data
WorldFluidPipeNet netWorldData = (WorldFluidPipeNet) world.loadData(WorldFluidPipeNet.class, DATA_ID);
// No saved data, create it and queue it to be saved
if (netWorldData == null) {
netWorldData = new WorldFluidPipeNet(DATA_ID);
world.setData(DATA_ID, netWorldData);
}
// See if we have old data
if (!netWorldData.checkedForOldData) {
netWorldData.oldData = (WorldFluidPipeNet) world.loadData(WorldFluidPipeNet.class, DATA_ID_BASE);
netWorldData.checkedForOldData = true;
}
// Initialise
netWorldData.setWorldAndInit(world);
return netWorldData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

public class WorldInventoryPipeNet extends TickableWorldPipeNet<EmptyNodeData, InventoryPipeNet> {

private static final String DATA_ID = "gregtech.inventory_pipe_net";
private static final String DATA_ID_BASE = "gregtech.inventory_pipe_net";

public static WorldInventoryPipeNet getWorldPipeNet(World world) {
final String DATA_ID = getDataID(DATA_ID_BASE, world);
WorldInventoryPipeNet netWorldData = (WorldInventoryPipeNet) world.loadData(WorldInventoryPipeNet.class, DATA_ID);
if (netWorldData == null) {
netWorldData = new WorldInventoryPipeNet(DATA_ID);
Expand Down