this.breakPhysics = false;
try {
// Prevent index exceptions: remove if not a train
if (this.isEmpty()) {
this.remove();
throw new GroupUnloadedException();
}
// Validate members and set max speed
for (MinecartMember<?> mm : this) {
mm.checkMissing();
mm.getEntity().setMaxSpeed(this.getProperties().getSpeedLimit() / (double) stepcount);
}
// Set up a valid network controller if needed
if (networkInvalid.clear()) {
for (MinecartMember<?> m : this) {
EntityNetworkController<?> controller = m.getEntity().getNetworkController();
if (!(controller instanceof MinecartMemberNetwork)) {
m.getEntity().setNetworkController(new MinecartMemberNetwork());
}
}
}
// Update some per-tick stuff
if (this.teleportImmunityTick > 0) {
this.teleportImmunityTick--;
}
// Update direction and executed actions prior to updates
this.updateDirection();
this.getActions().doTick();
for (MinecartMember<?> member : this) {
member.getActions().doTick();
}
// Perform block updates prior to doing the movement calculations
// First initialize all blocks and handle block change event
for (MinecartMember<?> member : this) {
member.onPhysicsStart();
}
this.getBlockTracker().refresh();
// Perform block change Minecart logic, also take care of potential new block changes
for (MinecartMember<?> member : this) {
member.checkMissing();
if (member.hasBlockChanged() | member.forcedBlockUpdate.clear()) {
// Perform events and logic - validate along the way
MemberBlockChangeEvent.call(member, member.getLastBlock(), member.getBlock());
member.checkMissing();
member.onBlockChange(member.getLastBlock(), member.getBlock());
this.getBlockTracker().updatePosition();
member.checkMissing();
}
}
this.getBlockTracker().refresh();
if (!this.doConnectionCheck(stepcount)) {
return false;
}
this.updateDirection();
// Perform velocity updates
for (MinecartMember<?> m : this) {
m.onPhysicsPreMove();
}
// Direction can change as a result of gravity
this.updateDirection();
if (this.size() == 1) {
//Simplified calculation for single carts
this.head().onPhysicsPostMove(1);
} else {
//Get the average forwarding force of all carts
double force = this.getAverageForce();
//Perform forward force or not? First check if we are not messing up...
boolean performUpdate = true;
for (int i = 0; i < this.size() - 1; i++) {
if (!head(i + 1).isFollowingOnTrack(head(i))) {
performUpdate = false;
break;
}
}
if (performUpdate) {
//update force
for (MinecartMember<?> m : this) {
m.setForwardForce(force);
}
}
//Apply force factors to carts from last cart and perform post positional updates
if (this.size() < 2) return false;
int i = 1;
double distance, threshold, forcer;
MinecartMember<?> after;
for (MinecartMember<?> member : this) {
after = this.get(i);
distance = member.getEntity().loc.distance(after.getEntity());
if (member.getDirectionDifference(after) >= 45 || member.getEntity().loc.getPitchDifference(after.getEntity()) > 10) {
threshold = TrainCarts.turnedCartDistance;
forcer = TrainCarts.turnedCartDistanceForcer;
} else {
threshold = TrainCarts.cartDistance;
forcer = TrainCarts.cartDistanceForcer;
}
if (distance < threshold) {
forcer *= TrainCarts.nearCartDistanceFactor;
}
member.onPhysicsPostMove(1 + (forcer * (threshold - distance)));
if (this.breakPhysics) return true;
if (i++ == this.size() - 1) {
this.tail().onPhysicsPostMove(1);
if (this.breakPhysics) return true;
break;
}
}
}
// Update directions and perform connection checks after the position changes
this.updateDirection();
if (!this.doConnectionCheck(stepcount)) {
return false;
}
// Check whether chunks are loaded, and load them if needed
// If chunks are not kept loaded, the member will unload the entire train
previousChunksBuffer.clear();
newChunksBuffer.clear();
for (MinecartMember<?> mm : this) {
mm.updateChunks(previousChunksBuffer, newChunksBuffer);
}
int cx, cz;
IntVector2 chunk;
final World world = getWorld();
Iterator<IntVector2> iter;
if (this.canUnload()) {
// Check whether the new chunks are unloaded
iter = newChunksBuffer.iterator();
while (iter.hasNext()) {
chunk = iter.next();
cx = chunk.x;
cz = chunk.z;
if (!world.isChunkLoaded(cx, cz)) {
this.unload();
throw new GroupUnloadedException();
}
}
} else {
// Mark previous chunks for unload
iter = previousChunksBuffer.iterator();