Skip to content

feat(Multistate): Shoehorned multistate#373

Draft
mostlynick3 wants to merge 13 commits intoazerothcore:masterfrom
mostlynick3:multistate-shoehorn
Draft

feat(Multistate): Shoehorned multistate#373
mostlynick3 wants to merge 13 commits intoazerothcore:masterfrom
mostlynick3:multistate-shoehorn

Conversation

@mostlynick3
Copy link
Copy Markdown

@mostlynick3 mostlynick3 commented Mar 19, 2026

Draft PR for a shoehorned multistate implementation, aiming to replicate "native" multistate without the need for further core-side support/development. Both compatibility mode and multistate mode are working out of the box but I haven't tested in a prod environment yet and thus cannot safely say it's good to go. I assume more changes will be needed.

Anyone is welcome to test it. I've set compatibility mode enabled by default (or, rather, disabled multistate by default), so that on the off chance that this PR gets merged, mod-ale users' existing scripts will work without requiring rewrites.

Note that Data() methods support is not included in this PR as the original Eluna implementation requires core edits to support it. It's of course possible to serialize and store reload-proof data in mod-ale's cache, and thus get Data() support without core edits, but since it's a bit of a hacky solution I've chosen to omit it.

@mostlynick3 mostlynick3 force-pushed the multistate-shoehorn branch 4 times, most recently from 1a7c679 to ac220f2 Compare March 22, 2026 03:24
@mostlynick3 mostlynick3 force-pushed the multistate-shoehorn branch from ac220f2 to 9c4051a Compare March 22, 2026 03:36
@mostlynick3
Copy link
Copy Markdown
Author

mostlynick3 commented Mar 22, 2026

Now with full instanced map support as well (BGs, raids, dungeons), with each instance running a separate Lua state :)

@mostlynick3 mostlynick3 force-pushed the multistate-shoehorn branch from 54c0c3c to 4bce66b Compare March 29, 2026 00:46
@mostlynick3 mostlynick3 force-pushed the multistate-shoehorn branch from 4bce66b to 922302c Compare March 30, 2026 02:42
@Foereaper
Copy link
Copy Markdown

So the idea is to have multiple Lua states, but still keep the single map update thread restriction? That is essentially what you'll achieve with the mutex locking, which isn't great. It's definitely better than overriding the map update thread count, but performance/resource wise this is probably going to be worse.

@mostlynick3
Copy link
Copy Markdown
Author

mostlynick3 commented Mar 30, 2026

I'm not sure I follow @Foereaper - by "keep the single map update thread restriction", are you referring to there only being one update thread per map? Or possibly just one map update thread in total, effectively, through mutexing?

This setup does parallelize load, each map thread gets a separate Lua state that executes its stuff independently of other states. So the map mutexes don't lock other map threads. With this setup, my world update time is reduced by 50% to 75% on theoretical tests like below. That was my goal, after all - ensuring that separate maps' updates do not turn sequential through the world update loop.

Did I perhaps misunderstand you?

Theoretical test used:

local loadTestActive = true

if not loadTestActive then return end

local sid = GetStateMapId()
local MAX = 2
local wCount = 0
local c0 = {}

local function load(m)
    local playerCount = 0
    local transportCount = 0
    local isArena = false
    local isBG = false
    local isDung = false
    for i = 1, 50000 do
        local pl = m:GetPlayers()
        local tr = m:GetTransports()
        isArena = m:IsArena()
        isBG = m:IsBattleground()
        isDung = m:IsDungeon()
        if i == 1 then
            playerCount = pl and #pl or 0
            transportCount = tr and #tr or 0
        end
    end
    local pl = m:GetPlayers()
    local names = {}
    if pl then
        for _, p in ipairs(pl) do
            p:GetLevel()
            p:GetGUIDLow()
            table.insert(names, p:GetName())
        end
    end
    return table.concat(names, ", "), playerCount, transportCount, isArena, isBG, isDung
end

local function onWorld(event, diff)
    if wCount >= MAX * 5 then return end
    wCount = wCount + 1
    if diff < 50 then return end
    print("[W] #" .. wCount .. " diff=" .. diff .. "ms")
end

local function onMap(event, map, diff)
    local mid = map:GetMapId()
    if not c0[mid] then c0[mid] = 0 end
    if c0[mid] >= MAX then return end
    c0[mid] = c0[mid] + 1
    local tStart = GetCurrTime()
    print("[MAP "..mid.."] #" .. c0[mid] .. " START TIME: "..tStart..". Diff: "..diff)
    local names, playerCount, transportCount, isArena, isBG, isDung = load(map)
    local elapsed = GetTimeDiff(tStart)
    print("[MAP "..mid.."] #" .. c0[mid] .. " END TIME: "..GetCurrTime()..". Elapsed: " .. elapsed .. "ms. Players=[" .. names .. "] playerCount="..playerCount.." transports="..transportCount.." arena="..tostring(isArena).." bg="..tostring(isBG).." dung="..tostring(isDung))
end

if sid == -1 then RegisterServerEvent(13, onWorld) end

local compat = IsCompatibilityMode()
if compat or sid > -1 then
    RegisterServerEvent(23, onMap)
end

@mostlynick3 mostlynick3 force-pushed the multistate-shoehorn branch from fe34771 to 21cd9be Compare March 30, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants