Format
This commit is contained in:
parent
6e0ec1d861
commit
4ac26ac086
137 changed files with 10339 additions and 7322 deletions
|
@ -5,41 +5,41 @@ import org.apache.logging.log4j.Level;
|
|||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
public class BeefCoreLog {
|
||||
|
||||
|
||||
private static final String CHANNEL = "BeefCore";
|
||||
|
||||
public static void log(Level level, String format, Object... data)
|
||||
{
|
||||
FMLLog.log(level, format, data);
|
||||
}
|
||||
public static void log(Level level, String format, Object... data)
|
||||
{
|
||||
FMLLog.log(level, format, data);
|
||||
}
|
||||
|
||||
public static void fatal(String format, Object... data)
|
||||
{
|
||||
log(Level.FATAL, format, data);
|
||||
}
|
||||
public static void fatal(String format, Object... data)
|
||||
{
|
||||
log(Level.FATAL, format, data);
|
||||
}
|
||||
|
||||
public static void error(String format, Object... data)
|
||||
{
|
||||
log(Level.ERROR, format, data);
|
||||
}
|
||||
public static void error(String format, Object... data)
|
||||
{
|
||||
log(Level.ERROR, format, data);
|
||||
}
|
||||
|
||||
public static void warning(String format, Object... data)
|
||||
{
|
||||
log(Level.WARN, format, data);
|
||||
}
|
||||
public static void warning(String format, Object... data)
|
||||
{
|
||||
log(Level.WARN, format, data);
|
||||
}
|
||||
|
||||
public static void info(String format, Object... data)
|
||||
{
|
||||
log(Level.INFO, format, data);
|
||||
}
|
||||
public static void info(String format, Object... data)
|
||||
{
|
||||
log(Level.INFO, format, data);
|
||||
}
|
||||
|
||||
public static void debug(String format, Object... data)
|
||||
{
|
||||
log(Level.DEBUG, format, data);
|
||||
}
|
||||
public static void debug(String format, Object... data)
|
||||
{
|
||||
log(Level.DEBUG, format, data);
|
||||
}
|
||||
|
||||
public static void trace(String format, Object... data)
|
||||
{
|
||||
log(Level.TRACE, format, data);
|
||||
}
|
||||
public static void trace(String format, Object... data)
|
||||
{
|
||||
log(Level.TRACE, format, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,41 +9,60 @@ import net.minecraftforge.common.util.ForgeDirection;
|
|||
public class CoordTriplet implements Comparable {
|
||||
public int x, y, z;
|
||||
|
||||
public CoordTriplet(int x, int y, int z) {
|
||||
public CoordTriplet(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public int getChunkX() { return x >> 4; }
|
||||
public int getChunkZ() { return z >> 4; }
|
||||
public long getChunkXZHash() { return ChunkCoordIntPair.chunkXZ2Int(x >> 4, z >> 4); }
|
||||
|
||||
|
||||
public int getChunkX()
|
||||
{
|
||||
return x >> 4;
|
||||
}
|
||||
|
||||
public int getChunkZ()
|
||||
{
|
||||
return z >> 4;
|
||||
}
|
||||
|
||||
public long getChunkXZHash()
|
||||
{
|
||||
return ChunkCoordIntPair.chunkXZ2Int(x >> 4, z >> 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if(other == null)
|
||||
{ return false; }
|
||||
else if(other instanceof CoordTriplet) {
|
||||
CoordTriplet otherTriplet = (CoordTriplet)other;
|
||||
return this.x == otherTriplet.x && this.y == otherTriplet.y && this.z == otherTriplet.z;
|
||||
}
|
||||
else {
|
||||
public boolean equals(Object other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
} else if (other instanceof CoordTriplet)
|
||||
{
|
||||
CoordTriplet otherTriplet = (CoordTriplet) other;
|
||||
return this.x == otherTriplet.x && this.y == otherTriplet.y
|
||||
&& this.z == otherTriplet.z;
|
||||
} else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void translate(ForgeDirection dir) {
|
||||
|
||||
public void translate(ForgeDirection dir)
|
||||
{
|
||||
this.x += dir.offsetX;
|
||||
this.y += dir.offsetY;
|
||||
this.z += dir.offsetZ;
|
||||
}
|
||||
|
||||
public boolean equals(int x, int y, int z) {
|
||||
|
||||
public boolean equals(int x, int y, int z)
|
||||
{
|
||||
return this.x == x && this.y == y && this.z == z;
|
||||
}
|
||||
|
||||
|
||||
// Suggested implementation from NetBeans 7.1
|
||||
public int hashCode() {
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 71 * hash + this.x;
|
||||
hash = 71 * hash + this.y;
|
||||
|
@ -51,78 +70,144 @@ public class CoordTriplet implements Comparable {
|
|||
return hash;
|
||||
}
|
||||
|
||||
public CoordTriplet copy() {
|
||||
public CoordTriplet copy()
|
||||
{
|
||||
return new CoordTriplet(x, y, z);
|
||||
}
|
||||
|
||||
public void copy(CoordTriplet other) {
|
||||
|
||||
public void copy(CoordTriplet other)
|
||||
{
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
this.z = other.z;
|
||||
}
|
||||
|
||||
public CoordTriplet[] getNeighbors() {
|
||||
return new CoordTriplet[]{
|
||||
new CoordTriplet(x + 1, y, z),
|
||||
new CoordTriplet(x - 1, y, z),
|
||||
new CoordTriplet(x, y + 1, z),
|
||||
new CoordTriplet(x, y - 1, z),
|
||||
new CoordTriplet(x, y, z + 1),
|
||||
new CoordTriplet(x, y, z - 1)
|
||||
};
|
||||
public CoordTriplet[] getNeighbors()
|
||||
{
|
||||
return new CoordTriplet[]
|
||||
{ new CoordTriplet(x + 1, y, z), new CoordTriplet(x - 1, y, z),
|
||||
new CoordTriplet(x, y + 1, z), new CoordTriplet(x, y - 1, z),
|
||||
new CoordTriplet(x, y, z + 1), new CoordTriplet(x, y, z - 1) };
|
||||
}
|
||||
|
||||
///// IComparable
|
||||
|
||||
|
||||
// /// IComparable
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
if(o instanceof CoordTriplet) {
|
||||
CoordTriplet other = (CoordTriplet)o;
|
||||
if(this.x < other.x) { return -1; }
|
||||
else if(this.x > other.x) { return 1; }
|
||||
else if(this.y < other.y) { return -1; }
|
||||
else if(this.y > other.y) { return 1; }
|
||||
else if(this.z < other.z) { return -1; }
|
||||
else if(this.z > other.z) { return 1; }
|
||||
else { return 0; }
|
||||
public int compareTo(Object o)
|
||||
{
|
||||
if (o instanceof CoordTriplet)
|
||||
{
|
||||
CoordTriplet other = (CoordTriplet) o;
|
||||
if (this.x < other.x)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.x > other.x)
|
||||
{
|
||||
return 1;
|
||||
} else if (this.y < other.y)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.y > other.y)
|
||||
{
|
||||
return 1;
|
||||
} else if (this.z < other.z)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.z > other.z)
|
||||
{
|
||||
return 1;
|
||||
} else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
///// Really confusing code that should be cleaned up
|
||||
|
||||
public ForgeDirection getDirectionFromSourceCoords(int x, int y, int z) {
|
||||
if(this.x < x) { return ForgeDirection.WEST; }
|
||||
else if(this.x > x) { return ForgeDirection.EAST; }
|
||||
else if(this.y < y) { return ForgeDirection.DOWN; }
|
||||
else if(this.y > y) { return ForgeDirection.UP; }
|
||||
else if(this.z < z) { return ForgeDirection.SOUTH; }
|
||||
else if(this.z > z) { return ForgeDirection.NORTH; }
|
||||
else { return ForgeDirection.UNKNOWN; }
|
||||
|
||||
// /// Really confusing code that should be cleaned up
|
||||
|
||||
public ForgeDirection getDirectionFromSourceCoords(int x, int y, int z)
|
||||
{
|
||||
if (this.x < x)
|
||||
{
|
||||
return ForgeDirection.WEST;
|
||||
} else if (this.x > x)
|
||||
{
|
||||
return ForgeDirection.EAST;
|
||||
} else if (this.y < y)
|
||||
{
|
||||
return ForgeDirection.DOWN;
|
||||
} else if (this.y > y)
|
||||
{
|
||||
return ForgeDirection.UP;
|
||||
} else if (this.z < z)
|
||||
{
|
||||
return ForgeDirection.SOUTH;
|
||||
} else if (this.z > z)
|
||||
{
|
||||
return ForgeDirection.NORTH;
|
||||
} else
|
||||
{
|
||||
return ForgeDirection.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public ForgeDirection getOppositeDirectionFromSourceCoords(int x, int y, int z) {
|
||||
if(this.x < x) { return ForgeDirection.EAST; }
|
||||
else if(this.x > x) { return ForgeDirection.WEST; }
|
||||
else if(this.y < y) { return ForgeDirection.UP; }
|
||||
else if(this.y > y) { return ForgeDirection.DOWN; }
|
||||
else if(this.z < z) { return ForgeDirection.NORTH; }
|
||||
else if(this.z > z) { return ForgeDirection.SOUTH; }
|
||||
else { return ForgeDirection.UNKNOWN; }
|
||||
|
||||
public ForgeDirection getOppositeDirectionFromSourceCoords(int x, int y,
|
||||
int z)
|
||||
{
|
||||
if (this.x < x)
|
||||
{
|
||||
return ForgeDirection.EAST;
|
||||
} else if (this.x > x)
|
||||
{
|
||||
return ForgeDirection.WEST;
|
||||
} else if (this.y < y)
|
||||
{
|
||||
return ForgeDirection.UP;
|
||||
} else if (this.y > y)
|
||||
{
|
||||
return ForgeDirection.DOWN;
|
||||
} else if (this.z < z)
|
||||
{
|
||||
return ForgeDirection.NORTH;
|
||||
} else if (this.z > z)
|
||||
{
|
||||
return ForgeDirection.SOUTH;
|
||||
} else
|
||||
{
|
||||
return ForgeDirection.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String toString()
|
||||
{
|
||||
return String.format("(%d, %d, %d)", this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
public int compareTo(int xCoord, int yCoord, int zCoord) {
|
||||
if(this.x < xCoord) { return -1; }
|
||||
else if(this.x > xCoord) { return 1; }
|
||||
else if(this.y < yCoord) { return -1; }
|
||||
else if(this.y > yCoord) { return 1; }
|
||||
else if(this.z < zCoord) { return -1; }
|
||||
else if(this.z > zCoord) { return 1; }
|
||||
else { return 0; }
|
||||
public int compareTo(int xCoord, int yCoord, int zCoord)
|
||||
{
|
||||
if (this.x < xCoord)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.x > xCoord)
|
||||
{
|
||||
return 1;
|
||||
} else if (this.y < yCoord)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.y > yCoord)
|
||||
{
|
||||
return 1;
|
||||
} else if (this.z < zCoord)
|
||||
{
|
||||
return -1;
|
||||
} else if (this.z > zCoord)
|
||||
{
|
||||
return 1;
|
||||
} else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ import net.minecraft.block.material.Material;
|
|||
*/
|
||||
public abstract class BlockMultiblockBase extends BlockContainer {
|
||||
|
||||
protected BlockMultiblockBase(Material material) {
|
||||
protected BlockMultiblockBase(Material material)
|
||||
{
|
||||
super(material);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,106 +7,132 @@ import net.minecraft.tileentity.TileEntity;
|
|||
import erogenousbeef.coreTR.common.CoordTriplet;
|
||||
|
||||
/**
|
||||
* Basic interface for a multiblock machine part. This is defined as an abstract class
|
||||
* as we need the basic functionality of a TileEntity as well.
|
||||
* Preferably, you should derive from MultiblockTileEntityBase,
|
||||
* which does all the hard work for you.
|
||||
* Basic interface for a multiblock machine part. This is defined as an abstract
|
||||
* class as we need the basic functionality of a TileEntity as well. Preferably,
|
||||
* you should derive from MultiblockTileEntityBase, which does all the hard work
|
||||
* for you.
|
||||
*
|
||||
* {@link erogenousbeef.coreTR.multiblock.MultiblockTileEntityBase}
|
||||
*/
|
||||
public abstract class IMultiblockPart extends TileEntity {
|
||||
public static final int INVALID_DISTANCE = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
* @return True if this block is connected to a multiblock controller. False otherwise.
|
||||
* @return True if this block is connected to a multiblock controller. False
|
||||
* otherwise.
|
||||
*/
|
||||
public abstract boolean isConnected();
|
||||
|
||||
|
||||
/**
|
||||
* @return The attached multiblock controller for this tile entity.
|
||||
* @return The attached multiblock controller for this tile entity.
|
||||
*/
|
||||
public abstract MultiblockControllerBase getMultiblockController();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the location of this tile entity in the world, in CoordTriplet form.
|
||||
* @return A CoordTriplet with its x,y,z members set to the location of this tile entity in the world.
|
||||
* Returns the location of this tile entity in the world, in CoordTriplet
|
||||
* form.
|
||||
*
|
||||
* @return A CoordTriplet with its x,y,z members set to the location of this
|
||||
* tile entity in the world.
|
||||
*/
|
||||
public abstract CoordTriplet getWorldLocation();
|
||||
|
||||
|
||||
// Multiblock connection-logic callbacks
|
||||
|
||||
|
||||
/**
|
||||
* Called after this block has been attached to a new multiblock controller.
|
||||
* @param newController The new multiblock controller to which this tile entity is attached.
|
||||
*
|
||||
* @param newController
|
||||
* The new multiblock controller to which this tile entity is
|
||||
* attached.
|
||||
*/
|
||||
public abstract void onAttached(MultiblockControllerBase newController);
|
||||
|
||||
|
||||
/**
|
||||
* Called after this block has been detached from a multiblock controller.
|
||||
* @param multiblockController The multiblock controller that no longer controls this tile entity.
|
||||
*
|
||||
* @param multiblockController
|
||||
* The multiblock controller that no longer controls this tile
|
||||
* entity.
|
||||
*/
|
||||
public abstract void onDetached(MultiblockControllerBase multiblockController);
|
||||
|
||||
public abstract void onDetached(
|
||||
MultiblockControllerBase multiblockController);
|
||||
|
||||
/**
|
||||
* Called when this block is being orphaned. Use this to copy game-data values that
|
||||
* should persist despite a machine being broken.
|
||||
* This should NOT mark the part as disconnected. onDetached will be called immediately afterwards.
|
||||
* Called when this block is being orphaned. Use this to copy game-data
|
||||
* values that should persist despite a machine being broken. This should
|
||||
* NOT mark the part as disconnected. onDetached will be called immediately
|
||||
* afterwards.
|
||||
*
|
||||
* @see #onDetached(MultiblockControllerBase)
|
||||
* @param oldController The controller which is orphaning this block.
|
||||
* @param oldControllerSize The number of connected blocks in the controller prior to shedding orphans.
|
||||
* @param newControllerSize The number of connected blocks in the controller after shedding orphans.
|
||||
* @param oldController
|
||||
* The controller which is orphaning this block.
|
||||
* @param oldControllerSize
|
||||
* The number of connected blocks in the controller prior to
|
||||
* shedding orphans.
|
||||
* @param newControllerSize
|
||||
* The number of connected blocks in the controller after
|
||||
* shedding orphans.
|
||||
*/
|
||||
public abstract void onOrphaned(MultiblockControllerBase oldController, int oldControllerSize, int newControllerSize);
|
||||
|
||||
public abstract void onOrphaned(MultiblockControllerBase oldController,
|
||||
int oldControllerSize, int newControllerSize);
|
||||
|
||||
// Multiblock fuse/split helper methods. Here there be dragons.
|
||||
/**
|
||||
* Factory method. Creates a new multiblock controller and returns it.
|
||||
* Does not attach this tile entity to it.
|
||||
* Override this in your game code!
|
||||
* @return A new Multiblock Controller, derived from MultiblockControllerBase.
|
||||
* Factory method. Creates a new multiblock controller and returns it. Does
|
||||
* not attach this tile entity to it. Override this in your game code!
|
||||
*
|
||||
* @return A new Multiblock Controller, derived from
|
||||
* MultiblockControllerBase.
|
||||
*/
|
||||
public abstract MultiblockControllerBase createNewMultiblock();
|
||||
|
||||
/**
|
||||
* Retrieve the type of multiblock controller which governs this part.
|
||||
* Used to ensure that incompatible multiblocks are not merged.
|
||||
* @return The class/type of the multiblock controller which governs this type of part.
|
||||
* Retrieve the type of multiblock controller which governs this part. Used
|
||||
* to ensure that incompatible multiblocks are not merged.
|
||||
*
|
||||
* @return The class/type of the multiblock controller which governs this
|
||||
* type of part.
|
||||
*/
|
||||
public abstract Class<? extends MultiblockControllerBase> getMultiblockControllerType();
|
||||
|
||||
|
||||
/**
|
||||
* Called when this block is moved from its current controller into a new controller.
|
||||
* A special case of attach/detach, done here for efficiency to avoid triggering
|
||||
* lots of recalculation logic.
|
||||
* @param newController The new controller into which this tile entity is being merged.
|
||||
* Called when this block is moved from its current controller into a new
|
||||
* controller. A special case of attach/detach, done here for efficiency to
|
||||
* avoid triggering lots of recalculation logic.
|
||||
*
|
||||
* @param newController
|
||||
* The new controller into which this tile entity is being
|
||||
* merged.
|
||||
*/
|
||||
public abstract void onAssimilated(MultiblockControllerBase newController);
|
||||
|
||||
// Multiblock connection data access.
|
||||
// You generally shouldn't toy with these!
|
||||
// They're for use by Multiblock Controllers.
|
||||
|
||||
|
||||
/**
|
||||
* Set that this block has been visited by your validation algorithms.
|
||||
*/
|
||||
public abstract void setVisited();
|
||||
|
||||
|
||||
/**
|
||||
* Set that this block has not been visited by your validation algorithms;
|
||||
*/
|
||||
public abstract void setUnvisited();
|
||||
|
||||
|
||||
/**
|
||||
* @return True if this block has been visited by your validation algorithms since the last reset.
|
||||
* @return True if this block has been visited by your validation algorithms
|
||||
* since the last reset.
|
||||
*/
|
||||
public abstract boolean isVisited();
|
||||
|
||||
|
||||
/**
|
||||
* Called when this block becomes the designated block for saving data and
|
||||
* transmitting data across the wire.
|
||||
*/
|
||||
public abstract void becomeMultiblockSaveDelegate();
|
||||
|
||||
|
||||
/**
|
||||
* Called when this block is no longer the designated block for saving data
|
||||
* and transmitting data across the wire.
|
||||
|
@ -116,60 +142,72 @@ public abstract class IMultiblockPart extends TileEntity {
|
|||
/**
|
||||
* Is this block the designated save/load & network delegate?
|
||||
*/
|
||||
public abstract boolean isMultiblockSaveDelegate();
|
||||
public abstract boolean isMultiblockSaveDelegate();
|
||||
|
||||
/**
|
||||
* Returns an array containing references to neighboring IMultiblockPart tile entities.
|
||||
* Primarily a utility method. Only works after tileentity construction, so it cannot be used in
|
||||
* Returns an array containing references to neighboring IMultiblockPart
|
||||
* tile entities. Primarily a utility method. Only works after tileentity
|
||||
* construction, so it cannot be used in
|
||||
* MultiblockControllerBase::attachBlock.
|
||||
*
|
||||
* This method is chunk-safe on the server; it will not query for parts in chunks that are unloaded.
|
||||
* Note that no method is chunk-safe on the client, because ChunkProviderClient is stupid.
|
||||
* @return An array of references to neighboring IMultiblockPart tile entities.
|
||||
* This method is chunk-safe on the server; it will not query for parts in
|
||||
* chunks that are unloaded. Note that no method is chunk-safe on the
|
||||
* client, because ChunkProviderClient is stupid.
|
||||
*
|
||||
* @return An array of references to neighboring IMultiblockPart tile
|
||||
* entities.
|
||||
*/
|
||||
public abstract IMultiblockPart[] getNeighboringParts();
|
||||
|
||||
// Multiblock business-logic callbacks - implement these!
|
||||
/**
|
||||
* Called when a machine is fully assembled from the disassembled state, meaning
|
||||
* it was broken by a player/entity action, not by chunk unloads.
|
||||
* Note that, for non-square machines, the min/max coordinates may not actually be part
|
||||
* of the machine! They form an outer bounding box for the whole machine itself.
|
||||
* @param multiblockControllerBase The controller to which this part is being assembled.
|
||||
* Called when a machine is fully assembled from the disassembled state,
|
||||
* meaning it was broken by a player/entity action, not by chunk unloads.
|
||||
* Note that, for non-square machines, the min/max coordinates may not
|
||||
* actually be part of the machine! They form an outer bounding box for the
|
||||
* whole machine itself.
|
||||
*
|
||||
* @param multiblockControllerBase
|
||||
* The controller to which this part is being assembled.
|
||||
*/
|
||||
public abstract void onMachineAssembled(MultiblockControllerBase multiblockControllerBase);
|
||||
|
||||
public abstract void onMachineAssembled(
|
||||
MultiblockControllerBase multiblockControllerBase);
|
||||
|
||||
/**
|
||||
* Called when the machine is broken for game reasons, e.g. a player removed a block
|
||||
* or an explosion occurred.
|
||||
* Called when the machine is broken for game reasons, e.g. a player removed
|
||||
* a block or an explosion occurred.
|
||||
*/
|
||||
public abstract void onMachineBroken();
|
||||
|
||||
|
||||
/**
|
||||
* Called when the user activates the machine. This is not called by default, but is included
|
||||
* as most machines have this game-logical concept.
|
||||
* Called when the user activates the machine. This is not called by
|
||||
* default, but is included as most machines have this game-logical concept.
|
||||
*/
|
||||
public abstract void onMachineActivated();
|
||||
|
||||
/**
|
||||
* Called when the user deactivates the machine. This is not called by default, but is included
|
||||
* as most machines have this game-logical concept.
|
||||
* Called when the user deactivates the machine. This is not called by
|
||||
* default, but is included as most machines have this game-logical concept.
|
||||
*/
|
||||
public abstract void onMachineDeactivated();
|
||||
|
||||
// Block events
|
||||
/**
|
||||
* Called when this part should check its neighbors.
|
||||
* This method MUST NOT cause additional chunks to load.
|
||||
* ALWAYS check to see if a chunk is loaded before querying for its tile entity
|
||||
* This part should inform the controller that it is attaching at this time.
|
||||
* @return A Set of multiblock controllers to which this object would like to attach. It should have attached to one of the controllers in this list. Return null if there are no compatible controllers nearby.
|
||||
* Called when this part should check its neighbors. This method MUST NOT
|
||||
* cause additional chunks to load. ALWAYS check to see if a chunk is loaded
|
||||
* before querying for its tile entity This part should inform the
|
||||
* controller that it is attaching at this time.
|
||||
*
|
||||
* @return A Set of multiblock controllers to which this object would like
|
||||
* to attach. It should have attached to one of the controllers in
|
||||
* this list. Return null if there are no compatible controllers
|
||||
* nearby.
|
||||
*/
|
||||
public abstract Set<MultiblockControllerBase> attachToNeighbors();
|
||||
|
||||
/**
|
||||
* Assert that this part is detached. If not, log a warning and set the part's controller to null.
|
||||
* Do NOT fire the full disconnection logic.
|
||||
* Assert that this part is detached. If not, log a warning and set the
|
||||
* part's controller to null. Do NOT fire the full disconnection logic.
|
||||
*/
|
||||
public abstract void assertDetached();
|
||||
|
||||
|
@ -177,15 +215,17 @@ public abstract class IMultiblockPart extends TileEntity {
|
|||
* @return True if a part has multiblock game-data saved inside it.
|
||||
*/
|
||||
public abstract boolean hasMultiblockSaveData();
|
||||
|
||||
|
||||
/**
|
||||
* @return The part's saved multiblock game-data in NBT format, or null if there isn't any.
|
||||
* @return The part's saved multiblock game-data in NBT format, or null if
|
||||
* there isn't any.
|
||||
*/
|
||||
public abstract NBTTagCompound getMultiblockSaveData();
|
||||
|
||||
/**
|
||||
* Called after a block is added and the controller has incorporated the part's saved
|
||||
* multiblock game-data into itself. Generally, you should clear the saved data here.
|
||||
* Called after a block is added and the controller has incorporated the
|
||||
* part's saved multiblock game-data into itself. Generally, you should
|
||||
* clear the saved data here.
|
||||
*/
|
||||
public abstract void onMultiblockDataAssimilated();
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import cpw.mods.fml.common.gameevent.TickEvent;
|
|||
|
||||
public class MultiblockClientTickHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onClientTick(TickEvent.ClientTickEvent event) {
|
||||
if(event.phase == TickEvent.Phase.START) {
|
||||
MultiblockRegistry.tickStart(Minecraft.getMinecraft().theWorld);
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void onClientTick(TickEvent.ClientTickEvent event)
|
||||
{
|
||||
if (event.phase == TickEvent.Phase.START)
|
||||
{
|
||||
MultiblockRegistry.tickStart(Minecraft.getMinecraft().theWorld);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,22 +8,25 @@ import cpw.mods.fml.common.eventhandler.EventPriority;
|
|||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
/**
|
||||
* In your mod, subscribe this on both the client and server sides side to handle chunk
|
||||
* load events for your multiblock machines.
|
||||
* Chunks can load asynchronously in environments like MCPC+, so we cannot
|
||||
* process any blocks that are in chunks which are still loading.
|
||||
* In your mod, subscribe this on both the client and server sides side to
|
||||
* handle chunk load events for your multiblock machines. Chunks can load
|
||||
* asynchronously in environments like MCPC+, so we cannot process any blocks
|
||||
* that are in chunks which are still loading.
|
||||
*/
|
||||
public class MultiblockEventHandler {
|
||||
@SubscribeEvent(priority = EventPriority.NORMAL)
|
||||
public void onChunkLoad(ChunkEvent.Load loadEvent) {
|
||||
public void onChunkLoad(ChunkEvent.Load loadEvent)
|
||||
{
|
||||
Chunk chunk = loadEvent.getChunk();
|
||||
World world = loadEvent.world;
|
||||
MultiblockRegistry.onChunkLoaded(world, chunk.xPosition, chunk.zPosition);
|
||||
MultiblockRegistry.onChunkLoaded(world, chunk.xPosition,
|
||||
chunk.zPosition);
|
||||
}
|
||||
|
||||
// Cleanup, for nice memory usageness
|
||||
@SubscribeEvent(priority = EventPriority.NORMAL)
|
||||
public void onWorldUnload(WorldEvent.Unload unloadWorldEvent) {
|
||||
public void onWorldUnload(WorldEvent.Unload unloadWorldEvent)
|
||||
{
|
||||
MultiblockRegistry.onWorldUnloaded(unloadWorldEvent.world);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,122 +7,170 @@ import net.minecraft.world.World;
|
|||
import erogenousbeef.coreTR.common.BeefCoreLog;
|
||||
|
||||
/**
|
||||
* This is a very static singleton registry class which directs incoming events to sub-objects, which
|
||||
* actually manage each individual world's multiblocks.
|
||||
* This is a very static singleton registry class which directs incoming events
|
||||
* to sub-objects, which actually manage each individual world's multiblocks.
|
||||
*
|
||||
* @author Erogenous Beef
|
||||
*/
|
||||
public class MultiblockRegistry {
|
||||
// World > WorldRegistry map
|
||||
private static HashMap<World, MultiblockWorldRegistry> registries = new HashMap<World, MultiblockWorldRegistry>();
|
||||
|
||||
|
||||
/**
|
||||
* Called before Tile Entities are ticked in the world. Do bookkeeping here.
|
||||
* @param world The world being ticked
|
||||
*
|
||||
* @param world
|
||||
* The world being ticked
|
||||
*/
|
||||
public static void tickStart(World world) {
|
||||
if(registries.containsKey(world)) {
|
||||
public static void tickStart(World world)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
MultiblockWorldRegistry registry = registries.get(world);
|
||||
registry.processMultiblockChanges();
|
||||
registry.tickStart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the world has finished loading a chunk.
|
||||
* @param world The world which has finished loading a chunk
|
||||
* @param chunkX The X coordinate of the chunk
|
||||
* @param chunkZ The Z coordinate of the chunk
|
||||
*
|
||||
* @param world
|
||||
* The world which has finished loading a chunk
|
||||
* @param chunkX
|
||||
* The X coordinate of the chunk
|
||||
* @param chunkZ
|
||||
* The Z coordinate of the chunk
|
||||
*/
|
||||
public static void onChunkLoaded(World world, int chunkX, int chunkZ) {
|
||||
if(registries.containsKey(world)) {
|
||||
public static void onChunkLoaded(World world, int chunkX, int chunkZ)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
registries.get(world).onChunkLoaded(chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new part in the system. The part has been created either through user action or via a chunk loading.
|
||||
* @param world The world into which this part is loading.
|
||||
* @param part The part being loaded.
|
||||
* Register a new part in the system. The part has been created either
|
||||
* through user action or via a chunk loading.
|
||||
*
|
||||
* @param world
|
||||
* The world into which this part is loading.
|
||||
* @param part
|
||||
* The part being loaded.
|
||||
*/
|
||||
public static void onPartAdded(World world, IMultiblockPart part) {
|
||||
public static void onPartAdded(World world, IMultiblockPart part)
|
||||
{
|
||||
MultiblockWorldRegistry registry = getOrCreateRegistry(world);
|
||||
registry.onPartAdded(part);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to remove a part from world lists.
|
||||
* @param world The world from which a multiblock part is being removed.
|
||||
* @param part The part being removed.
|
||||
*
|
||||
* @param world
|
||||
* The world from which a multiblock part is being removed.
|
||||
* @param part
|
||||
* The part being removed.
|
||||
*/
|
||||
public static void onPartRemovedFromWorld(World world, IMultiblockPart part) {
|
||||
if(registries.containsKey(world)) {
|
||||
public static void onPartRemovedFromWorld(World world, IMultiblockPart part)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
registries.get(world).onPartRemovedFromWorld(part);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called whenever a world is unloaded. Unload the relevant registry, if we have one.
|
||||
* @param world The world being unloaded.
|
||||
* Called whenever a world is unloaded. Unload the relevant registry, if we
|
||||
* have one.
|
||||
*
|
||||
* @param world
|
||||
* The world being unloaded.
|
||||
*/
|
||||
public static void onWorldUnloaded(World world) {
|
||||
if(registries.containsKey(world)) {
|
||||
public static void onWorldUnloaded(World world)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
registries.get(world).onWorldUnloaded();
|
||||
registries.remove(world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to mark a controller as dirty. Dirty means that parts have
|
||||
* been added or removed this tick.
|
||||
* @param world The world containing the multiblock
|
||||
* @param controller The dirty controller
|
||||
* Call to mark a controller as dirty. Dirty means that parts have been
|
||||
* added or removed this tick.
|
||||
*
|
||||
* @param world
|
||||
* The world containing the multiblock
|
||||
* @param controller
|
||||
* The dirty controller
|
||||
*/
|
||||
public static void addDirtyController(World world,
|
||||
MultiblockControllerBase controller) {
|
||||
if(registries.containsKey(world)) {
|
||||
MultiblockControllerBase controller)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
registries.get(world).addDirtyController(controller);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Adding a dirty controller to a world that has no registered controllers!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to mark a controller as dead. It should only be marked as dead
|
||||
* when it has no connected parts. It will be removed after the next world tick.
|
||||
* @param world The world formerly containing the multiblock
|
||||
* @param controller The dead controller
|
||||
*/
|
||||
public static void addDeadController(World world, MultiblockControllerBase controller) {
|
||||
if(registries.containsKey(world)) {
|
||||
registries.get(world).addDeadController(controller);
|
||||
}
|
||||
else {
|
||||
BeefCoreLog.warning("Controller %d in world %s marked as dead, but that world is not tracked! Controller is being ignored.", controller.hashCode(), world);
|
||||
} else
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Adding a dirty controller to a world that has no registered controllers!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world The world whose controllers you wish to retrieve.
|
||||
* @return An unmodifiable set of controllers active in the given world, or null if there are none.
|
||||
* Call to mark a controller as dead. It should only be marked as dead when
|
||||
* it has no connected parts. It will be removed after the next world tick.
|
||||
*
|
||||
* @param world
|
||||
* The world formerly containing the multiblock
|
||||
* @param controller
|
||||
* The dead controller
|
||||
*/
|
||||
public static Set<MultiblockControllerBase> getControllersFromWorld(World world) {
|
||||
if(registries.containsKey(world)) {
|
||||
public static void addDeadController(World world,
|
||||
MultiblockControllerBase controller)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
registries.get(world).addDeadController(controller);
|
||||
} else
|
||||
{
|
||||
BeefCoreLog
|
||||
.warning(
|
||||
"Controller %d in world %s marked as dead, but that world is not tracked! Controller is being ignored.",
|
||||
controller.hashCode(), world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param world
|
||||
* The world whose controllers you wish to retrieve.
|
||||
* @return An unmodifiable set of controllers active in the given world, or
|
||||
* null if there are none.
|
||||
*/
|
||||
public static Set<MultiblockControllerBase> getControllersFromWorld(
|
||||
World world)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
return registries.get(world).getControllers();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// *** PRIVATE HELPERS *** ///
|
||||
|
||||
private static MultiblockWorldRegistry getOrCreateRegistry(World world) {
|
||||
if(registries.containsKey(world)) {
|
||||
|
||||
// / *** PRIVATE HELPERS *** ///
|
||||
|
||||
private static MultiblockWorldRegistry getOrCreateRegistry(World world)
|
||||
{
|
||||
if (registries.containsKey(world))
|
||||
{
|
||||
return registries.get(world);
|
||||
}
|
||||
else {
|
||||
MultiblockWorldRegistry newRegistry = new MultiblockWorldRegistry(world);
|
||||
} else
|
||||
{
|
||||
MultiblockWorldRegistry newRegistry = new MultiblockWorldRegistry(
|
||||
world);
|
||||
registries.put(world, newRegistry);
|
||||
return newRegistry;
|
||||
}
|
||||
|
|
|
@ -4,20 +4,21 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
|||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
|
||||
/**
|
||||
* This is a generic multiblock tick handler. If you are using this code on your own,
|
||||
* you will need to register this with the Forge TickRegistry on both the
|
||||
* client AND server sides.
|
||||
* Note that different types of ticks run on different parts of the system.
|
||||
* CLIENT ticks only run on the client, at the start/end of each game loop.
|
||||
* SERVER and WORLD ticks only run on the server.
|
||||
* WORLDLOAD ticks run only on the server, and only when worlds are loaded.
|
||||
* This is a generic multiblock tick handler. If you are using this code on your
|
||||
* own, you will need to register this with the Forge TickRegistry on both the
|
||||
* client AND server sides. Note that different types of ticks run on different
|
||||
* parts of the system. CLIENT ticks only run on the client, at the start/end of
|
||||
* each game loop. SERVER and WORLD ticks only run on the server. WORLDLOAD
|
||||
* ticks run only on the server, and only when worlds are loaded.
|
||||
*/
|
||||
public class MultiblockServerTickHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldTick(TickEvent.WorldTickEvent event) {
|
||||
if(event.phase == TickEvent.Phase.START) {
|
||||
MultiblockRegistry.tickStart(event.world);
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void onWorldTick(TickEvent.WorldTickEvent event)
|
||||
{
|
||||
if (event.phase == TickEvent.Phase.START)
|
||||
{
|
||||
MultiblockRegistry.tickStart(event.world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,18 +15,20 @@ import erogenousbeef.coreTR.common.BeefCoreLog;
|
|||
import erogenousbeef.coreTR.common.CoordTriplet;
|
||||
|
||||
/**
|
||||
* Base logic class for Multiblock-connected tile entities. Most multiblock machines
|
||||
* should derive from this and implement their game logic in certain abstract methods.
|
||||
* Base logic class for Multiblock-connected tile entities. Most multiblock
|
||||
* machines should derive from this and implement their game logic in certain
|
||||
* abstract methods.
|
||||
*/
|
||||
public abstract class MultiblockTileEntityBase extends IMultiblockPart {
|
||||
private MultiblockControllerBase controller;
|
||||
private boolean visited;
|
||||
|
||||
|
||||
private boolean saveMultiblockData;
|
||||
private NBTTagCompound cachedMultiblockData;
|
||||
private boolean paused;
|
||||
|
||||
public MultiblockTileEntityBase() {
|
||||
public MultiblockTileEntityBase()
|
||||
{
|
||||
super();
|
||||
controller = null;
|
||||
visited = false;
|
||||
|
@ -35,36 +37,45 @@ public abstract class MultiblockTileEntityBase extends IMultiblockPart {
|
|||
cachedMultiblockData = null;
|
||||
}
|
||||
|
||||
///// Multiblock Connection Base Logic
|
||||
// /// Multiblock Connection Base Logic
|
||||
@Override
|
||||
public Set<MultiblockControllerBase> attachToNeighbors() {
|
||||
public Set<MultiblockControllerBase> attachToNeighbors()
|
||||
{
|
||||
Set<MultiblockControllerBase> controllers = null;
|
||||
MultiblockControllerBase bestController = null;
|
||||
|
||||
|
||||
// Look for a compatible controller in our neighboring parts.
|
||||
IMultiblockPart[] partsToCheck = getNeighboringParts();
|
||||
for(IMultiblockPart neighborPart : partsToCheck) {
|
||||
if(neighborPart.isConnected()) {
|
||||
MultiblockControllerBase candidate = neighborPart.getMultiblockController();
|
||||
if(!candidate.getClass().equals(this.getMultiblockControllerType())) {
|
||||
for (IMultiblockPart neighborPart : partsToCheck)
|
||||
{
|
||||
if (neighborPart.isConnected())
|
||||
{
|
||||
MultiblockControllerBase candidate = neighborPart
|
||||
.getMultiblockController();
|
||||
if (!candidate.getClass().equals(
|
||||
this.getMultiblockControllerType()))
|
||||
{
|
||||
// Skip multiblocks with incompatible types
|
||||
continue;
|
||||
}
|
||||
|
||||
if(controllers == null) {
|
||||
|
||||
if (controllers == null)
|
||||
{
|
||||
controllers = new HashSet<MultiblockControllerBase>();
|
||||
bestController = candidate;
|
||||
}
|
||||
else if(!controllers.contains(candidate) && candidate.shouldConsume(bestController)) {
|
||||
} else if (!controllers.contains(candidate)
|
||||
&& candidate.shouldConsume(bestController))
|
||||
{
|
||||
bestController = candidate;
|
||||
}
|
||||
|
||||
controllers.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we've located a valid neighboring controller, attach to it.
|
||||
if(bestController != null) {
|
||||
if (bestController != null)
|
||||
{
|
||||
// attachBlock will call onAttached, which will set the controller.
|
||||
this.controller = bestController;
|
||||
bestController.attachBlock(this);
|
||||
|
@ -74,152 +85,193 @@ public abstract class MultiblockTileEntityBase extends IMultiblockPart {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void assertDetached() {
|
||||
if(this.controller != null) {
|
||||
BeefCoreLog.info("[assert] Part @ (%d, %d, %d) should be detached already, but detected that it was not. This is not a fatal error, and will be repaired, but is unusual.", xCoord, yCoord, zCoord);
|
||||
public void assertDetached()
|
||||
{
|
||||
if (this.controller != null)
|
||||
{
|
||||
BeefCoreLog
|
||||
.info("[assert] Part @ (%d, %d, %d) should be detached already, but detected that it was not. This is not a fatal error, and will be repaired, but is unusual.",
|
||||
xCoord, yCoord, zCoord);
|
||||
this.controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
///// Overrides from base TileEntity methods
|
||||
|
||||
|
||||
// /// Overrides from base TileEntity methods
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound data) {
|
||||
public void readFromNBT(NBTTagCompound data)
|
||||
{
|
||||
super.readFromNBT(data);
|
||||
|
||||
// We can't directly initialize a multiblock controller yet, so we cache the data here until
|
||||
// we receive a validate() call, which creates the controller and hands off the cached data.
|
||||
if(data.hasKey("multiblockData")) {
|
||||
|
||||
// We can't directly initialize a multiblock controller yet, so we cache
|
||||
// the data here until
|
||||
// we receive a validate() call, which creates the controller and hands
|
||||
// off the cached data.
|
||||
if (data.hasKey("multiblockData"))
|
||||
{
|
||||
this.cachedMultiblockData = data.getCompoundTag("multiblockData");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeToNBT(NBTTagCompound data) {
|
||||
public void writeToNBT(NBTTagCompound data)
|
||||
{
|
||||
super.writeToNBT(data);
|
||||
|
||||
if(isMultiblockSaveDelegate() && isConnected()) {
|
||||
if (isMultiblockSaveDelegate() && isConnected())
|
||||
{
|
||||
NBTTagCompound multiblockData = new NBTTagCompound();
|
||||
this.controller.writeToNBT(multiblockData);
|
||||
data.setTag("multiblockData", multiblockData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generally, TileEntities that are part of a multiblock should not subscribe to updates
|
||||
* from the main game loop. Instead, you should have lists of TileEntities which need to
|
||||
* be notified during an update() in your Controller and perform callbacks from there.
|
||||
* Generally, TileEntities that are part of a multiblock should not
|
||||
* subscribe to updates from the main game loop. Instead, you should have
|
||||
* lists of TileEntities which need to be notified during an update() in
|
||||
* your Controller and perform callbacks from there.
|
||||
*
|
||||
* @see net.minecraft.tileentity.TileEntity#canUpdate()
|
||||
*/
|
||||
@Override
|
||||
public boolean canUpdate() { return false; }
|
||||
|
||||
public boolean canUpdate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a block is removed by game actions, such as a player breaking the block
|
||||
* or the block being changed into another block.
|
||||
* Called when a block is removed by game actions, such as a player breaking
|
||||
* the block or the block being changed into another block.
|
||||
*
|
||||
* @see net.minecraft.tileentity.TileEntity#invalidate()
|
||||
*/
|
||||
@Override
|
||||
public void invalidate() {
|
||||
public void invalidate()
|
||||
{
|
||||
super.invalidate();
|
||||
detachSelf(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called from Minecraft's tile entity loop, after all tile entities have been ticked,
|
||||
* as the chunk in which this tile entity is contained is unloading.
|
||||
* Happens before the Forge TickEnd event.
|
||||
* Called from Minecraft's tile entity loop, after all tile entities have
|
||||
* been ticked, as the chunk in which this tile entity is contained is
|
||||
* unloading. Happens before the Forge TickEnd event.
|
||||
*
|
||||
* @see net.minecraft.tileentity.TileEntity#onChunkUnload()
|
||||
*/
|
||||
@Override
|
||||
public void onChunkUnload() {
|
||||
public void onChunkUnload()
|
||||
{
|
||||
super.onChunkUnload();
|
||||
detachSelf(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a block is being marked as valid by the chunk, but has not yet fully
|
||||
* been placed into the world's TileEntity cache. this.worldObj, xCoord, yCoord and zCoord have
|
||||
* been initialized, but any attempts to read data about the world can cause infinite loops -
|
||||
* if you call getTileEntity on this TileEntity's coordinate from within validate(), you will
|
||||
* blow your call stack.
|
||||
* This is called when a block is being marked as valid by the chunk, but
|
||||
* has not yet fully been placed into the world's TileEntity cache.
|
||||
* this.worldObj, xCoord, yCoord and zCoord have been initialized, but any
|
||||
* attempts to read data about the world can cause infinite loops - if you
|
||||
* call getTileEntity on this TileEntity's coordinate from within
|
||||
* validate(), you will blow your call stack.
|
||||
*
|
||||
* TL;DR: Here there be dragons.
|
||||
*
|
||||
* @see net.minecraft.tileentity.TileEntity#validate()
|
||||
*/
|
||||
@Override
|
||||
public void validate() {
|
||||
public void validate()
|
||||
{
|
||||
super.validate();
|
||||
MultiblockRegistry.onPartAdded(this.worldObj, this);
|
||||
}
|
||||
|
||||
// Network Communication
|
||||
@Override
|
||||
public Packet getDescriptionPacket() {
|
||||
public Packet getDescriptionPacket()
|
||||
{
|
||||
NBTTagCompound packetData = new NBTTagCompound();
|
||||
encodeDescriptionPacket(packetData);
|
||||
return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 0, packetData);
|
||||
return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 0,
|
||||
packetData);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDataPacket(NetworkManager network, S35PacketUpdateTileEntity packet) {
|
||||
public void onDataPacket(NetworkManager network,
|
||||
S35PacketUpdateTileEntity packet)
|
||||
{
|
||||
decodeDescriptionPacket(packet.func_148857_g());
|
||||
}
|
||||
|
||||
///// Things to override in most implementations (IMultiblockPart)
|
||||
|
||||
// /// Things to override in most implementations (IMultiblockPart)
|
||||
/**
|
||||
* Override this to easily modify the description packet's data without having
|
||||
* to worry about sending the packet itself.
|
||||
* Decode this data in decodeDescriptionPacket.
|
||||
* @param packetData An NBT compound tag into which you should write your custom description data.
|
||||
* Override this to easily modify the description packet's data without
|
||||
* having to worry about sending the packet itself. Decode this data in
|
||||
* decodeDescriptionPacket.
|
||||
*
|
||||
* @param packetData
|
||||
* An NBT compound tag into which you should write your custom
|
||||
* description data.
|
||||
* @see erogenousbeef.coreTR.multiblock.MultiblockTileEntityBase#decodeDescriptionPacket(NBTTagCompound)
|
||||
*/
|
||||
protected void encodeDescriptionPacket(NBTTagCompound packetData) {
|
||||
if(this.isMultiblockSaveDelegate() && isConnected()) {
|
||||
protected void encodeDescriptionPacket(NBTTagCompound packetData)
|
||||
{
|
||||
if (this.isMultiblockSaveDelegate() && isConnected())
|
||||
{
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
getMultiblockController().formatDescriptionPacket(tag);
|
||||
packetData.setTag("multiblockData", tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override this to easily read in data from a TileEntity's description packet.
|
||||
* Encoded in encodeDescriptionPacket.
|
||||
* @param packetData The NBT data from the tile entity's description packet.
|
||||
* Override this to easily read in data from a TileEntity's description
|
||||
* packet. Encoded in encodeDescriptionPacket.
|
||||
*
|
||||
* @param packetData
|
||||
* The NBT data from the tile entity's description packet.
|
||||
* @see erogenousbeef.coreTR.multiblock.MultiblockTileEntityBase#encodeDescriptionPacket(NBTTagCompound)
|
||||
*/
|
||||
protected void decodeDescriptionPacket(NBTTagCompound packetData) {
|
||||
if(packetData.hasKey("multiblockData")) {
|
||||
protected void decodeDescriptionPacket(NBTTagCompound packetData)
|
||||
{
|
||||
if (packetData.hasKey("multiblockData"))
|
||||
{
|
||||
NBTTagCompound tag = packetData.getCompoundTag("multiblockData");
|
||||
if(isConnected()) {
|
||||
if (isConnected())
|
||||
{
|
||||
getMultiblockController().decodeDescriptionPacket(tag);
|
||||
}
|
||||
else {
|
||||
// This part hasn't been added to a machine yet, so cache the data.
|
||||
} else
|
||||
{
|
||||
// This part hasn't been added to a machine yet, so cache the
|
||||
// data.
|
||||
this.cachedMultiblockData = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMultiblockSaveData() {
|
||||
public boolean hasMultiblockSaveData()
|
||||
{
|
||||
return this.cachedMultiblockData != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NBTTagCompound getMultiblockSaveData() {
|
||||
public NBTTagCompound getMultiblockSaveData()
|
||||
{
|
||||
return this.cachedMultiblockData;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMultiblockDataAssimilated() {
|
||||
public void onMultiblockDataAssimilated()
|
||||
{
|
||||
this.cachedMultiblockData = null;
|
||||
}
|
||||
|
||||
///// Game logic callbacks (IMultiblockPart)
|
||||
|
||||
// /// Game logic callbacks (IMultiblockPart)
|
||||
|
||||
@Override
|
||||
public abstract void onMachineAssembled(MultiblockControllerBase multiblockControllerBase);
|
||||
public abstract void onMachineAssembled(
|
||||
MultiblockControllerBase multiblockControllerBase);
|
||||
|
||||
@Override
|
||||
public abstract void onMachineBroken();
|
||||
|
@ -230,120 +282,148 @@ public abstract class MultiblockTileEntityBase extends IMultiblockPart {
|
|||
@Override
|
||||
public abstract void onMachineDeactivated();
|
||||
|
||||
///// Miscellaneous multiblock-assembly callbacks and support methods (IMultiblockPart)
|
||||
|
||||
// /// Miscellaneous multiblock-assembly callbacks and support methods
|
||||
// (IMultiblockPart)
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
public boolean isConnected()
|
||||
{
|
||||
return (controller != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiblockControllerBase getMultiblockController() {
|
||||
public MultiblockControllerBase getMultiblockController()
|
||||
{
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoordTriplet getWorldLocation() {
|
||||
public CoordTriplet getWorldLocation()
|
||||
{
|
||||
return new CoordTriplet(this.xCoord, this.yCoord, this.zCoord);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void becomeMultiblockSaveDelegate() {
|
||||
public void becomeMultiblockSaveDelegate()
|
||||
{
|
||||
this.saveMultiblockData = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forfeitMultiblockSaveDelegate() {
|
||||
public void forfeitMultiblockSaveDelegate()
|
||||
{
|
||||
this.saveMultiblockData = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiblockSaveDelegate() { return this.saveMultiblockData; }
|
||||
|
||||
@Override
|
||||
public void setUnvisited() {
|
||||
public boolean isMultiblockSaveDelegate()
|
||||
{
|
||||
return this.saveMultiblockData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnvisited()
|
||||
{
|
||||
this.visited = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setVisited() {
|
||||
public void setVisited()
|
||||
{
|
||||
this.visited = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isVisited() {
|
||||
public boolean isVisited()
|
||||
{
|
||||
return this.visited;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssimilated(MultiblockControllerBase newController) {
|
||||
assert(this.controller != newController);
|
||||
public void onAssimilated(MultiblockControllerBase newController)
|
||||
{
|
||||
assert (this.controller != newController);
|
||||
this.controller = newController;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttached(MultiblockControllerBase newController) {
|
||||
public void onAttached(MultiblockControllerBase newController)
|
||||
{
|
||||
this.controller = newController;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDetached(MultiblockControllerBase oldController) {
|
||||
public void onDetached(MultiblockControllerBase oldController)
|
||||
{
|
||||
this.controller = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract MultiblockControllerBase createNewMultiblock();
|
||||
|
||||
|
||||
@Override
|
||||
public IMultiblockPart[] getNeighboringParts() {
|
||||
CoordTriplet[] neighbors = new CoordTriplet[] {
|
||||
new CoordTriplet(this.xCoord-1, this.yCoord, this.zCoord),
|
||||
new CoordTriplet(this.xCoord, this.yCoord-1, this.zCoord),
|
||||
new CoordTriplet(this.xCoord, this.yCoord, this.zCoord-1),
|
||||
new CoordTriplet(this.xCoord, this.yCoord, this.zCoord+1),
|
||||
new CoordTriplet(this.xCoord, this.yCoord+1, this.zCoord),
|
||||
new CoordTriplet(this.xCoord+1, this.yCoord, this.zCoord)
|
||||
};
|
||||
public IMultiblockPart[] getNeighboringParts()
|
||||
{
|
||||
CoordTriplet[] neighbors = new CoordTriplet[]
|
||||
{ new CoordTriplet(this.xCoord - 1, this.yCoord, this.zCoord),
|
||||
new CoordTriplet(this.xCoord, this.yCoord - 1, this.zCoord),
|
||||
new CoordTriplet(this.xCoord, this.yCoord, this.zCoord - 1),
|
||||
new CoordTriplet(this.xCoord, this.yCoord, this.zCoord + 1),
|
||||
new CoordTriplet(this.xCoord, this.yCoord + 1, this.zCoord),
|
||||
new CoordTriplet(this.xCoord + 1, this.yCoord, this.zCoord) };
|
||||
|
||||
TileEntity te;
|
||||
List<IMultiblockPart> neighborParts = new ArrayList<IMultiblockPart>();
|
||||
IChunkProvider chunkProvider = worldObj.getChunkProvider();
|
||||
for(CoordTriplet neighbor : neighbors) {
|
||||
if(!chunkProvider.chunkExists(neighbor.getChunkX(), neighbor.getChunkZ())) {
|
||||
for (CoordTriplet neighbor : neighbors)
|
||||
{
|
||||
if (!chunkProvider.chunkExists(neighbor.getChunkX(),
|
||||
neighbor.getChunkZ()))
|
||||
{
|
||||
// Chunk not loaded, skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
te = this.worldObj.getTileEntity(neighbor.x, neighbor.y, neighbor.z);
|
||||
if(te instanceof IMultiblockPart) {
|
||||
neighborParts.add((IMultiblockPart)te);
|
||||
te = this.worldObj
|
||||
.getTileEntity(neighbor.x, neighbor.y, neighbor.z);
|
||||
if (te instanceof IMultiblockPart)
|
||||
{
|
||||
neighborParts.add((IMultiblockPart) te);
|
||||
}
|
||||
}
|
||||
IMultiblockPart[] tmp = new IMultiblockPart[neighborParts.size()];
|
||||
return neighborParts.toArray(tmp);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onOrphaned(MultiblockControllerBase controller, int oldSize, int newSize) {
|
||||
public void onOrphaned(MultiblockControllerBase controller, int oldSize,
|
||||
int newSize)
|
||||
{
|
||||
this.markDirty();
|
||||
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this);
|
||||
}
|
||||
|
||||
//// Helper functions for notifying neighboring blocks
|
||||
protected void notifyNeighborsOfBlockChange() {
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, getBlockType());
|
||||
|
||||
// // Helper functions for notifying neighboring blocks
|
||||
protected void notifyNeighborsOfBlockChange()
|
||||
{
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord,
|
||||
getBlockType());
|
||||
}
|
||||
|
||||
protected void notifyNeighborsOfTileChange() {
|
||||
|
||||
protected void notifyNeighborsOfTileChange()
|
||||
{
|
||||
worldObj.func_147453_f(xCoord, yCoord, zCoord, getBlockType());
|
||||
}
|
||||
|
||||
///// Private/Protected Logic Helpers
|
||||
// /// Private/Protected Logic Helpers
|
||||
/*
|
||||
* Detaches this block from its controller. Calls detachBlock() and clears the controller member.
|
||||
* Detaches this block from its controller. Calls detachBlock() and clears
|
||||
* the controller member.
|
||||
*/
|
||||
protected void detachSelf(boolean chunkUnloading) {
|
||||
if(this.controller != null) {
|
||||
protected void detachSelf(boolean chunkUnloading)
|
||||
{
|
||||
if (this.controller != null)
|
||||
{
|
||||
// Clean part out of controller
|
||||
this.controller.detachBlock(this, chunkUnloading);
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
package erogenousbeef.coreTR.multiblock;
|
||||
|
||||
/**
|
||||
* An exception thrown when trying to validate a multiblock. Requires a string describing why the multiblock
|
||||
* could not assemble.
|
||||
* An exception thrown when trying to validate a multiblock. Requires a string
|
||||
* describing why the multiblock could not assemble.
|
||||
*
|
||||
* @author Erogenous Beef
|
||||
*/
|
||||
public class MultiblockValidationException extends Exception {
|
||||
|
||||
public MultiblockValidationException(String reason) {
|
||||
public MultiblockValidationException(String reason)
|
||||
{
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,45 +15,51 @@ import erogenousbeef.coreTR.common.BeefCoreLog;
|
|||
import erogenousbeef.coreTR.common.CoordTriplet;
|
||||
|
||||
/**
|
||||
* This class manages all the multiblock controllers that exist in a given world,
|
||||
* either client- or server-side.
|
||||
* You must create different registries for server and client worlds.
|
||||
* This class manages all the multiblock controllers that exist in a given
|
||||
* world, either client- or server-side. You must create different registries
|
||||
* for server and client worlds.
|
||||
*
|
||||
* @author Erogenous Beef
|
||||
*/
|
||||
public class MultiblockWorldRegistry {
|
||||
|
||||
private World worldObj;
|
||||
|
||||
private Set<MultiblockControllerBase> controllers; // Active controllers
|
||||
private Set<MultiblockControllerBase> dirtyControllers; // Controllers whose parts lists have changed
|
||||
private Set<MultiblockControllerBase> deadControllers; // Controllers which are empty
|
||||
|
||||
// A list of orphan parts - parts which currently have no master, but should seek one this tick
|
||||
private Set<MultiblockControllerBase> controllers; // Active controllers
|
||||
private Set<MultiblockControllerBase> dirtyControllers; // Controllers whose
|
||||
// parts lists have
|
||||
// changed
|
||||
private Set<MultiblockControllerBase> deadControllers; // Controllers which
|
||||
// are empty
|
||||
|
||||
// A list of orphan parts - parts which currently have no master, but should
|
||||
// seek one this tick
|
||||
// Indexed by the hashed chunk coordinate
|
||||
// This can be added-to asynchronously via chunk loads!
|
||||
private Set<IMultiblockPart> orphanedParts;
|
||||
|
||||
// A list of parts which have been detached during internal operations
|
||||
private Set<IMultiblockPart> detachedParts;
|
||||
|
||||
|
||||
// A list of parts whose chunks have not yet finished loading
|
||||
// They will be added to the orphan list when they are finished loading.
|
||||
// Indexed by the hashed chunk coordinate
|
||||
// This can be added-to asynchronously via chunk loads!
|
||||
private HashMap<Long, Set<IMultiblockPart>> partsAwaitingChunkLoad;
|
||||
|
||||
// Mutexes to protect lists which may be changed due to asynchronous events, such as chunk loads
|
||||
|
||||
// Mutexes to protect lists which may be changed due to asynchronous events,
|
||||
// such as chunk loads
|
||||
private Object partsAwaitingChunkLoadMutex;
|
||||
private Object orphanedPartsMutex;
|
||||
|
||||
public MultiblockWorldRegistry(World world) {
|
||||
|
||||
public MultiblockWorldRegistry(World world)
|
||||
{
|
||||
worldObj = world;
|
||||
|
||||
|
||||
controllers = new HashSet<MultiblockControllerBase>();
|
||||
deadControllers = new HashSet<MultiblockControllerBase>();
|
||||
dirtyControllers = new HashSet<MultiblockControllerBase>();
|
||||
|
||||
|
||||
detachedParts = new HashSet<IMultiblockPart>();
|
||||
orphanedParts = new HashSet<IMultiblockPart>();
|
||||
|
||||
|
@ -61,20 +67,27 @@ public class MultiblockWorldRegistry {
|
|||
partsAwaitingChunkLoadMutex = new Object();
|
||||
orphanedPartsMutex = new Object();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called before Tile Entities are ticked in the world. Run game logic.
|
||||
*/
|
||||
public void tickStart() {
|
||||
if(controllers.size() > 0) {
|
||||
for(MultiblockControllerBase controller : controllers) {
|
||||
if(controller.worldObj == worldObj && controller.worldObj.isRemote == worldObj.isRemote) {
|
||||
if(controller.isEmpty()) {
|
||||
// This happens on the server when the user breaks the last block. It's fine.
|
||||
public void tickStart()
|
||||
{
|
||||
if (controllers.size() > 0)
|
||||
{
|
||||
for (MultiblockControllerBase controller : controllers)
|
||||
{
|
||||
if (controller.worldObj == worldObj
|
||||
&& controller.worldObj.isRemote == worldObj.isRemote)
|
||||
{
|
||||
if (controller.isEmpty())
|
||||
{
|
||||
// This happens on the server when the user breaks the
|
||||
// last block. It's fine.
|
||||
// Mark 'er dead and move on.
|
||||
deadControllers.add(controller);
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
// Run the game logic for this world
|
||||
controller.updateMultiblockEntity();
|
||||
}
|
||||
|
@ -82,87 +95,119 @@ public class MultiblockWorldRegistry {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called prior to processing multiblock controllers. Do bookkeeping.
|
||||
*/
|
||||
public void processMultiblockChanges() {
|
||||
public void processMultiblockChanges()
|
||||
{
|
||||
IChunkProvider chunkProvider = worldObj.getChunkProvider();
|
||||
CoordTriplet coord;
|
||||
|
||||
// Merge pools - sets of adjacent machines which should be merged later on in processing
|
||||
// Merge pools - sets of adjacent machines which should be merged later
|
||||
// on in processing
|
||||
List<Set<MultiblockControllerBase>> mergePools = null;
|
||||
if(orphanedParts.size() > 0) {
|
||||
if (orphanedParts.size() > 0)
|
||||
{
|
||||
Set<IMultiblockPart> orphansToProcess = null;
|
||||
|
||||
// Keep the synchronized block small. We can't iterate over orphanedParts directly
|
||||
// because the client does not know which chunks are actually loaded, so attachToNeighbors()
|
||||
|
||||
// Keep the synchronized block small. We can't iterate over
|
||||
// orphanedParts directly
|
||||
// because the client does not know which chunks are actually
|
||||
// loaded, so attachToNeighbors()
|
||||
// is not chunk-safe on the client, because Minecraft is stupid.
|
||||
// It's possible to polyfill this, but the polyfill is too slow for comfort.
|
||||
synchronized(orphanedPartsMutex) {
|
||||
if(orphanedParts.size() > 0) {
|
||||
// It's possible to polyfill this, but the polyfill is too slow for
|
||||
// comfort.
|
||||
synchronized (orphanedPartsMutex)
|
||||
{
|
||||
if (orphanedParts.size() > 0)
|
||||
{
|
||||
orphansToProcess = orphanedParts;
|
||||
orphanedParts = new HashSet<IMultiblockPart>();
|
||||
}
|
||||
}
|
||||
|
||||
if(orphansToProcess != null && orphansToProcess.size() > 0) {
|
||||
|
||||
if (orphansToProcess != null && orphansToProcess.size() > 0)
|
||||
{
|
||||
Set<MultiblockControllerBase> compatibleControllers;
|
||||
|
||||
|
||||
// Process orphaned blocks
|
||||
// These are blocks that exist in a valid chunk and require a controller
|
||||
for(IMultiblockPart orphan : orphansToProcess) {
|
||||
// These are blocks that exist in a valid chunk and require a
|
||||
// controller
|
||||
for (IMultiblockPart orphan : orphansToProcess)
|
||||
{
|
||||
coord = orphan.getWorldLocation();
|
||||
if(!chunkProvider.chunkExists(coord.getChunkX(), coord.getChunkZ())) {
|
||||
if (!chunkProvider.chunkExists(coord.getChunkX(),
|
||||
coord.getChunkZ()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This can occur on slow machines.
|
||||
if(orphan.isInvalid()) { continue; }
|
||||
if (orphan.isInvalid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(worldObj.getTileEntity(coord.x, coord.y, coord.z) != orphan) {
|
||||
if (worldObj.getTileEntity(coord.x, coord.y, coord.z) != orphan)
|
||||
{
|
||||
// This block has been replaced by another.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// THIS IS THE ONLY PLACE WHERE PARTS ATTACH TO MACHINES
|
||||
// Try to attach to a neighbor's master controller
|
||||
compatibleControllers = orphan.attachToNeighbors();
|
||||
if(compatibleControllers == null) {
|
||||
if (compatibleControllers == null)
|
||||
{
|
||||
// FOREVER ALONE! Create and register a new controller.
|
||||
// THIS IS THE ONLY PLACE WHERE NEW CONTROLLERS ARE CREATED.
|
||||
MultiblockControllerBase newController = orphan.createNewMultiblock();
|
||||
// THIS IS THE ONLY PLACE WHERE NEW CONTROLLERS ARE
|
||||
// CREATED.
|
||||
MultiblockControllerBase newController = orphan
|
||||
.createNewMultiblock();
|
||||
newController.attachBlock(orphan);
|
||||
this.controllers.add(newController);
|
||||
}
|
||||
else if(compatibleControllers.size() > 1) {
|
||||
if(mergePools == null) { mergePools = new ArrayList<Set<MultiblockControllerBase>>(); }
|
||||
} else if (compatibleControllers.size() > 1)
|
||||
{
|
||||
if (mergePools == null)
|
||||
{
|
||||
mergePools = new ArrayList<Set<MultiblockControllerBase>>();
|
||||
}
|
||||
|
||||
// THIS IS THE ONLY PLACE WHERE MERGES ARE DETECTED
|
||||
// Multiple compatible controllers indicates an impending merge.
|
||||
// Multiple compatible controllers indicates an
|
||||
// impending merge.
|
||||
// Locate the appropriate merge pool(s)
|
||||
boolean hasAddedToPool = false;
|
||||
List<Set<MultiblockControllerBase>> candidatePools = new ArrayList<Set<MultiblockControllerBase>>();
|
||||
for(Set<MultiblockControllerBase> candidatePool : mergePools) {
|
||||
if(!Collections.disjoint(candidatePool, compatibleControllers)) {
|
||||
// They share at least one element, so that means they will all touch after the merge
|
||||
for (Set<MultiblockControllerBase> candidatePool : mergePools)
|
||||
{
|
||||
if (!Collections.disjoint(candidatePool,
|
||||
compatibleControllers))
|
||||
{
|
||||
// They share at least one element, so that
|
||||
// means they will all touch after the merge
|
||||
candidatePools.add(candidatePool);
|
||||
}
|
||||
}
|
||||
|
||||
if(candidatePools.size() <= 0) {
|
||||
|
||||
if (candidatePools.size() <= 0)
|
||||
{
|
||||
// No pools nearby, create a new merge pool
|
||||
mergePools.add(compatibleControllers);
|
||||
}
|
||||
else if(candidatePools.size() == 1) {
|
||||
} else if (candidatePools.size() == 1)
|
||||
{
|
||||
// Only one pool nearby, simply add to that one
|
||||
candidatePools.get(0).addAll(compatibleControllers);
|
||||
}
|
||||
else {
|
||||
// Multiple pools- merge into one, then add the compatible controllers
|
||||
Set<MultiblockControllerBase> masterPool = candidatePools.get(0);
|
||||
} else
|
||||
{
|
||||
// Multiple pools- merge into one, then add the
|
||||
// compatible controllers
|
||||
Set<MultiblockControllerBase> masterPool = candidatePools
|
||||
.get(0);
|
||||
Set<MultiblockControllerBase> consumedPool;
|
||||
for(int i = 1; i < candidatePools.size(); i++) {
|
||||
for (int i = 1; i < candidatePools.size(); i++)
|
||||
{
|
||||
consumedPool = candidatePools.get(i);
|
||||
masterPool.addAll(consumedPool);
|
||||
mergePools.remove(consumedPool);
|
||||
|
@ -174,28 +219,42 @@ public class MultiblockWorldRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
if(mergePools != null && mergePools.size() > 0) {
|
||||
// Process merges - any machines that have been marked for merge should be merged
|
||||
if (mergePools != null && mergePools.size() > 0)
|
||||
{
|
||||
// Process merges - any machines that have been marked for merge
|
||||
// should be merged
|
||||
// into the "master" machine.
|
||||
// To do this, we combine lists of machines that are touching one another and therefore
|
||||
// To do this, we combine lists of machines that are touching one
|
||||
// another and therefore
|
||||
// should voltron the fuck up.
|
||||
for(Set<MultiblockControllerBase> mergePool : mergePools) {
|
||||
// Search for the new master machine, which will take over all the blocks contained in the other machines
|
||||
for (Set<MultiblockControllerBase> mergePool : mergePools)
|
||||
{
|
||||
// Search for the new master machine, which will take over all
|
||||
// the blocks contained in the other machines
|
||||
MultiblockControllerBase newMaster = null;
|
||||
for(MultiblockControllerBase controller : mergePool) {
|
||||
if(newMaster == null || controller.shouldConsume(newMaster)) {
|
||||
for (MultiblockControllerBase controller : mergePool)
|
||||
{
|
||||
if (newMaster == null
|
||||
|| controller.shouldConsume(newMaster))
|
||||
{
|
||||
newMaster = controller;
|
||||
}
|
||||
}
|
||||
|
||||
if(newMaster == null) {
|
||||
BeefCoreLog.fatal("Multiblock system checked a merge pool of size %d, found no master candidates. This should never happen.", mergePool.size());
|
||||
}
|
||||
else {
|
||||
// Merge all the other machines into the master machine, then unregister them
|
||||
|
||||
if (newMaster == null)
|
||||
{
|
||||
BeefCoreLog
|
||||
.fatal("Multiblock system checked a merge pool of size %d, found no master candidates. This should never happen.",
|
||||
mergePool.size());
|
||||
} else
|
||||
{
|
||||
// Merge all the other machines into the master machine,
|
||||
// then unregister them
|
||||
addDirtyController(newMaster);
|
||||
for(MultiblockControllerBase controller : mergePool) {
|
||||
if(controller != newMaster) {
|
||||
for (MultiblockControllerBase controller : mergePool)
|
||||
{
|
||||
if (controller != newMaster)
|
||||
{
|
||||
newMaster.assimilate(controller);
|
||||
addDeadController(controller);
|
||||
addDirtyController(newMaster);
|
||||
|
@ -206,109 +265,142 @@ public class MultiblockWorldRegistry {
|
|||
}
|
||||
|
||||
// Process splits and assembly
|
||||
// Any controllers which have had parts removed must be checked to see if some parts are no longer
|
||||
// Any controllers which have had parts removed must be checked to see
|
||||
// if some parts are no longer
|
||||
// physically connected to their master.
|
||||
if(dirtyControllers.size() > 0) {
|
||||
if (dirtyControllers.size() > 0)
|
||||
{
|
||||
Set<IMultiblockPart> newlyDetachedParts = null;
|
||||
for(MultiblockControllerBase controller : dirtyControllers) {
|
||||
for (MultiblockControllerBase controller : dirtyControllers)
|
||||
{
|
||||
// Tell the machine to check if any parts are disconnected.
|
||||
// It should return a set of parts which are no longer connected.
|
||||
// POSTCONDITION: The controller must have informed those parts that
|
||||
// It should return a set of parts which are no longer
|
||||
// connected.
|
||||
// POSTCONDITION: The controller must have informed those parts
|
||||
// that
|
||||
// they are no longer connected to this machine.
|
||||
newlyDetachedParts = controller.checkForDisconnections();
|
||||
|
||||
if(!controller.isEmpty()) {
|
||||
|
||||
if (!controller.isEmpty())
|
||||
{
|
||||
controller.recalculateMinMaxCoords();
|
||||
controller.checkIfMachineIsWhole();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
addDeadController(controller);
|
||||
}
|
||||
|
||||
if(newlyDetachedParts != null && newlyDetachedParts.size() > 0) {
|
||||
// Controller has shed some parts - add them to the detached list for delayed processing
|
||||
|
||||
if (newlyDetachedParts != null && newlyDetachedParts.size() > 0)
|
||||
{
|
||||
// Controller has shed some parts - add them to the detached
|
||||
// list for delayed processing
|
||||
detachedParts.addAll(newlyDetachedParts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dirtyControllers.clear();
|
||||
}
|
||||
|
||||
|
||||
// Unregister dead controllers
|
||||
if(deadControllers.size() > 0) {
|
||||
for(MultiblockControllerBase controller : deadControllers) {
|
||||
// Go through any controllers which have marked themselves as potentially dead.
|
||||
if (deadControllers.size() > 0)
|
||||
{
|
||||
for (MultiblockControllerBase controller : deadControllers)
|
||||
{
|
||||
// Go through any controllers which have marked themselves as
|
||||
// potentially dead.
|
||||
// Validate that they are empty/dead, then unregister them.
|
||||
if(!controller.isEmpty()) {
|
||||
BeefCoreLog.fatal("Found a non-empty controller. Forcing it to shed its blocks and die. This should never happen!");
|
||||
if (!controller.isEmpty())
|
||||
{
|
||||
BeefCoreLog
|
||||
.fatal("Found a non-empty controller. Forcing it to shed its blocks and die. This should never happen!");
|
||||
detachedParts.addAll(controller.detachAllBlocks());
|
||||
}
|
||||
|
||||
// THIS IS THE ONLY PLACE WHERE CONTROLLERS ARE UNREGISTERED.
|
||||
this.controllers.remove(controller);
|
||||
}
|
||||
|
||||
|
||||
deadControllers.clear();
|
||||
}
|
||||
|
||||
|
||||
// Process detached blocks
|
||||
// Any blocks which have been detached this tick should be moved to the orphaned
|
||||
// list, and will be checked next tick to see if their chunk is still loaded.
|
||||
for(IMultiblockPart part : detachedParts) {
|
||||
// Any blocks which have been detached this tick should be moved to the
|
||||
// orphaned
|
||||
// list, and will be checked next tick to see if their chunk is still
|
||||
// loaded.
|
||||
for (IMultiblockPart part : detachedParts)
|
||||
{
|
||||
// Ensure parts know they're detached
|
||||
part.assertDetached();
|
||||
}
|
||||
|
||||
|
||||
addAllOrphanedPartsThreadsafe(detachedParts);
|
||||
detachedParts.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a multiblock part is added to the world, either via chunk-load or user action.
|
||||
* If its chunk is loaded, it will be processed during the next tick.
|
||||
* If the chunk is not loaded, it will be added to a list of objects waiting for a chunkload.
|
||||
* @param part The part which is being added to this world.
|
||||
* Called when a multiblock part is added to the world, either via
|
||||
* chunk-load or user action. If its chunk is loaded, it will be processed
|
||||
* during the next tick. If the chunk is not loaded, it will be added to a
|
||||
* list of objects waiting for a chunkload.
|
||||
*
|
||||
* @param part
|
||||
* The part which is being added to this world.
|
||||
*/
|
||||
public void onPartAdded(IMultiblockPart part) {
|
||||
public void onPartAdded(IMultiblockPart part)
|
||||
{
|
||||
CoordTriplet worldLocation = part.getWorldLocation();
|
||||
|
||||
if(!worldObj.getChunkProvider().chunkExists(worldLocation.getChunkX(), worldLocation.getChunkZ())) {
|
||||
|
||||
if (!worldObj.getChunkProvider().chunkExists(worldLocation.getChunkX(),
|
||||
worldLocation.getChunkZ()))
|
||||
{
|
||||
// Part goes into the waiting-for-chunk-load list
|
||||
Set<IMultiblockPart> partSet;
|
||||
long chunkHash = worldLocation.getChunkXZHash();
|
||||
synchronized(partsAwaitingChunkLoadMutex) {
|
||||
if(!partsAwaitingChunkLoad.containsKey(chunkHash)) {
|
||||
synchronized (partsAwaitingChunkLoadMutex)
|
||||
{
|
||||
if (!partsAwaitingChunkLoad.containsKey(chunkHash))
|
||||
{
|
||||
partSet = new HashSet<IMultiblockPart>();
|
||||
partsAwaitingChunkLoad.put(chunkHash, partSet);
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
partSet = partsAwaitingChunkLoad.get(chunkHash);
|
||||
}
|
||||
|
||||
|
||||
partSet.add(part);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
// Part goes into the orphan queue, to be checked this tick
|
||||
addOrphanedPartThreadsafe(part);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a part is removed from the world, via user action or via chunk unloads.
|
||||
* This part is removed from any lists in which it may be, and its machine is marked for recalculation.
|
||||
* @param part The part which is being removed.
|
||||
* Called when a part is removed from the world, via user action or via
|
||||
* chunk unloads. This part is removed from any lists in which it may be,
|
||||
* and its machine is marked for recalculation.
|
||||
*
|
||||
* @param part
|
||||
* The part which is being removed.
|
||||
*/
|
||||
public void onPartRemovedFromWorld(IMultiblockPart part) {
|
||||
public void onPartRemovedFromWorld(IMultiblockPart part)
|
||||
{
|
||||
CoordTriplet coord = part.getWorldLocation();
|
||||
if(coord != null) {
|
||||
if (coord != null)
|
||||
{
|
||||
long hash = coord.getChunkXZHash();
|
||||
|
||||
if(partsAwaitingChunkLoad.containsKey(hash)) {
|
||||
synchronized(partsAwaitingChunkLoadMutex) {
|
||||
if(partsAwaitingChunkLoad.containsKey(hash)) {
|
||||
|
||||
if (partsAwaitingChunkLoad.containsKey(hash))
|
||||
{
|
||||
synchronized (partsAwaitingChunkLoadMutex)
|
||||
{
|
||||
if (partsAwaitingChunkLoad.containsKey(hash))
|
||||
{
|
||||
partsAwaitingChunkLoad.get(hash).remove(part);
|
||||
if(partsAwaitingChunkLoad.get(hash).size() <= 0) {
|
||||
if (partsAwaitingChunkLoad.get(hash).size() <= 0)
|
||||
{
|
||||
partsAwaitingChunkLoad.remove(hash);
|
||||
}
|
||||
}
|
||||
|
@ -317,51 +409,65 @@ public class MultiblockWorldRegistry {
|
|||
}
|
||||
|
||||
detachedParts.remove(part);
|
||||
if(orphanedParts.contains(part)) {
|
||||
synchronized(orphanedPartsMutex) {
|
||||
if (orphanedParts.contains(part))
|
||||
{
|
||||
synchronized (orphanedPartsMutex)
|
||||
{
|
||||
orphanedParts.remove(part);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
part.assertDetached();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the world which this World Registry represents is fully unloaded from the system.
|
||||
* Does some housekeeping just to be nice.
|
||||
* Called when the world which this World Registry represents is fully
|
||||
* unloaded from the system. Does some housekeeping just to be nice.
|
||||
*/
|
||||
public void onWorldUnloaded() {
|
||||
public void onWorldUnloaded()
|
||||
{
|
||||
controllers.clear();
|
||||
deadControllers.clear();
|
||||
dirtyControllers.clear();
|
||||
|
||||
|
||||
detachedParts.clear();
|
||||
|
||||
synchronized(partsAwaitingChunkLoadMutex) {
|
||||
|
||||
synchronized (partsAwaitingChunkLoadMutex)
|
||||
{
|
||||
partsAwaitingChunkLoad.clear();
|
||||
}
|
||||
|
||||
synchronized(orphanedPartsMutex) {
|
||||
|
||||
synchronized (orphanedPartsMutex)
|
||||
{
|
||||
orphanedParts.clear();
|
||||
}
|
||||
|
||||
|
||||
worldObj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a chunk has finished loading. Adds all of the parts which are awaiting
|
||||
* load to the list of parts which are orphans and therefore will be added to machines
|
||||
* after the next world tick.
|
||||
* Called when a chunk has finished loading. Adds all of the parts which are
|
||||
* awaiting load to the list of parts which are orphans and therefore will
|
||||
* be added to machines after the next world tick.
|
||||
*
|
||||
* @param chunkX Chunk X coordinate (world coordate >> 4) of the chunk that was loaded
|
||||
* @param chunkZ Chunk Z coordinate (world coordate >> 4) of the chunk that was loaded
|
||||
* @param chunkX
|
||||
* Chunk X coordinate (world coordate >> 4) of the chunk that was
|
||||
* loaded
|
||||
* @param chunkZ
|
||||
* Chunk Z coordinate (world coordate >> 4) of the chunk that was
|
||||
* loaded
|
||||
*/
|
||||
public void onChunkLoaded(int chunkX, int chunkZ) {
|
||||
public void onChunkLoaded(int chunkX, int chunkZ)
|
||||
{
|
||||
long chunkHash = ChunkCoordIntPair.chunkXZ2Int(chunkX, chunkZ);
|
||||
if(partsAwaitingChunkLoad.containsKey(chunkHash)) {
|
||||
synchronized(partsAwaitingChunkLoadMutex) {
|
||||
if(partsAwaitingChunkLoad.containsKey(chunkHash)) {
|
||||
addAllOrphanedPartsThreadsafe(partsAwaitingChunkLoad.get(chunkHash));
|
||||
if (partsAwaitingChunkLoad.containsKey(chunkHash))
|
||||
{
|
||||
synchronized (partsAwaitingChunkLoadMutex)
|
||||
{
|
||||
if (partsAwaitingChunkLoad.containsKey(chunkHash))
|
||||
{
|
||||
addAllOrphanedPartsThreadsafe(partsAwaitingChunkLoad
|
||||
.get(chunkHash));
|
||||
partsAwaitingChunkLoad.remove(chunkHash);
|
||||
}
|
||||
}
|
||||
|
@ -369,49 +475,64 @@ public class MultiblockWorldRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a controller as dead. It will be cleaned up at the end of the next world tick.
|
||||
* Note that a controller must shed all of its blocks before being marked as dead, or the system
|
||||
* will complain at you.
|
||||
* Registers a controller as dead. It will be cleaned up at the end of the
|
||||
* next world tick. Note that a controller must shed all of its blocks
|
||||
* before being marked as dead, or the system will complain at you.
|
||||
*
|
||||
* @param deadController The controller which is dead.
|
||||
* @param deadController
|
||||
* The controller which is dead.
|
||||
*/
|
||||
public void addDeadController(MultiblockControllerBase deadController) {
|
||||
public void addDeadController(MultiblockControllerBase deadController)
|
||||
{
|
||||
this.deadControllers.add(deadController);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a controller as dirty - its list of attached blocks has changed, and it
|
||||
* must be re-checked for assembly and, possibly, for orphans.
|
||||
* Registers a controller as dirty - its list of attached blocks has
|
||||
* changed, and it must be re-checked for assembly and, possibly, for
|
||||
* orphans.
|
||||
*
|
||||
* @param dirtyController The dirty controller.
|
||||
* @param dirtyController
|
||||
* The dirty controller.
|
||||
*/
|
||||
public void addDirtyController(MultiblockControllerBase dirtyController) {
|
||||
public void addDirtyController(MultiblockControllerBase dirtyController)
|
||||
{
|
||||
this.dirtyControllers.add(dirtyController);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this only if you know what you're doing. You should rarely need to iterate
|
||||
* over all controllers in a world!
|
||||
* Use this only if you know what you're doing. You should rarely need to
|
||||
* iterate over all controllers in a world!
|
||||
*
|
||||
* @return An (unmodifiable) set of controllers which are active in this world.
|
||||
* @return An (unmodifiable) set of controllers which are active in this
|
||||
* world.
|
||||
*/
|
||||
public Set<MultiblockControllerBase> getControllers() {
|
||||
public Set<MultiblockControllerBase> getControllers()
|
||||
{
|
||||
return Collections.unmodifiableSet(controllers);
|
||||
}
|
||||
|
||||
/* *** PRIVATE HELPERS *** */
|
||||
|
||||
private void addOrphanedPartThreadsafe(IMultiblockPart part) {
|
||||
synchronized(orphanedPartsMutex) {
|
||||
|
||||
private void addOrphanedPartThreadsafe(IMultiblockPart part)
|
||||
{
|
||||
synchronized (orphanedPartsMutex)
|
||||
{
|
||||
orphanedParts.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAllOrphanedPartsThreadsafe(Collection<? extends IMultiblockPart> parts) {
|
||||
synchronized(orphanedPartsMutex) {
|
||||
|
||||
private void addAllOrphanedPartsThreadsafe(
|
||||
Collection<? extends IMultiblockPart> parts)
|
||||
{
|
||||
synchronized (orphanedPartsMutex)
|
||||
{
|
||||
orphanedParts.addAll(parts);
|
||||
}
|
||||
}
|
||||
|
||||
private String clientOrServer() { return worldObj.isRemote ? "CLIENT" : "SERVER"; }
|
||||
|
||||
private String clientOrServer()
|
||||
{
|
||||
return worldObj.isRemote ? "CLIENT" : "SERVER";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
package erogenousbeef.coreTR.multiblock.rectangular;
|
||||
|
||||
public enum PartPosition {
|
||||
Unknown,
|
||||
Interior,
|
||||
FrameCorner,
|
||||
Frame,
|
||||
TopFace,
|
||||
BottomFace,
|
||||
NorthFace,
|
||||
SouthFace,
|
||||
EastFace,
|
||||
WestFace;
|
||||
|
||||
public boolean isFace(PartPosition position) {
|
||||
switch(position) {
|
||||
case TopFace:
|
||||
case BottomFace:
|
||||
case NorthFace:
|
||||
case SouthFace:
|
||||
case EastFace:
|
||||
case WestFace:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
public enum PartPosition
|
||||
{
|
||||
Unknown, Interior, FrameCorner, Frame, TopFace, BottomFace, NorthFace, SouthFace, EastFace, WestFace;
|
||||
|
||||
public boolean isFace(PartPosition position)
|
||||
{
|
||||
switch (position)
|
||||
{
|
||||
case TopFace:
|
||||
case BottomFace:
|
||||
case NorthFace:
|
||||
case SouthFace:
|
||||
case EastFace:
|
||||
case WestFace:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,122 +9,198 @@ import erogenousbeef.coreTR.multiblock.MultiblockValidationException;
|
|||
public abstract class RectangularMultiblockControllerBase extends
|
||||
MultiblockControllerBase {
|
||||
|
||||
protected RectangularMultiblockControllerBase(World world) {
|
||||
protected RectangularMultiblockControllerBase(World world)
|
||||
{
|
||||
super(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the machine is "whole" and should be assembled. False otherwise.
|
||||
* @return True if the machine is "whole" and should be assembled. False
|
||||
* otherwise.
|
||||
*/
|
||||
protected void isMachineWhole() throws MultiblockValidationException {
|
||||
if(connectedParts.size() < getMinimumNumberOfBlocksForAssembledMachine()) {
|
||||
protected void isMachineWhole() throws MultiblockValidationException
|
||||
{
|
||||
if (connectedParts.size() < getMinimumNumberOfBlocksForAssembledMachine())
|
||||
{
|
||||
throw new MultiblockValidationException("Machine is too small.");
|
||||
}
|
||||
|
||||
|
||||
CoordTriplet maximumCoord = getMaximumCoord();
|
||||
CoordTriplet minimumCoord = getMinimumCoord();
|
||||
|
||||
|
||||
// Quickly check for exceeded dimensions
|
||||
int deltaX = maximumCoord.x - minimumCoord.x + 1;
|
||||
int deltaY = maximumCoord.y - minimumCoord.y + 1;
|
||||
int deltaZ = maximumCoord.z - minimumCoord.z + 1;
|
||||
|
||||
|
||||
int maxX = getMaximumXSize();
|
||||
int maxY = getMaximumYSize();
|
||||
int maxZ = getMaximumZSize();
|
||||
int minX = getMinimumXSize();
|
||||
int minY = getMinimumYSize();
|
||||
int minZ = getMinimumZSize();
|
||||
|
||||
if(maxX > 0 && deltaX > maxX) { throw new MultiblockValidationException(String.format("Machine is too large, it may be at most %d blocks in the X dimension", maxX)); }
|
||||
if(maxY > 0 && deltaY > maxY) { throw new MultiblockValidationException(String.format("Machine is too large, it may be at most %d blocks in the Y dimension", maxY)); }
|
||||
if(maxZ > 0 && deltaZ > maxZ) { throw new MultiblockValidationException(String.format("Machine is too large, it may be at most %d blocks in the Z dimension", maxZ)); }
|
||||
if(deltaX < minX) { throw new MultiblockValidationException(String.format("Machine is too small, it must be at least %d blocks in the X dimension", minX)); }
|
||||
if(deltaY < minY) { throw new MultiblockValidationException(String.format("Machine is too small, it must be at least %d blocks in the Y dimension", minY)); }
|
||||
if(deltaZ < minZ) { throw new MultiblockValidationException(String.format("Machine is too small, it must be at least %d blocks in the Z dimension", minZ)); }
|
||||
|
||||
if (maxX > 0 && deltaX > maxX)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too large, it may be at most %d blocks in the X dimension",
|
||||
maxX));
|
||||
}
|
||||
if (maxY > 0 && deltaY > maxY)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too large, it may be at most %d blocks in the Y dimension",
|
||||
maxY));
|
||||
}
|
||||
if (maxZ > 0 && deltaZ > maxZ)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too large, it may be at most %d blocks in the Z dimension",
|
||||
maxZ));
|
||||
}
|
||||
if (deltaX < minX)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too small, it must be at least %d blocks in the X dimension",
|
||||
minX));
|
||||
}
|
||||
if (deltaY < minY)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too small, it must be at least %d blocks in the Y dimension",
|
||||
minY));
|
||||
}
|
||||
if (deltaZ < minZ)
|
||||
{
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Machine is too small, it must be at least %d blocks in the Z dimension",
|
||||
minZ));
|
||||
}
|
||||
|
||||
// Now we run a simple check on each block within that volume.
|
||||
// Any block deviating = NO DEAL SIR
|
||||
TileEntity te;
|
||||
RectangularMultiblockTileEntityBase part;
|
||||
Class<? extends RectangularMultiblockControllerBase> myClass = this.getClass();
|
||||
Class<? extends RectangularMultiblockControllerBase> myClass = this
|
||||
.getClass();
|
||||
|
||||
for(int x = minimumCoord.x; x <= maximumCoord.x; x++) {
|
||||
for(int y = minimumCoord.y; y <= maximumCoord.y; y++) {
|
||||
for(int z = minimumCoord.z; z <= maximumCoord.z; z++) {
|
||||
for (int x = minimumCoord.x; x <= maximumCoord.x; x++)
|
||||
{
|
||||
for (int y = minimumCoord.y; y <= maximumCoord.y; y++)
|
||||
{
|
||||
for (int z = minimumCoord.z; z <= maximumCoord.z; z++)
|
||||
{
|
||||
// Okay, figure out what sort of block this should be.
|
||||
|
||||
|
||||
te = this.worldObj.getTileEntity(x, y, z);
|
||||
if(te instanceof RectangularMultiblockTileEntityBase) {
|
||||
part = (RectangularMultiblockTileEntityBase)te;
|
||||
|
||||
// Ensure this part should actually be allowed within a cube of this controller's type
|
||||
if(!myClass.equals(part.getMultiblockControllerType()))
|
||||
if (te instanceof RectangularMultiblockTileEntityBase)
|
||||
{
|
||||
part = (RectangularMultiblockTileEntityBase) te;
|
||||
|
||||
// Ensure this part should actually be allowed within a
|
||||
// cube of this controller's type
|
||||
if (!myClass.equals(part.getMultiblockControllerType()))
|
||||
{
|
||||
throw new MultiblockValidationException(String.format("Part @ %d, %d, %d is incompatible with machines of type %s", x, y, z, myClass.getSimpleName()));
|
||||
throw new MultiblockValidationException(
|
||||
String.format(
|
||||
"Part @ %d, %d, %d is incompatible with machines of type %s",
|
||||
x, y, z, myClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is permitted so that we can incorporate certain non-multiblock parts inside interiors
|
||||
} else
|
||||
{
|
||||
// This is permitted so that we can incorporate certain
|
||||
// non-multiblock parts inside interiors
|
||||
part = null;
|
||||
}
|
||||
|
||||
// Validate block type against both part-level and material-level validators.
|
||||
// Validate block type against both part-level and
|
||||
// material-level validators.
|
||||
int extremes = 0;
|
||||
if(x == minimumCoord.x) { extremes++; }
|
||||
if(y == minimumCoord.y) { extremes++; }
|
||||
if(z == minimumCoord.z) { extremes++; }
|
||||
|
||||
if(x == maximumCoord.x) { extremes++; }
|
||||
if(y == maximumCoord.y) { extremes++; }
|
||||
if(z == maximumCoord.z) { extremes++; }
|
||||
|
||||
if(extremes >= 2) {
|
||||
if(part != null) {
|
||||
if (x == minimumCoord.x)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
if (y == minimumCoord.y)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
if (z == minimumCoord.z)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
|
||||
if (x == maximumCoord.x)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
if (y == maximumCoord.y)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
if (z == maximumCoord.z)
|
||||
{
|
||||
extremes++;
|
||||
}
|
||||
|
||||
if (extremes >= 2)
|
||||
{
|
||||
if (part != null)
|
||||
{
|
||||
part.isGoodForFrame();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
isBlockGoodForFrame(this.worldObj, x, y, z);
|
||||
}
|
||||
}
|
||||
else if(extremes == 1) {
|
||||
if(y == maximumCoord.y) {
|
||||
if(part != null) {
|
||||
} else if (extremes == 1)
|
||||
{
|
||||
if (y == maximumCoord.y)
|
||||
{
|
||||
if (part != null)
|
||||
{
|
||||
part.isGoodForTop();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
isBlockGoodForTop(this.worldObj, x, y, z);
|
||||
}
|
||||
}
|
||||
else if(y == minimumCoord.y) {
|
||||
if(part != null) {
|
||||
} else if (y == minimumCoord.y)
|
||||
{
|
||||
if (part != null)
|
||||
{
|
||||
part.isGoodForBottom();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
isBlockGoodForBottom(this.worldObj, x, y, z);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
// Side
|
||||
if(part != null) {
|
||||
if (part != null)
|
||||
{
|
||||
part.isGoodForSides();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
isBlockGoodForSides(this.worldObj, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(part != null) {
|
||||
} else
|
||||
{
|
||||
if (part != null)
|
||||
{
|
||||
part.isGoodForInterior();
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
isBlockGoodForInterior(this.worldObj, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,89 +11,114 @@ public abstract class RectangularMultiblockTileEntityBase extends
|
|||
|
||||
PartPosition position;
|
||||
ForgeDirection outwards;
|
||||
|
||||
public RectangularMultiblockTileEntityBase() {
|
||||
|
||||
public RectangularMultiblockTileEntityBase()
|
||||
{
|
||||
super();
|
||||
|
||||
|
||||
position = PartPosition.Unknown;
|
||||
outwards = ForgeDirection.UNKNOWN;
|
||||
}
|
||||
|
||||
// Positional Data
|
||||
public ForgeDirection getOutwardsDir() {
|
||||
public ForgeDirection getOutwardsDir()
|
||||
{
|
||||
return outwards;
|
||||
}
|
||||
|
||||
public PartPosition getPartPosition() {
|
||||
|
||||
public PartPosition getPartPosition()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
// Handlers from MultiblockTileEntityBase
|
||||
// Handlers from MultiblockTileEntityBase
|
||||
@Override
|
||||
public void onAttached(MultiblockControllerBase newController) {
|
||||
public void onAttached(MultiblockControllerBase newController)
|
||||
{
|
||||
super.onAttached(newController);
|
||||
recalculateOutwardsDirection(newController.getMinimumCoord(), newController.getMaximumCoord());
|
||||
recalculateOutwardsDirection(newController.getMinimumCoord(),
|
||||
newController.getMaximumCoord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onMachineAssembled(MultiblockControllerBase controller) {
|
||||
public void onMachineAssembled(MultiblockControllerBase controller)
|
||||
{
|
||||
CoordTriplet maxCoord = controller.getMaximumCoord();
|
||||
CoordTriplet minCoord = controller.getMinimumCoord();
|
||||
|
||||
|
||||
// Discover where I am on the reactor
|
||||
recalculateOutwardsDirection(minCoord, maxCoord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMachineBroken() {
|
||||
public void onMachineBroken()
|
||||
{
|
||||
position = PartPosition.Unknown;
|
||||
outwards = ForgeDirection.UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
// Positional helpers
|
||||
public void recalculateOutwardsDirection(CoordTriplet minCoord, CoordTriplet maxCoord) {
|
||||
public void recalculateOutwardsDirection(CoordTriplet minCoord,
|
||||
CoordTriplet maxCoord)
|
||||
{
|
||||
outwards = ForgeDirection.UNKNOWN;
|
||||
position = PartPosition.Unknown;
|
||||
|
||||
int facesMatching = 0;
|
||||
if(maxCoord.x == this.xCoord || minCoord.x == this.xCoord) { facesMatching++; }
|
||||
if(maxCoord.y == this.yCoord || minCoord.y == this.yCoord) { facesMatching++; }
|
||||
if(maxCoord.z == this.zCoord || minCoord.z == this.zCoord) { facesMatching++; }
|
||||
|
||||
if(facesMatching <= 0) { position = PartPosition.Interior; }
|
||||
else if(facesMatching >= 3) { position = PartPosition.FrameCorner; }
|
||||
else if(facesMatching == 2) { position = PartPosition.Frame; }
|
||||
else {
|
||||
if (maxCoord.x == this.xCoord || minCoord.x == this.xCoord)
|
||||
{
|
||||
facesMatching++;
|
||||
}
|
||||
if (maxCoord.y == this.yCoord || minCoord.y == this.yCoord)
|
||||
{
|
||||
facesMatching++;
|
||||
}
|
||||
if (maxCoord.z == this.zCoord || minCoord.z == this.zCoord)
|
||||
{
|
||||
facesMatching++;
|
||||
}
|
||||
|
||||
if (facesMatching <= 0)
|
||||
{
|
||||
position = PartPosition.Interior;
|
||||
} else if (facesMatching >= 3)
|
||||
{
|
||||
position = PartPosition.FrameCorner;
|
||||
} else if (facesMatching == 2)
|
||||
{
|
||||
position = PartPosition.Frame;
|
||||
} else
|
||||
{
|
||||
// 1 face matches
|
||||
if(maxCoord.x == this.xCoord) {
|
||||
if (maxCoord.x == this.xCoord)
|
||||
{
|
||||
position = PartPosition.EastFace;
|
||||
outwards = ForgeDirection.EAST;
|
||||
}
|
||||
else if(minCoord.x == this.xCoord) {
|
||||
} else if (minCoord.x == this.xCoord)
|
||||
{
|
||||
position = PartPosition.WestFace;
|
||||
outwards = ForgeDirection.WEST;
|
||||
}
|
||||
else if(maxCoord.z == this.zCoord) {
|
||||
} else if (maxCoord.z == this.zCoord)
|
||||
{
|
||||
position = PartPosition.SouthFace;
|
||||
outwards = ForgeDirection.SOUTH;
|
||||
}
|
||||
else if(minCoord.z == this.zCoord) {
|
||||
} else if (minCoord.z == this.zCoord)
|
||||
{
|
||||
position = PartPosition.NorthFace;
|
||||
outwards = ForgeDirection.NORTH;
|
||||
}
|
||||
else if(maxCoord.y == this.yCoord) {
|
||||
} else if (maxCoord.y == this.yCoord)
|
||||
{
|
||||
position = PartPosition.TopFace;
|
||||
outwards = ForgeDirection.UP;
|
||||
}
|
||||
else {
|
||||
} else
|
||||
{
|
||||
position = PartPosition.BottomFace;
|
||||
outwards = ForgeDirection.DOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///// Validation Helpers (IMultiblockPart)
|
||||
|
||||
// /// Validation Helpers (IMultiblockPart)
|
||||
public abstract void isGoodForFrame() throws MultiblockValidationException;
|
||||
|
||||
public abstract void isGoodForSides() throws MultiblockValidationException;
|
||||
|
@ -102,5 +127,6 @@ public abstract class RectangularMultiblockTileEntityBase extends
|
|||
|
||||
public abstract void isGoodForBottom() throws MultiblockValidationException;
|
||||
|
||||
public abstract void isGoodForInterior() throws MultiblockValidationException;
|
||||
public abstract void isGoodForInterior()
|
||||
throws MultiblockValidationException;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue