Java tutorial
/******************************************************************************* * Copyright (c) 2012-2013 Yancarlo Ramsey and CJ Bowman * Licensed as open source with restrictions. Please see attached LICENSE.txt. ******************************************************************************/ package com.kaijin.AdvPowerMan.tileentities; import com.kaijin.AdvPowerMan.AdvancedPowerManagement; import com.kaijin.AdvPowerMan.Info; import com.kaijin.AdvPowerMan.MovingAverage; import com.kaijin.AdvPowerMan.Utils; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import ic2.api.energy.EnergyNet; import ic2.api.energy.event.EnergyTileLoadEvent; import ic2.api.energy.tile.IEnergySource; import ic2.api.item.ElectricItem; import ic2.api.item.IElectricItem; import io.netty.buffer.ByteBuf; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.Packet; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import java.io.IOException; public class TEBatteryStation extends TECommonBench implements IEnergySource, IInventory, ISidedInventory { public int opMode; // Base values public int packetSize; public int currentEnergy = 0; private boolean invChanged = false; private boolean hasEnoughItems = false; // For outside texture display public boolean doingWork; private int energyOut = 0; public MovingAverage outputTracker = new MovingAverage(12); private static final int[] BatteryStationSideInput = { Info.BS_SLOT_INPUT }; private static final int[] BatteryStationSideOutput = { Info.BS_SLOT_OUTPUT }; private static final int[] BatteryStationSideInOut = { Info.BS_SLOT_INPUT, Info.BS_SLOT_OUTPUT }; public TEBatteryStation() { // Default constructor used only when loading tile entity from world save super(); // Do nothing else; Creating the inventory array and loading previous // values will be handled in NBT read method momentarily. } public TEBatteryStation(int i) { // Constructor used when placing a new tile entity, to set up correct parameters super(); contents = new ItemStack[14]; // base tier = what we're passed, so 1, 2 or 3 baseTier = i; opMode = 1; initializeValues(); } private void initializeValues() { powerTier = baseTier; // Output math = 32 for tier 1, 128 for tier 2, 512 for tier 3 packetSize = (int) Math.pow(2.0D, EnergyNet.instance.getPowerFromTier(powerTier)); } // IC2 API functions @Override public boolean emitsEnergyTo(TileEntity receiver, ForgeDirection direction) { return true; } @Override public double getOfferedEnergy() { return (!receivingRedstoneSignal()) ? Math.min(currentEnergy, packetSize) : 0; } @Override public void drawEnergy(double amount) { if (!receivingRedstoneSignal()) { drainPowerSource(); outputTracker.tick((int) amount); currentEnergy -= amount; } } @Override public int getSourceTier() { return 4; // XXX: cause I dunno what to put... / READ THE JAVADOC YOU NOOB - xbony2 } // End IC2 API @Override public int getGuiID() { return Info.GUI_ID_BATTERY_STATION; } /** * This will cause the block to drop anything inside it, create a new item * in the world of its type, invalidate the tile entity, remove itself from * the IC2 EnergyNet and clear the block space (set it to air) */ @Override protected void selfDestroy() { dropContents(); ItemStack stack = new ItemStack(AdvancedPowerManagement.blockAdvPwrMan, 1, Info.BS_META + baseTier - 1); dropItem(stack); worldObj.setBlockToAir(xCoord, yCoord, zCoord); this.invalidate(); } public boolean isItemValid(int slot, ItemStack stack) { // Decide if the item is a valid IC2 electrical item if (stack != null && stack.getItem() instanceof IElectricItem) { IElectricItem item = (IElectricItem) (stack.getItem()); // Is the item appropriate for this slot? if (slot == Info.BS_SLOT_OUTPUT) return true; // GUI won't allow placement of items here, but if // the bench or an external machine does, it // should at least let it sit there as long as // it's an electrical item. if (item.canProvideEnergy(stack) && item.getTier(stack) <= powerTier) { if ((slot >= Info.BS_SLOT_POWER_START && slot < Info.BS_SLOT_POWER_START + 12) || slot == Info.BS_SLOT_INPUT) return true; } } return false; } /** * Reads a tile entity from NBT. */ @Override public void readFromNBT(NBTTagCompound nbttagcompound) { super.readFromNBT(nbttagcompound); if (Info.isDebugging) System.out.println("BS ID: " + nbttagcompound.getString("id")); baseTier = nbttagcompound.getInteger("baseTier"); opMode = nbttagcompound.getInteger("opMode"); currentEnergy = nbttagcompound.getInteger("currentEnergy"); // Our inventory contents = new ItemStack[Info.BS_INVENTORY_SIZE]; NBTTagList nbttaglist = nbttagcompound.getTagList("Items", Constants.NBT.TAG_COMPOUND); for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = (NBTTagCompound) nbttaglist.getCompoundTagAt(i); int j = nbttagcompound1.getByte("Slot") & 255; if (j >= 0 && j < contents.length) { contents[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } // We can calculate these, no need to save/load them. initializeValues(); } /** * Writes a tile entity to NBT. */ @Override public void writeToNBT(NBTTagCompound nbttagcompound) { super.writeToNBT(nbttagcompound); nbttagcompound.setInteger("baseTier", baseTier); nbttagcompound.setInteger("opMode", opMode); nbttagcompound.setInteger("currentEnergy", currentEnergy); // Our inventory NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < contents.length; ++i) { if (contents[i] != null) { // if (ChargingBench.isDebugging) // System.out.println("WriteNBT contents[" + i + "] stack tag: " // + contents[i].stackTagCompound); NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Slot", (byte) i); contents[i].writeToNBT(nbttagcompound1); nbttaglist.appendTag(nbttagcompound1); } } nbttagcompound.setTag("Items", nbttaglist); } @Override public void updateEntity() { if (AdvancedPowerManagement.proxy.isClient()) return; if (!initialized && worldObj != null) { EnergyTileLoadEvent loadEvent = new EnergyTileLoadEvent(this); MinecraftForge.EVENT_BUS.post(loadEvent); // EnergyNet.getForWorld(worldObj).addTileEntity(this); initialized = true; } boolean lastWorkState = doingWork; doingWork = false; invChanged = false; hasEnoughItems = true; if (!receivingRedstoneSignal()) { // Work done only when not redstone powered drainPowerSource(); } // Work done every tick moveOutputItems(); repositionItems(); acceptInputItems(); if (invChanged) { this.markDirty(); // This doesn't need to be called multiple times, so it gets flagged to happen here if needed. } // Trigger this only when it would need to update the client texture if (lastWorkState != doingWork) { // if (ChargingBench.isDebugging) // System.out.println("TE oldChargeLevel: " + oldChargeLevel + // " chargeLevel: " + chargeLevel); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } } /** * This method is constantly called, wether something is in it or not. * It checks that stuff automatically, assuming it isn't powered by redstone. */ private void drainPowerSource() { hasEnoughItems = false; for (int i = Info.BS_SLOT_POWER_START; i < Info.BS_SLOT_POWER_START + 12; i++) { if (currentEnergy >= packetSize) { hasEnoughItems = true; break; } ItemStack stack = contents[i]; if (stack != null && stack.getItem() instanceof IElectricItem && stack.stackSize == 1) { IElectricItem item = (IElectricItem) stack.getItem(); if (item.getTier(stack) <= powerTier && item.canProvideEnergy(stack)) { //System.out.println("Do we even get here?"); we do -xbony2 Item emptyItem = item.getEmptyItem(stack); //Funfact: nobody use ids anymore. if (true/*stack.getItem() == item.getChargedItem(stack)*/) { // this doesn't work, but who cares? double transferLimit = item.getTransferLimit(stack); // int amountNeeded = baseMaxOutput - currentEnergy; if (transferLimit == 0) transferLimit = packetSize; // if (transferLimit > amountNeeded) transferLimit = // amountNeeded; double chargeReturned = ElectricItem.manager.discharge(stack, transferLimit, powerTier, false, false, false); //^hold up... if it discharges it, why would it return anything but zero? System.out.println(chargeReturned); if (chargeReturned > 0) { System.out.println("THIS *SHOULD* BE WORKING"); // Add the energy we received to our current energy // level currentEnergy += chargeReturned; doingWork = true; } else { System.out.println("BROKEN SHIT GOIN' ON RIGHT HERE RIGHT NOW"); } // Workaround for buggy IC2 API .discharge that // automatically switches stack to emptyItemID but // leaves a stackTagCompound on it, so it can't be // stacked with never-used empties if (item.getChargedItem(stack) != emptyItem && (chargeReturned < transferLimit || ElectricItem.manager.discharge(stack, 1, powerTier, false, true, false) == 0)) { // if (ChargingBench.isDebugging) // System.out.println("Switching to emptyItemID: " + // emptyItemID + " from stack.itemID: " + // stack.itemID + " - chargedItemID: " + // chargedItemID); setInventorySlotContents(i, new ItemStack(emptyItem, 1, 0)); } } } } } } /** * First, check the output slot to see if it's empty. If so, look to see if * there are any fully DIScharged items in the main inventory. Move the * first empty item to the output slot. If output slot contains stackable * empties, check for matching empties to add to that stack. */ private void moveOutputItems() { rejectInvalidInput(); ItemStack outputStack = contents[Info.BS_SLOT_OUTPUT]; if (outputStack == null || (outputStack.isStackable() && outputStack.stackSize < outputStack.getMaxStackSize())) { // Output slot could receive item(s). Try to find something to move // there. for (int slot = 0; slot < contents.length; ++slot) { if (slot == Info.BS_SLOT_OUTPUT) continue; ItemStack currentStack = contents[slot]; if (currentStack != null && currentStack.getItem() instanceof IElectricItem) { IElectricItem powerSource = (IElectricItem) (currentStack.getItem()); if (powerSource.getTier(currentStack) <= powerTier) // && // powerSource.canProvideEnergy() { int emptyItemID = Item.getIdFromItem(powerSource.getEmptyItem(currentStack)); int chargedItemID = Item.getIdFromItem(powerSource.getChargedItem(currentStack)); if (emptyItemID != chargedItemID) { if (Item.getIdFromItem(currentStack.getItem()) == emptyItemID) { // Pick Me if (outputStack == null) { contents[Info.BS_SLOT_OUTPUT] = currentStack; contents[slot] = null; } else { // We already know the stack isn't full yet contents[Info.BS_SLOT_OUTPUT].stackSize++; contents[slot].stackSize--; if (contents[slot].stackSize < 1) contents[slot] = null; } invChanged = true; break; } } else if (outputStack == null) { boolean empty = ElectricItem.manager.discharge(currentStack, 1, powerTier, true, true, false) == 0; if (empty) { // Pick Me contents[Info.BS_SLOT_OUTPUT] = currentStack; contents[slot] = null; invChanged = true; break; } } } } } } } /** * Adjust positions of items in inventory to preserve FIFO order where * possible. */ private void repositionItems() { final int lastIndex = Info.BS_SLOT_POWER_START + 11; int vacancy = Info.BS_SLOT_POWER_START; while (vacancy < lastIndex && contents[vacancy] != null) { vacancy++; } int hunt = vacancy + 1; while (vacancy < lastIndex && hunt <= lastIndex) // Mix of < and <= is // not an error: Avoids // needing +1 or -1 // added to something. { if (contents[vacancy] == null && contents[hunt] != null) { contents[vacancy] = contents[hunt]; contents[hunt] = null; invChanged = true; vacancy++; } hunt++; } } /** * Check to see if there are any items in the input slot. If so, check to * see if there are any free discharging slots. If so, move one from the * input slot to a free discharging slot. */ private void acceptInputItems() { // System.out.println("aII: opMode " + opMode); ItemStack stack = contents[Info.BS_SLOT_INPUT]; if (stack == null || !(stack.getItem() instanceof IElectricItem) || (opMode == 1 && hasEnoughItems)) return; IElectricItem item = (IElectricItem) stack.getItem(); if (item.canProvideEnergy(stack)) { // Input slot contains a power source. If possible, move one of it // into the discharging area. for (int slot = Info.BS_SLOT_POWER_START; slot < Info.BS_SLOT_POWER_START + 12; ++slot) { if (contents[slot] == null) { // Grab one unit from input and move it to the selected // slot. contents[slot] = decrStackSize(Info.BS_SLOT_INPUT, 1); break; } } } } private void rejectInvalidInput() { // Move item from input to output if not valid. (Wrong tier or not // electric item.) if (contents[Info.BS_SLOT_INPUT] != null && contents[Info.BS_SLOT_OUTPUT] == null) { if (!isItemValid(Info.BS_SLOT_INPUT, contents[Info.BS_SLOT_INPUT])) { contents[Info.BS_SLOT_OUTPUT] = contents[Info.BS_SLOT_INPUT]; contents[Info.BS_SLOT_INPUT] = null; invChanged = true; } } } // Add up amount of energy stored in items in all slots except output and // return that value public int getTotalEnergy() { int energySum = 0; for (int i = 0; i < Info.BS_SLOT_POWER_START + 12; i++) { if (i == Info.BS_SLOT_OUTPUT) continue; final ItemStack stack = contents[i]; if (stack != null && stack.getItem() instanceof IElectricItem && stack.stackSize == 1) { final IElectricItem item = (IElectricItem) (stack.getItem()); if (item.getTier(stack) <= powerTier && item.canProvideEnergy(stack) && Item.getIdFromItem(stack.getItem()) == Item.getIdFromItem(item.getChargedItem(stack))) { final double chargeReturned = ElectricItem.manager.discharge(stack, Integer.MAX_VALUE, powerTier, true, true, false); if (chargeReturned > 0) { // Add the energy we received to our current energy // level energySum += chargeReturned; } } } } return energySum; } // Networking stuff @Override public Packet getDescriptionPacket() { return createDescPacket(); } @Override protected void addUniqueDescriptionData(ByteBuf data) throws IOException { data.writeBoolean(doingWork); } @SideOnly(Side.CLIENT) @Override public void receiveDescriptionData(int packetID, ByteBuf stream) { boolean b = doingWork; // try // { b = stream.readBoolean(); /* * } catch (IOException e) { logDescPacketError(e); return; } */ doingWork = b; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } @Override public void receiveGuiButton(int buttonID) { if (buttonID == 0) { opMode ^= 1; } } // ISidedInventory /* * @Override public int getStartInventorySide(ForgeDirection side) { switch * (side) { case UP: case DOWN: return Info.BS_SLOT_INPUT; default: return * Info.BS_SLOT_OUTPUT; } } * * @Override public int getSizeInventorySide(ForgeDirection side) { // Each * side accesses a single slot return 1; } */ @Override public int[] getAccessibleSlotsFromSide(int side) { return BatteryStationSideInOut; // Testing I/O constraint methods // func_102007_a, func_102008_b } @Override public boolean isItemValidForSlot(int i, ItemStack stack) { if (i == Info.BS_SLOT_INPUT) return Utils.isItemDrainable(stack, powerTier); return false; } // Returns true if automation can insert the given item in the given slot // from the given side. Args: Slot, item, side @Override public boolean canInsertItem(int i, ItemStack itemstack, int j) // canInsertItem { if (i == Info.BS_SLOT_INPUT) return true; return false; } // Returns true if automation can extract the given item in the given slot // from the given side. Args: Slot, item, side @Override public boolean canExtractItem(int i, ItemStack itemstack, int j) { // canExtractItem if (i == Info.BS_SLOT_OUTPUT) return true; return false; } // IInventory @Override public int getSizeInventory() { // Only input/output slots are accessible to machines return 2; } @Override public String getInventoryName() { switch (baseTier) { case 1: return Info.KEY_BLOCK_NAMES[8] + Info.KEY_NAME_SUFFIX; case 3: return Info.KEY_BLOCK_NAMES[9] + Info.KEY_NAME_SUFFIX; case 4: return Info.KEY_BLOCK_NAMES[10] + Info.KEY_NAME_SUFFIX; } return ""; } @Override public void markDirty(int slot) { if (slot == Info.BS_SLOT_INPUT || slot == Info.BS_SLOT_OUTPUT) { rejectInvalidInput(); } super.markDirty(); } }