blob: 2fa39169538f860a9f216c86d150a25b9f9b166e [file] [log] [blame] [raw]
package li.cil.oc.driver.redstoneinmotion;
import li.cil.oc.api.network.Arguments;
import li.cil.oc.api.network.Callback;
import li.cil.oc.api.network.Context;
import li.cil.oc.api.network.ManagedEnvironment;
import li.cil.oc.api.prefab.DriverTileEntity;
import li.cil.oc.driver.ManagedTileEntityEnvironment;
import li.cil.oc.util.Reflection;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import java.util.HashMap;
import java.util.Map;
public final class DriverCarriageController extends DriverTileEntity {
private static final Class<?> CarriageControllerEntity = Reflection.getClass("JAKJ.RedstoneInMotion.CarriageControllerEntity");
private static final Class<?> CarriageObstructionException = Reflection.getClass("JAKJ.RedstoneInMotion.CarriageObstructionException");
private static final Class<?> Directions = Reflection.getClass("JAKJ.RedstoneInMotion.Directions");
@Override
public Class<?> getTileEntityClass() {
return CarriageControllerEntity;
}
@Override
public ManagedEnvironment createEnvironment(final World world, final int x, final int y, final int z) {
return new Environment(world.getBlockTileEntity(x, y, z));
}
public static final class Environment extends ManagedTileEntityEnvironment<TileEntity> {
private boolean isAnchored;
// Arguments for an actual move, stored here until we hit the next
// call to update(). See below for more information as to why.
private boolean shouldMove;
private boolean isSimulating;
private int direction;
// Used to check whether we should send a success signal after loading.
private boolean isMoving;
// Used to delay success signals after a move to make sure the computer
// that triggered the move is reachable again.
private int signalDelay = 10;
public Environment(final TileEntity tileEntity) {
super(tileEntity, "carriage");
}
@Callback(direct = true)
public Object[] getAnchored(final Context context, final Arguments args) {
return new Object[]{isAnchored};
}
@Callback
public Object[] setAnchored(final Context context, final Arguments args) {
isAnchored = args.checkBoolean(0);
return new Object[]{isAnchored};
}
@Callback
public Object[] move(final Context context, final Arguments args) {
// We execute moves in the update() call to the environment instead
// of in here, because the move may cause the calling computer to
// be persisted - which is not possible while it has an active call
// (namely to this function).
direction = checkDirection(args);
isSimulating = args.count() > 1 && args.checkBoolean(1);
shouldMove = true;
context.pause(0.1);
return new Object[]{true};
}
@Callback
public Object[] simulate(final Context context, final Arguments args) {
// IMPORTANT: we have to do the simulation asynchronously, too,
// because that may also try to persist the computer that called us,
// and it must not be running when we do that.
direction = checkDirection(args);
isSimulating = true;
shouldMove = true;
context.pause(0.1);
return new Object[]{true};
}
@Override
public boolean canUpdate() {
return true;
}
@Override
public void update() {
if (node != null && node.network() != null && isMoving) {
--signalDelay;
if (signalDelay <= 0) {
isMoving = false;
node.sendToReachable("computer.signal", "carriage_moved", true);
}
}
if (shouldMove) {
shouldMove = false;
isMoving = true;
try {
Reflection.invoke(tileEntity, "SetupMotion", Directions.getEnumConstants()[direction], isSimulating, isAnchored);
Reflection.invoke(tileEntity, "Move");
if (isSimulating) {
node.sendToReachable("computer.signal", "carriage_moved", true);
}
} catch (final Throwable e) {
if (CarriageObstructionException != null && CarriageObstructionException.isAssignableFrom(e.getClass())) {
try {
final int x = (Integer) Reflection.get(e, "X");
final int y = (Integer) Reflection.get(e, "Y");
final int z = (Integer) Reflection.get(e, "Z");
node.sendToReachable("computer.signal", "carriage_moved", false, e.getMessage() != null ? e.getMessage() : e.toString(), x, y, z);
} catch (Throwable e2) {
node.sendToReachable("computer.signal", "carriage_moved", false, e2.getMessage() != null ? e2.getMessage() : e2.toString());
}
} else {
node.sendToReachable("computer.signal", "carriage_moved", false, e.getMessage() != null ? e.getMessage() : e.toString());
}
} finally {
// At this point we have already been saved if the move was
// successful, so we can safely always revert this to false.
isMoving = false;
}
}
}
@Override
public void load(final NBTTagCompound nbt) {
super.load(nbt);
isMoving = nbt.getBoolean("moving");
isAnchored = nbt.getBoolean("anchored");
}
@Override
public void save(final NBTTagCompound nbt) {
super.save(nbt);
nbt.setBoolean("moving", isMoving);
nbt.setBoolean("anchored", isAnchored);
}
private int checkDirection(final Arguments args) {
if (shouldMove || isMoving) {
throw new RuntimeException("already moving");
}
if (args.isString(0)) {
final String name = args.checkString(0).toLowerCase();
if (!sideNames.containsKey(name)) {
throw new IllegalArgumentException("invalid direction");
}
return sideNames.get(name);
} else {
final int index = args.checkInteger(0);
if (index < 0 || index > 5) {
throw new IllegalArgumentException("invalid direction");
}
return index;
}
}
private static final Map<String, Integer> sideNames;
static {
sideNames = new HashMap<String, Integer>();
sideNames.put("negy", 0);
sideNames.put("posy", 1);
sideNames.put("negz", 2);
sideNames.put("posz", 3);
sideNames.put("negx", 4);
sideNames.put("posx", 5);
sideNames.put("down", 0);
sideNames.put("up", 1);
sideNames.put("north", 2);
sideNames.put("south", 3);
sideNames.put("west", 4);
sideNames.put("east", 5);
}
}
}