Files
WeirdUtils/src/dpslog/WSBT/WSBT_CLEUAdapter.lua
T
MarcelineVQ d84f723539 dpslog: handler table swaps, CombatLogGetCurrentEventInfo, unit flags, boolean fix
Major architectural changes:

- Migrate 12 packet handlers from JMP-patching Detours to NetClient opcode
  handler table pointer swaps. No code bytes modified — only heap pointers.
  Invisible to Warden memory scans. Hook InitializeGameEngine (0x401570,
  __thiscall) to install swaps after all handlers are registered.

- Implement CombatLogGetCurrentEventInfo() — WotLK-style lazy arg retrieval.
  Event fires with no args; addons call the function to get all fields from
  a C-side buffer. No arg count limit (bypasses ExecuteLuaCallback 19-arg cap).
  Includes unit flags, raid flags, and all suffix fields.

- Compute COMBATLOG_OBJECT_* unit flags: affiliation (MINE/PARTY/RAID/OUTSIDER),
  reaction (FRIENDLY/NEUTRAL/HOSTILE via UnitReaction), control (PLAYER/NPC),
  type (PLAYER/NPC/PET/GUARDIAN/OBJECT via SUMMONEDBY check), special (TARGET),
  and raid target markers.

- Fix boolean fields (critical/glancing/crushing): changed from %d (Lua number 0,
  which is TRUTHY) to nil/"1" via boolToLua(). Details addon was counting every
  hit as critical because it uses truthiness checks.

- SPELL_INSTAKILL now fires from SPELLLOGEXECUTE (has caster GUID).
  Standalone SMSG_SPELLINSTAKILLLOG handler is pass-through only.

- SPELL_EXTRA_ATTACKS now uses target GUID from packet (was discarded).

- SPELL_CAST_START/SUCCESS now parse spell target from packet.

- SPELL_DRAIN subevent added for instant power drain effects.

- Rename event from COMBAT_LOG_EVENT to COMBAT_LOG_EVENT_UNFILTERED.

- Update DPSMate and WSBT CLEU adapters to use CombatLogGetCurrentEventInfo().

- Remove unnecessary `or 0` guards on numeric fields (always non-nil from C).
2026-03-18 16:55:27 -07:00

570 lines
22 KiB
Lua

-- WSBT CLEU Adapter
-- Replaces MikCEH's CHAT_MSG_* string parsing with structured COMBAT_LOG_EVENT_UNFILTERED.
-- Toggle: /wsbtcleu (switches between CLEU adapter and original string parser)
-- Requires: DPSLog module (provides COMBAT_LOG_EVENT_UNFILTERED)
-- Require DPSLog module and MikCEH
if not GetWeirdUtilsVersion or not GetWeirdUtilsVersion("dpslog") then return end
if not MikCEH or not MikCEH.SendEvent then return end
local CEH = MikCEH
local SendEvent = MikCEH.SendEvent
local GetDamageData = MikCEH.GetDamageEventData
local GetHealData = MikCEH.GetHealEventData
local GetNotifData = MikCEH.GetNotificationEventData
-- ============================================================================
-- Constants (local refs for speed)
-- ============================================================================
local INCOMING = CEH.DIRECTIONTYPE_PLAYER_INCOMING
local OUTGOING = CEH.DIRECTIONTYPE_PLAYER_OUTGOING
local PET_OUT = CEH.DIRECTIONTYPE_PET_OUTGOING
local PET_IN = CEH.DIRECTIONTYPE_PET_INCOMING
local HIT = CEH.ACTIONTYPE_HIT
local MISS = CEH.ACTIONTYPE_MISS
local DODGE = CEH.ACTIONTYPE_DODGE
local PARRY = CEH.ACTIONTYPE_PARRY
local BLOCK = CEH.ACTIONTYPE_BLOCK
local RESIST = CEH.ACTIONTYPE_RESIST
local ABSORB = CEH.ACTIONTYPE_ABSORB
local IMMUNE = CEH.ACTIONTYPE_IMMUNE
local EVADE = CEH.ACTIONTYPE_EVADE
local REFLECT = CEH.ACTIONTYPE_REFLECT
local HIT_NORMAL = CEH.HITTYPE_NORMAL
local HIT_CRIT = CEH.HITTYPE_CRIT
local HIT_DOT = CEH.HITTYPE_OVER_TIME
local HEAL_NORMAL = CEH.HEALTYPE_NORMAL
local HEAL_CRIT = CEH.HEALTYPE_CRIT
local HEAL_HOT = CEH.HEALTYPE_OVER_TIME
local DMG_PHYSICAL = CEH.DAMAGETYPE_PHYSICAL
local DMG_UNKNOWN = CEH.DAMAGETYPE_UNKNOWN
local PARTIAL_ABSORB = CEH.PARTIALACTIONTYPE_ABSORB
local PARTIAL_BLOCK = CEH.PARTIALACTIONTYPE_BLOCK
local PARTIAL_RESIST = CEH.PARTIALACTIONTYPE_RESIST
local PARTIAL_CRUSHING = CEH.PARTIALACTIONTYPE_CRUSHING
local PARTIAL_GLANCING = CEH.PARTIALACTIONTYPE_GLANCING
local PARTIAL_OVERHEAL = CEH.PARTIALACTIONTYPE_OVERHEAL
local NOTIF_DEBUFF = CEH.NOTIFICATIONTYPE_DEBUFF
local NOTIF_BUFF = CEH.NOTIFICATIONTYPE_BUFF
local NOTIF_BUFF_FADE = CEH.NOTIFICATIONTYPE_BUFF_FADE
local NOTIF_POWER_GAIN = CEH.NOTIFICATIONTYPE_POWER_GAIN
local NOTIF_POWER_LOSS = CEH.NOTIFICATIONTYPE_POWER_LOSS
-- Miss type string → MikCEH action type
local missActionMap = {
MISS = MISS,
DODGE = DODGE,
PARRY = PARRY,
BLOCK = BLOCK,
RESIST = RESIST,
ABSORB = ABSORB,
IMMUNE = IMMUNE,
EVADE = EVADE,
REFLECT = REFLECT,
DEFLECT = MISS, -- no DEFLECT in MSBT, treat as miss
}
-- WoW spell school bitmask → MSBT damage type
-- 1=Physical, 2=Holy, 4=Fire, 8=Nature, 16=Frost, 32=Shadow, 64=Arcane
local schoolMap = {
[1] = 1, -- Physical
[2] = 2, -- Holy
[4] = 4, -- Fire
[8] = 3, -- Nature
[16] = 5, -- Frost
[32] = 6, -- Shadow
[64] = 7, -- Arcane
}
local function schoolToDamageType(school)
if not school or school == 0 then return DMG_PHYSICAL end
return schoolMap[school] or DMG_UNKNOWN
end
-- Power type ID → localized string
local powerTypeNames = {
[0] = MANA or "Mana",
[1] = RAGE or "Rage",
[2] = "Focus",
[3] = ENERGY or "Energy",
}
-- ============================================================================
-- Player/pet identity
-- ============================================================================
local playerName = UnitName("player")
local petName = UnitName("pet")
local identityFrame = CreateFrame("Frame")
identityFrame:RegisterEvent("UNIT_NAME_UPDATE")
identityFrame:RegisterEvent("PLAYER_PET_CHANGED")
identityFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
identityFrame:SetScript("OnEvent", function()
playerName = UnitName("player")
petName = UnitName("pet")
end)
local function getDirection(srcName, dstName)
-- Returns direction, counterpartName
if srcName == playerName then
return OUTGOING, dstName
elseif dstName == playerName then
return INCOMING, srcName
elseif petName and srcName == petName then
return PET_OUT, dstName
elseif petName and dstName == petName then
return PET_IN, srcName
end
return nil, nil -- not relevant to player/pet
end
-- ============================================================================
-- Toggle state
-- ============================================================================
local cleuActive = false
local cleuFrame = CreateFrame("Frame")
local function enableCLEU()
-- Only unregister CHAT_MSG_* events; keep core events (PLAYER_REGEN_*, UNIT_HEALTH, etc.)
MikCEH.UnregisterCombatParseEvents()
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
cleuActive = true
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00WSBT CLEU|r: |cff00ff00ON|r (structured events)")
end
local function enableOriginal()
cleuFrame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
MikCEH.RegisterCombatParseEvents()
cleuActive = false
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00WSBT CLEU|r: |cffff4444OFF|r (string parser)")
end
local function toggle()
if cleuActive then enableOriginal() else enableCLEU() end
end
SLASH_WSBTCLEU1 = "/wsbtcleu"
SlashCmdList["WSBTCLEU"] = function(msg)
if msg == "on" then
if not cleuActive then enableCLEU() end
elseif msg == "off" then
if cleuActive then enableOriginal() end
elseif msg == "status" then
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00WSBT CLEU|r: " .. (cleuActive and "|cff00ff00ON|r" or "|cffff4444OFF|r"))
else
toggle()
end
end
-- ============================================================================
-- Partial action helpers
-- ============================================================================
local function applyPartials(eventData, resisted, blocked, absorbed, glancing, crushing)
-- Only one partial action per event in MSBT's model
if crushing and crushing == 1 then
eventData.PartialActionType = PARTIAL_CRUSHING
elseif glancing and glancing == 1 then
eventData.PartialActionType = PARTIAL_GLANCING
elseif absorbed and absorbed > 0 then
eventData.PartialActionType = PARTIAL_ABSORB
eventData.PartialAmount = absorbed
elseif blocked and blocked > 0 then
eventData.PartialActionType = PARTIAL_BLOCK
eventData.PartialAmount = blocked
elseif resisted and resisted > 0 then
eventData.PartialActionType = PARTIAL_RESIST
eventData.PartialAmount = resisted
end
end
-- ============================================================================
-- CLEU handler
-- ============================================================================
local envActionMap = {
DROWNING = CEH.ACTIONTYPE_DROWNING,
FALLING = CEH.ACTIONTYPE_FALLING,
FATIGUE = CEH.ACTIONTYPE_FATIGUE,
FIRE = CEH.ACTIONTYPE_FIRE,
LAVA = CEH.ACTIONTYPE_LAVA,
SLIME = CEH.ACTIONTYPE_SLIME,
EXHAUSTED = CEH.ACTIONTYPE_FATIGUE,
}
local cleuHandler
cleuHandler = function()
if not CombatLogGetCurrentEventInfo then return end
local sub, srcGUID, srcName, srcFlags, srcRaidFlags,
dstGUID, dstName, dstFlags, dstRaidFlags = CombatLogGetCurrentEventInfo()
if not sub then return end
if not srcName or srcName == "" then srcName = "Unknown" end
if not dstName or dstName == "" then dstName = "Unknown" end
-- ========================================================================
-- SWING_DAMAGE
-- (base 9), amount, overkill, school, resisted, blocked,
-- absorbed, critical, glancing, crushing
-- ========================================================================
if sub == "SWING_DAMAGE" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
amount, overkill, school, resisted, blocked, absorbed,
critical, glancing, crushing = CombatLogGetCurrentEventInfo()
glancing = glancing and 1 or 0
crushing = crushing and 1 or 0
local hitType = critical and HIT_CRIT or HIT_NORMAL
local data = GetDamageData(dir, HIT, hitType, DMG_PHYSICAL, amount, nil, name)
applyPartials(data, resisted, blocked, absorbed, glancing, crushing)
SendEvent(data)
-- ========================================================================
-- SWING_MISSED
-- args: missType(6), amountMissed(7)
-- ========================================================================
elseif sub == "SWING_MISSED" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _, missType = CombatLogGetCurrentEventInfo()
local action = missActionMap[missType] or MISS
local data = GetDamageData(dir, action, nil, nil, nil, nil, name)
SendEvent(data)
-- ========================================================================
-- SPELL_DAMAGE / RANGE_DAMAGE / SPELL_PERIODIC_DAMAGE / DAMAGE_SHIELD / DAMAGE_SPLIT
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: amount(9), overkill(10), school(11), resisted(12), blocked(13),
-- absorbed(14), critical(15), glancing(16), crushing(17)
-- ========================================================================
elseif sub == "SPELL_DAMAGE" or sub == "RANGE_DAMAGE" or sub == "DAMAGE_SHIELD"
or sub == "DAMAGE_SPLIT" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool,
amount, overkill, school, resisted, blocked, absorbed,
critical, glancing, crushing = CombatLogGetCurrentEventInfo()
crushing = crushing and 1 or 0
local hitType = critical and HIT_CRIT or HIT_NORMAL
local dmgType = schoolToDamageType(school)
local data = GetDamageData(dir, HIT, hitType, dmgType, amount, spellName, name)
applyPartials(data, resisted, blocked, absorbed, 0, crushing)
SendEvent(data)
elseif sub == "SPELL_PERIODIC_DAMAGE" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool,
amount, overkill, school, resisted, blocked, absorbed = CombatLogGetCurrentEventInfo()
-- numbers always non-nil from CombatLogGetCurrentEventInfo
local dmgType = schoolToDamageType(school)
local data = GetDamageData(dir, HIT, HIT_DOT, dmgType, amount, spellName, name)
applyPartials(data, resisted, 0, absorbed, 0, 0)
SendEvent(data)
-- ========================================================================
-- SPELL_MISSED / RANGE_MISSED / SPELL_PERIODIC_MISSED / DAMAGE_SHIELD_MISSED
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: missType(9), amountMissed(10)
-- ========================================================================
elseif sub == "SPELL_MISSED" or sub == "RANGE_MISSED"
or sub == "SPELL_PERIODIC_MISSED" or sub == "DAMAGE_SHIELD_MISSED" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool, missType = CombatLogGetCurrentEventInfo()
local action = missActionMap[missType] or MISS
local data = GetDamageData(dir, action, nil, nil, nil, spellName, name)
SendEvent(data)
-- ========================================================================
-- ENVIRONMENTAL_DAMAGE
-- args: envType(6), amount(7), overkill(8), school(9), resisted(10),
-- blocked(11), absorbed(12), critical(13), glancing(14), crushing(15)
-- ========================================================================
elseif sub == "ENVIRONMENTAL_DAMAGE" then
if dstName ~= playerName then return end
local _, _, _, _, _, _, _, _, _, envType, amount = CombatLogGetCurrentEventInfo()
local action = envActionMap[envType] or HIT
local data = GetDamageData(INCOMING, action, HIT_NORMAL, DMG_PHYSICAL, amount, nil, envType or "Environment")
SendEvent(data)
-- ========================================================================
-- SPELL_HEAL / SPELL_PERIODIC_HEAL
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: amount(9), overheal(10), absorbed(11), critical(12)
-- ========================================================================
elseif sub == "SPELL_HEAL" or sub == "SPELL_PERIODIC_HEAL" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool,
amount, overheal, absorbed, critical = CombatLogGetCurrentEventInfo()
local isPeriodic = (sub == "SPELL_PERIODIC_HEAL")
local healType
if isPeriodic then
healType = HEAL_HOT
elseif critical then
healType = HEAL_CRIT
else
healType = HEAL_NORMAL
end
-- For outgoing heals, name = target (dstName); for incoming, name = healer (srcName)
local data = GetHealData(dir, healType, amount, spellName, name)
-- Overheal partial
if overheal > 0 then
data.PartialActionType = PARTIAL_OVERHEAL
data.PartialAmount = overheal
end
SendEvent(data)
-- ========================================================================
-- SPELL_ENERGIZE / SPELL_PERIODIC_ENERGIZE
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: amount(9), powerType(10)
-- ========================================================================
elseif sub == "SPELL_ENERGIZE" or sub == "SPELL_PERIODIC_ENERGIZE" then
if dstName ~= playerName then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool, amount, powerType = CombatLogGetCurrentEventInfo()
local powerName = powerTypeNames[powerType] or "Mana"
local data = GetNotifData(NOTIF_POWER_GAIN, amount .. " " .. powerName, spellName)
SendEvent(data)
-- ========================================================================
-- SPELL_PERIODIC_DRAIN / SPELL_PERIODIC_LEECH
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: amount(9), powerType(10), extraAmount(11)
-- ========================================================================
elseif sub == "SPELL_PERIODIC_DRAIN" then
if dstName ~= playerName then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool, amount, powerType = CombatLogGetCurrentEventInfo()
local powerName = powerTypeNames[powerType] or "Mana"
local data = GetNotifData(NOTIF_POWER_LOSS, amount .. " " .. powerName, spellName)
SendEvent(data)
elseif sub == "SPELL_PERIODIC_LEECH" then
local dir, name = getDirection(srcName, dstName)
if not dir then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool, amount = CombatLogGetCurrentEventInfo()
local dmgType = schoolToDamageType(spellSchool)
local data = GetDamageData(dir, HIT, HIT_DOT, dmgType, amount, spellName, name)
SendEvent(data)
-- ========================================================================
-- AURA events
-- prefix: spellId(6), spellName(7), spellSchool(8)
-- suffix: auraType(9)
-- ========================================================================
elseif sub == "SPELL_AURA_APPLIED" then
if dstName ~= playerName then return end
local _, _, _, _, _, _, _, _, _,
spellId, spellName, spellSchool, auraType = CombatLogGetCurrentEventInfo()
if auraType == "DEBUFF" then
local data = GetNotifData(NOTIF_DEBUFF, nil, spellName)
SendEvent(data)
else
local data = GetNotifData(NOTIF_BUFF, nil, spellName)
SendEvent(data)
end
elseif sub == "SPELL_AURA_REMOVED" then
if dstName ~= playerName then return end
local _, _, _, _, _, _, _, _, _, spellId, spellName = CombatLogGetCurrentEventInfo()
local data = GetNotifData(NOTIF_BUFF_FADE, nil, spellName)
SendEvent(data)
-- ========================================================================
-- PARTY_KILL — killing blow notification
-- ========================================================================
elseif sub == "PARTY_KILL" then
if srcName ~= playerName then return end
local notifType = CEH.NOTIFICATIONTYPE_NPC_KILLING_BLOW
if CEH.recentlySelectedPlayers[dstName] then
notifType = CEH.NOTIFICATIONTYPE_PC_KILLING_BLOW
end
local data = GetNotifData(notifType, nil, dstName)
SendEvent(data)
end
end
-- ============================================================================
-- Performance profiling (A/B per-combat)
-- ============================================================================
local profiling = false
local profCLEU = { events = 0, totalMs = 0, gcStart = 0 }
local profOrig = { events = 0, totalMs = 0, gcStart = 0 }
local profCurrent = nil
-- Hook the original MikCEH.OnEvent to measure original mode
-- Only count events that CLEU replaces (combat parse), not honor/XP/rep/health/etc.
local origOnEvent = MikCEH.OnEvent
local cleuReplacedLookup = CEH.cleuReplacedLookup
local function measuredOrigOnEvent()
if profiling and profCurrent == profOrig and cleuReplacedLookup[event] then
local before = debugprofilestop()
origOnEvent()
local after = debugprofilestop()
profOrig.totalMs = profOrig.totalMs + (after - before)
profOrig.events = profOrig.events + 1
else
origOnEvent()
end
end
-- Patch MikCEH.OnEvent so the XML OnEvent handler calls the measured version
MikCEH.OnEvent = measuredOrigOnEvent
local cleuEventsSkipped = 0
local function measuredCLEUHandler()
if profiling and profCurrent == profCLEU then
-- Quick relevance check: is player or pet involved?
local _, _, srcName, _, _, _, dstName = CombatLogGetCurrentEventInfo()
srcName = srcName or ""
dstName = dstName or ""
local relevant = (srcName == playerName or dstName == playerName
or (petName and (srcName == petName or dstName == petName)))
local before = debugprofilestop()
cleuHandler()
local after = debugprofilestop()
if relevant then
profCLEU.totalMs = profCLEU.totalMs + (after - before)
profCLEU.events = profCLEU.events + 1
else
cleuEventsSkipped = cleuEventsSkipped + 1
end
else
cleuHandler()
end
end
cleuFrame:SetScript("OnEvent", measuredCLEUHandler)
local function profReset(tbl)
tbl.events = 0
tbl.totalMs = 0
tbl.gcStart = gcinfo()
end
local lastCLEUAvg = nil
local lastOrigAvg = nil
local function profReport(label, tbl)
local gcEnd = gcinfo()
local gcDelta = gcEnd - tbl.gcStart
local totalMs = tbl.totalMs / 1000
local avgUs = tbl.events > 0 and (tbl.totalMs / tbl.events) or 0
if label == "CLEU" then
lastCLEUAvg = avgUs
else
lastOrigAvg = avgUs
end
local skipMsg = ""
if label == "CLEU" then
skipMsg = string.format(" (%d skipped)", cleuEventsSkipped)
cleuEventsSkipped = 0
end
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff00ff00[WSBT %s]|r %d events%s, %.1fms total, %.1f us/event, %+.1f KB gc",
label, tbl.events, skipMsg, totalMs, avgUs, gcDelta))
if lastCLEUAvg and lastOrigAvg and lastOrigAvg > 0 then
local pct = ((lastCLEUAvg - lastOrigAvg) / lastOrigAvg) * 100
local sign = pct < 0 and "" or "+"
local color = pct < 0 and "|cff00ff00" or "|cffff4444"
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"%s[WSBT CLEU vs ORIGINAL]|r %.1f vs %.1f us/event = %s%.1f%%|r %s",
color, lastCLEUAvg, lastOrigAvg, sign, pct,
pct < 0 and "(CLEU faster)" or "(ORIGINAL faster)"))
end
end
local benchActive = true
local benchFrame = CreateFrame("Frame")
local function benchCombatStart()
if not benchActive then return end
profCurrent = cleuActive and profCLEU or profOrig
profReset(profCurrent)
debugprofilestart()
profiling = true
local label = cleuActive and "CLEU" or "ORIGINAL"
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[WSBT Bench]|r combat started, measuring %s", label))
end
local function benchCombatEnd()
if not benchActive or not profiling then return end
profiling = false
local label = cleuActive and "CLEU" or "ORIGINAL"
profReport(label, profCurrent)
toggle()
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff00ff00[WSBT Bench]|r next combat will use: %s", cleuActive and "CLEU" or "ORIGINAL"))
end
benchFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
benchFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
benchFrame:SetScript("OnEvent", function()
if event == "PLAYER_REGEN_DISABLED" then
benchCombatStart()
elseif event == "PLAYER_REGEN_ENABLED" then
benchCombatEnd()
end
end)
SLASH_WSBTBENCH1 = "/wsbtbench"
SlashCmdList["WSBTBENCH"] = function()
benchActive = not benchActive
if benchActive then
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff00ff00[WSBT Bench]|r enabled. Current mode: %s. Enter combat to begin.",
cleuActive and "CLEU" or "ORIGINAL"))
else
profiling = false
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[WSBT Bench]|r disabled.")
end
end
-- ============================================================================
-- Start in CLEU mode by default
-- ============================================================================
enableCLEU()