buildcraft.transport.Pipe.java Source code

Java tutorial

Introduction

Here is the source code for buildcraft.transport.Pipe.java

Source

/**
 * Copyright (c) SpaceToad, 2011
 * 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.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeDirection;
import buildcraft.BuildCraftTransport;
import buildcraft.api.core.IIconProvider;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.api.gates.ActionManager;
import buildcraft.api.gates.IAction;
import buildcraft.api.gates.IActionReceptor;
import buildcraft.api.gates.ITrigger;
import buildcraft.api.gates.ITriggerParameter;
import buildcraft.api.gates.TriggerParameter;
import buildcraft.api.transport.IPipe;
import buildcraft.core.IDropControlInventory;
import buildcraft.core.network.TilePacketWrapper;
import buildcraft.core.triggers.ActionRedstoneOutput;
import buildcraft.core.utils.Utils;
import buildcraft.transport.Gate.GateConditional;
import buildcraft.transport.pipes.PipeLogic;
import buildcraft.transport.triggers.ActionSignalOutput;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

public abstract class Pipe implements IPipe, IDropControlInventory {

    public int[] signalStrength = new int[] { 0, 0, 0, 0 };

    public int xCoord;
    public int yCoord;
    public int zCoord;
    public World worldObj;
    public TileGenericPipe container;

    public final PipeTransport transport;
    public final PipeLogic logic;
    public final int itemID;

    private boolean internalUpdateScheduled = false;

    public boolean[] wireSet = new boolean[] { false, false, false, false };

    public Gate gate;

    @SuppressWarnings("rawtypes")
    private static Map<Class, TilePacketWrapper> networkWrappers = new HashMap<Class, TilePacketWrapper>();

    public ITrigger[] activatedTriggers = new ITrigger[8];
    public ITriggerParameter[] triggerParameters = new ITriggerParameter[8];
    public IAction[] activatedActions = new IAction[8];

    public boolean broadcastSignal[] = new boolean[] { false, false, false, false };
    public boolean broadcastRedstone = false;

    public SafeTimeTracker actionTracker = new SafeTimeTracker();

    public Pipe(PipeTransport transport, PipeLogic logic, int itemID) {
        this.transport = transport;
        this.logic = logic;
        this.itemID = itemID;

        if (!networkWrappers.containsKey(this.getClass())) {
            networkWrappers.put(this.getClass(), new TilePacketWrapper(
                    new Class[] { TileGenericPipe.class, this.transport.getClass(), this.logic.getClass() }));
        }

    }

    private void setPosition(int xCoord, int yCoord, int zCoord) {
        this.xCoord = xCoord;
        this.yCoord = yCoord;
        this.zCoord = zCoord;

        transport.setPosition(xCoord, yCoord, zCoord);
        logic.setPosition(xCoord, yCoord, zCoord);
    }

    private void setWorld(World worldObj) {
        if (worldObj != null && this.worldObj == null) {
            this.worldObj = worldObj;
            transport.setWorld(worldObj);
            logic.setWorld(worldObj);
        }
    }

    public void setTile(TileEntity tile) {

        this.container = (TileGenericPipe) tile;

        transport.setTile((TileGenericPipe) tile);
        logic.setTile((TileGenericPipe) tile);

        setPosition(tile.xCoord, tile.yCoord, tile.zCoord);
        setWorld(tile.worldObj);
    }

    public boolean blockActivated(World world, int i, int j, int k, EntityPlayer entityplayer) {
        return logic.blockActivated(entityplayer);
    }

    public void onBlockPlaced() {
        logic.onBlockPlaced();
        transport.onBlockPlaced();
    }

    public void onBlockPlacedBy(EntityLiving placer) {
    }

    public void onNeighborBlockChange(int blockId) {
        logic.onNeighborBlockChange(blockId);
        transport.onNeighborBlockChange(blockId);

        updateSignalState();
    }

    public boolean canPipeConnect(TileEntity tile, ForgeDirection side) {
        return logic.canPipeConnect(tile, side) && 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
     *
     * @return
     */
    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();
        logic.updateEntity();

        if (internalUpdateScheduled) {
            internalUpdate();
            internalUpdateScheduled = false;
        }

        // Do not try to update gates client side.
        if (worldObj.isRemote)
            return;

        if (actionTracker.markTimeIfDelay(worldObj, 10)) {
            resolveActions();
        }

        // Update the gate if we have any
        if (gate != null) {
            gate.update();
        }

    }

    private void internalUpdate() {
        updateSignalState();
    }

    public void writeToNBT(NBTTagCompound nbttagcompound) {
        transport.writeToNBT(nbttagcompound);
        logic.writeToNBT(nbttagcompound);

        // Save pulser if any
        if (gate != null) {
            NBTTagCompound nbttagcompoundC = new NBTTagCompound();
            gate.writeToNBT(nbttagcompoundC);
            nbttagcompound.setTag("Gate", nbttagcompoundC);
        }

        for (int i = 0; i < 4; ++i) {
            nbttagcompound.setBoolean("wireSet[" + i + "]", wireSet[i]);
        }

        for (int i = 0; i < 8; ++i) {
            nbttagcompound.setInteger("action[" + i + "]",
                    activatedActions[i] != null ? activatedActions[i].getId() : 0);
            nbttagcompound.setInteger("trigger[" + i + "]",
                    activatedTriggers[i] != null ? activatedTriggers[i].getId() : 0);
        }

        for (int i = 0; i < 8; ++i)
            if (triggerParameters[i] != null) {
                NBTTagCompound cpt = new NBTTagCompound();
                triggerParameters[i].writeToNBT(cpt);
                nbttagcompound.setTag("triggerParameters[" + i + "]", cpt);
            }
    }

    public void readFromNBT(NBTTagCompound nbttagcompound) {
        transport.readFromNBT(nbttagcompound);
        logic.readFromNBT(nbttagcompound);

        // Load pulser if any
        if (nbttagcompound.hasKey("Gate")) {
            NBTTagCompound nbttagcompoundP = nbttagcompound.getCompoundTag("Gate");
            gate = new GateVanilla(this);
            gate.readFromNBT(nbttagcompoundP);
        } else if (nbttagcompound.hasKey("gateKind")) {
            // Legacy implementation
            Gate.GateKind kind = Gate.GateKind.values()[nbttagcompound.getInteger("gateKind")];
            if (kind != Gate.GateKind.None) {
                gate = new GateVanilla(this);
                gate.kind = kind;
            }
        }

        for (int i = 0; i < 4; ++i) {
            wireSet[i] = nbttagcompound.getBoolean("wireSet[" + i + "]");
        }

        for (int i = 0; i < 8; ++i) {
            activatedActions[i] = ActionManager.actions[nbttagcompound.getInteger("action[" + i + "]")];
            activatedTriggers[i] = ActionManager.triggers[nbttagcompound.getInteger("trigger[" + i + "]")];
        }

        for (int i = 0; i < 8; ++i)
            if (nbttagcompound.hasKey("triggerParameters[" + i + "]")) {
                triggerParameters[i] = new TriggerParameter();
                triggerParameters[i].readFromNBT(nbttagcompound.getCompoundTag("triggerParameters[" + i + "]"));
            }
    }

    private boolean initialized = false;

    public void initialize() {
        if (!initialized) {
            transport.initialize();
            logic.initialize();
            initialized = true;
            updateSignalState();
        }
    }

    private void readNearbyPipesSignal(WireColor 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(xCoord, 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;
                    }
                }
            }
        }
    }

    private void updateSignalState() {
        for (IPipe.WireColor c : IPipe.WireColor.values()) {
            updateSignalStateForColor(c);
        }
    }

    private void updateSignalStateForColor(IPipe.WireColor color) {
        if (!wireSet[color.ordinal()])
            return;

        // STEP 1: compute internal signal strength

        if (broadcastSignal[color.ordinal()]) {
            receiveSignal(255, color);
        } else {
            readNearbyPipesSignal(color);
        }

        // STEP 2: transmit signal in nearby blocks

        if (signalStrength[color.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[color.ordinal()])
                        if (isWireConnectedTo(tile, color)) {
                            tilePipe.pipe.receiveSignal(signalStrength[color.ordinal()] - 1, color);
                        }
                }
            }
        }
    }

    private boolean receiveSignal(int signal, IPipe.WireColor color) {
        if (worldObj == null)
            return false;

        int oldSignal = signalStrength[color.ordinal()];

        if (signal >= signalStrength[color.ordinal()] && signal != 0) {
            signalStrength[color.ordinal()] = signal;
            internalUpdateScheduled = true;

            if (oldSignal == 0) {
                // worldObj.markBlockNeedsUpdate(xCoord, yCoord, zCoord);
                container.scheduleRenderUpdate();

            }

            return true;
        } else
            return false;
    }

    public boolean inputOpen(ForgeDirection from) {
        return transport.inputOpen(from) && logic.inputOpen(from);
    }

    public boolean outputOpen(ForgeDirection to) {
        return transport.outputOpen(to) && logic.outputOpen(to);
    }

    public void onEntityCollidedWithBlock(Entity entity) {

    }

    public boolean canConnectRedstone() {
        if (hasGate())
            return true;

        return false;
    }

    public int isPoweringTo(int l) {
        if (!broadcastRedstone)
            return 0;

        ForgeDirection o = ForgeDirection.values()[l].getOpposite();
        TileEntity tile = container.getTile(o);

        if (tile instanceof TileGenericPipe && Utils.checkPipesConnections(this.container, tile))
            return 0;

        return 15;
    }

    public int isIndirectlyPoweringTo(int l) {
        return isPoweringTo(l);
    }

    public void randomDisplayTick(Random random) {
    }

    // / @Override TODO: should be in IPipe
    public boolean isWired() {
        for (WireColor color : WireColor.values())
            if (isWired(color))
                return true;

        return false;
    }

    @Override
    public boolean isWired(WireColor color) {
        return wireSet[color.ordinal()];
    }

    @Override
    public boolean hasInterface() {
        return hasGate();
    }

    public boolean hasGate() {
        return gate != null;
    }

    protected void updateNeighbors(boolean needSelf) {
        if (needSelf) {
            worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord,
                    BuildCraftTransport.genericPipeBlock.blockID);
        }
        worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord - 1, zCoord,
                BuildCraftTransport.genericPipeBlock.blockID);
        worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord + 1, zCoord,
                BuildCraftTransport.genericPipeBlock.blockID);
        worldObj.notifyBlocksOfNeighborChange(xCoord - 1, yCoord, zCoord,
                BuildCraftTransport.genericPipeBlock.blockID);
        worldObj.notifyBlocksOfNeighborChange(xCoord + 1, yCoord, zCoord,
                BuildCraftTransport.genericPipeBlock.blockID);
        worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord - 1,
                BuildCraftTransport.genericPipeBlock.blockID);
        worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord + 1,
                BuildCraftTransport.genericPipeBlock.blockID);
    }

    public void onBlockRemoval() {
        if (wireSet[IPipe.WireColor.Red.ordinal()]) {
            Utils.dropItems(worldObj, new ItemStack(BuildCraftTransport.redPipeWire), xCoord, yCoord, zCoord);
        }

        if (wireSet[IPipe.WireColor.Blue.ordinal()]) {
            Utils.dropItems(worldObj, new ItemStack(BuildCraftTransport.bluePipeWire), xCoord, yCoord, zCoord);
        }

        if (wireSet[IPipe.WireColor.Green.ordinal()]) {
            Utils.dropItems(worldObj, new ItemStack(BuildCraftTransport.greenPipeWire), xCoord, yCoord, zCoord);
        }

        if (wireSet[IPipe.WireColor.Yellow.ordinal()]) {
            Utils.dropItems(worldObj, new ItemStack(BuildCraftTransport.yellowPipeWire), xCoord, yCoord, zCoord);
        }

        if (hasGate()) {
            gate.dropGate(worldObj, xCoord, yCoord, zCoord);
        }

        for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
            if (container.hasFacade(direction)) {
                container.dropFacade(direction);
            }
        }

        if (broadcastRedstone) {
            updateNeighbors(false); // self will update due to block id changing
        }
    }

    public void setTrigger(int position, ITrigger trigger) {
        activatedTriggers[position] = trigger;
    }

    public ITrigger getTrigger(int position) {
        return activatedTriggers[position];
    }

    public void setTriggerParameter(int position, ITriggerParameter p) {
        triggerParameters[position] = p;
    }

    public ITriggerParameter getTriggerParameter(int position) {
        return triggerParameters[position];
    }

    public boolean isNearbyTriggerActive(ITrigger trigger, ITriggerParameter parameter) {
        if (trigger instanceof ITriggerPipe)
            return ((ITriggerPipe) trigger).isTriggerActive(this, parameter);
        else if (trigger != null) {
            for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS) {
                TileEntity tile = container.getTile(o);

                if (tile != null && !(tile instanceof TileGenericPipe)) {
                    if (trigger.isTriggerActive(o.getOpposite(), tile, parameter))
                        return true;
                }
            }
        }

        return false;
    }

    public boolean isTriggerActive(ITrigger trigger) {
        return false;
    }

    public LinkedList<IAction> getActions() {
        LinkedList<IAction> result = new LinkedList<IAction>();

        if (hasGate()) {
            gate.addActions(result);
        }

        return result;
    }

    public IAction getAction(int position) {
        return activatedActions[position];
    }

    public void setAction(int position, IAction action) {
        activatedActions[position] = action;
    }

    public void resetGate() {
        gate = null;
        activatedTriggers = new ITrigger[activatedTriggers.length];
        triggerParameters = new ITriggerParameter[triggerParameters.length];
        activatedActions = new IAction[activatedActions.length];
        broadcastSignal = new boolean[] { false, false, false, false };
        if (broadcastRedstone) {
            updateNeighbors(true);
        }
        broadcastRedstone = false;
        // worldObj.markBlockNeedsUpdate(xCoord, yCoord, zCoord);
        container.scheduleRenderUpdate();
    }

    private void resolveActions() {
        if (!hasGate())
            return;

        boolean oldBroadcastRedstone = broadcastRedstone;
        boolean[] oldBroadcastSignal = broadcastSignal;

        broadcastRedstone = false;
        broadcastSignal = new boolean[] { false, false, false, false };

        // Tell the gate to prepare for resolving actions. (Disable pulser)
        gate.startResolution();

        HashMap<Integer, Boolean> actions = new HashMap<Integer, Boolean>();
        Multiset<Integer> actionCount = HashMultiset.create();

        // Computes the actions depending on the triggers
        for (int it = 0; it < 8; ++it) {
            ITrigger trigger = activatedTriggers[it];
            IAction action = activatedActions[it];
            ITriggerParameter parameter = triggerParameters[it];

            if (trigger != null && action != null) {
                actionCount.add(action.getId());
                if (!actions.containsKey(action.getId())) {
                    actions.put(action.getId(), isNearbyTriggerActive(trigger, parameter));
                } else if (gate.getConditional() == GateConditional.AND) {
                    actions.put(action.getId(),
                            actions.get(action.getId()) && isNearbyTriggerActive(trigger, parameter));
                } else {
                    actions.put(action.getId(),
                            actions.get(action.getId()) || isNearbyTriggerActive(trigger, parameter));
                }
            }
        }

        // Activate the actions
        for (Integer i : actions.keySet())
            if (actions.get(i)) {

                // Custom gate actions take precedence over defaults.
                if (gate.resolveAction(ActionManager.actions[i], actionCount.count(i))) {
                    continue;
                }

                if (ActionManager.actions[i] instanceof ActionRedstoneOutput) {
                    broadcastRedstone = true;
                } else if (ActionManager.actions[i] instanceof ActionSignalOutput) {
                    broadcastSignal[((ActionSignalOutput) ActionManager.actions[i]).color.ordinal()] = true;
                } else {
                    for (int a = 0; a < container.tileBuffer.length; ++a)
                        if (container.tileBuffer[a].getTile() instanceof IActionReceptor) {
                            IActionReceptor recept = (IActionReceptor) container.tileBuffer[a].getTile();
                            recept.actionActivated(ActionManager.actions[i]);
                        }
                }
            }

        actionsActivated(actions);

        if (oldBroadcastRedstone != broadcastRedstone) {
            container.scheduleRenderUpdate();
            updateNeighbors(true);
        }

        for (int i = 0; i < oldBroadcastSignal.length; ++i)
            if (oldBroadcastSignal[i] != broadcastSignal[i]) {
                // worldObj.markBlockNeedsUpdate(xCoord, yCoord, zCoord);
                container.scheduleRenderUpdate();
                updateSignalState();
                break;
            }
    }

    protected void actionsActivated(HashMap<Integer, Boolean> actions) {

    }

    @Override
    public TileGenericPipe getContainer() {
        return container;
    }

    @Override
    public boolean isWireConnectedTo(TileEntity tile, WireColor 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 void onDropped(EntityItem item) {

    }

    /**
     * If this pipe is open on one side, return it.
     */
    public ForgeDirection getOpenOrientation() {
        int Connections_num = 0;

        ForgeDirection target_orientation = ForgeDirection.UNKNOWN;

        for (ForgeDirection o : ForgeDirection.VALID_DIRECTIONS)
            if (Utils.checkPipesConnections(container.getTile(o), container)) {

                Connections_num++;

                if (Connections_num == 1) {
                    target_orientation = o;
                }
            }

        if (Connections_num > 1 || Connections_num == 0)
            return ForgeDirection.UNKNOWN;

        return target_orientation.getOpposite();
    }

    @Override
    public boolean doDrop() {
        return logic.doDrop();
    }

    public boolean isGateActive() {
        for (boolean b : broadcastSignal) {
            if (b)
                return true;
        }
        return broadcastRedstone;
    }

    /**
     * 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() {
    }

}