Thread Tools Display Modes
08/30/15, 08:36 PM   #1
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
PTS: Stack All Items Bug

The new "Stack All Items" is calling
Lua Code:
  1. PLAYER_INVENTORY:UpdateList(...)
way to often. It needs to loop through the inventory, stack all of the items, & only call UpdateList(...) once. At the very least StackBag(...) looks like it needs a check to see if a slot is already at max stack size before attempting to stack it with another stack of the same type of item. As it is now its attempting to stack slots, even if they are at max stack size, with every other stack of the same type of item and calling UpdateList(...) each time.

If you have, for example (it could be any stackable item in any slot, this is just an example)
slotIndex, item, stackSize
1, Sanded Nightwood, 200
2, Sanded Nightwood, 200
3, Sanded Nightwood, 200
4, Sanded Nightwood, 200
5, Sanded Nightwood, 45
and you hit the "Stack All Item" keybind PLAYER_INVENTORY:UpdateList(...) will get called 20 times. Even if that's all you have in that inventory.

Each slot/stack attempts to stack with every other slot/stack (of the same type of item), even if they are at max stack size, causing
Lua Code:
  1. #NumOfStacks * (#NumOfStacks-1)
calls to:
Lua Code:
  1. PLAYER_INVENTORY:UpdateList(...)

Reproduction Steps:
1) Empty the inventory to be tested on (this is just so we can see the exact update count caused by the stacks we add and don't have to worry about other stacks throwing off the count).
2) Place 2 stacks of the same type of item into the inventory. Must have over 200 (max stack size) items of the same type so you still have 2 slot/stacks after it stacks the items. Both stacks can be max stack size it does not matter.
3) Use the following code and watch how many times the UpdateList gets called. It will get called 2 times.

Lua Code:
  1. local iCount = 0
  2. local function OnUpdateList(self, iInventoryType)
  3.     iCount = iCount + 1
  4.     d("OnUpdateList Count: "..iCount..", "..iInventoryType)end
  5. ZO_PreHook(PLAYER_INVENTORY, "UpdateList", OnUpdateList)

4) Now add a 3rd stack (must have a total of over 400 of the same type of item, 2 slots at max stack size) and run the "Stack All Items" again. This time it will call UpdateList(...) 6 times.
5) With 4 stacks (over 600 of the same type of item) it will call UpdateList(...) 12 times
6) With 5 stacks (over 800 of the same type of item) it will call UpdateList(...) 20 times
Exc...exc....


A user was having a problem with my addon crashing & after great help from the users I managed to figure this out. He had a LOT of stacked items & lots of them were full stacks. UpdateList(...) was getting called nearly 200 times in a single use of "Stack All Items" which in turn was causing way to much of my code to run and it was crashing the game. I think I need to optimize something, but StackBag(...) needs some work too.

Last edited by circonian : 08/30/15 at 08:39 PM.
  Reply With Quote
08/30/15, 09:14 PM   #2
Ayantir
 
Ayantir's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 1,019
That's how I do in Roomba to stack in stackable bags, it does exactly the same feature as the function :
Little bit modified for the exemple!


Lua Code:
  1. -- Called when user click on the keybind / press the key.
  2.    
  3. local bagToScan = SHARED_INVENTORY:GenerateFullSlotData(nil, bagId)
  4. Roomba.scanInStackableBag(bagToScan)
  5. if NonContiguousCount(Roomba.duplicates) > 0 then Roomba.restackStackableBag(bagId, duplicateList) end
  6.  
  7. -- Scan in a stackable bag
  8. function Roomba.scanInStackableBag(bagToScan)
  9.  
  10.     Roomba.lookUp = {}
  11.     Roomba.duplicates = {}
  12.     local duplitemp = {}
  13.    
  14.     -- We only need to store slots with items
  15.     for index, slot in pairs(bagToScan) do
  16.        
  17.         -- Stack at max?
  18.         local stack, maxStack = GetSlotStackSize(slot.bagId, slot.slotIndex)
  19.        
  20.         -- Stack is not at max
  21.         if stack ~= maxStack then
  22.            
  23.             -- itemId
  24.             local itemInstanceId = slot.itemInstanceId
  25.            
  26.             -- We already find this item before
  27.             if Roomba.lookUp[itemInstanceId] then
  28.                 -- Already marked as duplicate?
  29.                 if not duplitemp[itemInstanceId] then
  30.                     -- Duplicate
  31.                     duplitemp[itemInstanceId] = Roomba.lookUp[itemInstanceId]
  32.                 end
  33.             else
  34.                 -- New item found
  35.                 Roomba.lookUp[itemInstanceId] = {}
  36.             end
  37.            
  38.             -- Now group all items by id
  39.             table.insert(Roomba.lookUp[itemInstanceId], {slot = slot.slotIndex, stack = stack, texture = slot.iconFile, name = slot.name, itemInstanceId = slot.itemInstanceId})
  40.            
  41.         end
  42.        
  43.     end
  44.    
  45.     local i = 1
  46.     for _, data in pairs(duplitemp) do
  47.         Roomba.duplicates[i] = data
  48.         i = i + 1
  49.     end
  50.  
  51. end
  52.  
  53. -- duplicateList is Roomba.duplicates
  54. function Roomba.restackStackableBag(bagId, duplicateList)
  55.  
  56.     local restartAfter = false
  57.     local itemIndex = 0
  58.     local indexItemsDuplicated, dataItems = next(duplicateList)
  59.     local result = {}
  60.    
  61.     -- Loop for item X/Y/Z
  62.     while indexItemsDuplicated do
  63.        
  64.         local index = 1
  65.         local itemInfo = dataItems[index]
  66.         local baseSlot = nil
  67.         local lastMoveWasSingleStack = false
  68.         local lastMoveWasMultiStack = false
  69.         result[indexItemsDuplicated] = {}
  70.        
  71.         -- Loop for item X and slots 1/2/3/y
  72.         while itemInfo do
  73.            
  74.             -- 1st loop : we go in
  75.             if baseSlot == nil then
  76.                 -- Our actual stack / Our max size
  77.                 baseSlot = itemInfo
  78.                 baseSlot.actualStack, baseSlot.maxStack = GetSlotStackSize(bagId, itemInfo.slot)
  79.             else
  80.                
  81.                 local qty
  82.                 -- If stacking, will we get 1 or 2 stacks ?
  83.                 itemInfo.maxStack = baseSlot.maxStack
  84.                
  85.                 if (baseSlot.actualStack + itemInfo.stack) <= baseSlot.maxStack then
  86.                    
  87.                     -- Only 1 stack, we can merge stacks
  88.                     qty = itemInfo.stack
  89.                    
  90.                     -- Merging
  91.                     if IsProtectedFunction("RequestMoveItem") then
  92.                         CallSecureProtected("RequestMoveItem", bagId, itemInfo.slot, bagId, baseSlot.slot, qty)
  93.                     else
  94.                         RequestMoveItem(bagId, itemInfo.slot, bagId, baseSlot.slot, qty)
  95.                     end
  96.                    
  97.                     -- Update values baseSlot can still be used in next loop
  98.                     baseSlot.actualStack = baseSlot.actualStack + itemInfo.stack
  99.                     baseSlot.stack = baseSlot.actualStack
  100.                    
  101.                     if lastMoveWasMultiStack == true then
  102.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]] = baseSlot
  103.                         lastMoveWasSingleStack = true
  104.                         lastMoveWasMultiStack = false
  105.                     elseif lastMoveWasSingleStack == true then
  106.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]] = baseSlot
  107.                         lastMoveWasMultiStack = false
  108.                     else
  109.                         table.insert(result[indexItemsDuplicated], baseSlot)
  110.                         lastMoveWasSingleStack = true
  111.                         lastMoveWasMultiStack = false
  112.                     end
  113.                    
  114.                 else
  115.                    
  116.                     -- It won't fit, just move the qty to match maxStack, no need to rescan because slots don't move, only stacks.
  117.                     qty = baseSlot.maxStack - baseSlot.actualStack
  118.                    
  119.                     -- Merging
  120.                     if IsProtectedFunction("RequestMoveItem") then
  121.                         CallSecureProtected("RequestMoveItem", bagId, itemInfo.slot, bagId, baseSlot.slot, qty)
  122.                     else
  123.                         RequestMoveItem(bagId, itemInfo.slot, bagId, baseSlot.slot, qty)
  124.                     end
  125.                    
  126.                     if lastMoveWasMultiStack == true then
  127.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = baseSlot.maxStack
  128.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = baseSlot.maxStack
  129.                         table.insert(result[indexItemsDuplicated], itemInfo)
  130.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = itemInfo.stack - qty
  131.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = itemInfo.stack - qty
  132.                         lastMoveWasSingleStack = false
  133.                     elseif lastMoveWasSingleStack == true then
  134.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = baseSlot.maxStack
  135.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = baseSlot.maxStack
  136.                         table.insert(result[indexItemsDuplicated], itemInfo)
  137.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = itemInfo.stack - qty
  138.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = itemInfo.stack - qty
  139.                         lastMoveWasSingleStack = false
  140.                         lastMoveWasMultiStack = true
  141.                     else
  142.                         table.insert(result[indexItemsDuplicated], baseSlot)
  143.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = baseSlot.maxStack
  144.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = baseSlot.maxStack
  145.                         table.insert(result[indexItemsDuplicated], itemInfo)
  146.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].stack = itemInfo.stack - qty
  147.                         result[indexItemsDuplicated][#result[indexItemsDuplicated]].actualStack = itemInfo.stack - qty
  148.                         lastMoveWasSingleStack = false
  149.                         lastMoveWasMultiStack = true
  150.                     end
  151.                    
  152.                     -- Baseslot is now at max, we cannot use it anymore
  153.                     baseSlot = itemInfo
  154.                     baseSlot.actualStack = itemInfo.stack - qty
  155.                    
  156.                 end
  157.                
  158.             end
  159.            
  160.             index = index + 1
  161.             itemInfo = dataItems[index]
  162.        
  163.         end
  164.        
  165.         itemIndex = itemIndex + 1
  166.         indexItemsDuplicated, dataItems = next(duplicateList, itemIndex)
  167.        
  168.     end
  169.    
  170.     -- Rescan to permit the user press the keybind again. Because Bank got a lag, we need to call Roomba.onBankReady() and its zo_callLater to avoid having our restacking job still unknown
  171.     if bagId == BAG_BANK then
  172.         Roomba.onBankReady()
  173.     -- Only on restacking bag
  174.     elseif bagId == BAG_BACKPACK then
  175.         Roomba.onSceneStateChange(SCENE_HIDDEN, SCENE_SHOWING)
  176.     end
  177.    
  178. end

Last edited by Ayantir : 08/30/15 at 09:19 PM.
  Reply With Quote
08/31/15, 01:14 AM   #3
QuadroTony
Banned
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 828
we tested it all night, ye

we spent like 1.5m gold on different upgdares and 10k crowns
really annoying thing

and looks like it need very specific conditions to reproduce
i will be fine if some1 just write a code to hide this new Stack All Items feature from my eyes
  Reply With Quote
08/31/15, 11:30 AM   #4
ZOS_ChipHilseberg
ZOS Staff!
Premium Member
Yes this person is from ZeniMax!
Join Date: Oct 2014
Posts: 551
This is now fixed internally.
  Reply With Quote
08/31/15, 02:26 PM   #5
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,989
Just to be informed: There will be a "Stack all items" at the banks implemented by ZOs with the next update?
Hope Roomba will get it's credit then
  Reply With Quote
08/31/15, 02:36 PM   #6
QuadroTony
Banned
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 828
roomba still nedeed for a guild bank tho
  Reply With Quote
08/31/15, 03:03 PM   #7
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,989
Great news!
Alright, thought they would implement it for bank & guild banks
  Reply With Quote
08/31/15, 03:18 PM   #8
circonian
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 613
Originally Posted by ZOS_ChipHilseberg View Post
This is now fixed internally.
Thanks Chip !!
  Reply With Quote
09/09/15, 07:16 AM   #9
Ayantir
 
Ayantir's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 1,019
Test in live :

stack of 199 items
another of 1

stack all should :

set a stack at 200

now it's :

a stack of 200
a false stack of 1

I did a /bug
thanks

  Reply With Quote
09/14/15, 12:05 AM   #10
DerBombo
 
DerBombo's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 29
I only just now noticed this new feature.
Well.. no need to update Restacker anymore, I guess..
  Reply With Quote

ESOUI » Developer Discussions » General Authoring Discussion » PTS: Stack All Items Bug


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off