/* * Copyright (c) CovertJaguar, 2014 http://railcraft.info * * This code is the property of CovertJaguar * and may only be used with explicit written * permission unless otherwise specified on the * license page at http://railcraft.info/wiki/info:license. */ package mods.railcraft.common.blocks.signals; import mods.railcraft.api.signals.*; import mods.railcraft.common.plugins.buildcraft.triggers.IAspectProvider; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import static net.minecraftforge.common.util.ForgeDirection.*; public class TileBoxInterlock extends TileBoxBase implements IControllerTile, IReceiverTile, IAspectProvider { private static final ForgeDirection[] SIDES = {NORTH, WEST, SOUTH, EAST}; private final SimpleSignalController controller = new SimpleSignalController(getLocalizationTag(), this); private final SimpleSignalReceiver receiver = new SimpleSignalReceiver(getLocalizationTag(), this); private Interlock interlock = new Interlock(this); private SignalAspect overrideAspect = SignalAspect.RED; public TileBoxInterlock() { } @Override public EnumSignal getSignalType() { return EnumSignal.BOX_INTERLOCK; } @Override public void onControllerAspectChange(SignalController con, SignalAspect aspect) { } @Override public void updateEntity() { super.updateEntity(); if (worldObj.isRemote) { controller.tickClient(); receiver.tickClient(); return; } controller.tickServer(); receiver.tickServer(); overrideAspect = getOverrideAspect(); mergeInterlocks(); interlock.tick(this); SignalAspect prevAspect = controller.getAspect(); if (receiver.isBeingPaired() || controller.isBeingPaired()) controller.setAspect(SignalAspect.BLINK_YELLOW); else if (controller.isPaired() && receiver.isPaired()) controller.setAspect(determineAspect()); else controller.setAspect(SignalAspect.BLINK_RED); if (prevAspect != controller.getAspect()) sendUpdateToClient(); } private void mergeInterlocks() { for (ForgeDirection side : SIDES) { TileEntity tile = tileCache.getTileOnSide(side); if (tile instanceof TileBoxInterlock) { TileBoxInterlock box = (TileBoxInterlock) tile; if (box.interlock != interlock) { box.interlock.merge(interlock); return; } } } } private SignalAspect getOverrideAspect() { SignalAspect newAspect = SignalAspect.GREEN; for (int side = 2; side < 6; side++) { ForgeDirection forgeSide = ForgeDirection.getOrientation(side); TileEntity t = tileCache.getTileOnSide(forgeSide); if (t instanceof TileBoxBase) { TileBoxBase tile = (TileBoxBase) t; if (tile.canTransferAspect()) newAspect = SignalAspect.mostRestrictive(newAspect, tile.getBoxSignalAspect(forgeSide.getOpposite())); } } return newAspect; } private SignalAspect determineAspect() { interlock.requestLock(this, receiver.getAspect().ordinal() <= SignalAspect.YELLOW.ordinal()); return interlock.getAspect(this, receiver.getAspect()); } @Override public SignalAspect getBoxSignalAspect(ForgeDirection side) { return controller.getAspect(); } @Override public void writeToNBT(NBTTagCompound data) { super.writeToNBT(data); controller.writeToNBT(data); receiver.writeToNBT(data); } @Override public void readFromNBT(NBTTagCompound data) { super.readFromNBT(data); controller.readFromNBT(data); receiver.readFromNBT(data); } @Override public void writePacketData(DataOutputStream data) throws IOException { super.writePacketData(data); controller.writePacketData(data); receiver.writePacketData(data); } @Override public void readPacketData(DataInputStream data) throws IOException { super.readPacketData(data); controller.readPacketData(data); receiver.readPacketData(data); markBlockForUpdate(); } @Override public boolean isConnected(ForgeDirection side) { TileEntity tile = tileCache.getTileOnSide(side); if (tile instanceof TileBoxInterlock) return true; if (tile instanceof TileBoxBase) return ((TileBoxBase) tile).canTransferAspect(); return false; } @Override public boolean canTransferAspect() { return false; } @Override public boolean canReceiveAspect() { return true; } @Override public SignalController getController() { return controller; } @Override public SimpleSignalReceiver getReceiver() { return receiver; } @Override public SignalAspect getTriggerAspect() { return getBoxSignalAspect(null); } @Override public List<String> getDebugOutput() { List<String> debug = super.getDebugOutput(); debug.add("Interlock Obj: " + interlock); debug.add("Interlock Pool: " + interlock.interlocks); debug.add("Lock Requests: " + interlock.lockRequests); debug.add("Active: " + interlock.active); debug.add("Delay: " + interlock.delay); debug.add("In Aspect: " + receiver.getAspect().name()); debug.add("Out Aspect: " + controller.getAspect().name()); debug.add("Override Aspect: " + overrideAspect.name()); return debug; } private static class TileComparator implements Comparator<TileBoxInterlock> { public static TileComparator INSTANCE = new TileComparator(); @Override public int compare(TileBoxInterlock o1, TileBoxInterlock o2) { if (o1.xCoord != o2.xCoord) return o1.xCoord - o2.xCoord; if (o1.zCoord != o2.zCoord) return o1.zCoord - o2.zCoord; if (o1.yCoord != o2.yCoord) return o1.yCoord - o2.yCoord; return 0; } } private class Interlock { private static final int DELAY = 20 * 10; private TreeSet<TileBoxInterlock> interlocks = new TreeSet<TileBoxInterlock>(TileComparator.INSTANCE); private TreeSet<TileBoxInterlock> lockRequests = new TreeSet<TileBoxInterlock>(TileComparator.INSTANCE); private TileBoxInterlock active; private int delay; public Interlock(TileBoxInterlock tile) { interlocks.add(tile); } public void merge(Interlock interlock) { interlocks.addAll(interlock.interlocks); for (TileBoxInterlock box : interlocks) { box.interlock = this; } } public void tick(TileBoxInterlock host) { Iterator<TileBoxInterlock> it = interlocks.iterator(); while (it.hasNext()) { TileBoxInterlock box = it.next(); if (box.isInvalid()) { it.remove(); } } if (delay < DELAY) { delay++; return; } if (active != null && active.isInvalid()) active = null; if (active == null && !lockRequests.isEmpty() && interlocks.first() == host) { active = lockRequests.last(); lockRequests.clear(); } } public void requestLock(TileBoxInterlock host, boolean request) { if (request) lockRequests.add(host); else if (active == host) active = null; } public SignalAspect getAspect(TileBoxInterlock host, SignalAspect requestedAspect) { if (host == active) { SignalAspect overrideAspect = SignalAspect.GREEN; for (TileBoxInterlock box : interlocks) { overrideAspect = SignalAspect.mostRestrictive(overrideAspect, box.overrideAspect); } return SignalAspect.mostRestrictive(overrideAspect, requestedAspect); } return SignalAspect.RED; } } }