promotional bannermobile promotional banner

CEPGP LootMaster - ANADOLU [Core]

CEPGP LootMaster - ANADOLU - Core version is made for our guild standarts to use our raider members.

File Details

cepgp_lootmaster-8.7.2.zip

  • R
  • Jun 1, 2026
  • 847.63 KB
  • 612
  • 2.5.5
  • Classic TBC

File Name

cepgp_lootmaster-8.7.2.zip

Supported Versions

  • 2.5.5

Changes

8.7.2 — 2026-05-22 → 2026-06-01

Features

Raid Groups — Duplicate Members Fixed + Visual Class-Coloured Grid

  • Reported bug: Adding a member like "akathriel" then later "Akathriel" (or "Otuken" vs "Ötüken") created TWO entries in the same group — table keys were case- and diacritic-sensitive and AddPlayerToRaidGroup only stripped the realm before storing. The same person could end up in the group N times.
  • Fix: new LootMaster:CanonicalizeMemberName(name) helper:
    1. Strips realm suffix and trims whitespace.
    2. Looks up the name in the guild cache case-insensitively and, if found, returns the exact form the WoW server stores (so "akathriel" → "Akathriel", "otuken" → "Ötüken" with the correct diacritics).
    3. Falls back to reusing the casing of an already-existing raid-group member with the same lower-cased form.
    4. Otherwise ASCII title-cases the first byte (leaves multi-byte diacritic-leading names untouched).
  • Both AddPlayerToRaidGroup and RemovePlayerFromRaidGroup now canonicalise first, plus a defensive case-insensitive dedup loop in AddPlayerToRaidGroup makes a duplicate insertion impossible even if canonicalisation misses a corner case. CreateRaidGroup also became case-insensitive for group names ("ASYA" and "asya" can no longer coexist).
  • Display upgrade — proper popup: AceConfig forces each inline description cell to ~20 % of the panel width, which truncates names to 4 letters (Asa…, Avi…, Co…). So the inline grid was scrapped in favour of a standalone WoW frame opened by a new "Show Members" button next to the group selector. The popup is a real raid-roster grid: 5 columns × however-many-rows it needs, each cell ~155 px wide, with the class icon (32×32 from Interface\WORLDSTATEFRAME\Icons-Classes) on the left and the full class-coloured name on the right. Hover any cell to see class / level / rank in a tooltip. The popup is draggable, ESC-closable, and auto-sizes to the member count. The inline header line now also shows a small class-count summary (e.g. 5 paladin · 4 priest · 3 mage…) coloured per class so you can scan composition without opening the popup.
  • Where: lootmaster_core.lua (CanonicalizeMemberName, updated CreateRaidGroup / AddPlayerToRaidGroup / RemovePlayerFromRaidGroup); lootmaster_player_ui.lua (new LootMaster:ShowRaidGroupMembersFrame(groupName) + helper locals _RG_GetOrCreateFrame / _RG_GetOrCreateCell); cepgp_lootmaster_ml/lootmaster_options.lua (rgMembersHeader now shows class-count summary; new rgMembersShow button replaces the broken inline grid).

Auto-EP — Visible Reasons When It Doesn't Fire + ML-Check Retry

  • Reported by another guild: "Randomly the addon is not giving the EP even though auto-give EP is selected." With no feedback in chat, the ML had no way to diagnose what was different about those kills.
  • Root cause: every "skip" path in LootMaster:AutoAwardBossEP used self:Debug(...) — debug-only output that doesn't show in chat unless debug mode is on. So a kill could silently abort for half a dozen reasons (not the ML, profile setting off, per-boss disabled, EP value 0, etc.) and the ML would just see nothing.
  • Additional cause we found while there: at the moment ENCOUNTER_END fires, the loot-method state can briefly be unset / transitional, causing IsPlayerMasterLooter() to return false even when the player IS the ML. The function then aborted with no retry — guaranteed to look "random".
  • Fix:
    1. Every skip path is now self:Print(...) with a clear coloured reason. The ML sees exactly why EP wasn't awarded for a given boss ("you are not the Master Looter", "the global Auto-Award EP toggle is OFF", "this specific boss is disabled", "configured EP is 0", "raidEPSettings missing from profile", etc.).
    2. The ML check now retries once after a 2-second delay if it initially returns false — covers the transient ENCOUNTER_END flicker.
    3. GuildRoster() is called once right before the bulk award, refreshing the cached guild roster so officer-note writes don't fail silently for stale/missing roster entries (a known cause of partial misses on raid-wide bulk EP).
  • Where: lootmaster_core.lua LootMaster:AutoAwardBossEP(bossName, _attempt).

Roll Tracker — Broadcasts Stop Once Item Is Awarded

  • Reported by another guild: After the ML awarded an item to someone, the roll tracker on other clients sometimes lingered or kept getting updated for the decided item — late-arriving responses from candidates (someone changing their mind, or a delayed click) still triggered ROLLUPDATE broadcasts even though the loot was already in someone's bag.
  • Two-part fix:
    1. GiveLootToCandidate (cepgp_lootmaster_ml/lootmaster_ml.lua) now sets loot.awarded = true after GiveMasterLoot runs, cancels any in-flight 1-second SendRollTrackerUpdate throttle for the same item, and broadcasts a new LOOTAWARDED command with the item link so every client knows the item is done.
    2. SendRollTrackerUpdate and _DoSendRollTrackerUpdate both bail early when loot.awarded is set, so subsequent candidate responses can never produce a ghost update for an awarded item.
    3. Player-side (cepgp_lootmaster/lootmaster_player.lua) handles LOOTAWARDED: removes the item from the roll display immediately (not after the existing 5-second LOOTED-cleanup delay) and drops any queued late-arriving ROLLUPDATEs.

Roll Tracker — Close Button Actually Closes Now

  • Reported by another guild: The roll tracker (the "Roll Sonuclari" frame that pops up on the right showing who needed/passed/rolled what) kept reopening every time a new response arrived, even after the player had explicitly clicked its X button. Made the close button useless during loot announcements.
  • Root cause in lootmaster_player_ui.lua UpdateRollDisplay(): frame:Show() was called unconditionally on EVERY incoming ROLLUPDATE broadcast from the ML. So a closed frame got re-shown as soon as any candidate changed their response.
  • Fix: The close button now sets frame._userClosed = true. UpdateRollDisplay() respects that flag on subsequent updates and skips frame:Show() — UNLESS a genuinely new item arrives (isNewItem == true), in which case the flag is cleared and the frame reopens (new content is worth surfacing). The flag is also cleared on ClearRollDisplay() and on the safety auto-hide timer, so a fresh announcement cycle always starts clean.
  • Effect: Close → stays closed for the current item. Next item arriving → pops back up. Same-item updates → silently update in the background.

AceDB Profile Is Now Guild-Scoped (not character-scoped)

  • Before: The shared AceDB profile that every character was forced onto was named "Fhe - Thunderstrike" — a character/realm name. Conceptually weird (the data belonged to a guild, not a character) and it broke if Fhe ever transferred or got renamed.
  • Now: The profile is named after the GUILD ("A N A D O L U" for the ANADOLU guild). Every character in the same guild automatically shares one profile — sessions, audit log, raid groups, EP settings, button presets, root admins, the lot. Characters apply to the guild, not the other way around.
  • One-time migration (MigrateProfileToGuildScope): on next login, the addon detects the guild name (from live GetGuildInfo or the persisted db.global.guildCache.guildName), copies the legacy "Fhe - Thunderstrike" profile into <GuildName>, deletes the old profile, re-points profileKeys for every character to the new name, and sets db.global.profileV3GuildScoped = true. Prints a green "Profile renamed to guild scope" chat line. Idempotent — only runs once.
  • Chicken-and-egg handling: At addon OnEnable time GetGuildInfo can be nil for 2-3 seconds. We try the guild-scoped name first, then fall back to the legacy name so existing data still loads. As soon as RefreshGuildCache succeeds and reveals the guild name, the migration kicks in and switches the profile.
  • Where: lootmaster_core.lua — new GetGuildScopedProfileName() helper, new MigrateProfileToGuildScope(targetName), updated V2 migration block and RefreshGuildCache to call them.
  • Effect on data: Nothing is lost. Fhe becomes just "a character in ANADOLU with admin rights" instead of "the magic profile name everyone shares". Hardcoded root admin status for Fhe (in ROOT_ADMIN_CHARACTERS) is unaffected; the auto-recorded GM is unaffected.

Zombie Guild-Store Keys — Source Fixed + Existing Cleaned

  • Found: CEPGPLootMasterGuild (the per-guild data store for raid groups, alts, EP settings, etc.) had 5 zombie top-level keys alongside the legitimate "A N A D O L U" entry — "Fhe - Thunderstrike", "Kurtadam", "Fhe", "Akathriel", "Akathriel - Thunderstrike". Total: 518 lines (~16 KB) of duplicate guild data that never should have existed.
  • Root cause: LootMaster:GetSessionStoreKey() had a player-name fallback for the case where GetGuildInfo("player") returned nil. On login that API takes 2-3 seconds to populate after the addon's OnEnable fires — so the addon would write under the player name (a zombie key) during that early window, then write under the real guild name once guild info loaded. The zombie keys piled up across sessions because nothing ever cleaned them up.
  • Fix in code: GetSessionStoreKey() now returns nil (not the player name) when the guild isn't available. Every Load/Save store function (LoadSessionsFromStore, SaveSessionsToStore, LoadAuditLogFromStore, SaveAuditLogToStore, LoadGuildDataFromStore, SaveGuildDataToStore) returns early on nil. RefreshGuildCache (which fires on GUILD_ROSTER_UPDATE) now re-runs all three loads once the guild name resolves, so the early bail is invisible to users — sessions / audit / guild-data all load slightly later instead of being missed.
  • Fix on disk: The 5 zombie blocks were surgically deleted from WTF/Account/<acct>/SavedVariables/cepgp_lootmaster.lua (with a timestamped .bak backup). File shrank from 27,053 lines → 26,535 lines.

Profile Storage Consolidation — One Area, Account-Wide Sharing Preserved

  • Changed: The addon used to keep "saved profile snapshots" (the data behind /lm cfg save / /lm cfg load and the Save Profile / Load Profile From UI controls) in a separate top-level table at CEPGPLootMaster.global.characterProfiles. The live data lived in CEPGPLootMaster.profiles[<profileName>] (the AceDB profile). The two could drift — most painfully when raidGroups ended up only in a snapshot and the active profile lost it.
  • Now: Snapshots live INSIDE the active profile at db.profile.savedSnapshots[<charName>]. Since every character is forced onto the shared Fhe - Thunderstrike AceDB profile, the snapshots are still account-wide (every alt sees the same snapshot list). There is no second top-level area anymore.
  • Save/Load feature unchanged in behaviour: /lm cfg save | load | list | delete | info, the Save/Load Profile UI controls, and the snapshot list all keep working — they just point at the new location.
  • Direct file cleanup (because the game was closed at the time): the legacy ["characterProfiles"] block was surgically removed from WTF/Account/<acct>/SavedVariables/cepgp_lootmaster.lua directly (947 lines, with a timestamped .bak written alongside). No runtime migration was needed.
  • Removed: MigrateCharacterProfilesToSnapshots(), the OnEnable call that invoked it, the older RecoverFromCharacterProfileSnapshots(verbose) function, and the /lm migrate slash command — all were diagnostic scaffolding while the bug was being chased. The codebase no longer references db.global.characterProfiles anywhere (the lingering nil-assignment inside the V2 migration block is left in place as harmless legacy cleanup for users who haven't run V2 yet).
  • Fixed: /lm cfg list was still reading from db.global.characterProfiles[charName] to fetch each snapshot's timestamp — now reads from EnsureSavedSnapshots() directly.
  • API surface for ML options panel: New LootMaster:EnsureSavedSnapshots() helper returns the db.profile.savedSnapshots table (lazy-init). The ML AceConfig UI uses this instead of touching db.global.characterProfiles directly.
  • Where: lootmaster_core.lua (Save/Load/Get/Delete/Info functions, migration, OnEnable hook); cepgp_lootmaster_ml/lootmaster_options.lua (Account Profile UI section).

lm-sync Web Admin — One Canonical Settings View (no more per-character clones)

  • Changed: The admin "Synced Settings" panel used to render one profile_<character> card per character that had ever synced (profile_Fhe, profile_Akathriel, etc.) — all displaying the same data in raw JSON. Now there is exactly one canonical settings row per guild (primary_profile), derived from the most authoritative character available: Fhe → first recorded root admin → first non-empty profile. The character it came from is shown as a small source: <Name> tag on the card.
  • New structured panel: primary_profile renders as cards grouped by area (Loot Buttons, EPGP, Auto Looter) instead of a JSON blob. Each loot button shows its name, short label, and GP value; EPGP shows decay percent, auto-decay enabled, last-decay date, always-on flag, and attendance start week; Auto Looter shows the BoE/BoU threshold, enabled flag, and default candidate.
  • EPGP Config card renders the epgp_config row as a two-column key/value grid.
  • JSON is opt-in: A "Show JSON" toggle in the section header pops a raw-JSON view of every setting row; "Export JSON" downloads the full settings blob as lm-sync-settings-<guild>.json.
  • Backend: extract_settings() in server/luaparser.py now picks ONE canonical profile (ranking: short name Fhe → recorded root admin → SHARED_PROFILE name → first profile) and emits exactly one primary_profile setting row containing _source_character plus the synced keys. /sync/upload-raw now purges legacy profile_* rows for the guild on every upload — so existing per-character clones in the DB go away on the next sync.
  • One-off cleanup for installations with already-stale profile_* rows that never get re-synced:
    docker exec lootmaster_sync_db psql -U lmsync -d lmsync -c "DELETE FROM settings WHERE setting_key LIKE 'profile_%';"
    

Raid Groups — Config Panel UI + Bulk Add by Guild Rank

  • New panel: ML Settings → LootMaster Admin → Raid Groups. Root admins can create groups, select an existing group, see its current members (class-coloured), add/remove individual members, and delete the group. The previously orphaned core API (CreateRaidGroup, AddPlayerToRaidGroup, DeleteRaidGroup, etc.) finally has a front-end.
  • Bulk add by guild rank: Within the selected group, root admins now get a rank dropdown + checkbox list of every guild member at that rank. Currently-in-group members are pre-checked and decorated with a green ✓; unchecking + Apply removes them; checking + Apply adds them. Mirrors the rank-based bulk-select pattern from the legacy session admin panel.
  • Bulk add from current raid: A one-click "Add All Current Raid Members" button adds every player in your current WoW raid group to the selected raid group. Useful for snapshotting a fresh team.
  • Filter pill in attendance view: The "All Raid Groups" pill on the Loot History → Attendance dashboard filters sessions to those a group "belongs to" (≥ half the group's members attended). The pill no longer overlaps the search bar — search is now anchored to the rightmost pill (pillGroup:RIGHT) instead of pillFrom:RIGHT.

lm-sync Web Admin — Raid Groups Section

  • New section on the guild detail page (c:/Projects/lm-sync/admin/src/App.jsx): a card grid showing every raid group and its members, rendered above the Synced Settings block. Members display as pill chips with realm stripped.
  • Backend: luaparser.py gains extract_raid_groups() (de-duplicates across profiles) and extract_root_admins() (reads db.global.rootAdminNames). The /sync/upload-raw endpoint now persists both as singleton setting rows (setting_key="raid_groups" and setting_key="root_admins"), so the existing /admin/guilds/{id} payload carries them with no schema change.
  • i18n: New keys raidGroups, members added to both English and Turkish dictionaries.

Localisation Sweep — Loot Add/Edit Dialog

  • All previously hardcoded English strings in the Add/Edit Loot dialog ("Charged %d GP to %s for %s", "Failed to charge GP...", "Reversed %d GP...", "Edit reversal...", "Manual add: %s", "Manual", "Free", the announce-to-raid templates, and the "Search players..." placeholder) are now wrapped in L[] with Turkish translations. The audit-log strings ("Added: %s ...", "Edited: %s ...") are deliberately kept in English so they remain greppable for ops.

Hardcoded Root Admin List Reduced to "Fhe" Only

  • Changed: ROOT_ADMIN_CHARACTERS in lootmaster_core.lua is now a single-entry table containing only Fhe. The previous hardcoded list (Akathriel, Fhe, Majihu, Avianthus) has been removed.
  • Why: Hardcoded admin lists are unfair to other guilds using this addon. The only character we need pinned for guarantee is Fhe (the primary developer / config source). The current Guild Master is auto-recorded as a root admin at runtime by RecordGuildMasterAsRootAdmin() (introduced in 8.7.2) and persisted into db.global.rootAdminNames — so the GM always has root access for free, on any guild, without needing a hardcoded entry. Any additional root admins are added by the GM/Fhe via /lm rootadmin add <player> or the Root Admins config panel.
  • Cleanup: Removed the unused IsPlayerAkathriel() helper from cepgp_lootmaster_ml/lootmaster_options.lua (it was defined but never referenced). Removed a stale "Start from existing Akathriel profile" comment in the V2 migration block. Test/dryrun mock-candidate fake names (Avianthus, Akathriel) are kept as harmless test fixtures — they do not grant permissions.

Permission System Overhaul — Tier-Based Matrix + Root Admin Management UI

  • New centralised gate: Every UI button and comm handler now resolves authorisation through LootMaster:Can(action, ctx) instead of mixing inline IsRootAdmin / IsPlayerMasterLooter / IsSessionAdmin checks. The default policy is stored as tier strings in PERMISSION_DEFAULTS (lootmaster_core.lua), and root admins can override any action's tier via the in-game Permission Matrix.
  • Tiers: root (root admins only), ml_or_root (master looters and root admins), all (everyone), off (disabled).
  • Split edit/delete gates in loot history: The [E] (edit) action button now appears for MLs and root admins (loot.edit_entry = ml_or_root); the [X] (delete) action button is now root-admin only (loot.delete_entry = root). Previously both flowed through a single "session admin" gate.
  • Session admins removed: The legacy session-admin tier (gold "permanent" admins, green temporary admins, /lm xJ7q secret command) is gone. Only root admins can delete sessions or loot entries. Master Looters still start, continue, and end sessions and award loot exactly as before — only destructive operations now require root.
  • Multi-root-admin support: The Guild Master is auto-recorded as a root admin (introduced in 8.7.2), but additional root admins can be added via /lm rootadmin add <player> or the new Root Admins config panel (see below). No secret command needed.

Root Admins + Permission Matrix Config Panel

  • Location: ML Settings → LootMaster Admin tab → Root Admins subgroup and Permission Matrix subgroup.
  • Root Admins panel: Shows the current list of root admins (gold). For root admins, adds an "Add Root Admin" input and a "Remove Root Admin" dropdown. Hardcoded ROOT_ADMIN_CHARACTERS are immutable; recorded admins (db.global.rootAdminNames) can be added/removed.
  • Permission Matrix panel (root-admin only): For every action in PERMISSION_DEFAULTS, root admins can pick a tier from a dropdown (Root / ML+Root / All / Off). Overrides are stored in db.global.permissionOverrides[action]. A "Reset to Defaults" button clears all overrides.
  • Why: The old "Session Administration" panel (with permanent-vs-temporary admin terminology and rank-based bulk-add) was confusing and exposed too many ways to hand out elevated access. The new panel makes the policy explicit and lets each root admin tune the local gates on their own machine.

Slash command

  • /lm rootadmin (alias /lm ra) — [list | add <player> | remove <player>]. The old hidden /lm xJ7q command is removed.

Raid Composition Builder — Player Picker Hides Sub-68 Characters

  • When assigning a player to a spec slot, the picker listed every guild member of the matching class regardless of level, so low-level alts and bank characters cluttered the list. Members below level 68 are now filtered out. Members with an unknown level (a brief mid-roster-load state) are still shown so nobody is lost to a stale cache.
  • Applied to BOTH composition builders: the embedded module (cepgp_lootmaster/Libs/RaidGroups/lrg_ui.lua) and the standalone Raid Tools addon (cepgp_lootmaster_rt/rt_ui.lua).

Raid Groups Module — Trimmed to the Essentials

  • The embedded raid-composition planner under Libs/RaidGroups/ (ported from the RT addon) carried a lot of machinery the core addon doesn't need. Removed: composition cloning (the copy button + Comp:Clone); the multi-active-composition view (_activeComps, AddActiveComp/RemoveActiveComp, the "+ Comp" picker, the isMulti render branch); all sharing/sync UI (Sync with All, Sync with…, the owner padlock/lock, the RT_SYNC_PLAYER popup — sharing was already a no-op stub); every audit hook (the RT.audit:Record call sites, the HideAuditPanel / UpdateFooterStatus no-op stubs, and the audit/controller/inspect/options/minimap/sharing stub block in lrg_init.lua); and a 305-line dead Comp:GetCompositionOptionsTable AceConfig table that nothing referenced.
  • Kept (by design): single-composition editing, rename, bench rows, Invite All, the rank filter, and the buff/debuff coverage panel.
  • Net size: lrg_ui.lua 77 KB → 60 KB, lrg_compositions.lua 22 KB → 9 KB.

Attunement Check — Account-Wide Aware + Persistent Cache + Selective Guild Polling

  • Reported: Players who completed an attunement on their MAIN are auto-attuned on alts (account-wide), but /lm att showed those alts as NOT attuned — the check only read the alt character's own quest flags, which aren't set for an account-wide auto-attunement.
  • Key constraint: there is no API to query another player's quest/item state remotely — each client can only self-report (which is why the check whispers everyone). The account-wide quest API C_QuestLog.IsQuestFlaggedCompletedOnAccount does not exist on this Anniversary client (confirmed nil), so a purely client-side fix isn't possible. GetLocalAttuneStatus still probes for it at runtime and uses it when present, for forward-compatibility.
  • Fix — alt→main aggregation via the existing officer-note alt links: attune status is cached per MAIN character (db.global.attuneCache, upgrade-only since TBC attunement is permanent). A character counts as attuned for a raid when its main is. New helpers GetEffectiveAttune, CacheAttuneStatus, _AttuneMainKey, IsAttuneFullyCached. The response handler and the initiator's own row both cache and display the effective (own OR main-cached) status; AddAttuneCheckMember backfills from cache so auto-attuned alts show green immediately.
  • Selective Guild polling: the Guild button no longer broadcasts to the whole guild every run. Every online member is still listed (backfilled from cache), but a live request goes only to members whose main is NOT already cached as fully attuned. Confirmed-attuned members are shown from cache and never re-polled (permanent); the not-yet-attuned are re-polled each run so progress is caught. The first check populates the cache; later checks shrink to just the people still missing attunements.
  • Where: Libs/RosterChecks/rosterchecks.lua; lootmaster_player_ui.lua (AddAttuneCheckMember, the Guild/Raid button handlers).

Guild Master Auto-Recorded as Root Admin (Multi-Guild Support)

  • New: On every guild roster refresh, the current Guild Master's character name is recorded into a persisted list and granted root-admin access — name-based, not via a live "is GM" check at every call site. This lets the addon work cleanly for guilds outside A N A D O L U: any guild's GM automatically becomes a root admin without needing the hardcoded ROOT_ADMIN_CHARACTERS table to be edited.
  • How: New LootMaster:RecordGuildMasterAsRootAdmin() in lootmaster_core.lua iterates self.GuildCache.members looking for rankIndex == 0 and writes the GM's short name into db.global.rootAdminNames. Called at the tail end of RefreshGuildCache (after the cache becomes valid + after PersistGuildCacheToDisk). Additive and idempotent — safe to call on every roster update.
  • Where the name is consulted: IsRootAdmin now also matches against db.global.rootAdminNames (plain case-insensitive name match) in addition to the existing hardcoded list and the live rankIndex == 0 cache check. Because the list is persisted, root-admin status survives /reload and is in effect on subsequent logins even before the live roster has loaded for the session.
  • Also: GetSessionAdminsFromConfig now includes the recorded GM names, so they appear in admin enumerations alongside ROOT_ADMIN_CHARACTERS and TEMP_SESSION_ADMINS.
  • Storage: db.global.rootAdminNames is a {shortName = true} table; lazy-initialised on first record. The existing live rankIndex == 0 fallback in IsRootAdmin is intentionally retained as a safety net.

UI Improvements

Raid Session History — Wider Frame for Long Instance Names

  • Changed: The Loot History dashboard (LootMasterHistoryFrame) is now 1180px wide (was 1000), and each of the four dashboard cards (Instance / Status / Master Looter / Items) is 230px wide (was 190). Long instance names such as "Coilfang: Serpentshrine Cavern" now fit comfortably inside the Instance card.
  • Why the in-between width: An initial bump to 255px caused the cards to overlap the Attendance tab button (anchored top-right of the frame). The 230px / 1180px combination keeps the cards, search bar and Attendance button visually clear of each other.
  • Where: lootmaster_player_ui.luaframe:SetWidth(1180) in the history frame creator; card:SetSize(230, HIST_CARD_HEIGHT) in the local CreateHistCard helper. Layout is otherwise unchanged.

Bug Fixes

Add / Edit Loot Entry dialog now accepts shift-click from anywhere

  • Fixed: Shift-clicking an item link in chat, bags, the character pane, inspect, AH, mail, or any other Blizzard UI surface now populates the item slot in the Add Loot Entry and Edit Loot Entry dialogs. Previously the dialog only listened to ChatEdit_InsertLink, which fires only when a chat edit box happens to be open — so shift-clicking a bag item with no chat box up did nothing. We now also hook HandleModifiedItemClick, Blizzard's universal modified-click chokepoint, so the dialog captures the link regardless of where the click originates.
  • Also: The item slot now supports right-click to clear the selected item (previously you had to close the dialog to change your mind). Left-click still picks up a cursor-held item; drag-and-drop still works.
  • Where: lootmaster_player_ui.lua — new _AdminLootDialog_AcceptLink helper, hooksecurefunc("HandleModifiedItemClick", ...), itemBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp") and the right-button branch in the OnClick handler.

Missing locale entry crash: "All Raid Groups"

  • Fixed: AceLocale strict mode threw Missing entry for 'All Raid Groups' when opening the Loot History → Attendance view because the new raid-group filter pill (added in the 8.7.2 raid-groups feature) was looking up a locale key that had not been added to locale\enUS.lua / locale\trTR.lua.
  • Where: Added L["All Raid Groups"], L["Raid Groups"], L["Root Admins"], L["Permission Matrix"], L["Add Root Admin"], L["Remove Root Admin"] (plus several long description strings used by the new Root Admins / Permission Matrix panels) to both locale files.

Loot History — 2× of the Same Item to the Same Player Now Records Both

  • Reported: When a boss dropped two of the same item and both went to one player, only one entry appeared in the loot history.
  • Two causes: (1) a proximity de-dup in RecordLootHistoryEntry dropped any matching item/player/GP/distributor seen within 2 seconds — regardless of award id — collapsing two genuine awards into one; (2) award ids were built from time() (1-second resolution), so two awards in the same second that fell back to an itemLink-based id collided and the second was dropped by the seenAwardIds gate.
  • Fix: the proximity de-dup now runs only when there is no award id (its intended fallback role for chat-scraped loot); when an award id is present the seenAwardIds gate is authoritative, so two distinct ids both record. Award ids are built by a new LootMaster:MakeAwardId(base) helper that appends a per-login monotonic counter, guaranteeing uniqueness even within the same second. All seven generation sites switched to it (cepgp_lootmaster_ml/lootmaster_ml.lua ×4, lootmaster_lootframe.lua, the manual-entry path in lootmaster_player_ui.lua, and the auto: placeholder in Libs/TradeWatch/tradewatch.lua — the auto: / manual: prefixes are preserved so the placeholder-removal logic still matches ^auto:).
  • Where: Libs/SessionSync/sessionsync.lua (RecordLootHistoryEntry), lootmaster_core.lua (MakeAwardId), plus the seven call sites.

Loot History — Sort Crash on Numeric Columns (base out of range)

  • Fixed: Sorting the loot-history table by a numstr column (e.g. GP) could throw bad argument #2 to 'tonumber' (base out of range). string.gsub returns two values (the cleaned string AND a replacement count); as the last argument to tonumber(...) that count was passed as the numeric base, which errors whenever it lands outside 2–36. Parenthesising the gsub truncates it to one value. Latent bug — it surfaced now because the loot-history de-dup fix above lets a genuine second row exist, which the comparator then evaluated.
  • Where: lootmaster_player_ui.lua SortHistoryData.

Internal

  • New core API surface: LootMaster.PERMISSION_DEFAULTS, LootMaster.PERMISSION_TIERS, LootMaster:GetPermissionTier(action), LootMaster:SetPermissionTier(action, tier), LootMaster:ResetPermissionOverrides(), LootMaster:GetPermissionActions(), LootMaster:ListRootAdmins() (now returns a sorted list), LootMaster:PrintRootAdmins() (chat output, slash-command use).
  • Can() evaluates the tier resolved from overrides → defaults. Unknown actions and unknown tiers both return false.
  • The old admin_members AceConfig group in cepgp_lootmaster_ml/lootmaster_options.lua is renamed to legacy_admin_members and hidden — the args (rank-based bulk add, manual add/remove, permanent-vs-temporary distinction) remain in-tree for one release as a safety net before removal.

Core File Split Into 10 Libs/ Subsystem Modules

  • lootmaster_core.lua went from 11,405 → ~5,800 lines. Ten self-contained subsystems were extracted into their own folders, loaded from the TOC immediately after the core file (they only attach methods to the LootMaster table, so the only ordering requirement is "after core"):
    • Libs/Permissions/permissions.lua — root admins, Can(), permission tiers, session admins.
    • Libs/RaidTeams/raidteams.lua — guild-team grouping + NormalizeRaidName.
    • Libs/AdminAudit/adminaudit.lua — admin-action log + comm audit request/response + the raw-comm / chat-audit listeners.
    • Libs/Guild/guild.lua — guild cache, alt linking, EP/GP-by-rank.
    • Libs/InstanceTracking/instancetracking.lua — zone/instance segments, combat-log boss detection, auto-EP.
    • Libs/TradeWatch/tradewatch.lua — trade-window loot watch + chat-loot scrape + GetRaidWeekStart.
    • Libs/ImportExport/importexport.lua — JSON/CSV/encoded session I/O + dialogs + session summary.
    • Libs/SessionSync/sessionsync.lua — comm broadcasts, chunked transfer, the central CommandReceived dispatcher, root-admin session browser.
    • Libs/RosterChecks/rosterchecks.lua — version + attunement checks.
    • Libs/Minimap/minimap.lua — LDB icon + right-click menu.
  • File-local upvalues that crossed the new module boundaries were lifted onto the LootMaster table: TEMP_SESSION_ADMINSLootMaster._sessionAdminsRuntime; GetShortNameLootMaster._GetShortName; and COMM_PREFIX_MAP / SEND_PREFIX / iVersion / version / debug are now exposed as LootMaster.* fields (snapshotted at module load in rosterchecks.lua).
  • Load-order fixes (the subtle part of the split): the broadcast-log hooks that wrap SendSyncCommand / CommandReceived — plus the LootMaster._CoreCommandReceived snapshot that lootmaster_player.lua falls through to — were moved from the end of lootmaster_core.lua to the end of Libs/SessionSync/sessionsync.lua, because those two methods are now defined there and load after core (the original hooks captured nil, throwing attempt to call upvalue 'origCommandReceived'). The InitRawCommListener / InitChatAuditListener startup calls likewise moved to the end of Libs/AdminAudit/adminaudit.lua.

Configuration Changes

Minimap Menu Permission Panel Removed; Visibility Now Fixed in Code

  • Removed from UI: The configurable "Minimap Menu" group under ML Settings → Tools → Minimap Permissions — the per-item Guild Members / Admins / Specific Rank toggles and rank dropdowns — has been hidden from the AceConfig panel. The dead config-table args remain in cepgp_lootmaster_ml/lootmaster_options.lua (the group has hidden = true) rather than being excised, to avoid risking a brace-mismatch that would break the entire config window.
  • Why: The previous configurable system added complexity for little gain and was reported as not behaving consistently. A small, predictable set of fixed rules is simpler and easier to reason about.
  • New fixed behaviour (hardcoded in LootMaster:HasMinimapMenuPermission, lootmaster_core.lua):
    • Show EPGP Ranks — visible to everyone.
    • Show EPGP History — visible to everyone.
    • Mini Session Bar — visible only to the Master Looter and Root Admins.
  • One chokepoint, both menus: HasMinimapMenuPermission is called from both the core's ShowMinimapMenu and the Raid Tools module's RT:ShowExtendedMinimapMenu (cepgp_lootmaster_rt/rt_core.lua). Changing the function once propagates to both — no per-call-site edits.
  • Implementation note: HasMinimapMenuPermission short-circuits at the top with a do ... end block returning the new fixed visibility; the legacy permission-table evaluation code below remains intentionally unreachable (kept in place for safety / reference).
  • Migration: No SavedVariables migration required. db.profile.minimapMenuPermissions and db.profile.minimapMenuRanks continue to exist in the DB but are no longer consulted at runtime.