|  | package li.cil.occ.handler.redstoneinmotion; | 
|  |  | 
|  | import li.cil.oc.api.driver.NamedBlock; | 
|  | 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.occ.handler.ManagedTileEntityEnvironment; | 
|  | import li.cil.occ.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 implements NamedBlock { | 
|  | 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 String preferredName() { | 
|  | return "carriage"; | 
|  | } | 
|  |  | 
|  | @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); | 
|  | } | 
|  | } | 
|  | } |