Optimize GetContainerItemInfo hook and bump to 1.4.11

- Reduce temporary table allocations in GetContainerItemInfo
- Preserve the client return signature safely
- Fall back to the original hook if the signature is unexpected
- Keep mailbox, Open All and send-mail behavior unchanged
- Bump version to 1.4.11
This commit is contained in:
2026-09-14 09:08:50 +00:00
parent e3d2dc1034
commit 83367549c4
4 changed files with 92 additions and 6 deletions
+5 -1
View File
@@ -31,6 +31,10 @@ Target: Turtle WoW 1.18.x / Octo client.
- **First mailbox-open freeze/stutter after the 1.4.9 recovery fix:** 1.4.9 called `ensure_open_mail_button()` on every `MAIL_INBOX_UPDATE`. Initial inbox population can emit a burst of those events, so the button was repeatedly re-parented, re-anchored, re-labeled, resized, and given a new frame level. 1.4.10 removes that work from `MAIL_INBOX_UPDATE` and makes `ensure_open_mail_button()` state-aware, so it only mutates the UI when the button is missing, hidden, moved, re-parented, or otherwise incorrect.
## Fixed in 1.4.11
- **Residual mailbox hitch / excessive garbage generation:** the upstream `GetContainerItemInfo` hook used `pack()` + `unpack()` for every bag query, allocating a temporary table each time. Bag/mailbox refreshes can call this path very frequently and trigger avoidable garbage collection pauses. 1.4.11 replaces the installed hook after login with a fixed-local wrapper that performs no per-call table allocation. The native return count is detected once and the exact arity is preserved for signatures of up to 12 return values; unusual signatures fall back to the previous hook.
## Remaining architectural risks upstream
These are intentionally not rewritten in the compatibility layer because changing them would be much more invasive and could alter mail behavior:
@@ -52,4 +56,4 @@ The compatibility build was tested in-game on Turtle WoW 1.18.x and the reported
## Status
**1.4.10 is the current stable release.** The fixes remain isolated in `TurtleMailFix.lua` so upstream code stays easy to compare and future upstream changes remain easier to merge.
**1.4.11 is the current test build pending in-game validation.** The fixes remain isolated in `TurtleMailFix.lua` so upstream code stays easy to compare and future upstream changes remain easier to merge.
+5 -3
View File
@@ -76,7 +76,7 @@ When enabled, TurtleMail records sent and received mail information in its dedic
## 🔧 Turtle WoW compatibility
Version **1.4.10** extends the compatibility and safety layer for Turtle WoW 1.18.x environments.
Version **1.4.11** extends the compatibility, safety, and performance layer for Turtle WoW 1.18.x environments.
The compatibility work is isolated in `TurtleMailFix.lua` so the original TurtleMail code remains easier to compare with upstream versions.
@@ -130,14 +130,16 @@ TurtleMail includes localization support for:
## 📜 Version
Current stable version:
Current test version:
**1.4.10**
**1.4.11**
Based on upstream **TurtleMail 1.4.5**.
Version 1.4.10 keeps the **Open Mail** recovery from 1.4.9 but makes it lightweight: the button is repaired on load/mailbox open only when needed, instead of being re-parented and re-anchored on every inbox refresh. This avoids repeated layout work during the initial mailbox update burst.
Version 1.4.11 also removes the per-call `pack()`/`unpack()` allocation from TurtleMail's `GetContainerItemInfo` hook. The native return count is detected once at login and preserved exactly (up to 12 values), while normal bag queries use fixed local values. This reduces garbage collection pressure during bag/mailbox refreshes without changing mail behavior.
## 🖼️ Screenshots
![TurtleMail](https://i.imgur.com/H0MUmXd.png)
+1 -1
View File
@@ -2,7 +2,7 @@
## Interface: 11200
## Title: |cffabd473Turtle|cffffffffMail
## Author: shirsig/sica, Dusk-92 (compatibility fixes)
## Version: 1.4.10
## Version: 1.4.11
## Notes: Mailbox enhancement - Turtle WoW 1.18.x compatibility
## SavedVariables: TurtleMail_AutoCompleteNames
## SavedVariablesPerCharacter: TurtleMail_To TurtleMail_Point TurtleMail_Log
+81 -1
View File
@@ -8,7 +8,7 @@ local m = TurtleMail
local getn = table.getn
local function pack( ... ) return arg end
m.compat_version = "1.4.10"
m.compat_version = "1.4.11"
local DEFAULT_SENT_FILTERS = {
Money = 1,
@@ -910,3 +910,83 @@ do
end
end
end
-- Performance compatibility fix inspired by Otari98/TurtleMail: the upstream
-- GetContainerItemInfo hook builds a temporary table with pack()/unpack() on
-- every bag query. Bag and mailbox refreshes can call this hook very frequently,
-- creating avoidable garbage and occasional GC hitches. Detect the native return
-- count once after the normal PLAYER_LOGIN hook setup, then use a fixed-local
-- wrapper with no per-call temporary table. Exact return arity is preserved for
-- up to 12 values so extended/custom clients remain compatible; if detection is
-- inconclusive, the existing upstream hook is left untouched.
do
local original_player_login = m.PLAYER_LOGIN
local legacy_hook = m.hooks and m.hooks.GetContainerItemInfo or nil
local detected_return_count
local function count_returns( ... )
return arg.n or getn( arg )
end
local function detect_container_info_return_count()
local fn = m.orig and m.orig.GetContainerItemInfo
if type( fn ) ~= "function" then return nil end
-- Prefer an occupied inventory slot so clients that return no values for an
-- empty slot cannot make us detect the wrong arity.
if m.api.GetContainerNumSlots then
for bag = 0, 4 do
local slots = tonumber( m.api.GetContainerNumSlots( bag ) ) or 0
for slot = 1, slots do
local texture = fn( bag, slot )
if texture then
return count_returns( fn( bag, slot ) )
end
end
end
end
-- A normal character should always have at least one item, but retain a
-- conservative fallback for unusual test clients or empty inventories.
local count = count_returns( fn( 0, 1 ) )
if count and count > 0 then return count end
return nil
end
local function optimized_get_container_item_info( bag, slot )
local r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12 =
m.orig.GetContainerItemInfo( bag, slot )
if not r3 and m.sendmail_attached( bag, slot ) then
r3 = 1
end
if detected_return_count == 1 then return r1 end
if detected_return_count == 2 then return r1, r2 end
if detected_return_count == 3 then return r1, r2, r3 end
if detected_return_count == 4 then return r1, r2, r3, r4 end
if detected_return_count == 5 then return r1, r2, r3, r4, r5 end
if detected_return_count == 6 then return r1, r2, r3, r4, r5, r6 end
if detected_return_count == 7 then return r1, r2, r3, r4, r5, r6, r7 end
if detected_return_count == 8 then return r1, r2, r3, r4, r5, r6, r7, r8 end
if detected_return_count == 9 then return r1, r2, r3, r4, r5, r6, r7, r8, r9 end
if detected_return_count == 10 then return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 end
if detected_return_count == 11 then return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11 end
if detected_return_count == 12 then return r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12 end
-- Should not normally be reached; keep the previous behavior if a custom
-- client exposes an unexpected signature.
if legacy_hook then return legacy_hook( bag, slot ) end
return r1, r2, r3, r4, r5
end
function m.PLAYER_LOGIN()
if original_player_login then original_player_login() end
detected_return_count = detect_container_info_return_count()
if detected_return_count and detected_return_count >= 1 and detected_return_count <= 12 then
m.api.GetContainerItemInfo = optimized_get_container_item_info
end
end
end