366 lines
13 KiB
Markdown
366 lines
13 KiB
Markdown
# QuestieLearner — In-Game Verification Guide
|
||
|
||
This guide walks you through verifying every subsystem of QuestieLearner is working correctly after the v1.2.6 rewrite. Run each test in order and check expected outputs.
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
### Enable Develop Debug Output
|
||
|
||
In chat before starting any test:
|
||
```
|
||
/script Questie.db.profile.debugLevel = Questie.DEBUG_DEVELOP
|
||
```
|
||
|
||
Or enable **Develop** in Questie Options → Advanced → Debug Level.
|
||
|
||
> You do NOT need Critical enabled separately — Develop includes Critical-tier output.
|
||
|
||
### Verify Settings Are Enabled
|
||
|
||
If you upgraded from an older version, `learnedData` in your SavedVariables may be missing the newer settings keys (fixed in v1.2.7). Confirm all are `true` before testing:
|
||
```
|
||
/script local s=Questie.db.global.learnedData.settings; print("enabled:", tostring(s.enabled), "npcs:", tostring(s.learnNpcs), "quests:", tostring(s.learnQuests), "items:", tostring(s.learnItems), "objects:", tostring(s.learnObjects))
|
||
```
|
||
|
||
All five should print `true`. If any print `nil`, do a `/reload` — v1.2.7's `EnsureLearnedData` backfill will fix it on next load.
|
||
|
||
---
|
||
|
||
## Test 1 — Initialization
|
||
|
||
After `/reload`, look for this in chat:
|
||
```
|
||
[QuestieLearner] Events registered
|
||
[QuestieLearner] Initialized
|
||
```
|
||
|
||
If you also had previously learned data, you should see:
|
||
```
|
||
[QuestieLearner] Injected learned data: N NPCs, N quests, N items, N objects
|
||
```
|
||
|
||
**Failure:** If you see nothing, check that `QuestieLearner.lua` and `QuestieLearnerComms.lua` are in the TOC and loading without errors.
|
||
|
||
---
|
||
|
||
## Test 2 — Mouseover Filter (Quest Giver ONLY)
|
||
|
||
> **Note:** Since v1.2.6, debug output only prints when an NPC is seen for the **first time ever**. If the NPC already exists in `learnedData` from a previous session, mouseover is silent — this is correct behavior. Use the steps below to force a visible test.
|
||
|
||
### 2a — Find the NPC ID
|
||
|
||
**For normal NPCs you can target** (dash-format GUID, e.g. `Creature-0-...`):
|
||
```
|
||
/script local g=UnitGUID("target"); print(g)
|
||
```
|
||
Then parse the 6th dash-separated field — that is the NPC ID.
|
||
|
||
**For GameObjects / objects you interact with** (must have gossip window open):
|
||
```
|
||
/script print(UnitGUID("npc"))
|
||
```
|
||
|
||
> `UnitGUID("target")` returns `nil` for GameObjects — they cannot be traditionally targeted. Always use `UnitGUID("npc")` during an active gossip/click interaction. If you are not in a gossip window, `UnitGUID("npc")` also returns nil.
|
||
|
||
**Hex GUID decode** (if the GUID starts with `0x`, not dash-separated):
|
||
```
|
||
/script local g=UnitGUID("npc") or UnitGUID("target"); if g then local p=string.upper(string.sub(g,3,6)); local id=math.mod(tonumber(string.sub(g,11,18),16),8388608); print(p,id) end
|
||
```
|
||
|
||
### 2b — Clear it from learned data, then hover
|
||
|
||
```
|
||
/script local id = <npcId>; Questie.db.global.learnedData.npcs[id] = nil; print("Cleared NPC", id)
|
||
```
|
||
|
||
Now move your cursor off and back onto the quest giver.
|
||
|
||
**Expected output:**
|
||
```
|
||
[QuestieLearner] New NPC learned: <id> <NpcName>
|
||
```
|
||
|
||
### 2c — Verify the npcFlags filter directly
|
||
|
||
While mousing over the quest giver:
|
||
```
|
||
/script print("Flags:", UnitNPCFlags and UnitNPCFlags("mouseover") or "nil")
|
||
```
|
||
Expected: a number with bit 1 set (e.g. `2`, `3`, `35` — any value where `math.floor(value/2) % 2 == 1`).
|
||
|
||
While mousing over a random mob:
|
||
```
|
||
/script print("Flags:", UnitNPCFlags and UnitNPCFlags("mouseover") or "nil")
|
||
```
|
||
Expected: `0` or a value without the questgiver bit — and **no** `[QuestieLearner]` line should appear.
|
||
|
||
### 2d — Random Mob (Should Be Ignored)
|
||
|
||
Mouseover any wolf, enemy soldier, or non-quest creature.
|
||
|
||
**Expected:** Nothing printed. No `[QuestieLearner]` line.
|
||
|
||
---
|
||
|
||
## Test 3 — Quest Accept
|
||
|
||
Pick up any quest from an NPC. Look for:
|
||
```
|
||
[QuestieLearner] New quest learned: <id> <QuestName>
|
||
[QuestieLearner] New NPC learned: <id> <GiverName>
|
||
```
|
||
|
||
> Only prints on the **first time** each quest/NPC is learned. Subsequent accepts of the same quest are silent (data is updated internally).
|
||
|
||
Verify data was stored:
|
||
```
|
||
/script local q=Questie.db.global.learnedData.quests; local n=0; for _ in pairs(q) do n=n+1 end; print("Learned quests:", n)
|
||
```
|
||
|
||
Inspect a specific quest (replace `<questId>` with the ID you just accepted):
|
||
```
|
||
/script local q=Questie.db.global.learnedData.quests[<questId>]; if q then print("name:", q[1], "level:", q[5], "reqLvl:", q[4], "zone:", q[8]) else print("NOT captured") end
|
||
```
|
||
|
||
If it says `NOT captured`, check your settings (Prerequisites section) — `learnQuests` being `nil` silently blocks all quest recording.
|
||
|
||
Check if the quest is already in a DB plugin (if so, learner skips it):
|
||
```
|
||
/script local q = QuestieDB:GetQuest(<questId>); if q then print("In DB:", q[1]) else print("NOT in DB - learner should capture it") end
|
||
```
|
||
|
||
---
|
||
|
||
## Test 4 — Quest Turn-In
|
||
|
||
Complete and turn in a quest. Look for:
|
||
```
|
||
[QuestieLearner] New NPC learned: <id> <FinisherName>
|
||
```
|
||
|
||
Verify both starter and finisher are recorded:
|
||
```
|
||
/script local q=Questie.db.global.learnedData.quests[<questId>]; if q then print("starters:", tostring(q[2]), "finishers:", tostring(q[3])) end
|
||
```
|
||
|
||
Expected: Both `q[2]` and `q[3]` should be tables, not nil.
|
||
- `q[2][1]` = list of NPC IDs that start the quest
|
||
- `q[3][1]` = list of NPC IDs that finish the quest
|
||
|
||
---
|
||
|
||
## Test 5 — Kill Coordinate Recording
|
||
|
||
### 5a — Quest Objective Kill (Should Record)
|
||
|
||
Have an active kill quest in your quest log (e.g., "Kill 10 Wolves"). Kill one of the objective mobs.
|
||
|
||
**Expected output:**
|
||
```
|
||
[QuestieLearner] Kill recorded: NPC <id> <MobName>
|
||
[QuestieLearner] New NPC learned: <id> <MobName>
|
||
```
|
||
|
||
> "New NPC learned" only appears the first time. Subsequent kills of the same NPC type are silent but still update coordinates.
|
||
|
||
Verify coordinates were stored:
|
||
```
|
||
/script local n=Questie.db.global.learnedData.npcs[<npcId>]; if n and n[7] then for z,c in pairs(n[7]) do print("zone "..z.." has "..#c.." coord cluster(s)") end else print("no coords") end
|
||
```
|
||
|
||
Kill the same mob type multiple times in the same area. The cluster count should NOT increase for every kill — only when you move to a meaningfully different location (>2% of zone width apart).
|
||
|
||
### 5b — Non-Quest Mob (Should Be Ignored)
|
||
|
||
Kill a mob that is NOT a quest objective and is NOT in QuestieDB.
|
||
|
||
**Expected output:** Nothing. `[QuestieLearner] Kill recorded` should NOT appear.
|
||
|
||
---
|
||
|
||
## Test 6 — Item Loot Recording
|
||
|
||
Loot a mob or chest that drops a quest item.
|
||
|
||
**Expected output (immediate):**
|
||
```
|
||
[QuestieLearner] New item learned: <id> <ItemName>
|
||
```
|
||
|
||
**Expected output (delayed ~1 second if item never seen before):**
|
||
|
||
Same line, but fires when `GET_ITEM_INFO_RECEIVED` resolves the async `GetItemInfo` call.
|
||
|
||
Verify:
|
||
```
|
||
/script local i=Questie.db.global.learnedData.items; local n=0; for _ in pairs(i) do n=n+1 end; print("Learned items:", n)
|
||
```
|
||
|
||
---
|
||
|
||
## Test 7 — Object Recording
|
||
|
||
Interact with a clickable game object (chest, crate, brazier, scroll, etc.).
|
||
|
||
> **Important:** Some objects that look like interactable objects (e.g. Objectives Board) are actually implemented as **Creature NPCs** by the server. These will be recorded as NPCs, not objects — this is correct behavior.
|
||
|
||
### 7a — Identify what type an object/NPC is
|
||
|
||
With the gossip/interaction window open, run:
|
||
```
|
||
/script local g=UnitGUID("npc"); if g then local p=string.upper(string.sub(g,3,6)); print("GUID:", g, "Prefix:", p, "IsGameObject:", tostring(p=="F140")) end
|
||
```
|
||
|
||
| Prefix | Type | Recorded as |
|
||
|---|---|---|
|
||
| `F140` | GameObject | Object (`learnedData.objects`) |
|
||
| `F130` | Creature | NPC (`learnedData.npcs`) |
|
||
| `F110` | Creature/Pet | NPC (`learnedData.npcs`) |
|
||
| `F131` | Vehicle | NPC (`learnedData.npcs`) |
|
||
|
||
### 7b — Decode a hex GUID to get the ID
|
||
|
||
With the gossip window open (hex format `0xF110...`):
|
||
```
|
||
/script local g=UnitGUID("npc"); if g then local p=string.upper(string.sub(g,3,6)); local id=math.mod(tonumber(string.sub(g,11,18),16),8388608); print(p,id) end
|
||
```
|
||
|
||
> `strsplit("-", guid)` does **not** work on hex-format GUIDs. Always use substring extraction for `0x...` GUIDs.
|
||
|
||
Standalone decode for any known hex GUID string (replace the value):
|
||
```
|
||
/script local g="0xF110092A18000718"; local p=string.upper(string.sub(g,3,6)); print(p, math.mod(tonumber(string.sub(g,11,18),16),8388608))
|
||
```
|
||
|
||
### 7c — Print all learned objects
|
||
|
||
```
|
||
/script for id, data in pairs(Questie.db.global.learnedData.objects) do print("Object ID:", id, "Name:", tostring(data[1]), "Zone:", tostring(data[5])) end
|
||
```
|
||
|
||
With coordinates:
|
||
```
|
||
/script for id, data in pairs(Questie.db.global.learnedData.objects) do print("Object:", id, data[1]); if data[4] then for z, coords in pairs(data[4]) do print(" zone "..z.." - "..#coords.." coord(s)") end end end
|
||
```
|
||
|
||
---
|
||
|
||
## Test 8 — Overall Stats Check
|
||
|
||
Run this anytime to see a full summary:
|
||
```
|
||
/script local QL=QuestieLoader:ImportModule("QuestieLearner"); local n,q,i,o=QL:GetStats(); print("NPCs:"..n.." Quests:"..q.." Items:"..i.." Objects:"..o)
|
||
```
|
||
|
||
Expected counts increase as you play. `Quests:0` after accepting quests = settings backfill issue (see Prerequisites).
|
||
|
||
---
|
||
|
||
## Test 9 — Export
|
||
|
||
```
|
||
/script local E=QuestieLoader:ImportModule("QuestieLearnerExport"); local s,stats=E:Export(); if stats then print("Export OK | total:", stats.total, "| len:", string.len(s)) else print("Export FAILED:", tostring(stats)) end
|
||
```
|
||
|
||
Expected: `Export OK | total: N | len: XXXX`
|
||
|
||
The export string starts with `QxLD:1!`. Copy it from the Database tab → Export box in Questie Options.
|
||
|
||
---
|
||
|
||
## Test 10 — Import (Round-Trip)
|
||
|
||
1. Export your data (Test 9).
|
||
2. Wipe learned data: `/script QuestieLoader:ImportModule("QuestieLearner"):ClearAllData()`
|
||
3. Open Questie Options → Database → paste the export string → click Import.
|
||
|
||
Expected in chat:
|
||
```
|
||
Import complete: merged N entries, skipped 0 (already known).
|
||
[QuestieLearner] Injected learned data: N NPCs, N quests, N items, N objects
|
||
```
|
||
|
||
Verify stats match original count (Test 8). Data takes effect immediately — no `/reload` required for override-based map pins.
|
||
|
||
---
|
||
|
||
## Test 11 — Plugin Panel Stats (WotLKDB)
|
||
|
||
Open **Questie Options → Database** tab → scroll to **Loaded Questie-X Plugins**.
|
||
|
||
Expected for WotLKDB:
|
||
```
|
||
[Questie-WotLKDB] Quests: 9086 NPCs: XXXX Objects: XXXX Items: XXXX
|
||
```
|
||
|
||
If you see all zeros: `QuestieX_WotLKDB_Counts` is not set yet. This global is written by `LoadBaseDB()` inside an async coroutine — open the options panel a few seconds after login rather than immediately.
|
||
|
||
---
|
||
|
||
## Test 12 — Peer Sharing (Requires Two Clients)
|
||
|
||
On Client 1, learn a new NPC (walk up to a quest giver not previously recorded).
|
||
|
||
On Client 2, run:
|
||
```
|
||
/script print(type(Questie.db.global.learnedData.npcs[<npcId>]))
|
||
```
|
||
|
||
Expected: `table` (arrived via hidden `questiecomm` channel or AceComm guild broadcast).
|
||
|
||
> Both clients must be in the same guild OR on the same server. Allow up to 10 seconds for the token-bucket throttle.
|
||
|
||
---
|
||
|
||
## GUID Reference
|
||
|
||
WoW uses two GUID formats. The learner handles both automatically.
|
||
|
||
### Dash-format (modern)
|
||
```
|
||
Creature-0-3726-0-189-5638296-000001ABCD
|
||
```
|
||
Parse with: `strsplit("-", guid)` → 6th element = ID, 1st = unit type string
|
||
|
||
### Hex-format (legacy, common on private servers)
|
||
```
|
||
0xF110092A18000718
|
||
```
|
||
Parse with position extraction:
|
||
- Characters 3–6 = unit type prefix (`F110`, `F130`, `F140`, etc.)
|
||
- Characters 11–18 = low 32 bits → `math.mod(tonumber(low32hex, 16), 8388608)` = entity ID
|
||
|
||
**Do NOT use `strsplit("-", ...)` on hex GUIDs** — it will return nil for all fields after the first.
|
||
|
||
Quick decode command for any hex GUID:
|
||
```
|
||
/script local g="0xF110092A18000718"; local p=string.upper(string.sub(g,3,6)); print(p, math.mod(tonumber(string.sub(g,11,18),16),8388608))
|
||
```
|
||
|
||
---
|
||
|
||
## Quick Diagnostic — All At Once
|
||
|
||
```
|
||
/script local QL=QuestieLoader:ImportModule("QuestieLearner"); local n,q,i,o=QL:GetStats(); print("=== QuestieLearner Stats ==="); print("NPCs:"..n.." Quests:"..q.." Items:"..i.." Objects:"..o); local s=Questie.db.global.learnedData.settings; print("enabled:", tostring(s.enabled), "| npcs:", tostring(s.learnNpcs), "| quests:", tostring(s.learnQuests), "| items:", tostring(s.learnItems), "| objects:", tostring(s.learnObjects))
|
||
```
|
||
|
||
---
|
||
|
||
## Common Issues
|
||
|
||
| Symptom | Cause | Fix |
|
||
|---|---|---|
|
||
| Nothing prints at all | Debug level not set to DEVELOP | `/script Questie.db.profile.debugLevel = Questie.DEBUG_DEVELOP` |
|
||
| `Quests:0` despite accepting quests | `learnQuests = nil` in old SavedVariables | `/reload` — v1.2.7 backfill sets it to `true` |
|
||
| Random mobs being recorded | Old QuestieLearner.lua still loaded | Confirm v1.2.6+ deployed, `/reload` |
|
||
| Kill coords not recording | NPC not in QuestieDB and not targeted/hovered | Target the mob before killing it |
|
||
| Object recorded as NPC | Server implements it as a Creature (`F110`/`F130`) | Correct behavior — check prefix to confirm |
|
||
| `strsplit` returns nil | GUID is hex format, not dash-separated | Use hex decode method (see GUID Reference) |
|
||
| `UnitGUID("target")` returns nil | GameObjects can't be traditionally targeted | Use `UnitGUID("npc")` during gossip interaction instead |
|
||
| Export returns nil | `learnedData` is empty | Run Tests 2–7 first to accumulate data |
|
||
| WotLKDB shows 0s in plugin panel | Options opened before async init completed | Wait a few seconds after login then reopen options |
|
||
| Item not recorded after loot | `GetItemInfo` async on first encounter | Wait 1–2 seconds for `GET_ITEM_INFO_RECEIVED` |
|