/**
* Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
* <p/>
* 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.gui;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.TreeSet;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ICrafting;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import net.minecraftforge.common.util.ForgeDirection;
import buildcraft.BuildCraftCore;
import buildcraft.api.statements.IStatement;
import buildcraft.api.statements.IStatementParameter;
import buildcraft.api.statements.StatementManager;
import buildcraft.api.transport.IPipe;
import buildcraft.core.lib.gui.BuildCraftContainer;
import buildcraft.core.lib.network.Packet;
import buildcraft.core.lib.network.command.CommandWriter;
import buildcraft.core.lib.network.command.ICommandReceiver;
import buildcraft.core.lib.network.command.PacketCommand;
import buildcraft.core.lib.utils.NetworkUtils;
import buildcraft.transport.ActionActiveState;
import buildcraft.transport.Gate;
import buildcraft.transport.gates.GateDefinition;
public class ContainerGateInterface extends BuildCraftContainer implements ICommandReceiver {
public ActionActiveState[] actionsState = new ActionActiveState[8];
public GuiGateInterface gateCallback;
IInventory playerIInventory;
private final IPipe pipe;
private Gate gate;
private final NavigableSet<IStatement> potentialTriggers = new TreeSet<IStatement>(new Comparator<IStatement>() {
@Override
public int compare(IStatement o1, IStatement o2) {
return o1.getUniqueTag().compareTo(o2.getUniqueTag());
}
});
private final NavigableSet<IStatement> potentialActions = new TreeSet<IStatement>(new Comparator<IStatement>() {
@Override
public int compare(IStatement o1, IStatement o2) {
return o1.getUniqueTag().compareTo(o2.getUniqueTag());
}
});
private boolean isSynchronized = false;
private boolean isNetInitialized = false;
private int lastTriggerState = 0;
public ContainerGateInterface(IInventory playerInventory, IPipe pipe) {
super(0);
for (int i = 0; i < actionsState.length; ++i) {
actionsState[i] = ActionActiveState.Deactivated;
}
this.pipe = pipe;
this.playerIInventory = playerInventory;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 9; x++) {
addSlotToContainer(new Slot(playerIInventory, x + y * 9 + 9, 8 + x * 18, 0));
}
}
for (int x = 0; x < 9; x++) {
addSlotToContainer(new Slot(playerIInventory, x, 8 + x * 18, 0));
}
}
public void init() {
if (gate == null) {
return;
}
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 9; x++) {
getSlot(x + y * 9).yDisplayPosition = gate.material.guiHeight - 84 + y * 18;
}
}
for (int x = 0; x < 9; x++) {
getSlot(x + 27).yDisplayPosition = gate.material.guiHeight - 26;
}
// Do not attempt to create a list of potential actions and triggers on
// the client.
if (!pipe.getTile().getWorld().isRemote) {
potentialTriggers.addAll(gate.getAllValidTriggers());
potentialActions.addAll(gate.getAllValidActions());
Iterator<IStatement> it = potentialTriggers.iterator();
while (it.hasNext()) {
IStatement trigger = it.next();
if (trigger.minParameters() > gate.material.numTriggerParameters) {
it.remove();
}
}
it = potentialActions.iterator();
while (it.hasNext()) {
IStatement action = it.next();
if (action.minParameters() > gate.material.numActionParameters) {
it.remove();
}
}
}
if (gateCallback != null) {
gateCallback.setGate(gate);
}
}
private static String[] statementsToStrings(Collection<IStatement> statements) {
final int size = statements.size();
String[] array = new String[size];
int pos = 0;
for (IStatement statement : statements) {
array[pos++] = statement.getUniqueTag();
}
return array;
}
private static void stringsToStatements(Collection<IStatement> statements, String[] strings) {
statements.clear();
for (String id : strings) {
statements.add(StatementManager.statements.get(id));
}
}
@Override
public boolean canInteractWith(EntityPlayer player) {
return gate != null;
}
/**
* CLIENT SIDE *
*/
/**
* Marks client side gate container as needing to be synchronized with the
* server.
*/
public void markDirty() {
isSynchronized = false;
}
/**
* Initializes the list of triggers and actions on the gate and
* (re-)requests the current selection on the gate if needed.
*/
public void synchronize() {
if (!isNetInitialized && pipe.getTile().getWorld().isRemote) {
isNetInitialized = true;
BuildCraftCore.instance.sendToServer(new PacketCommand(this, "initRequest", null));
}
if (!isSynchronized && pipe.getTile().getWorld().isRemote && gate != null) {
isSynchronized = true;
BuildCraftCore.instance.sendToServer(new PacketCommand(this, "selectionRequest", null));
}
}
@Override
public void updateProgressBar(int id, int state) {
if (id == 0 /* Action state update */) {
for (int i = 0; i < 8; i++) {
/* Bit mask of triggers */
actionsState[i] = ActionActiveState.values()[(state >> (i * 2)) & 0x03];
}
}
}
/**
* SERVER SIDE *
*/
private int calculateTriggerState() {
if (gate == null) {
return 0;
}
int state = 0;
for (int i = 0; i < actionsState.length; i++) {
actionsState[i] = getActionState(i);
state |= (actionsState[i].ordinal() & 0x03) << i * 2;
}
return state;
}
@Override
public void detectAndSendChanges() {
super.detectAndSendChanges();
int state = calculateTriggerState();
if (state != lastTriggerState) {
for (Object crafter : this.crafters) {
ICrafting viewingPlayer = (ICrafting) crafter;
viewingPlayer.sendProgressBarUpdate(this, 0 /* State update */, state);
}
lastTriggerState = state;
}
}
/**
* TRIGGERS *
*/
public boolean hasTriggers() {
return potentialTriggers.size() > 0;
}
public IStatement getFirstTrigger() {
if (potentialTriggers.isEmpty()) {
return null;
} else {
return potentialTriggers.first();
}
}
public IStatement getLastTrigger() {
if (potentialTriggers.isEmpty()) {
return null;
} else {
return potentialTriggers.last();
}
}
public Iterator<IStatement> getTriggerIterator(boolean descending) {
return descending ? potentialTriggers.descendingIterator() : potentialTriggers.iterator();
}
public ActionActiveState getActionState(int i) {
if (gate == null) {
return ActionActiveState.Deactivated;
} else {
return gate.actionsState[i];
}
}
public IStatementParameter getTriggerParameter(int trigger, int param) {
if (gate == null) {
return null;
} else {
return gate.getTriggerParameter(trigger, param);
}
}
/**
* ACTIONS *
*/
public boolean hasActions() {
return !potentialActions.isEmpty();
}
public IStatement getFirstAction() {
if (potentialActions.isEmpty()) {
return null;
} else {
return potentialActions.first();
}
}
public IStatement getLastAction() {
if (potentialActions.isEmpty()) {
return null;
} else {
return potentialActions.last();
}
}
public Iterator<IStatement> getActionIterator(boolean descending) {
return descending ? potentialActions.descendingIterator() : potentialActions.iterator();
}
// PACKET GENERATION
public Packet getStatementPacket(final String name, final int slot, final IStatement statement) {
final String statementKind = statement != null ? statement.getUniqueTag() : null;
return new PacketCommand(this, name, new CommandWriter() {
public void write(ByteBuf data) {
data.writeByte(slot);
NetworkUtils.writeUTF(data, statementKind);
}
});
}
public Packet getStatementParameterPacket(final String name, final int slot,
final int paramSlot, final IStatementParameter parameter) {
final String parameterName = parameter != null ? parameter.getUniqueTag() : null;
final NBTTagCompound parameterNBT = new NBTTagCompound();
if (parameter != null) {
parameter.writeToNBT(parameterNBT);
}
return new PacketCommand(this, name, new CommandWriter() {
public void write(ByteBuf data) {
data.writeByte(slot);
data.writeByte(paramSlot);
NetworkUtils.writeUTF(data, parameterName);
NetworkUtils.writeNBT(data, parameterNBT);
}
});
}
public void setGate(int direction) {
this.gate = (Gate) pipe.getGate(ForgeDirection.getOrientation(direction));
init();
}
@Override
public void receiveCommand(String command, Side side, Object sender, ByteBuf stream) {
if (side.isServer()) {
EntityPlayer player = (EntityPlayer) sender;
if ("initRequest".equals(command)) {
final String[] triggerStrings = statementsToStrings(potentialTriggers);
final String[] actionStrings = statementsToStrings(potentialActions);
BuildCraftCore.instance.sendToPlayer(player, new PacketCommand(this, "init", new CommandWriter() {
public void write(ByteBuf data) {
data.writeByte(gate.getDirection().ordinal());
data.writeShort(triggerStrings.length);
data.writeShort(actionStrings.length);
for (String trigger : triggerStrings) {
NetworkUtils.writeUTF(data, trigger);
}
for (String action : actionStrings) {
NetworkUtils.writeUTF(data, action);
}
}
}));
} else if ("selectionRequest".equals(command)) {
for (int position = 0; position < gate.material.numSlots; position++) {
IStatement action = gate.getAction(position);
IStatement trigger = gate.getTrigger(position);
BuildCraftCore.instance.sendToPlayer(player, getStatementPacket("setAction", position, action));
BuildCraftCore.instance.sendToPlayer(player, getStatementPacket("setTrigger", position, trigger));
for (int p = 0; p < gate.material.numActionParameters; ++p) {
BuildCraftCore.instance.sendToPlayer(player, getStatementParameterPacket(
"setActionParameter", position, p, gate.getActionParameter(position, p)));
}
for (int q = 0; q < gate.material.numTriggerParameters; ++q) {
BuildCraftCore.instance.sendToPlayer(player, getStatementParameterPacket(
"setTriggerParameter", position, q, gate.getTriggerParameter(position, q)));
}
}
}
} else if (side.isClient()) {
if ("init".equals(command)) {
setGate(stream.readByte());
String[] triggerStrings = new String[stream.readShort()];
String[] actionStrings = new String[stream.readShort()];
for (int i = 0; i < triggerStrings.length; i++) {
triggerStrings[i] = NetworkUtils.readUTF(stream);
}
for (int i = 0; i < actionStrings.length; i++) {
actionStrings[i] = NetworkUtils.readUTF(stream);
}
stringsToStatements(this.potentialTriggers, triggerStrings);
stringsToStatements(this.potentialActions, actionStrings);
}
}
if ("setAction".equals(command)) {
setAction(stream.readUnsignedByte(), NetworkUtils.readUTF(stream), false);
} else if ("setTrigger".equals(command)) {
setTrigger(stream.readUnsignedByte(), NetworkUtils.readUTF(stream), false);
} else if ("setActionParameter".equals(command) || "setTriggerParameter".equals(command)) {
int slot = stream.readUnsignedByte();
int param = stream.readUnsignedByte();
String parameterName = NetworkUtils.readUTF(stream);
NBTTagCompound parameterData = NetworkUtils.readNBT(stream);
IStatementParameter parameter = null;
if (parameterName != null && parameterName.length() > 0) {
parameter = StatementManager.createParameter(parameterName);
}
if (parameter != null) {
parameter.readFromNBT(parameterData);
if ("setActionParameter".equals(command)) {
setActionParameter(slot, param, parameter, false);
} else {
setTriggerParameter(slot, param, parameter, false);
}
}
}
}
public void setAction(int action, String tag, boolean notifyServer) {
if (gate == null) {
return;
}
IStatement statement = null;
if (tag != null && tag.length() > 0) {
statement = StatementManager.statements.get(tag);
}
gate.setAction(action, statement);
if (pipe.getTile().getWorld().isRemote && notifyServer) {
BuildCraftCore.instance.sendToServer(getStatementPacket("setAction", action, statement));
}
}
public void setTrigger(int trigger, String tag, boolean notifyServer) {
if (gate == null) {
return;
}
IStatement statement = null;
if (tag != null && tag.length() > 0) {
statement = StatementManager.statements.get(tag);
}
gate.setTrigger(trigger, statement);
if (pipe.getTile().getWorld().isRemote && notifyServer) {
BuildCraftCore.instance.sendToServer(getStatementPacket("setTrigger", trigger, statement));
}
}
public void setActionParameter(int action, int param, IStatementParameter parameter, boolean notifyServer) {
if (gate == null) {
return;
}
gate.setActionParameter(action, param, parameter);
if (pipe.getTile().getWorld().isRemote && notifyServer) {
BuildCraftCore.instance.sendToServer(getStatementParameterPacket("setActionParameter", action, param, parameter));
}
}
public void setTriggerParameter(int trigger, int param, IStatementParameter parameter, boolean notifyServer) {
if (gate == null) {
return;
}
gate.setTriggerParameter(trigger, param, parameter);
if (pipe.getTile().getWorld().isRemote && notifyServer) {
BuildCraftCore.instance.sendToServer(getStatementParameterPacket("setTriggerParameter", trigger, param, parameter));
}
}
/**
* GATE INFORMATION *
*/
public ResourceLocation getGateGuiFile() {
return gate.material.guiFile;
}
public String getGateName() {
return GateDefinition.getLocalizedName(gate.material, gate.logic);
}
}