forked from MagicBane/Server
				
			
				 283 changed files with 2304 additions and 3493 deletions
			
			
		| @ -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 @@ | |||||||
|  | // • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 | ||||||
|  | // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 | ||||||
|  | // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 | ||||||
|  | // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 | ||||||
|  | // ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 | ||||||
|  | //      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 @@ | |||||||
| // • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 |  | ||||||
| // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 |  | ||||||
| // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 |  | ||||||
| // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 |  | ||||||
| // ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 |  | ||||||
| //      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 @@ | |||||||
| // • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
 |  | ||||||
| // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
 |  | ||||||
| // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
 |  | ||||||
| // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
 |  | ||||||
| // ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
 |  | ||||||
| //      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