Skip to content

Reflector rework: survive EQ bundle refactors (structural anchor + fuzzy matching) — discovery map + spec #28

Description

@atapifire

Tracking issue for the Reflector rework after EvilQuest's 2026-06-21 bundle refactor. KKonaOG and api are working ideas on this — collaborative. The window.gm trigger fix already shipped (#27); this issue is the durable redesign so EQ updates become a signature refresh, not an outage.

Discovery below was produced from the live in-world bundle (read-only CDP). Goal aligns with KKonaOG's plan: parse index.js / source structurally for the entry point, then build the reflector from there in a semi-agnostic way.

Why it broke (two independent things)

  1. window.gm removed → our parse poller was gated on it → never parsed → dead client. Fixed in Fix: restore client against EvilQuest's bundle update (window.gm removed) #27 (gate now keys off "GameManager source present in captured modules").
  2. ~23 manager signatures + enums broke. The matcher requires ALL signature methods (AND-match), so a single rename kills a hook. Two sub-cases: renamed (new signature) and restructured/merged (class dissolved into another → retarget consumers).

Recommended design

Trigger (separate from matching — this is what bricked everyone): parse on modules-captured-and-settled, never on a game global; re-run when the bundle changes mid-session (track module set / content hash).

Structural anchor for GameManager (KKonaOG's index.js idea — keep it): in the captured source GameManager is the class whose body has static get BaseUrl and is instantiated exactly once (new <GM>( appears 1×). Name-independent → renames can't break it. Runtime cross-check: GameManager.Instance.constructor.name.

Fuzzy, multi-signal matching for the rest (replace AND-match):

  • score by member overlap (methods + getters across the prototype chain), accept ≥ ~60%;
  • combine signals: member names + the existing contains source-text check + member-count band;
  • assert exactly-one match, log ambiguity (fuzziness raises collision odds — fail loud, never silent).
    Verified: every signature below resolves to exactly 1 class at the 0.6 threshold on this build (survives a single rename).

Discovery map — VERIFIED RENAMED (drop-in signatures)

Manager Fuzzy signature
GameManager updateMinimap, worldObjectDisplayName, waitForCurrentLocalPlayerReady, setupContextMenu (+ static get BaseUrl anchor)
EntityManager createRemotePlayer, createNpc, findNearestNpc, createGroundItem
SocketManager openSockets, sendFrame, handleOpcodeMapping, consumePendingInputTicket
ChunkManager getMapId, getMapWidth, ensureFloorLayer, getTerrainDetailStats
InputManager handlePrimaryActionAt, pickGround, setObjectClickHandler, setGroundClickHandler
BankUIManager openWithContents, updateBankSlot, sendWithdrawMode, makeWithdrawModeToggle
ChatManager appendMessage, addPrivateMessage, installChatStyles, isScrolledToChatBottom

Discovery map — RESTRUCTURED / MERGED (retarget consumers, NOT a signature swap)

  • ItemDefinitionManagerEntityManager (itemDefsCache: Map<id,{name,icon,model}>)
  • NpcDefinitionManagerEntityManager (npcDefsCache)
  • world ContextMenuManagerGameManager (setupContextMenu, openWorldContextMenuAt, showContextMenu, handleWorldContextMenuEvent)
  • QuestDefinitionManagerGameManager (questDefsCache, verify)

➡️ Context menus need an integration rewrite in core, not a new signature.

Still to locate (use the recipe) + enums

Renamed beyond keyword scan: ItemManager, ContextMenuItemManager (inventory items), UIManager, HTMLUIManager, NameplateManager, SpellManager/SpellMenuManager/MagicSkillManager, ExperienceManager, GameLoop, MeshManager, etc.
Enums: Skills lost accuracy → new includes: ['hitpoints','defence','strength']; re-derive the other 10 by source-scanning their member strings.

Only GameManager + EntityManager are needed for our plugins (WorldMap, CollectionLog, Mining) — they reach everything else via those two + the inventory DOM. The rest is for core features (context menus, bank, chat, spells); prioritise by feature.

Recipe (so the remaining list is fill-in-the-table)

Read-only CDP, in-world. document.client = Map<minifiedName, class> (~519 live classes); window.__eqSourceModules = captured source.

// member union (catches getters + inherited)
const members=cls=>{const s=new Set();let p=cls&&cls.prototype;while(p&&p!==Object.prototype){Object.getOwnPropertyNames(p).forEach(n=>n!=='constructor'&&s.add(n));p=Object.getPrototypeOf(p);}return s;};
// verify a candidate signature is unique at a threshold
const uniq=(sig,thr)=>{let full=0,fuzzy=0;for(const[,cls]of document.client){const m=members(cls);if(!m.size)continue;const hit=sig.filter(s=>m.has(s)).length;if(hit===sig.length)full++;if(hit>=thr)fuzzy++;}return{full,fuzzy};}; // want {full:1,fuzzy:1} at thr=ceil(0.6*sig.length)

Identify a class via a live instance (obj.constructor.name) or a keyword scan; pick 4–6 distinctive members; confirm {full:1,fuzzy:1}; record it.


api + KKonaOG will iterate here. I'll drop follow-up comments (additional resolved signatures, enum strings, the re-run-on-bundle-change trigger notes) as we verify them against the live bundle.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions