package gtPlusPlus.xmod.gregtech.api.objects; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.util.GT_Utility; import gtPlusPlus.api.objects.Logger; import gtPlusPlus.api.objects.minecraft.BlockPos; import gtPlusPlus.xmod.gregtech.api.metatileentity.implementations.GT_MetaTileEntity_ThreadedBuffer; import gtPlusPlus.xmod.gregtech.common.tileentities.automation.GT_MetaTileEntity_ThreadedChestBuffer; public class GregtechBufferThread extends Thread { public static final ConcurrentMap mBufferThreadAllocation = new ConcurrentHashMap(); private final BlockPos mBlockPos; private final int mMaxLife = 300; private int mLifeCycleTime = mMaxLife; private final String mID; public static synchronized final GregtechBufferThread getBufferThread(BlockPos pos) { if (pos != null && mBufferThreadAllocation.containsKey(""+pos.getUniqueIdentifier())){ Logger.INFO("[SB] Found an existing thread for this Buffer."); return mBufferThreadAllocation.get(""+pos.getUniqueIdentifier()); } else { return new GregtechBufferThread(pos); } } public GregtechBufferThread(BlockPos pos) { super(); String aID = pos != null ? pos.getUniqueIdentifier() : ""+Short.MIN_VALUE; this.mID = aID; if (pos != null && !mBufferThreadAllocation.containsKey(mID)){ mBlockPos = pos; mBufferThreadAllocation.put(mID, this); } else { this.mLifeCycleTime = 1; mBlockPos = null; } this.setName("GTPP-SuperBuffer("+mID+")"); this.setDaemon(true); if (mBlockPos != null && !this.isAlive()) { try { start(); Logger.INFO("[SB] Created a SuperBuffer Thread for dimension "+mID+"."); } catch (Throwable t_) { //Do nothing. } } } public synchronized int getTimeLeft() { return this.mLifeCycleTime; } public synchronized void fillStacksIntoFirstSlots(GT_MetaTileEntity_ThreadedChestBuffer mBuffer) { for (int i = 0; i < mBuffer.mInventorySynchro.length - 1; ++i) { for (int j = i + 1; j < mBuffer.mInventorySynchro.length - 1; ++j) { if (mBuffer.mInventorySynchro[j] != null && (mBuffer.mInventorySynchro[i] == null || areStacksEqual(mBuffer.mInventorySynchro[i], mBuffer.mInventorySynchro[j]))) { moveStackFromSlotAToSlotB((IInventory) mBuffer.getBaseMetaTileEntity(), (IInventory) mBuffer.getBaseMetaTileEntity(), j, i, (byte) 64, (byte) 1, (byte) 64, (byte) 1); } } } } public synchronized boolean moveItems(final IGregTechTileEntity aBaseMetaTileEntity, final long aTimer, GT_MetaTileEntity_ThreadedBuffer mBuffer) { final byte mTargetStackSize = (byte) mBuffer.mTargetStackSize; final int tCost = GT_Utility.moveOneItemStack((Object) aBaseMetaTileEntity, (Object) aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()), aBaseMetaTileEntity.getBackFacing(), aBaseMetaTileEntity.getFrontFacing(), (List) null, false, (byte) ((mTargetStackSize == 0) ? 64 : ((byte) mTargetStackSize)), (byte) ((mTargetStackSize == 0) ? 1 : ((byte) mTargetStackSize)), (byte) 64, (byte) 1); if (tCost > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) { mBuffer.mSuccess = 50; aBaseMetaTileEntity.decreaseStoredEnergyUnits((long) Math.abs(tCost), true); return true; } return false; } public synchronized void onPostTick(final IGregTechTileEntity aBaseMetaTileEntity, final long aTimer, GT_MetaTileEntity_ThreadedBuffer mBuffer) { if (aBaseMetaTileEntity.isAllowedToWork() && aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isUniversalEnergyStored(mBuffer.getMinimumStoredEU()) && (aBaseMetaTileEntity.hasWorkJustBeenEnabled() || aBaseMetaTileEntity.hasInventoryBeenModified() || aTimer % 200L == 0L || mBuffer.mSuccess > 0)) { --mBuffer.mSuccess; if (mLifeCycleTime < (mMaxLife-1)){ mLifeCycleTime += 1; } //Logger.INFO("Ticking SB @ "+mBuffer.getLogicThread().mBlockPos.getUniqueIdentifier() + " | Time Left: "+mLifeCycleTime); moveItems(aBaseMetaTileEntity, aTimer, mBuffer); for (byte b = 0; b < 6; ++b) { aBaseMetaTileEntity.setInternalOutputRedstoneSignal(b, (byte) (mBuffer.bInvert ? 15 : 0)); } if (mBuffer.bRedstoneIfFull) { for (byte b = 0; b < 6; ++b) { aBaseMetaTileEntity.setInternalOutputRedstoneSignal(b, (byte) (mBuffer.bInvert ? 0 : 15)); } for (int i = 0; i < mBuffer.mInventorySynchro.length; ++i) { if (mBuffer.isValidSlot(i) && mBuffer.mInventorySynchro[i] == null) { for (byte b2 = 0; b2 < 6; ++b2) { aBaseMetaTileEntity.setInternalOutputRedstoneSignal(b2, (byte) (mBuffer.bInvert ? 15 : 0)); } aBaseMetaTileEntity.decreaseStoredEnergyUnits(1L, true); break; } } } } } /** * Some GT logic we'd like to move off thread */ public synchronized boolean areStacksEqual(final ItemStack aStack1, final ItemStack aStack2) { return areStacksEqual(aStack1, aStack2, false); } public synchronized boolean areStacksEqual(final ItemStack aStack1, final ItemStack aStack2, final boolean aIgnoreNBT) { return aStack1 != null && aStack2 != null && aStack1.getItem() == aStack2.getItem() && (aIgnoreNBT || (aStack1.getTagCompound() == null == (aStack2.getTagCompound() == null) && (aStack1.getTagCompound() == null || aStack1.getTagCompound().equals((Object) aStack2.getTagCompound())))) && (Items.feather.getDamage(aStack1) == Items.feather.getDamage(aStack2) || Items.feather.getDamage(aStack1) == 32767 || Items.feather.getDamage(aStack2) == 32767); } public synchronized byte moveStackFromSlotAToSlotB(final IInventory aTileEntity1, final IInventory aTileEntity2, final int aGrabFrom, final int aPutTo, byte aMaxTargetStackSize, final byte aMinTargetStackSize, final byte aMaxMoveAtOnce, final byte aMinMoveAtOnce) { if (aTileEntity1 == null || aTileEntity2 == null || aMaxTargetStackSize <= 0 || aMinTargetStackSize <= 0 || aMinTargetStackSize > aMaxTargetStackSize || aMaxMoveAtOnce <= 0 || aMinMoveAtOnce > aMaxMoveAtOnce) { return 0; } final ItemStack tStack1 = aTileEntity1.getStackInSlot(aGrabFrom); final ItemStack tStack2 = aTileEntity2.getStackInSlot(aPutTo); ItemStack tStack3 = null; if (tStack1 != null) { if (tStack2 != null && !areStacksEqual(tStack1, tStack2)) { return 0; } tStack3 = GT_Utility.copy(tStack1); aMaxTargetStackSize = (byte) Math.min(aMaxTargetStackSize, Math.min(tStack3.getMaxStackSize(), Math.min((tStack2 == null) ? Integer.MAX_VALUE : tStack2.getMaxStackSize(), aTileEntity2.getInventoryStackLimit()))); tStack3.stackSize = Math.min(tStack3.stackSize, aMaxTargetStackSize - ((tStack2 == null) ? 0 : tStack2.stackSize)); if (tStack3.stackSize > aMaxMoveAtOnce) { tStack3.stackSize = aMaxMoveAtOnce; } if (tStack3.stackSize + ((tStack2 == null) ? 0 : tStack2.stackSize) >= Math.min(tStack3.getMaxStackSize(), aMinTargetStackSize) && tStack3.stackSize >= aMinMoveAtOnce) { tStack3 = aTileEntity1.decrStackSize(aGrabFrom, tStack3.stackSize); aTileEntity1.markDirty(); if (tStack3 != null) { if (tStack2 == null) { aTileEntity2.setInventorySlotContents(aPutTo, GT_Utility.copy(tStack3)); aTileEntity2.markDirty(); } else { final ItemStack itemStack = tStack2; itemStack.stackSize += tStack3.stackSize; aTileEntity2.markDirty(); } return (byte) tStack3.stackSize; } } } return 0; } //Logic Vars private boolean mRunning = true; @Override public void run() { //While thread is alive. run: while (mRunning) { //While thread is active, lets tick it's life down. life: while (mLifeCycleTime > 0) { if (!mRunning) { break life; } //Remove invalid threads if (this.mBlockPos.world == null || this.mBlockPos.getBlockAtPos() == null) { destroy(); break run; } //Prevent Overflows if (mLifeCycleTime > mMaxLife) { mLifeCycleTime = mMaxLife; } try { sleep(1000); mLifeCycleTime--; Logger.WARNING("[SB] Ticking Thread "+mID+" | Remaining: "+mLifeCycleTime+"s"); } catch (InterruptedException e) { mLifeCycleTime = 0; } } if (mLifeCycleTime <= 0) { destroy(); break run; } } } @SuppressWarnings("deprecation") @Override public void destroy() { mRunning = false; GregtechBufferThread.mBufferThreadAllocation.remove(mID, this); Logger.INFO("[SB] Removing Thread "+mID); try { stop(); this.finalize(); } catch (Throwable t) { //Do nothing. } } }