• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • 💡 We're thrilled to announce that our upcoming texturing contest is in the works, and we're eager to hear your suggestions! Please take this opportunity to share your ideas in this theme discussion thread for the Texturing Contest #34!
  • 🏆 Hive's 7th HD Modeling Contest: Icecrown Creature is now open! The frozen wastes of Icecrown are home to some of Azeroth’s most terrifying and resilient creatures. For this contest, your challenge is to design and model a HD 3D monster that embodies the cold, undead, and sinister essence of Icecrown! 📅 Submissions close on April 13, 2025. Don't miss this opportunity to let your creativity shine! Enter now and show us your frozen masterpiece! 🔗 Click here to enter!

Lua GetStackTrace

Status
Not open for further replies.
Level 24
Joined
Jun 26, 2020
Messages
1,924
I don't know if this was actually found, but there is a way to get the stack trace in W3 Lua, just using the error function, because is the unique function that can tell us what function called it and the function that called this and so on, so I did something like this:
Lua:
function GetStackTrace()
    xpcall(error, print, "Fourth", 7)
    xpcall(error, print, "Third", 6)
    xpcall(error, print, "Second", 5)
    xpcall(error, print, "First", 4)
end
Why from start 4?, because the first will refer to the xpcall function; the second, the GetStackTrace function; and probably you will add this right before an error will happen, so it will display what would be the third position.

This is the result:
1655178632407.png

Is not very clean, but is the better solution I can think. Pls give feedback.
 
Last edited:
Level 18
Joined
Jun 13, 2016
Messages
584
It could be, but how can I know when stops?
needs some testing to figure out how "error" behaves when you go outside the stack
depending on what it does, collect the stacktrace in a loop and catch that behaviour and then terminate the loop
if it starts returning an empty string instead of a file location, then watch for that. if it keeps repeating the same message, check if we've seen that location already (but watch out for recursive functions)
i dont have a working wc3 install rn so i cant test it, but should be easy to figure out with some tinkering


unless it crashes the game, of course, lol
 
Level 24
Joined
Jun 26, 2020
Messages
1,924
@mori I can do that.
After testing I found that in a new map, I should have the error function inside a function when I using the xpcall to get the position, like this:
Lua:
function func()
    xpcall(function ()
        error("Pos", 1)
    end, print)
end
I don't exactly know what have of special my map to work like that before, but oh well.
I also could test it without W3 just installing Lua (for who use Windows 10: Windows - How to install Lua on Windows?)
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,924
Ok, I got a better aproach, after testing I found the function error only returns the message and not the position once it reached the end of the stack trace, so I do this:

Lua:
do
    local prefix = "war3map.lua"
    local prefixLen = string.len(prefix)
    local list = {}
    local lastMsg = nil

    local function getPos(msg, pos)
        error(msg, pos)
    end

    local function store(msg)
        table.insert(list, msg)
        lastMsg = msg
    end

    function GetStackTrace()
        local stack = ""

        local i = 4
        local p = 1
        while true do
            xpcall(getPos, store, "- " .. p, i)
            if string.sub(lastMsg, 1, prefixLen) ~= prefix then break end
            i = i + 1
            p = p + 1
        end

        for j = #list - 1, 1, - 1 do -- Don't consider the last
            stack = stack .. list[j] .. "\n"
        end

        list = {}

        return stack
    end
end
And here is a map to test:
 

Attachments

  • Lua GetStackTrace.w3m
    18.4 KB · Views: 18

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,006
Lua:
Test = function(chatMsg)
    local playerName = GetPlayerName(GetTriggerPlayer())
    local loadedFunc, error = load(chatMsg, playerName, "t")
    if loadedFunc ~= nil then
        DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Player " .. playerName .. " executed the function:\r\n" .. chatMsg)
        loadedFunc()
    else
        DisplayTextToPlayer(GetTriggerPlayer(), 0, 0, error)
    end
end
Essentially this as a player chat event.
load's 2nd parameter is chuckname,
1655478541040.png

eof = end of file

I'm still not sure if this answers your question in its entirety.
 
Level 24
Joined
Jun 26, 2020
Messages
1,924
Lua:
Test = function(chatMsg)
    local playerName = GetPlayerName(GetTriggerPlayer())
    local loadedFunc, error = load(chatMsg, playerName, "t")
    if loadedFunc ~= nil then
        DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Player " .. playerName .. " executed the function:\r\n" .. chatMsg)
        loadedFunc()
    else
        DisplayTextToPlayer(GetTriggerPlayer(), 0, 0, error)
    end
end
Essentially this as a player chat event.
load's 2nd parameter is chuckname,
View attachment 401409
eof = end of file

I'm still not sure if this answers your question in its entirety.
Ok, so how can I apply it to what I asked?
I remember that some functions calls could come from the blizzard.j.lua file, in that cases this GetStackTrace function could stop in that moments when it shouldn't, I could fix that, but is there other files that I should consider?
 
Level 24
Joined
Jun 26, 2020
Messages
1,924
Ok, I improved the system to also consider the blizzard.j.lua, and updated the test map
Lua:
do
    local prefixes = {"war3map.lua", "blizzard.j.lua"}
    local n = #prefixes
    local prefixesLen = {}
    for i = 1, n do
        prefixesLen[i] = string.len(prefixes[i])
    end
    local list = {}
    local lastMsg = nil

    local function getPos(msg, pos)
        error(msg, pos)
    end

    local function store(msg)
        lastMsg = msg
    end

    local function checkPrefixes()
        for i = 1, n do
            if string.sub(lastMsg, 1, prefixesLen[i]) == prefixes[i] then
                return true
            end
        end
        return false
    end

    function GetStackTrace()
        local stack = ""

        local i = 4
        local p = 1
        while true do
            xpcall(getPos, store, "- " .. p, i)
            if not checkPrefixes() then break end
            table.insert(list, lastMsg)
            i = i + 1
            p = p + 1
        end

        for j = #list, 1, -1 do
            stack = stack .. list[j] .. "\n"
        end

        list = {}

        return stack
    end
end
 

Attachments

  • Lua GetStackTrace.w3m
    18.8 KB · Views: 14
Status
Not open for further replies.
Top