diff --git a/src/main/java/mekanism/client/ClientProxy.java b/src/main/java/mekanism/client/ClientProxy.java index 4e5e87cf5..0a745a9ad 100644 --- a/src/main/java/mekanism/client/ClientProxy.java +++ b/src/main/java/mekanism/client/ClientProxy.java @@ -491,9 +491,9 @@ public class ClientProxy extends CommonProxy } @Override - public void doTankAnimation(TileEntityMultiblock tileEntity) + public void doAnimation(TileEntityMultiblock tileEntity) { - new ThreadTankSparkle(tileEntity).start(); + new ThreadMultiblockSparkle(tileEntity).start(); } @Override diff --git a/src/main/java/mekanism/client/ThreadTankSparkle.java b/src/main/java/mekanism/client/ThreadMultiblockSparkle.java similarity index 91% rename from src/main/java/mekanism/client/ThreadTankSparkle.java rename to src/main/java/mekanism/client/ThreadMultiblockSparkle.java index 1c531b9ea..8f894092f 100644 --- a/src/main/java/mekanism/client/ThreadTankSparkle.java +++ b/src/main/java/mekanism/client/ThreadMultiblockSparkle.java @@ -6,6 +6,7 @@ import java.util.Set; import mekanism.api.Coord4D; import mekanism.api.MekanismConfig.general; +import mekanism.common.multiblock.MultiblockManager; import mekanism.common.tile.TileEntityMultiblock; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; @@ -14,7 +15,7 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) -public class ThreadTankSparkle extends Thread +public class ThreadMultiblockSparkle extends Thread { public TileEntityMultiblock pointer; @@ -22,7 +23,7 @@ public class ThreadTankSparkle extends Thread public Set iteratedNodes = new HashSet(); - public ThreadTankSparkle(TileEntityMultiblock tileEntity) + public ThreadMultiblockSparkle(TileEntityMultiblock tileEntity) { pointer = tileEntity; } @@ -83,7 +84,7 @@ public class ThreadTankSparkle extends Thread { TileEntity tile = Coord4D.get(tileEntity).getFromSide(side).getTileEntity(pointer.getWorldObj()); - if(tile instanceof TileEntityMultiblock && tile.getClass() == pointer.getClass() && !iteratedNodes.contains(tile)) + if(MultiblockManager.areEqual(tile, pointer) && !iteratedNodes.contains(tile)) { loop((TileEntityMultiblock)tile); } diff --git a/src/main/java/mekanism/common/CommonProxy.java b/src/main/java/mekanism/common/CommonProxy.java index f2654a7b2..9fbb1d64f 100644 --- a/src/main/java/mekanism/common/CommonProxy.java +++ b/src/main/java/mekanism/common/CommonProxy.java @@ -311,9 +311,9 @@ public class CommonProxy } /** - * Does the Dynamic Tank creation animation, starting from the rendering block. + * Does the multiblock creation animation, starting from the rendering block. */ - public void doTankAnimation(TileEntityMultiblock tileEntity) {} + public void doAnimation(TileEntityMultiblock tileEntity) {} /** * Get the actual interface for a GUI. Client-only. diff --git a/src/main/java/mekanism/common/content/tank/TankUpdateProtocol.java b/src/main/java/mekanism/common/content/tank/TankUpdateProtocol.java index 6326ad31c..0e2b80b3d 100644 --- a/src/main/java/mekanism/common/content/tank/TankUpdateProtocol.java +++ b/src/main/java/mekanism/common/content/tank/TankUpdateProtocol.java @@ -363,7 +363,7 @@ public class TankUpdateProtocol /** * Runs the protocol and updates all tanks that make a part of the dynamic tank. */ - public void updateTanks() + public void doUpdate() { loopThrough(pointer); diff --git a/src/main/java/mekanism/common/multiblock/MultiblockManager.java b/src/main/java/mekanism/common/multiblock/MultiblockManager.java index 3521b7a0b..7b2b63ee3 100644 --- a/src/main/java/mekanism/common/multiblock/MultiblockManager.java +++ b/src/main/java/mekanism/common/multiblock/MultiblockManager.java @@ -7,7 +7,7 @@ import java.util.Map; import java.util.Set; import mekanism.api.Coord4D; -import mekanism.common.tile.TileEntityDynamicTank; +import mekanism.common.tile.TileEntityMultiblock; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; @@ -155,7 +155,7 @@ public class MultiblockManager { TileEntity tileEntity = obj.getTileEntity(world); - if(!(tileEntity instanceof TileEntityDynamicTank) || (getStructureId(((TileEntityDynamicTank)tileEntity)) != -1 && getStructureId(((TileEntityDynamicTank)tileEntity)) != inventoryID)) + if(!(tileEntity instanceof TileEntityMultiblock) || ((TileEntityMultiblock)tileEntity).getManager() != manager || (getStructureId(((TileEntityMultiblock)tileEntity)) != -1 && getStructureId(((TileEntityMultiblock)tileEntity)) != inventoryID)) { if(!tilesToKill.containsKey(inventoryID)) { @@ -190,12 +190,22 @@ public class MultiblockManager } } - public static int getStructureId(TileEntityDynamicTank tile) + public static int getStructureId(TileEntityMultiblock tile) { - return tile.structure != null ? tile.structure.inventoryID : -1; + return tile.structure != null ? tile.getSynchronizedData().inventoryID : -1; } - public int getInventoryId(TileEntityDynamicTank tile) + public static boolean areEqual(TileEntity tile1, TileEntity tile2) + { + if(!(tile1 instanceof TileEntityMultiblock) || !(tile2 instanceof TileEntityMultiblock)) + { + return false; + } + + return ((TileEntityMultiblock)tile1).getManager() == ((TileEntityMultiblock)tile2).getManager(); + } + + public int getInventoryId(TileEntityMultiblock tile) { Coord4D coord = Coord4D.get(tile); diff --git a/src/main/java/mekanism/common/multiblock/UpdateProtocol.java b/src/main/java/mekanism/common/multiblock/UpdateProtocol.java new file mode 100644 index 000000000..c3996adab --- /dev/null +++ b/src/main/java/mekanism/common/multiblock/UpdateProtocol.java @@ -0,0 +1,447 @@ +package mekanism.common.multiblock; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import mekanism.api.Coord4D; +import mekanism.common.Mekanism; +import mekanism.common.MekanismBlocks; +import mekanism.common.tile.TileEntityMultiblock; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +public abstract class UpdateProtocol +{ + public static final int FLUID_PER_TANK = 16000; + + /** The multiblock nodes that have already been iterated over. */ + public Set> iteratedNodes = new HashSet>(); + + /** The structures found, all connected by some nodes to the pointer. */ + public SynchronizedData structureFound = null; + + /** The original block the calculation is getting run from. */ + public TileEntityMultiblock pointer; + + public UpdateProtocol(TileEntityMultiblock tileEntity) + { + pointer = tileEntity; + } + + /** + * Recursively loops through each node connected to the given TileEntity. + * @param tile - the TileEntity to loop over + */ + public void loopThrough(TileEntityMultiblock tile) + { + World worldObj = tile.getWorldObj(); + + int origX = tile.xCoord, origY = tile.yCoord, origZ = tile.zCoord; + + boolean isCorner = true; + boolean isHollow = true; + boolean rightBlocks = true; + boolean rightFrame = true; + + Set locations = new HashSet(); + + int xmin = 0, xmax = 0, ymin = 0, ymax = 0, zmin = 0, zmax = 0; + + int x = 0, y = 0, z = 0; + + int volume = 0; + + if((isViableNode(origX + 1, origY, origZ) && isViableNode(origX - 1, origY, origZ)) || + (isViableNode(origX, origY + 1, origZ) && isViableNode(origX, origY - 1, origZ)) || + (isViableNode(origX, origY, origZ + 1) && isViableNode(origX, origY, origZ - 1))) + { + isCorner = false; + } + + if(isCorner) + { + if(isViableNode(origX+1, origY, origZ)) + { + xmin = 0; + + while(isViableNode(origX+x+1, origY, origZ)) + { + x++; + } + + xmax = x; + } + else { + xmax = 0; + + while(isViableNode(origX+x-1, origY, origZ)) + { + x--; + } + + xmin = x; + } + + if(isViableNode(origX, origY+1, origZ)) + { + ymin = 0; + + while(isViableNode(origX, origY+y+1, origZ)) + { + y++; + } + + ymax = y; + } + else { + ymax = 0; + + while(isViableNode(origX, origY+y-1 ,origZ)) + { + y--; + } + + ymin = y; + } + + if(isViableNode(origX, origY, origZ+1)) + { + zmin = 0; + + while(isViableNode(origX, origY, origZ+z+1)) + { + z++; + } + + zmax = z; + } + else { + zmax = 0; + + while(isViableNode(origX, origY, origZ+z-1)) + { + z--; + } + + zmin = z; + } + + for(x = xmin; x <= xmax; x++) + { + for(y = ymin; y <= ymax; y++) + { + for(z = zmin; z <= zmax; z++) + { + if(x == xmin || x == xmax || y == ymin || y == ymax || z == zmin || z == zmax) + { + if(!isViableNode(origX+x, origY+y, origZ+z)) + { + rightBlocks = false; + break; + } + else if(isFrame(Coord4D.get(tile).translate(x, y, z), origX+xmin, origX+xmax, origY+ymin, origY+ymax, origZ+zmin, origZ+zmax) && !isValidFrame(origX+x, origY+y, origZ+z)) + { + rightFrame = false; + break; + } + else { + locations.add(Coord4D.get(tile).translate(x, y, z)); + } + } + else { + if(!isAir(origX+x, origY+y, origZ+z)) + { + isHollow = false; + break; + } + + volume++; + } + } + if(!isHollow || !rightBlocks || !rightFrame) + { + break; + } + } + if(!isHollow || !rightBlocks || !rightFrame) + { + break; + } + } + } + + volume += locations.size(); + + if(volume >= 27 && volume <= 5832 && locations.size() >= 26) + { + if(rightBlocks && rightFrame && isHollow && isCorner) + { + SynchronizedData structure = getNewStructure(); + structure.locations = locations; + structure.volLength = Math.abs(xmax-xmin)+1; + structure.volHeight = Math.abs(ymax-ymin)+1; + structure.volWidth = Math.abs(zmax-zmin)+1; + structure.volume = volume; + structure.renderLocation = Coord4D.get(tile).translate(0, 1, 0); + + onStructureCreated(structure, origX, origY, origZ, xmin, xmax, ymin, ymax, zmin, zmax); + + if(structure.locations.contains(Coord4D.get(pointer)) && isCorrectCorner(Coord4D.get(tile), origX+xmin, origY+ymin, origZ+zmin)) + { + structureFound = structure; + return; + } + } + } + + iteratedNodes.add(tile); + + for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) + { + TileEntity tileEntity = Coord4D.get(tile).getFromSide(side).getTileEntity(tile.getWorldObj()); + + if(MultiblockManager.areEqual(tileEntity, pointer)) + { + if(!iteratedNodes.contains(tileEntity)) + { + loopThrough((TileEntityMultiblock)tileEntity); + } + } + } + } + + public ForgeDirection getSide(Coord4D obj, int xmin, int xmax, int ymin, int ymax, int zmin, int zmax) + { + if(obj.xCoord == xmin) + { + return ForgeDirection.WEST; + } + else if(obj.xCoord == xmax) + { + return ForgeDirection.EAST; + } + else if(obj.yCoord == ymin) + { + return ForgeDirection.DOWN; + } + else if(obj.yCoord == ymax) + { + return ForgeDirection.UP; + } + else if(obj.zCoord == zmin) + { + return ForgeDirection.NORTH; + } + else if(obj.zCoord == zmax) + { + return ForgeDirection.SOUTH; + } + + return ForgeDirection.UNKNOWN; + } + + /** + * Whether or not the block at the specified location is an air block. + * @param x - x coordinate + * @param y - y coordinate + * @param z - z coordinate + * @return + */ + private boolean isAir(int x, int y, int z) + { + return pointer.getWorldObj().isAirBlock(x, y, z); + } + + /** + * Whether or not the block at the specified location is a viable node for a dynamic tank. + * @param x - x coordinate + * @param y - y coordinate + * @param z - z coordinate + * @return + */ + private boolean isViableNode(int x, int y, int z) + { + TileEntity tile = pointer.getWorldObj().getTileEntity(x, y, z); + + if(MultiblockManager.areEqual(tile, pointer)) + { + return true; + } + + return false; + } + + /** + * If the block at the specified location is on the minimum of all angles of this dynamic tank, and the one to use for the + * actual calculation. + * @param obj - location to check + * @param xmin - minimum x value + * @param ymin - minimum y value + * @param zmin - minimum z value + * @return + */ + private boolean isCorrectCorner(Coord4D obj, int xmin, int ymin, int zmin) + { + if(obj.xCoord == xmin && obj.yCoord == ymin && obj.zCoord == zmin) + { + return true; + } + + return false; + } + + /** + * Whether or not the block at the specified location is considered a frame on the dynamic tank. + * @param obj - location to check + * @param xmin - minimum x value + * @param xmax - maximum x value + * @param ymin - minimum y value + * @param ymax - maximum y value + * @param zmin - minimum z value + * @param zmax - maximum z value + * @return + */ + private boolean isFrame(Coord4D obj, int xmin, int xmax, int ymin, int ymax, int zmin, int zmax) + { + if(obj.xCoord == xmin && obj.yCoord == ymin) + return true; + if(obj.xCoord == xmax && obj.yCoord == ymin) + return true; + if(obj.xCoord == xmin && obj.yCoord == ymax) + return true; + if(obj.xCoord == xmax && obj.yCoord == ymax) + return true; + + if(obj.xCoord == xmin && obj.zCoord == zmin) + return true; + if(obj.xCoord == xmax && obj.zCoord == zmin) + return true; + if(obj.xCoord == xmin && obj.zCoord == zmax) + return true; + if(obj.xCoord == xmax && obj.zCoord == zmax) + return true; + + if(obj.yCoord == ymin && obj.zCoord == zmin) + return true; + if(obj.yCoord == ymax && obj.zCoord == zmin) + return true; + if(obj.yCoord == ymin && obj.zCoord == zmax) + return true; + if(obj.yCoord == ymax && obj.zCoord == zmax) + return true; + + return false; + } + + /** + * Whether or not the block at the specified location serves as a frame for a dynamic tank. + * @param x - x coordinate + * @param y - y coordinate + * @param z - z coordinate + * @return + */ + private boolean isValidFrame(int x, int y, int z) + { + return pointer.getWorldObj().getBlock(x, y, z) == MekanismBlocks.BasicBlock && pointer.getWorldObj().getBlockMetadata(x, y, z) == 9; + } + + protected abstract MultiblockCache getNewCache(); + + protected abstract SynchronizedData getNewStructure(); + + protected abstract MultiblockManager getManager(); + + protected abstract void mergeCaches(MultiblockCache cache, MultiblockCache merge); + + protected void onFormed() {} + + protected void onStructureCreated(SynchronizedData structure, int origX, int origY, int origZ, int xmin, int xmax, int ymin, int ymax, int zmin, int zmax) {} + + /** + * Runs the protocol and updates all tanks that make a part of the dynamic tank. + */ + public void doUpdate() + { + loopThrough(pointer); + + if(structureFound != null) + { + for(TileEntityMultiblock tileEntity : iteratedNodes) + { + if(!structureFound.locations.contains(Coord4D.get(tileEntity))) + { + for(TileEntity tile : iteratedNodes) + { + ((TileEntityMultiblock)tileEntity).structure = null; + } + + return; + } + } + + List idsFound = new ArrayList(); + int idToUse = -1; + + for(Coord4D obj : structureFound.locations) + { + TileEntityMultiblock tileEntity = (TileEntityMultiblock)obj.getTileEntity(pointer.getWorldObj()); + int id = getManager().getInventoryId(tileEntity); + + if(id != -1) + { + idsFound.add(id); + } + } + + MultiblockCache cache = getNewCache(); + List rejectedItems = new ArrayList(); + + if(!idsFound.isEmpty()) + { + for(int id : idsFound) + { + if(Mekanism.tankManager.inventories.get(id) != null) + { + if(cache == null) + { + cache = (MultiblockCache)Mekanism.tankManager.pullInventory(pointer.getWorldObj(), id); + } + else { + mergeCaches(cache, (MultiblockCache)getManager().pullInventory(pointer.getWorldObj(), id)); + } + + idToUse = id; + } + } + } + else { + idToUse = Mekanism.tankManager.getUniqueInventoryID(); + } + + //TODO someday: drop all items in rejectedItems + + cache.apply((T)structureFound); + + onFormed(); + + structureFound.inventoryID = idToUse; + + for(Coord4D obj : structureFound.locations) + { + TileEntityMultiblock tileEntity = (TileEntityMultiblock)obj.getTileEntity(pointer.getWorldObj()); + + tileEntity.structure = (T)structureFound; + } + } + else { + for(TileEntity tileEntity : iteratedNodes) + { + ((TileEntityMultiblock)tileEntity).structure = null; + } + } + } +} diff --git a/src/main/java/mekanism/common/tile/TileEntityDynamicTank.java b/src/main/java/mekanism/common/tile/TileEntityDynamicTank.java index 2aa7b0026..eba28ed03 100644 --- a/src/main/java/mekanism/common/tile/TileEntityDynamicTank.java +++ b/src/main/java/mekanism/common/tile/TileEntityDynamicTank.java @@ -256,7 +256,7 @@ public class TileEntityDynamicTank extends TileEntityMultiblock extends TileEntityContainerBlock i { if(!prevStructure) { - Mekanism.proxy.doTankAnimation(this); + Mekanism.proxy.doAnimation(this); } } @@ -122,7 +122,7 @@ public abstract class TileEntityMultiblock extends TileEntityContainerBlock i { if(!worldObj.isRemote && (structure == null || !getSynchronizedData().didTick)) { - new TankUpdateProtocol(this).updateTanks(); + new TankUpdateProtocol(this).doUpdate(); if(structure != null) { @@ -149,7 +149,7 @@ public abstract class TileEntityMultiblock extends TileEntityContainerBlock i protected abstract T getNewStructure(); - protected abstract MultiblockManager getManager(); + public abstract MultiblockManager getManager(); @Override public ArrayList getNetworkedData(ArrayList data)