forked from MagicBane/Server
				
			
				 283 changed files with 2304 additions and 3493 deletions
			
			
		@ -0,0 +1,408 @@
				@@ -0,0 +1,408 @@
					 | 
				
			||||
// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 | 
				
			||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 | 
				
			||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 | 
				
			||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 | 
				
			||||
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 | 
				
			||||
//      Magicbane Emulator Project © 2013 - 2022
 | 
				
			||||
//                www.magicbane.com
 | 
				
			||||
 | 
				
			||||
package engine.gameManager; | 
				
			||||
 | 
				
			||||
import engine.loot.ModTableEntry; | 
				
			||||
import engine.loot.ModTypeTableEntry; | 
				
			||||
import engine.loot.WorkOrder; | 
				
			||||
import engine.mbEnums; | 
				
			||||
import engine.net.DispatchMessage; | 
				
			||||
import engine.net.client.msg.ItemProductionMsg; | 
				
			||||
import engine.objects.City; | 
				
			||||
import engine.objects.Item; | 
				
			||||
import engine.objects.ItemTemplate; | 
				
			||||
import engine.objects.NPC; | 
				
			||||
import engine.powers.EffectsBase; | 
				
			||||
import org.pmw.tinylog.Logger; | 
				
			||||
 | 
				
			||||
import java.util.ArrayList; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.concurrent.*; | 
				
			||||
import java.util.concurrent.atomic.AtomicInteger; | 
				
			||||
 | 
				
			||||
public enum ForgeManager implements Runnable { | 
				
			||||
 | 
				
			||||
    // MB Dev notes:
 | 
				
			||||
    // Class implements forge rolling mechanics for Magicbane.
 | 
				
			||||
    //
 | 
				
			||||
    // .submit(workOrder) may be called from any thread: (ItemProductionMsgHandler).
 | 
				
			||||
    // Concurrency is managed by the same lock used by the warehouse (city.cityTransactionLock).
 | 
				
			||||
    // WorkOrders are persisted then reconstituted at bootstrap using table dyn.workorders.
 | 
				
			||||
    //
 | 
				
			||||
    // Replaces garbage code that looked as if written by a mental patient with face boils.
 | 
				
			||||
 | 
				
			||||
    FORGE_MANAGER; | 
				
			||||
 | 
				
			||||
    public static final BlockingQueue<WorkOrder> forge = new DelayQueue<>(); | 
				
			||||
    public static final AtomicInteger workOrderCounter = new AtomicInteger(0); | 
				
			||||
    public static final ConcurrentHashMap<NPC, ConcurrentHashMap.KeySetView<WorkOrder, Boolean>> vendorWorkOrderLookup = new ConcurrentHashMap<>(); | 
				
			||||
    public static final ConcurrentHashMap<Item, WorkOrder> itemWorkOrderLookup = new ConcurrentHashMap<>(); | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
 | 
				
			||||
    public void run() { | 
				
			||||
 | 
				
			||||
        WorkOrder workOrder; | 
				
			||||
 | 
				
			||||
        while (true) { | 
				
			||||
 | 
				
			||||
            // .forge is a delayQueue (blocking priority queue using an epoc sort)
 | 
				
			||||
            // workOrders are popped and processed when their completion time has passed.
 | 
				
			||||
 | 
				
			||||
            try { | 
				
			||||
                workOrder = forge.take(); | 
				
			||||
 | 
				
			||||
                // This workOrder has completed production.
 | 
				
			||||
 | 
				
			||||
                if (workOrder.total_produced >= workOrder.total_to_produce) { | 
				
			||||
 | 
				
			||||
                    // Set items as completed in the window.
 | 
				
			||||
                    // First CONFIRM_PRODUCE adds virtual item to the interface.
 | 
				
			||||
                    // Second CONFIRM_PRODUCE sets virtual item to complete.
 | 
				
			||||
 | 
				
			||||
                    for (Item workOrderItem : workOrder.cooking) { | 
				
			||||
                        workOrderItem.flags.add(mbEnums.ItemFlags.Identified); | 
				
			||||
                        ItemProductionMsg outMsg = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, workOrderItem, mbEnums.ProductionActionType.CONFIRM_PRODUCE, true); | 
				
			||||
                        DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg, mbEnums.DispatchChannel.SECONDARY, 700, false, false); | 
				
			||||
                    } | 
				
			||||
 | 
				
			||||
                    workOrder.runCompleted = true; | 
				
			||||
 | 
				
			||||
                    // Update workOrder on disk
 | 
				
			||||
 | 
				
			||||
                    DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder); | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
                if (workOrder.runCompleted) | 
				
			||||
                    continue; | 
				
			||||
 | 
				
			||||
                // Move current cooking batch to vendor inventory
 | 
				
			||||
 | 
				
			||||
                completeWorkOrderBatch(workOrder); | 
				
			||||
 | 
				
			||||
                // Create new set of in-memory only virtual items
 | 
				
			||||
 | 
				
			||||
                forgeWorkOrderBatch(workOrder); | 
				
			||||
 | 
				
			||||
                // enQueue this workOrder again; back into the oven
 | 
				
			||||
                // until all items for this workOrder are completed.
 | 
				
			||||
 | 
				
			||||
                forge.add(workOrder); | 
				
			||||
 | 
				
			||||
                // Debugging: Logger.info(workOrder.toString());
 | 
				
			||||
 | 
				
			||||
            } catch (Exception e) { | 
				
			||||
                Logger.error(e); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static void start() { | 
				
			||||
 | 
				
			||||
        Thread forgeManager; | 
				
			||||
        forgeManager = new Thread(FORGE_MANAGER); | 
				
			||||
        forgeManager.setName("Forge Manager"); | 
				
			||||
        forgeManager.start(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static int submit(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Must have a city to roll anything
 | 
				
			||||
 | 
				
			||||
        City city = workOrder.vendor.building.getCity(); | 
				
			||||
 | 
				
			||||
        if (city == null) | 
				
			||||
            return 58; //58: The formula is beyond the means of this facility
 | 
				
			||||
 | 
				
			||||
        // Concurrency is rightly managed by same lock as warehouse
 | 
				
			||||
 | 
				
			||||
        city.transactionLock.writeLock().lock(); | 
				
			||||
 | 
				
			||||
        // Make sure vendor can roll the formulae, warehouse can
 | 
				
			||||
        // afford this wordOrder and other related checks.
 | 
				
			||||
 | 
				
			||||
        int validation_result = WorkOrder.validate(workOrder); | 
				
			||||
 | 
				
			||||
        // The return code is used by the caller (ItemProductionMsgHandler)
 | 
				
			||||
        // for display of a popup error message to the player.
 | 
				
			||||
 | 
				
			||||
        if (validation_result != 0) | 
				
			||||
            return validation_result; | 
				
			||||
 | 
				
			||||
        try { | 
				
			||||
            // Configure this production run.
 | 
				
			||||
 | 
				
			||||
            workOrder.workOrderID = workOrderCounter.incrementAndGet(); | 
				
			||||
            workOrder.rollingDuration = ForgeManager.calcRollingDuration(workOrder); | 
				
			||||
            workOrder.completionTime = System.currentTimeMillis() + workOrder.rollingDuration; | 
				
			||||
            workOrder.slots_used = calcAvailableSlots(workOrder); | 
				
			||||
 | 
				
			||||
            workOrder.total_produced = 0; | 
				
			||||
 | 
				
			||||
            // Single item configuration
 | 
				
			||||
 | 
				
			||||
            if (!workOrder.multiple_slot_request && workOrder.total_to_produce == 0) | 
				
			||||
                workOrder.total_to_produce = 1; | 
				
			||||
 | 
				
			||||
            // Set total cost for production run
 | 
				
			||||
 | 
				
			||||
            workOrder.total_to_produce *= workOrder.slots_used; | 
				
			||||
 | 
				
			||||
            workOrder.production_cost = calcProductionCost(workOrder); | 
				
			||||
            workOrder.production_cost_total.putAll(workOrder.production_cost); | 
				
			||||
            workOrder.production_cost_total.forEach((key, value) -> workOrder.production_cost_total.put(key, value * workOrder.total_to_produce)); | 
				
			||||
 | 
				
			||||
            // Withdraw gold and resource costs.  Availability has previously been validated.
 | 
				
			||||
 | 
				
			||||
            if (!WorkOrder.withdrawWorkOrderCost(workOrder)) | 
				
			||||
                return 58; //58: The formula is beyond the means of this facility
 | 
				
			||||
 | 
				
			||||
            // Create new batch of virtual items
 | 
				
			||||
 | 
				
			||||
            forgeWorkOrderBatch(workOrder); | 
				
			||||
 | 
				
			||||
            // Enqueue workOrder in the .forge and then
 | 
				
			||||
            // add the workOrder to it's vendor
 | 
				
			||||
 | 
				
			||||
            vendorWorkOrderLookup.get(workOrder.vendor).add(workOrder); | 
				
			||||
            forge.add(workOrder); | 
				
			||||
 | 
				
			||||
            // PERSIST workOrder (dyn_workorders)
 | 
				
			||||
 | 
				
			||||
            DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder); | 
				
			||||
 | 
				
			||||
        } catch (Exception e) { | 
				
			||||
            Logger.error(e); | 
				
			||||
        } finally { | 
				
			||||
            city.transactionLock.writeLock().unlock(); | 
				
			||||
        } | 
				
			||||
        Logger.info(workOrder.toString()); | 
				
			||||
        return validation_result; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static long calcRollingDuration(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        float rollingDuration; | 
				
			||||
 | 
				
			||||
        rollingDuration = workOrder.vendor.getBuilding().getRank() * -5L + 40; | 
				
			||||
        rollingDuration = TimeUnit.MINUTES.toMillis((long) rollingDuration); | 
				
			||||
        rollingDuration *= Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()); | 
				
			||||
 | 
				
			||||
        ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); | 
				
			||||
 | 
				
			||||
        // Bane circles
 | 
				
			||||
 | 
				
			||||
        if (template.item_bane_rank > 0) | 
				
			||||
            rollingDuration = (long) template.item_bane_rank * 60 * 60 * 3 * 1000 * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()); | 
				
			||||
 | 
				
			||||
        return (long) rollingDuration; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static int calcAvailableSlots(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Slots available in a forge are based on the npc rank
 | 
				
			||||
 | 
				
			||||
        int availableSlots = workOrder.vendor.getRank(); | 
				
			||||
 | 
				
			||||
        // Subtract the slots currently assigned to other workOrders for this vendor
 | 
				
			||||
 | 
				
			||||
        for (WorkOrder npcWorkOrder : ForgeManager.vendorWorkOrderLookup.get(workOrder.vendor)) | 
				
			||||
            availableSlots = availableSlots - npcWorkOrder.cooking.size(); | 
				
			||||
 | 
				
			||||
        // Single item rolls are always a single slot
 | 
				
			||||
 | 
				
			||||
        if (availableSlots > 0 && !workOrder.multiple_slot_request) | 
				
			||||
            availableSlots = 1; | 
				
			||||
 | 
				
			||||
        return availableSlots; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static HashMap<mbEnums.ResourceType, Integer> calcProductionCost(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Calculate production cost for a single run of the workOrder
 | 
				
			||||
 | 
				
			||||
        HashMap<mbEnums.ResourceType, Integer> production_cost = new HashMap<>(); | 
				
			||||
        ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); | 
				
			||||
 | 
				
			||||
        // Add gold and resource costs from template
 | 
				
			||||
 | 
				
			||||
        production_cost.put(mbEnums.ResourceType.GOLD, template.item_value); | 
				
			||||
        production_cost.putAll(template.item_resource_cost); | 
				
			||||
 | 
				
			||||
        // Calculate cost of prefix and suffix
 | 
				
			||||
 | 
				
			||||
        if (workOrder.prefixToken != 0) { | 
				
			||||
            EffectsBase prefix = PowersManager.getEffectByToken(workOrder.prefixToken); | 
				
			||||
            production_cost.putAll(PowersManager._effect_costMaps.get(prefix.getIDString())); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (workOrder.suffixToken != 0) { | 
				
			||||
            EffectsBase suffix = PowersManager.getEffectByToken(workOrder.suffixToken); | 
				
			||||
            production_cost.putAll(PowersManager._effect_costMaps.get(suffix.getIDString())); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return production_cost; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static Item forgeItem(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Create new virtual item from specified template
 | 
				
			||||
 | 
				
			||||
        ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); | 
				
			||||
        Item forgedItem = new Item(workOrder.templateID); | 
				
			||||
 | 
				
			||||
        // forgedItem gets a negative id; a virtual item which is not persisted
 | 
				
			||||
 | 
				
			||||
        forgedItem.objectUUID = ItemManager.lastNegativeID.getAndDecrement(); | 
				
			||||
        forgedItem.containerType = mbEnums.ItemContainerType.FORGE; | 
				
			||||
        forgedItem.ownerID = workOrder.vendor.getObjectUUID(); | 
				
			||||
 | 
				
			||||
        // item.upgradeDate is serialized (ItemProductionMsg)
 | 
				
			||||
        // for vendor forge window completion time.
 | 
				
			||||
 | 
				
			||||
        forgedItem.setDateToUpgrade(workOrder.completionTime); | 
				
			||||
 | 
				
			||||
        // Assign a prefix and suffix to this item if random rolled
 | 
				
			||||
 | 
				
			||||
        if (workOrder.prefixToken == 0) | 
				
			||||
            forgedItem.prefixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.PREFIX, template.modTable); | 
				
			||||
        else | 
				
			||||
            forgedItem.prefixToken = workOrder.prefixToken; | 
				
			||||
 | 
				
			||||
        if (workOrder.suffixToken == 0) | 
				
			||||
            forgedItem.suffixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.SUFFIX, template.modTable); | 
				
			||||
        else | 
				
			||||
            forgedItem.suffixToken = workOrder.suffixToken; | 
				
			||||
 | 
				
			||||
        // Random rolled items are unidentified until completed
 | 
				
			||||
 | 
				
			||||
        if (workOrder.prefixToken == 0 && workOrder.suffixToken == 0) | 
				
			||||
            forgedItem.flags.remove(mbEnums.ItemFlags.Identified); | 
				
			||||
        else | 
				
			||||
            forgedItem.flags.add(mbEnums.ItemFlags.Identified); | 
				
			||||
 | 
				
			||||
        // Add virtual item to in-memory caches
 | 
				
			||||
 | 
				
			||||
        workOrder.cooking.add(forgedItem); | 
				
			||||
        DbManager.addToCache(forgedItem); | 
				
			||||
        itemWorkOrderLookup.put(forgedItem, workOrder); | 
				
			||||
 | 
				
			||||
        return forgedItem; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static void completeWorkOrderBatch(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        ArrayList<Item> toRemove = new ArrayList<>(); | 
				
			||||
 | 
				
			||||
        for (Item virutalItem : workOrder.cooking) { | 
				
			||||
 | 
				
			||||
            // Identify completed items
 | 
				
			||||
 | 
				
			||||
            virutalItem.flags.add(mbEnums.ItemFlags.Identified); | 
				
			||||
            virutalItem.containerType = mbEnums.ItemContainerType.INVENTORY; | 
				
			||||
 | 
				
			||||
            // Persist item
 | 
				
			||||
 | 
				
			||||
            Item completedItem = DbManager.ItemQueries.PERSIST(virutalItem); | 
				
			||||
 | 
				
			||||
            // Copy Prefix and Suffix tokens from virtual item.
 | 
				
			||||
 | 
				
			||||
            completedItem.prefixToken = virutalItem.prefixToken; | 
				
			||||
            completedItem.suffixToken = virutalItem.suffixToken; | 
				
			||||
 | 
				
			||||
            // Add effects to these tokens.  Writes to disk.
 | 
				
			||||
 | 
				
			||||
            ItemManager.applyItemEffects(completedItem); | 
				
			||||
 | 
				
			||||
            // Add to the vendor inventory
 | 
				
			||||
 | 
				
			||||
            workOrder.vendor.charItemManager.addItemToInventory(completedItem); | 
				
			||||
 | 
				
			||||
            ItemProductionMsg outMsg1 = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, completedItem, mbEnums.ProductionActionType.DEPOSIT, true); | 
				
			||||
            DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg1, mbEnums.DispatchChannel.SECONDARY, 700, false, false); | 
				
			||||
            ItemProductionMsg outMsg2 = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, completedItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT, true); | 
				
			||||
            DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg2, mbEnums.DispatchChannel.SECONDARY, 700, false, false); | 
				
			||||
 | 
				
			||||
            toRemove.add(virutalItem); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        for (Item virtualItem : toRemove) { | 
				
			||||
 | 
				
			||||
            // Remove virtual items from the forge window
 | 
				
			||||
 | 
				
			||||
            ItemProductionMsg outMsg = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, virtualItem, mbEnums.ProductionActionType.CONFIRM_SETPRICE, true); | 
				
			||||
            DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg, mbEnums.DispatchChannel.SECONDARY, 700, false, false); | 
				
			||||
 | 
				
			||||
            // Remove virtual item from all collections
 | 
				
			||||
 | 
				
			||||
            workOrder.cooking.remove(virtualItem); | 
				
			||||
            itemWorkOrderLookup.remove(virtualItem); | 
				
			||||
            DbManager.removeFromCache(virtualItem); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static void forgeWorkOrderBatch(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Completion time for this batch is in the future
 | 
				
			||||
 | 
				
			||||
        workOrder.completionTime = System.currentTimeMillis() + workOrder.rollingDuration; | 
				
			||||
 | 
				
			||||
        for (int i = 0; i < workOrder.slots_used; ++i) { | 
				
			||||
 | 
				
			||||
            Item forged_item = forgeItem(workOrder); | 
				
			||||
 | 
				
			||||
            // Update NPC window
 | 
				
			||||
 | 
				
			||||
            ItemProductionMsg outMsg = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, forged_item, mbEnums.ProductionActionType.CONFIRM_PRODUCE, true); | 
				
			||||
            DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg, mbEnums.DispatchChannel.SECONDARY, 700, false, false); | 
				
			||||
            workOrder.total_produced = workOrder.total_produced + 1; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Write updated workOrder to disk
 | 
				
			||||
 | 
				
			||||
        DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder); | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static int calcRandomMod(NPC vendor, mbEnums.ItemModType itemModType, int modTable) { | 
				
			||||
 | 
				
			||||
        // Random prefix or suffix token based on item.template.modtable
 | 
				
			||||
 | 
				
			||||
        int modifier = 0; | 
				
			||||
        ModTypeTableEntry modTypeTableEntry = null; | 
				
			||||
        ModTableEntry modTableEntry; | 
				
			||||
        int rollForModifier; | 
				
			||||
 | 
				
			||||
        switch (itemModType) { | 
				
			||||
            case PREFIX: | 
				
			||||
                int randomPrefix = vendor.getModTypeTable().get(vendor.getItemModTable().indexOf(modTable)); | 
				
			||||
                modTypeTableEntry = ModTypeTableEntry.rollTable(randomPrefix, ThreadLocalRandom.current().nextInt(1, 100 + 1)); | 
				
			||||
                break; | 
				
			||||
            case SUFFIX: | 
				
			||||
                int randomSuffix = vendor.getModSuffixTable().get(vendor.getItemModTable().indexOf(modTable)); | 
				
			||||
                modTypeTableEntry = ModTypeTableEntry.rollTable(randomSuffix, ThreadLocalRandom.current().nextInt(1, 100 + 1)); | 
				
			||||
                break; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (modTypeTableEntry == null) | 
				
			||||
            return 0; | 
				
			||||
 | 
				
			||||
        rollForModifier = ThreadLocalRandom.current().nextInt(1, 100 + 1); | 
				
			||||
 | 
				
			||||
        if (rollForModifier < 80) { | 
				
			||||
            int randomModifier = LootManager.TableRoll(vendor.getLevel(), false); | 
				
			||||
            modTableEntry = ModTableEntry.rollTable(modTypeTableEntry.modTableID, randomModifier); | 
				
			||||
            EffectsBase effectsBase = PowersManager.getEffectByIDString(modTableEntry.action); | 
				
			||||
            modifier = effectsBase.getToken(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return modifier; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,255 @@
				@@ -0,0 +1,255 @@
					 | 
				
			||||
// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 | 
				
			||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 | 
				
			||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 | 
				
			||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 | 
				
			||||
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 | 
				
			||||
//      Magicbane Emulator Project © 2013 - 2022
 | 
				
			||||
//                www.magicbane.com
 | 
				
			||||
 | 
				
			||||
package engine.loot; | 
				
			||||
 | 
				
			||||
import engine.gameManager.DbManager; | 
				
			||||
import engine.gameManager.ForgeManager; | 
				
			||||
import engine.mbEnums; | 
				
			||||
import engine.objects.Item; | 
				
			||||
import engine.objects.ItemTemplate; | 
				
			||||
import engine.objects.NPC; | 
				
			||||
import engine.objects.Warehouse; | 
				
			||||
import org.json.JSONArray; | 
				
			||||
import org.json.JSONObject; | 
				
			||||
 | 
				
			||||
import java.time.Duration; | 
				
			||||
import java.time.Instant; | 
				
			||||
import java.time.LocalDateTime; | 
				
			||||
import java.time.ZoneId; | 
				
			||||
import java.util.ArrayList; | 
				
			||||
import java.util.EnumSet; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.concurrent.ConcurrentHashMap; | 
				
			||||
import java.util.concurrent.Delayed; | 
				
			||||
import java.util.concurrent.TimeUnit; | 
				
			||||
 | 
				
			||||
public class WorkOrder implements Delayed { | 
				
			||||
 | 
				
			||||
    // MB Dev notes:
 | 
				
			||||
    // Class defines a Forge rolling request made through a
 | 
				
			||||
    // vendor; then passed to the ForgeManager singleton
 | 
				
			||||
    // for completion.
 | 
				
			||||
    //
 | 
				
			||||
    // A workOrder once created will last until all items are
 | 
				
			||||
    // either completed or junked.  They are persisted in the
 | 
				
			||||
    // table dyn_workorders.
 | 
				
			||||
 | 
				
			||||
    public int workOrderID; | 
				
			||||
    public NPC vendor; | 
				
			||||
    public int slots_used; | 
				
			||||
    public int total_to_produce; | 
				
			||||
    public int total_produced; | 
				
			||||
    public boolean multiple_slot_request; | 
				
			||||
    public HashMap<mbEnums.ResourceType, Integer> production_cost = new HashMap<>(); | 
				
			||||
    public HashMap<mbEnums.ResourceType, Integer> production_cost_total = new HashMap<>(); | 
				
			||||
    public int templateID; | 
				
			||||
    public String item_name_override; | 
				
			||||
    public int prefixToken; | 
				
			||||
    public int suffixToken; | 
				
			||||
    public long rollingDuration; | 
				
			||||
    public long completionTime; | 
				
			||||
    public boolean runCompleted = false; | 
				
			||||
    public boolean runCanceled = false; | 
				
			||||
 | 
				
			||||
    // This collection is serialized to the vendor rolling window in ManageNPCMsg.
 | 
				
			||||
 | 
				
			||||
    public ConcurrentHashMap.KeySetView<Item, Boolean> cooking = ConcurrentHashMap.newKeySet(); | 
				
			||||
 | 
				
			||||
    public WorkOrder() { | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public WorkOrder(JSONObject jsonWorkOrder) { | 
				
			||||
 | 
				
			||||
        // This constructor is used to load workOrders from disk
 | 
				
			||||
        // during bootstrap.  (dyn_workorders)
 | 
				
			||||
 | 
				
			||||
        this.workOrderID = jsonWorkOrder.getInt("workOrderID"); | 
				
			||||
        this.vendor = NPC.getNPC(jsonWorkOrder.getInt("vendor")); | 
				
			||||
        this.slots_used = jsonWorkOrder.getInt("slots_used"); | 
				
			||||
        this.total_to_produce = jsonWorkOrder.getInt("total_to_produce"); | 
				
			||||
        this.total_produced = jsonWorkOrder.getInt("total_produced"); | 
				
			||||
        this.multiple_slot_request = jsonWorkOrder.getBoolean("multiple_slot_request"); | 
				
			||||
        this.templateID = jsonWorkOrder.getInt("templateID"); | 
				
			||||
        this.item_name_override = jsonWorkOrder.getString("item_name_override"); | 
				
			||||
        this.prefixToken = jsonWorkOrder.getInt("prefixToken"); | 
				
			||||
        this.suffixToken = jsonWorkOrder.getInt("suffixToken"); | 
				
			||||
        this.slots_used = jsonWorkOrder.getInt("slots_used"); | 
				
			||||
        this.rollingDuration = jsonWorkOrder.getLong("rollingDuration"); | 
				
			||||
        this.completionTime = jsonWorkOrder.getLong("completionTime"); | 
				
			||||
        this.runCompleted = jsonWorkOrder.getBoolean("runCompleted"); | 
				
			||||
 | 
				
			||||
        JSONObject productionCostMap = jsonWorkOrder.getJSONObject("production_cost"); | 
				
			||||
 | 
				
			||||
        for (String key : productionCostMap.keySet()) { | 
				
			||||
            mbEnums.ResourceType resourceType = mbEnums.ResourceType.valueOf(key); | 
				
			||||
            int value = productionCostMap.getInt(key); | 
				
			||||
            this.production_cost.put(resourceType, value); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        JSONObject productionTotalCostMap = jsonWorkOrder.getJSONObject("production_cost_total"); | 
				
			||||
 | 
				
			||||
        for (String key : productionTotalCostMap.keySet()) { | 
				
			||||
            mbEnums.ResourceType resourceType = mbEnums.ResourceType.valueOf(key); | 
				
			||||
            int value = productionTotalCostMap.getInt(key); | 
				
			||||
            this.production_cost_total.put(resourceType, value); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // Reconstruct cooking items
 | 
				
			||||
 | 
				
			||||
        JSONArray tokenList = jsonWorkOrder.getJSONArray("cookingTokens"); | 
				
			||||
 | 
				
			||||
        for (Object o : tokenList) { | 
				
			||||
            int prefix = ((JSONArray) o).getInt(0); | 
				
			||||
            int suffix = ((JSONArray) o).getInt(1); | 
				
			||||
            Item cookingItem = ForgeManager.forgeItem(this); | 
				
			||||
            cookingItem.prefixToken = prefix; | 
				
			||||
            cookingItem.suffixToken = suffix; | 
				
			||||
            cookingItem.setDateToUpgrade(this.completionTime); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static int validate(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Validate that a workOrder can be completed by both
 | 
				
			||||
        // the vendor and the forge.
 | 
				
			||||
 | 
				
			||||
        int validation_result = 0; | 
				
			||||
 | 
				
			||||
        ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); | 
				
			||||
 | 
				
			||||
        if (workOrder.vendor.getBuilding() == null) | 
				
			||||
            return 58; //58: The formula is beyond the means of this facility
 | 
				
			||||
 | 
				
			||||
        if (!workOrder.vendor.charItemManager.hasRoomInventory(template.item_wt)) | 
				
			||||
            return 30;  //30: That person cannot carry that item
 | 
				
			||||
 | 
				
			||||
        if (!workOrder.vendor.getItemModTable().contains((template.modTable))) | 
				
			||||
            return 59;   //59: This hireling does not have this formula
 | 
				
			||||
 | 
				
			||||
        if (!Warehouse.calcCostOverrun(workOrder).isEmpty()) | 
				
			||||
            return 10;     //18: You can't really afford that
 | 
				
			||||
 | 
				
			||||
        // Forge must be protected in order to access warehouse.
 | 
				
			||||
 | 
				
			||||
        if (ForgeManager.calcProductionCost(workOrder).size() > 1) | 
				
			||||
            if (!EnumSet.of(mbEnums.ProtectionState.PROTECTED, mbEnums.ProtectionState.CONTRACT).contains(workOrder.vendor.building.protectionState)) | 
				
			||||
                return 193;     //193: Production denied: This building must be protected to gain access to warehouse
 | 
				
			||||
 | 
				
			||||
        return validation_result; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static boolean withdrawWorkOrderCost(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        if (workOrder.vendor.building.getCity() == null) | 
				
			||||
            return false; | 
				
			||||
 | 
				
			||||
        int strongbox = workOrder.vendor.building.getStrongboxValue(); | 
				
			||||
 | 
				
			||||
        // Strongbox can cover total gold cost;
 | 
				
			||||
 | 
				
			||||
        if (workOrder.production_cost_total.get(mbEnums.ResourceType.GOLD) <= strongbox) { | 
				
			||||
 | 
				
			||||
            workOrder.vendor.building.setStrongboxValue(strongbox - workOrder.production_cost_total.get(mbEnums.ResourceType.GOLD)); | 
				
			||||
            workOrder.production_cost_total.put(mbEnums.ResourceType.GOLD, 0); | 
				
			||||
 | 
				
			||||
            // Early exit for Strongbox covering gold only rolls
 | 
				
			||||
 | 
				
			||||
            if (workOrder.production_cost_total.size() == 1) | 
				
			||||
                return true; | 
				
			||||
        } else { | 
				
			||||
            int remainingAmount = workOrder.production_cost_total.get(mbEnums.ResourceType.GOLD) - strongbox; | 
				
			||||
            workOrder.vendor.building.setStrongboxValue(0); | 
				
			||||
            workOrder.production_cost_total.put(mbEnums.ResourceType.GOLD, workOrder.production_cost_total.get(mbEnums.ResourceType.GOLD) - remainingAmount); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        // There is an overflow at this point and a warehouse is required
 | 
				
			||||
 | 
				
			||||
        Warehouse warehouse = workOrder.vendor.building.getCity().warehouse; | 
				
			||||
 | 
				
			||||
        if (warehouse == null) | 
				
			||||
            return false; | 
				
			||||
 | 
				
			||||
        // Deduct total production cost from warehouse
 | 
				
			||||
 | 
				
			||||
        workOrder.production_cost_total.forEach((key, value) -> warehouse.resources.put(key, warehouse.resources.get(key) - value)); | 
				
			||||
        DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse); | 
				
			||||
 | 
				
			||||
        return true; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public long getDelay(TimeUnit unit) { | 
				
			||||
        long timeRemaining = completionTime - System.currentTimeMillis(); | 
				
			||||
        return unit.convert(timeRemaining, TimeUnit.MILLISECONDS); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public int compareTo(Delayed o) { | 
				
			||||
        return Long.compare(this.completionTime, ((WorkOrder) o).completionTime); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static JSONObject toJson(WorkOrder workOrder) { | 
				
			||||
 | 
				
			||||
        // Workorders are persisted in JSON format.
 | 
				
			||||
 | 
				
			||||
        JSONObject jsonWorkOrder = new JSONObject(); | 
				
			||||
 | 
				
			||||
        jsonWorkOrder.put("workOrderID", workOrder.workOrderID); | 
				
			||||
        jsonWorkOrder.put("vendor", workOrder.vendor.getObjectUUID()); | 
				
			||||
        jsonWorkOrder.put("slots_used", workOrder.slots_used); | 
				
			||||
        jsonWorkOrder.put("total_to_produce", workOrder.total_to_produce); | 
				
			||||
        jsonWorkOrder.put("total_produced", workOrder.total_produced); | 
				
			||||
        jsonWorkOrder.put("multiple_slot_request", workOrder.multiple_slot_request); | 
				
			||||
        jsonWorkOrder.put("production_cost", workOrder.production_cost); | 
				
			||||
        jsonWorkOrder.put("production_cost_total", workOrder.production_cost_total); | 
				
			||||
        jsonWorkOrder.put("templateID", workOrder.templateID); | 
				
			||||
        jsonWorkOrder.put("item_name_override", workOrder.item_name_override); | 
				
			||||
        jsonWorkOrder.put("prefixToken", workOrder.prefixToken); | 
				
			||||
        jsonWorkOrder.put("suffixToken", workOrder.suffixToken); | 
				
			||||
        jsonWorkOrder.put("rollingDuration", workOrder.rollingDuration); | 
				
			||||
        jsonWorkOrder.put("completionTime", workOrder.completionTime); | 
				
			||||
        jsonWorkOrder.put("runCompleted", workOrder.runCompleted); | 
				
			||||
 | 
				
			||||
        ArrayList<Integer[]> cookingTokens = new ArrayList<>(); | 
				
			||||
 | 
				
			||||
        for (Item item : workOrder.cooking) | 
				
			||||
            cookingTokens.add(new Integer[]{item.prefixToken, item.suffixToken}); | 
				
			||||
 | 
				
			||||
        jsonWorkOrder.put("cookingTokens", cookingTokens); | 
				
			||||
 | 
				
			||||
        return jsonWorkOrder; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public String toString() { | 
				
			||||
 | 
				
			||||
        LocalDateTime localDateTime = Instant.ofEpochMilli(this.completionTime) | 
				
			||||
                .atZone(ZoneId.systemDefault()).toLocalDateTime(); | 
				
			||||
        Duration duration = Duration.ofMillis(this.rollingDuration); | 
				
			||||
 | 
				
			||||
        String outSTring = "\r\nwordOrderID: " + this.workOrderID + "\r\n" + | 
				
			||||
                "vendor: " + this.vendor.getObjectUUID() + "\r\n" + | 
				
			||||
                "slots_used: " + this.slots_used + "\r\n" + | 
				
			||||
                "total_to_produce: " + this.total_to_produce + "\r\n" + | 
				
			||||
                "total_produced: " + this.total_produced + "\r\n" + | 
				
			||||
                "templateID: " + this.templateID + "\r\n" + | 
				
			||||
                "item_name_override: " + this.item_name_override + "\r\n" + | 
				
			||||
                "prefixToken: " + this.prefixToken + "\r\n" + | 
				
			||||
                "suffixToken: " + this.suffixToken + "\r\n" + | 
				
			||||
                "rollingDuration: " + duration + "\r\n" + | 
				
			||||
                "completionTime: " + localDateTime + "\r\n" + | 
				
			||||
                "runCompleted: " + this.runCompleted + "\r\n" + | 
				
			||||
                "runCanceled: " + this.runCanceled + "\r\n" + | 
				
			||||
                "productionCost: " + this.production_cost.toString() + "\r\n" + | 
				
			||||
                "totalProductionCost:: " + this.production_cost_total.toString(); | 
				
			||||
 | 
				
			||||
        return outSTring; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -1,135 +0,0 @@
				@@ -1,135 +0,0 @@
					 | 
				
			||||
// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 | 
				
			||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 | 
				
			||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 | 
				
			||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 | 
				
			||||
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 | 
				
			||||
//      Magicbane Emulator Project © 2013 - 2022
 | 
				
			||||
//                www.magicbane.com
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
package engine.net; | 
				
			||||
 | 
				
			||||
import engine.mbEnums.DispatchChannel; | 
				
			||||
import engine.objects.ProducedItem; | 
				
			||||
import org.pmw.tinylog.Logger; | 
				
			||||
 | 
				
			||||
import java.util.HashSet; | 
				
			||||
import java.util.concurrent.DelayQueue; | 
				
			||||
import java.util.concurrent.atomic.LongAdder; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Thread blocks until MagicBane dispatch messages are | 
				
			||||
 * enqueued then processes them in FIFO order. The collection | 
				
			||||
 * is thread safe. | 
				
			||||
 * <p> | 
				
			||||
 * Any large messages not time sensitive such as load object | 
				
			||||
 * sent to more than a single individual should be spawned | 
				
			||||
 * individually on a DispatchMessageThread. | 
				
			||||
 */ | 
				
			||||
 | 
				
			||||
 | 
				
			||||
public enum ItemProductionManager implements Runnable { | 
				
			||||
 | 
				
			||||
    ITEMPRODUCTIONMANAGER; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    // Instance variables
 | 
				
			||||
 | 
				
			||||
    @SuppressWarnings("unchecked") // Cannot have arrays of generics in java.
 | 
				
			||||
    private static final DelayQueue<ItemQueue> producedQueue = new DelayQueue<>(); | 
				
			||||
    public static volatile long[] messageCount = new long[DispatchChannel.values().length]; | 
				
			||||
 | 
				
			||||
    // Class variables
 | 
				
			||||
    public static LongAdder[] dispatchCount = new LongAdder[DispatchChannel.values().length]; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    // Performance metrics
 | 
				
			||||
    public static volatile long[] maxRecipients = new long[DispatchChannel.values().length]; | 
				
			||||
    public static LongAdder dispatchPoolSize = new LongAdder(); | 
				
			||||
    public static HashSet<ProducedItem> FailedItems = new HashSet<>(); | 
				
			||||
    public Thread itemProductionThread = null; | 
				
			||||
    private ItemQueue itemQueue; | 
				
			||||
    private long nextFailedItemAudit; | 
				
			||||
 | 
				
			||||
    // Thread constructor
 | 
				
			||||
 | 
				
			||||
    public static void send(ItemQueue item) { | 
				
			||||
 | 
				
			||||
        // Don't queue up empty dispatches!
 | 
				
			||||
 | 
				
			||||
        if (item == null) | 
				
			||||
            return; | 
				
			||||
 | 
				
			||||
        producedQueue.add(item); | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static String getNetstatString() { | 
				
			||||
 | 
				
			||||
        String outString = null; | 
				
			||||
        String newLine = System.getProperty("line.separator"); | 
				
			||||
        outString = "[LUA_NETSTA()]" + newLine; | 
				
			||||
        outString += "poolSize: " + dispatchPoolSize.longValue() + '\n'; | 
				
			||||
 | 
				
			||||
        for (DispatchChannel dispatchChannel : DispatchChannel.values()) { | 
				
			||||
 | 
				
			||||
            outString += "Channel: " + dispatchChannel.name() + '\n'; | 
				
			||||
            outString += "Dispatches: " + dispatchCount[dispatchChannel.getChannelID()].longValue() + '\n'; | 
				
			||||
            outString += "Messages: " + messageCount[dispatchChannel.getChannelID()] + '\n'; | 
				
			||||
            outString += "maxRecipients: " + maxRecipients[dispatchChannel.getChannelID()] + '\n'; | 
				
			||||
        } | 
				
			||||
        return outString; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void startMessagePump() { | 
				
			||||
 | 
				
			||||
        itemProductionThread = new Thread(this); | 
				
			||||
        itemProductionThread.setName("ItemProductionManager"); | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void initialize() { | 
				
			||||
        itemProductionThread.start(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public void run() { | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        while (true) { | 
				
			||||
            try { | 
				
			||||
 | 
				
			||||
                this.itemQueue = producedQueue.take(); | 
				
			||||
 | 
				
			||||
                if (this.itemQueue == null) { | 
				
			||||
                    return; | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
                if (this.itemQueue != null) { | 
				
			||||
                    if (this.itemQueue.item == null) { | 
				
			||||
                        this.itemQueue.release(); | 
				
			||||
                        return; | 
				
			||||
                    } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                    boolean created = this.itemQueue.item.finishProduction(); | 
				
			||||
 | 
				
			||||
                    if (!created) | 
				
			||||
                        FailedItems.add(this.itemQueue.item); | 
				
			||||
 | 
				
			||||
                    this.itemQueue.release(); | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            } catch (Exception e) { | 
				
			||||
                Logger.error(e); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // For Debugging:
 | 
				
			||||
    //Logger.error("MessageDispatcher", messageDispatch.msg.getOpcodeAsString() + " sent to " + messageDispatch.playerList.size() + " players");
 | 
				
			||||
} | 
				
			||||
@ -1,86 +0,0 @@
				@@ -1,86 +0,0 @@
					 | 
				
			||||
// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 | 
				
			||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 | 
				
			||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 | 
				
			||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 | 
				
			||||
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 | 
				
			||||
//      Magicbane Emulator Project © 2013 - 2022
 | 
				
			||||
//                www.magicbane.com
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
package engine.net; | 
				
			||||
 | 
				
			||||
import engine.objects.ProducedItem; | 
				
			||||
 | 
				
			||||
import java.util.concurrent.ConcurrentLinkedQueue; | 
				
			||||
import java.util.concurrent.Delayed; | 
				
			||||
import java.util.concurrent.TimeUnit; | 
				
			||||
 | 
				
			||||
import static engine.net.MessageDispatcher.itemPoolSize; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Data class holds a message and a distribution list | 
				
			||||
 */ | 
				
			||||
 | 
				
			||||
public class ItemQueue implements Delayed { | 
				
			||||
 | 
				
			||||
    private static final ConcurrentLinkedQueue<ItemQueue> itemPool = new ConcurrentLinkedQueue<>(); | 
				
			||||
 | 
				
			||||
    public ProducedItem item; | 
				
			||||
    public long delayTime; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public ItemQueue(ProducedItem item, long delayTime) { | 
				
			||||
        this.item = item; | 
				
			||||
        this.delayTime = System.currentTimeMillis() + delayTime; | 
				
			||||
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public static ItemQueue borrow(ProducedItem item, long delayTime) { | 
				
			||||
 | 
				
			||||
        ItemQueue itemQueue; | 
				
			||||
 | 
				
			||||
        itemQueue = itemPool.poll(); | 
				
			||||
 | 
				
			||||
        if (itemQueue == null) { | 
				
			||||
            itemQueue = new ItemQueue(item, delayTime); | 
				
			||||
        } else { | 
				
			||||
            itemQueue.item = item; | 
				
			||||
            itemQueue.delayTime = System.currentTimeMillis() + delayTime; | 
				
			||||
            itemPoolSize.decrement(); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return itemQueue; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void reset() { | 
				
			||||
        this.item = null; | 
				
			||||
        this.delayTime = 0; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    public void release() { | 
				
			||||
        this.reset(); | 
				
			||||
        itemPool.add(this); | 
				
			||||
        itemPoolSize.increment(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public int compareTo(Delayed another) { | 
				
			||||
        ItemQueue anotherTask = (ItemQueue) another; | 
				
			||||
 | 
				
			||||
        if (this.delayTime < anotherTask.delayTime) { | 
				
			||||
            return -1; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        if (this.delayTime > anotherTask.delayTime) { | 
				
			||||
            return 1; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        return 0; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public long getDelay(TimeUnit unit) { | 
				
			||||
        long difference = delayTime - System.currentTimeMillis(); | 
				
			||||
        return unit.convert(difference, TimeUnit.MILLISECONDS); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue