diff --git a/src/resonantinduction/base/Vector3.java b/src/resonantinduction/base/Vector3.java index 35c9d434..cca2d2bf 100644 --- a/src/resonantinduction/base/Vector3.java +++ b/src/resonantinduction/base/Vector3.java @@ -12,6 +12,7 @@ import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.World; +import net.minecraftforge.common.ForgeDirection; /** * @author Calclavia @@ -108,6 +109,11 @@ public class Vector3 { return this.x * vec2.x + this.y * vec2.y + this.z * vec2.z; } + + public Vector3 getFromSide(ForgeDirection side) + { + return new Vector3(x+side.offsetX, y+side.offsetY, z+side.offsetZ); + } /** * @return The perpendicular vector. @@ -300,6 +306,34 @@ public class Vector3 { return new Vector3(this.x, this.y, this.z); } + + @Override + public int hashCode() + { + int code = 1; + code = 31 * new Double(x).hashCode(); + code = 31 * new Double(y).hashCode(); + code = 31 * new Double(z).hashCode(); + return code; + } + + @Override + public boolean equals(Object obj) + { + if(!(obj instanceof Vector3)) + { + return false; + } + + Vector3 vec = (Vector3)obj; + + if(vec.x != x || vec.y != y || vec.z != z) + { + return false; + } + + return true; + } @Override public String toString() diff --git a/src/resonantinduction/battery/BatteryCache.java b/src/resonantinduction/battery/BatteryCache.java deleted file mode 100644 index 75f21963..00000000 --- a/src/resonantinduction/battery/BatteryCache.java +++ /dev/null @@ -1,6 +0,0 @@ -package resonantinduction.battery; - -public class BatteryCache -{ - -} diff --git a/src/resonantinduction/battery/BatteryMultiblockManager.java b/src/resonantinduction/battery/BatteryMultiblockManager.java new file mode 100644 index 00000000..b5e31a33 --- /dev/null +++ b/src/resonantinduction/battery/BatteryMultiblockManager.java @@ -0,0 +1,190 @@ +package resonantinduction.battery; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; +import resonantinduction.base.Vector3; +import cpw.mods.fml.common.ITickHandler; +import cpw.mods.fml.common.TickType; + +public class BatteryMultiblockManager implements ITickHandler +{ + public static final int WILDCARD = -1; + + public static Map dynamicInventories = new HashMap(); + + /** + * Grabs an inventory from the world's caches, and removes all the world's references to it. + * @param world - world the cache is stored in + * @param id - inventory ID to pull + * @return correct Dynamic Tank inventory cache + */ + public static BatteryCache pullInventory(World world, int id) + { + BatteryCache toReturn = dynamicInventories.get(id); + + for(Vector3 obj : dynamicInventories.get(id).locations) + { + TileEntityBattery tileEntity = (TileEntityBattery)obj.getTileEntity(world); + + if(tileEntity != null) + { + tileEntity.inventory = new HashSet(); + tileEntity.inventoryID = -1; + } + } + + dynamicInventories.remove(id); + + return toReturn; + } + + /** + * Updates a dynamic tank cache with the defined inventory ID with the parameterized values. + * @param inventoryID - inventory ID of the dynamic tank + * @param fluid - cached fluid of the dynamic tank + * @param inventory - inventory of the dynamic tank + * @param tileEntity - dynamic tank TileEntity + */ + public static void updateCache(int inventoryID, HashSet inventory, TileEntityBattery tileEntity) + { + if(!dynamicInventories.containsKey(inventoryID)) + { + BatteryCache cache = new BatteryCache(); + cache.inventory = inventory; + cache.dimensionId = tileEntity.worldObj.provider.dimensionId; + cache.locations.add(new Vector3(tileEntity)); + + dynamicInventories.put(inventoryID, cache); + + return; + } + + dynamicInventories.get(inventoryID).inventory = inventory; + + dynamicInventories.get(inventoryID).locations.add(new Vector3(tileEntity)); + } + + /** + * Grabs a unique inventory ID for a dynamic tank. + * @return unique inventory ID + */ + public static int getUniqueInventoryID() + { + int id = 0; + + while(true) + { + for(Integer i : dynamicInventories.keySet()) + { + if(id == i) + { + id++; + continue; + } + } + + return id; + } + } + + @Override + public void tickStart(EnumSet type, Object... tickData) + { + + } + + @Override + public void tickEnd(EnumSet type, Object... tickData) + { + if(tickData[0] instanceof World) + { + ArrayList idsToKill = new ArrayList(); + HashMap> tilesToKill = new HashMap>(); + + World world = (World)tickData[0]; + + if(!world.isRemote) + { + for(Map.Entry entry : dynamicInventories.entrySet()) + { + int inventoryID = entry.getKey(); + + if(entry.getValue().dimensionId == world.provider.dimensionId) + { + for(Vector3 obj : entry.getValue().locations) + { + TileEntityBattery tileEntity = (TileEntityBattery)obj.getTileEntity(world); + + if(tileEntity == null || tileEntity.inventoryID != inventoryID) + { + if(!tilesToKill.containsKey(inventoryID)) + { + tilesToKill.put(inventoryID, new HashSet()); + } + + tilesToKill.get(inventoryID).add(obj); + } + } + } + + if(entry.getValue().locations.isEmpty()) + { + idsToKill.add(inventoryID); + } + } + + for(Map.Entry> entry : tilesToKill.entrySet()) + { + for(Vector3 obj : entry.getValue()) + { + dynamicInventories.get(entry.getKey()).locations.remove(obj); + } + } + + for(int inventoryID : idsToKill) + { + for(Vector3 obj : dynamicInventories.get(inventoryID).locations) + { + TileEntityBattery dynamicTank = (TileEntityBattery)obj.getTileEntity(world); + + if(dynamicTank != null) + { + dynamicTank.inventory = new HashSet(); + dynamicTank.inventoryID = -1; + } + } + + dynamicInventories.remove(inventoryID); + } + } + } + } + + @Override + public EnumSet ticks() + { + return EnumSet.of(TickType.WORLD); + } + + @Override + public String getLabel() + { + return "BatteryMultiblockManager"; + } + + public static class BatteryCache + { + public Set inventory = new HashSet(); + public int dimensionId; + + public Set locations = new HashSet(); + } +} diff --git a/src/resonantinduction/battery/BatteryUpdateProtocol.java b/src/resonantinduction/battery/BatteryUpdateProtocol.java index 43bc2bb7..d3015d20 100644 --- a/src/resonantinduction/battery/BatteryUpdateProtocol.java +++ b/src/resonantinduction/battery/BatteryUpdateProtocol.java @@ -1,6 +1,278 @@ package resonantinduction.battery; +import java.util.HashSet; +import java.util.Set; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeDirection; +import resonantinduction.base.Vector3; +import resonantinduction.battery.BatteryMultiblockManager.BatteryCache; + public class BatteryUpdateProtocol { - + /** The battery nodes that have already been iterated over. */ + public Set iteratedNodes = new HashSet(); + + /** The structures found, all connected by some nodes to the pointer. */ + public SynchronizedBatteryData structureFound = null; + + /** The original block the calculation is getting run from. */ + public TileEntity pointer; + + public BatteryUpdateProtocol(TileEntity tileEntity) + { + pointer = tileEntity; + } + + /** + * Recursively loops through each node connected to the given TileEntity. + * @param tile - the TileEntity to loop over + */ + public void loopThrough(TileEntity tile) + { + World worldObj = tile.worldObj; + + int origX = tile.xCoord, origY = tile.yCoord, origZ = tile.zCoord; + + boolean isCorner = true; + boolean rightBlocks = 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; + + 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(!isViableNode(origX+x, origY+y, origZ+z)) + { + rightBlocks = false; + break; + } + else { + locations.add(new Vector3(tile).translate(new Vector3(x, y, z))); + } + } + if(!rightBlocks) + { + break; + } + } + if(!rightBlocks) + { + break; + } + } + } + + if(locations.size() >= 1 && locations.size() < 512) + { + if(rightBlocks && isCorner) + { + SynchronizedBatteryData structure = new SynchronizedBatteryData(); + structure.locations = locations; + structure.length = Math.abs(xmax-xmin)+1; + structure.height = Math.abs(ymax-ymin)+1; + structure.width = Math.abs(zmax-zmin)+1; + + if(structure.locations.contains(new Vector3(pointer))) + { + structureFound = structure; + return; + } + } + } + + iteratedNodes.add((TileEntityBattery)tile); + + for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) + { + TileEntity tileEntity = new Vector3(tile).getFromSide(side).getTileEntity(tile.worldObj); + + if(tileEntity instanceof TileEntityBattery) + { + if(!iteratedNodes.contains(tileEntity)) + { + loopThrough(tileEntity); + } + } + } + } + + /** + * 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.worldObj.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) + { + if(pointer.worldObj.getBlockTileEntity(x, y, z) instanceof TileEntityBattery) + { + return true; + } + + return false; + } + + /** + * Runs the protocol and updates all tanks that make a part of the dynamic tank. + */ + public void updateTanks() + { + loopThrough(pointer); + + if(structureFound != null) + { + for(TileEntityBattery tileEntity : iteratedNodes) + { + if(!structureFound.locations.contains(new Vector3(tileEntity))) + { + for(TileEntity tile : iteratedNodes) + { + ((TileEntityBattery)tileEntity).structure = null; + } + + return; + } + } + + int idFound = -1; + + for(Vector3 obj : structureFound.locations) + { + TileEntityBattery tileEntity = (TileEntityBattery)obj.getTileEntity(pointer.worldObj); + + if(tileEntity.inventoryID != -1) + { + idFound = tileEntity.inventoryID; + break; + } + } + + BatteryCache cache = new BatteryCache(); + + if(idFound != -1) + { + if(BatteryMultiblockManager.dynamicInventories.get(idFound) != null) + { + cache = BatteryMultiblockManager.pullInventory(pointer.worldObj, idFound); + } + } + else { + idFound = BatteryMultiblockManager.getUniqueInventoryID(); + } + + structureFound.inventory = cache.inventory; + + for(Vector3 obj : structureFound.locations) + { + TileEntityBattery tileEntity = (TileEntityBattery)obj.getTileEntity(pointer.worldObj); + + tileEntity.inventoryID = idFound; + tileEntity.structure = structureFound; + tileEntity.inventory = cache.inventory; + } + } + else { + for(TileEntity tileEntity : iteratedNodes) + { + ((TileEntityBattery)tileEntity).structure = null; + } + } + } } diff --git a/src/resonantinduction/battery/SynchronizedBatteryData.java b/src/resonantinduction/battery/SynchronizedBatteryData.java index fd1f05db..950eee49 100644 --- a/src/resonantinduction/battery/SynchronizedBatteryData.java +++ b/src/resonantinduction/battery/SynchronizedBatteryData.java @@ -1,12 +1,56 @@ package resonantinduction.battery; +import java.util.HashSet; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import resonantinduction.base.Vector3; + public class SynchronizedBatteryData { + public Set locations = new HashSet(); + + public Set inventory = new HashSet(); + public int length; public int width; public int height; - public int volume; + public boolean didTick; + + @Override + public int hashCode() + { + int code = 1; + code = 31 * locations.hashCode(); + code = 31 * length; + code = 31 * width; + code = 31 * height; + return code; + } + + @Override + public boolean equals(Object obj) + { + if(!(obj instanceof SynchronizedBatteryData)) + { + return false; + } + + SynchronizedBatteryData data = (SynchronizedBatteryData)obj; + + if(!data.locations.equals(locations)) + { + return false; + } + + if(data.length != length || data.width != width || data.height != height) + { + return false; + } + + return true; + } } diff --git a/src/resonantinduction/battery/TileEntityBattery.java b/src/resonantinduction/battery/TileEntityBattery.java index 3fb68b05..82f62da7 100644 --- a/src/resonantinduction/battery/TileEntityBattery.java +++ b/src/resonantinduction/battery/TileEntityBattery.java @@ -3,6 +3,9 @@ */ package resonantinduction.battery; +import java.util.HashSet; +import java.util.Set; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; @@ -14,10 +17,14 @@ import resonantinduction.base.TileEntityBase; * * @author Calclavia */ -public class TileEntityBattery extends TileEntityBase implements IInventory +public class TileEntityBattery extends TileEntityBase { - private ItemStack[] inventory = new ItemStack[4 * 4]; + public Set inventory = new HashSet(); private byte[] sideStatus = new byte[] { 0, 0, 0, 0, 0, 0 }; + + public SynchronizedBatteryData structure; + + public int inventoryID; @Override public void updateEntity() @@ -29,10 +36,8 @@ public class TileEntityBattery extends TileEntityBase implements IInventory { float max = 0; - for (int i = 0; i < this.getSizeInventory(); i++) + for (ItemStack itemStack : inventory) { - ItemStack itemStack = this.getStackInSlot(i); - if (itemStack != null) { if (itemStack.getItem() instanceof IBattery) @@ -44,134 +49,4 @@ public class TileEntityBattery extends TileEntityBase implements IInventory return max; } - - /** - * Returns the number of slots in the inventory. - */ - public int getSizeInventory() - { - return this.inventory.length; - } - - /** - * Returns the stack in slot i - */ - public ItemStack getStackInSlot(int par1) - { - return this.inventory[par1]; - } - - /** - * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and - * returns them in a new stack. - */ - public ItemStack decrStackSize(int par1, int par2) - { - if (this.inventory[par1] != null) - { - ItemStack itemstack; - - if (this.inventory[par1].stackSize <= par2) - { - itemstack = this.inventory[par1]; - this.inventory[par1] = null; - return itemstack; - } - else - { - itemstack = this.inventory[par1].splitStack(par2); - - if (this.inventory[par1].stackSize == 0) - { - this.inventory[par1] = null; - } - - return itemstack; - } - } - else - { - return null; - } - } - - /** - * When some containers are closed they call this on each slot, then drop whatever it returns as - * an EntityItem - like when you close a workbench GUI. - */ - public ItemStack getStackInSlotOnClosing(int par1) - { - if (this.inventory[par1] != null) - { - ItemStack itemstack = this.inventory[par1]; - this.inventory[par1] = null; - return itemstack; - } - else - { - return null; - } - } - - /** - * Sets the given item stack to the specified slot in the inventory (can be crafting or armor - * sections). - */ - public void setInventorySlotContents(int par1, ItemStack par2ItemStack) - { - this.inventory[par1] = par2ItemStack; - - if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit()) - { - par2ItemStack.stackSize = this.getInventoryStackLimit(); - } - } - - @Override - public String getInvName() - { - return this.getBlockType().getLocalizedName(); - } - - @Override - public boolean isInvNameLocalized() - { - return true; - } - - @Override - public int getInventoryStackLimit() - { - return 0; - } - - @Override - public boolean isUseableByPlayer(EntityPlayer entityplayer) - { - // TODO Auto-generated method stub - return false; - } - - /* - * (non-Javadoc) - * - * @see net.minecraft.inventory.IInventory#openChest() - */ - @Override - public void openChest() - { - - } - - @Override - public void closeChest() - { - - } - - @Override - public boolean isItemValidForSlot(int i, ItemStack itemstack) - { - return false; - } }