/**
* Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
*
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.transport;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraftforge.common.util.ForgeDirection;
import buildcraft.BuildCraftTransport;
import buildcraft.api.core.IIconProvider;
import buildcraft.api.gates.IGate;
import buildcraft.api.statements.ActionState;
import buildcraft.api.statements.IActionInternal;
import buildcraft.api.transport.IPipe;
import buildcraft.api.transport.IPipeTile;
import buildcraft.api.transport.PipeWire;
import buildcraft.core.IDropControlInventory;
import buildcraft.core.inventory.InvUtils;
import buildcraft.core.network.TilePacketWrapper;
import buildcraft.core.utils.Utils;
import buildcraft.transport.gates.GateFactory;
import buildcraft.transport.gates.StatementSlot;
import buildcraft.transport.pipes.events.PipeEvent;
import buildcraft.transport.statements.ActionValve.ValveState;
public abstract class Pipe<T extends PipeTransport> implements IDropControlInventory, IPipe {
@SuppressWarnings("rawtypes")
private static Map<Class, TilePacketWrapper> networkWrappers = new HashMap<Class, TilePacketWrapper>();
private static Map<Class<? extends Pipe>, Map<Class<? extends PipeEvent>, EventHandler>> eventHandlers = new HashMap<Class<? extends Pipe>, Map<Class<? extends PipeEvent>, EventHandler>>();
public int[] signalStrength = new int[]{0, 0, 0, 0};
public TileGenericPipe container;
public final T transport;
public final Item item;
public boolean[] wireSet = new boolean[]{false, false, false, false};
public final Gate[] gates = new Gate[ForgeDirection.VALID_DIRECTIONS.length];
private boolean internalUpdateScheduled = false;
private boolean initialized = false;
private ArrayList<ActionState> actionStates = new ArrayList<ActionState>();
public Pipe(T transport, Item item) {
this.transport = transport;
this.item = item;
if (!networkWrappers.containsKey(this.getClass())) {
networkWrappers
.put(this.getClass(), new TilePacketWrapper(new Class[]{TileGenericPipe.class, this.transport.getClass()}));
}
}
public void setTile(TileEntity tile) {
this.container = (TileGenericPipe) tile;
transport.setTile((TileGenericPipe) tile);
}
public void resolveActions() {
for (Gate gate : gates) {
if (gate != null) {
gate.resolveActions();
}
}
}
// public final void handlePipeEvent(PipeEvent event) {
// try {
// Method method = getClass().getDeclaredMethod("eventHandler", event.getClass());
// method.invoke(this, event);
// } catch (Exception ex) {
// }
// }
private static class EventHandler {
public final Method method;
public EventHandler(Method method) {
this.method = method;
}
}
public final void handlePipeEvent(PipeEvent event) {
Map<Class<? extends PipeEvent>, EventHandler> handlerMap = eventHandlers.get(getClass());
if (handlerMap == null) {
handlerMap = new HashMap<Class<? extends PipeEvent>, EventHandler>();
eventHandlers.put(getClass(), handlerMap);
}
EventHandler handler = handlerMap.get(event.getClass());
if (handler == null) {
handler = makeEventHandler(event, handlerMap);
}
if (handler.method == null) {
return;
}
try {
handler.method.invoke(this, event);
} catch (Exception ex) {
}
}
private EventHandler makeEventHandler(PipeEvent event, Map<Class<? extends PipeEvent>, EventHandler> handlerMap) {
EventHandler handler;
try {
Method method = getClass().getDeclaredMethod("eventHandler", event.getClass());
handler = new EventHandler(method);
} catch (Exception ex) {
handler = new EventHandler(null);
}
handlerMap.put(event.getClass(), handler);
return handler;
}
public boolean blockActivated(EntityPlayer entityplayer) {
return false;
}
public void onBlockPlaced() {
transport.onBlockPlaced();
}
public void onBlockPlacedBy(EntityLivingBase placer) {
}
public void onNeighborBlockChange(int blockId) {
transport.onNeighborBlockChange(blockId);
}
public boolean canPipeConnect(TileEntity tile, ForgeDirection side) {
Pipe<?> otherPipe;
if (tile instanceof TileGenericPipe) {
otherPipe = ((TileGenericPipe) tile).pipe;
if (!BlockGenericPipe.isFullyDefined(otherPipe)) {
return false;
}
if (!PipeConnectionBans.canPipesConnect(getClass(), otherPipe.getClass())) {
return false;
}
}
return transport.canPipeConnect(tile, side);
}
/**
* Should return the textureindex used by the Pipe Item Renderer, as this is
* done client-side the default implementation might not work if your
* getTextureIndex(Orienations.Unknown) has logic. Then override this
*/
public int getIconIndexForItem() {
return getIconIndex(ForgeDirection.UNKNOWN);
}
/**
* Should return the IIconProvider that provides icons for this pipe
*
* @return An array of icons
*/
@SideOnly(Side.CLIENT)
public abstract IIconProvider getIconProvider();
/**
* Should return the index in the array returned by GetTextureIcons() for a
* specified direction
*
* @param direction - The direction for which the indexed should be
* rendered. Unknown for pipe center
*
* @return An index valid in the array returned by getTextureIcons()
*/
public abstract int getIconIndex(ForgeDirection direction);
public void updateEntity() {
transport.updateEntity();
if (internalUpdateScheduled) {
internalUpdate();
internalUpdateScheduled = false;
}
actionStates.clear();
// Update the gate if we have any
for (Gate gate : gates) {
if (gate == null) {
continue;
}
if (container.getWorldObj().isRemote) {
// on client, only update the graphical pulse if needed
gate.updatePulse();
} else {
// on server, do the internal gate update
gate.resolveActions();
gate.tick();
}
}
}
private void internalUpdate() {
updateSignalState();
}
public void writeToNBT(NBTTagCompound data) {
transport.writeToNBT(data);
// Save gate if any
for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) {
final String key = "Gate[" + i + "]";
Gate gate = gates[i];
if (gate != null) {
NBTTagCompound gateNBT = new NBTTagCompound();
gate.writeToNBT(gateNBT);
data.setTag(key, gateNBT);
} else {
data.removeTag(key);
}
}
for (int i = 0; i < 4; ++i) {
data.setBoolean("wireSet[" + i + "]", wireSet[i]);
}
}
public void readFromNBT(NBTTagCompound data) {
transport.readFromNBT(data);
for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) {
final String key = "Gate[" + i + "]";
gates[i] = data.hasKey(key) ? GateFactory.makeGate(this, data.getCompoundTag(key)) : null;
}
// Legacy support
if (data.hasKey("Gate")) {
transport.container.setGate(GateFactory.makeGate(this, data.getCompoundTag("Gate")), 0);
data.removeTag("Gate");
}
for (int i = 0; i < 4; ++i) {
wireSet[i] = data.getBoolean("wireSet[" + i + "]");
}
}
public boolean needsInit() {
return !initialized;
}
public void initialize() {
transport.initialize();
updateSignalState();
initialized = true;
}
private void readNearbyPipesSignal(PipeWire color) {
boolean foundBiggerSignal = false;
for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) {
TileEntity tile = container.getTile(o);
if (tile instanceof TileGenericPipe) {
TileGenericPipe tilePipe = (TileGenericPipe) tile;
if (BlockGenericPipe.isFullyDefined(tilePipe.pipe)) {
if (isWireConnectedTo(tile, color)) {
foundBiggerSignal |= receiveSignal(tilePipe.pipe.signalStrength[color.ordinal()] - 1, color);
}
}
}
}
if (!foundBiggerSignal && signalStrength[color.ordinal()] != 0) {
signalStrength[color.ordinal()] = 0;
// worldObj.markBlockNeedsUpdate(container.xCoord, container.yCoord, zCoord);
container.scheduleRenderUpdate();
for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) {
TileEntity tile = container.getTile(o);
if (tile instanceof TileGenericPipe) {
TileGenericPipe tilePipe = (TileGenericPipe) tile;
if (BlockGenericPipe.isFullyDefined(tilePipe.pipe)) {
tilePipe.pipe.internalUpdateScheduled = true;
}
}
}
}
}
public void updateSignalState() {
for (PipeWire c : PipeWire.values()) {
updateSignalStateForColor(c);
}
}
private void updateSignalStateForColor(PipeWire wire) {
if (!wireSet[wire.ordinal()]) {
return;
}
// STEP 1: compute internal signal strength
boolean readNearbySignal = true;
for (Gate gate : gates) {
if (gate != null && gate.broadcastSignal.get(wire.ordinal())) {
receiveSignal(255, wire);
readNearbySignal = false;
}
}
if (readNearbySignal) {
readNearbyPipesSignal(wire);
}
// STEP 2: transmit signal in nearby blocks
if (signalStrength[wire.ordinal()] > 1) {
for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) {
TileEntity tile = container.getTile(o);
if (tile instanceof TileGenericPipe) {
TileGenericPipe tilePipe = (TileGenericPipe) tile;
if (BlockGenericPipe.isFullyDefined(tilePipe.pipe) && tilePipe.pipe.wireSet[wire.ordinal()]) {
if (isWireConnectedTo(tile, wire)) {
tilePipe.pipe.receiveSignal(signalStrength[wire.ordinal()] - 1, wire);
}
}
}
}
}
}
private boolean receiveSignal(int signal, PipeWire color) {
if (container.getWorldObj() == null) {
return false;
}
int oldSignal = signalStrength[color.ordinal()];
if (signal >= signalStrength[color.ordinal()] && signal != 0) {
signalStrength[color.ordinal()] = signal;
internalUpdateScheduled = true;
if (oldSignal == 0) {
container.scheduleRenderUpdate();
}
return true;
} else {
return false;
}
}
public boolean inputOpen(ForgeDirection from) {
return transport.inputOpen(from);
}
public boolean outputOpen(ForgeDirection to) {
return transport.outputOpen(to);
}
public void onEntityCollidedWithBlock(Entity entity) {
}
public boolean canConnectRedstone() {
for (Gate gate : gates) {
if (gate != null) {
return true;
}
}
return false;
}
public int getMaxRedstoneOutput(ForgeDirection dir) {
int output = 0;
for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
output = Math.max(output, getRedstoneOutput(side));
if (side == dir) {
output = Math.max(output, getRedstoneOutputSide(side));
}
}
return output;
}
private int getRedstoneOutput(ForgeDirection dir) {
Gate gate = gates[dir.ordinal()];
return gate != null ? gate.getRedstoneOutput() : 0;
}
private int getRedstoneOutputSide(ForgeDirection dir) {
Gate gate = gates[dir.ordinal()];
return gate != null ? gate.getSidedRedstoneOutput() : 0;
}
public int isPoweringTo(int side) {
ForgeDirection o = ForgeDirection.getOrientation(side).getOpposite();
TileEntity tile = container.getTile(o);
if (tile instanceof TileGenericPipe && container.isPipeConnected(o)) {
return 0;
} else {
return getMaxRedstoneOutput(o);
}
}
public int isIndirectlyPoweringTo(int l) {
return isPoweringTo(l);
}
public void randomDisplayTick(Random random) {
}
@Override
public boolean isWired(PipeWire color) {
return wireSet[color.ordinal()];
}
@Override
public boolean isWireActive(PipeWire color) {
return signalStrength[color.ordinal()] > 0;
}
@Deprecated
public boolean hasGate() {
for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
if (hasGate(direction)) {
return true;
}
}
return false;
}
public boolean hasGate(ForgeDirection side) {
return container.hasGate(side);
}
protected void notifyBlocksOfNeighborChange(ForgeDirection side) {
container.getWorldObj().notifyBlocksOfNeighborChange(container.xCoord + side.offsetX, container.yCoord + side.offsetY, container.zCoord + side.offsetZ, BuildCraftTransport.genericPipeBlock);
}
protected void updateNeighbors(boolean needSelf) {
if (needSelf) {
container.getWorldObj().notifyBlocksOfNeighborChange(container.xCoord, container.yCoord, container.zCoord, BuildCraftTransport.genericPipeBlock);
}
for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
notifyBlocksOfNeighborChange(side);
}
}
public void dropItem(ItemStack stack) {
InvUtils.dropItems(container.getWorldObj(), stack, container.xCoord, container.yCoord, container.zCoord);
}
public ArrayList<ItemStack> computeItemDrop() {
ArrayList<ItemStack> result = new ArrayList<ItemStack>();
for (PipeWire pipeWire : PipeWire.VALUES) {
if (wireSet[pipeWire.ordinal()]) {
result.add(pipeWire.getStack());
}
}
for (Gate gate : gates) {
if (gate != null) {
result.add(gate.getGateItem());
}
}
for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
if (container.hasFacade(direction)) {
result.add (container.getFacade(direction));
}
if (container.hasPlug(direction)) {
result.add (new ItemStack(BuildCraftTransport.plugItem));
}
if (container.hasRobotStation(direction)) {
result.add (new ItemStack(BuildCraftTransport.robotStationItem));
}
}
return result;
}
public LinkedList<IActionInternal> getActions() {
LinkedList<IActionInternal> result = new LinkedList<IActionInternal>();
for (ValveState state : ValveState.VALUES) {
result.add(BuildCraftTransport.actionValve[state.ordinal()]);
}
return result;
}
public void resetGates() {
for (int i = 0; i < gates.length; i++) {
Gate gate = gates[i];
if (gate != null) {
gate.resetGate();
}
gates[i] = null;
}
internalUpdateScheduled = true;
container.scheduleRenderUpdate();
}
protected void actionsActivated(Collection<StatementSlot> actions) {
}
public TileGenericPipe getContainer() {
return container;
}
public boolean isWireConnectedTo(TileEntity tile, PipeWire color) {
if (!(tile instanceof TileGenericPipe)) {
return false;
}
TileGenericPipe tilePipe = (TileGenericPipe) tile;
if (!BlockGenericPipe.isFullyDefined(tilePipe.pipe)) {
return false;
}
if (!tilePipe.pipe.wireSet[color.ordinal()]) {
return false;
}
return tilePipe.pipe.transport instanceof PipeTransportStructure || transport instanceof PipeTransportStructure
|| Utils.checkPipesConnections(
container, tile);
}
public void dropContents() {
transport.dropContents();
}
public List<ItemStack> getDroppedItems() {
return transport.getDroppedItems();
}
/**
* If this pipe is open on one side, return it.
*/
public ForgeDirection getOpenOrientation() {
int connectionsNum = 0;
ForgeDirection targetOrientation = ForgeDirection.UNKNOWN;
for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) {
if (container.isPipeConnected(o)) {
connectionsNum++;
if (connectionsNum == 1) {
targetOrientation = o;
}
}
}
if (connectionsNum > 1 || connectionsNum == 0) {
return ForgeDirection.UNKNOWN;
}
return targetOrientation.getOpposite();
}
@Override
public boolean doDrop() {
return true;
}
/**
* Called when TileGenericPipe.invalidate() is called
*/
public void invalidate() {
}
/**
* Called when TileGenericPipe.validate() is called
*/
public void validate() {
}
/**
* Called when TileGenericPipe.onChunkUnload is called
*/
public void onChunkUnload() {
}
public World getWorld() {
return container.getWorldObj();
}
@Override
public int x() {
return container.xCoord;
}
@Override
public int y() {
return container.yCoord;
}
@Override
public int z() {
return container.zCoord;
}
@Override
public IPipeTile getTile() {
return container;
}
@Override
public IGate getGate(ForgeDirection side) {
if (side == ForgeDirection.UNKNOWN) {
return null;
}
return gates[side.ordinal()];
}
private void pushActionState(ActionState state) {
actionStates.add(state);
}
private Collection<ActionState> getActionStates() {
return actionStates;
}
}