• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[TFS 1.X] safeAddEvent

Infernum

Senator
Joined
Feb 14, 2015
Messages
5,643
Solutions
559
Reaction score
3,948
This provides a safe way to use addEvent with userdata without having to manually reconstruct the object yourself in order to avoid crashes. If the object can't automatically be reconstructed at the time of execution, it will not execute at all. The usage is the same as the normal addEvent.
Examples of this:
safeAddEvent(Player.addHealth, 3000, Player('Delusion'), 100)
safeAddEvent(Player.sendTextMessage, 1000, Player('Delusion'), MESSAGE_INFO_DESCR, 'Test')

Code:
Lua:
function safeAddEvent(f, interval, ...)
    local convertedArgs = {}

    for k, arg in ipairs({...}) do
        if type(arg) == 'userdata' then
            local convertedArg = {args = {arg:getId()}}
            if Player(arg) then
                convertedArg.constructor = Player
            elseif Monster(arg) then
                convertedArg.constructor = Monster
            elseif Npc(arg) then
                convertedArg.constructor = Npc
            elseif arg:isItem() then
                local parent = arg:getTopParent()
                if parent:isTile() then
                    convertedArg.args = {arg:getPosition(), arg:getId()}
                    convertedArg.constructor = function(pos, itemId)
                        local tile = Tile(pos)
                        if tile then
                            return tile:getItemById(itemId)
                        end
                    end
                end
            end
            convertedArgs[k] = convertedArg.constructor and convertedArg or arg
        else
            convertedArgs[k] = arg
        end
    end

    local function callback()
        local parsedArgs = {}
        for k, arg in ipairs(convertedArgs) do
            if type(arg) == 'table' and arg.constructor and arg.args then
                local obj = arg.constructor(unpack(arg.args))
                if not obj then
                    return nil
                end
                parsedArgs[k] = obj
            else
                parsedArgs[k] = arg
            end
        end
        f(unpack(parsedArgs))
    end

    return addEvent(callback, interval)
end

Everything written on my phone, hopefully no syntax errors.
 
Last edited:
Great idea - this caught me out previously and I never got around to fixing it.
Is there any reason this shouldn't be the only implementation of addEvent?
 
Great idea - this caught me out previously and I never got around to fixing it.
Is there any reason this shouldn't be the only implementation of addEvent?
Because if you pass Creature, Player or anything like that in the arguments, it's not guaranteed that they will be still in memory when callback is executed. Segmentation fault would be way more common.
 
I think you misunderstood his question.
He asks why it's not in addEvent directly. I think it's common EAFP or LBYL problem.
 
This provides a safe way to use addEvent with userdata without having to manually reconstruct the object yourself in order to avoid crashes. If the object can't automatically be reconstructed at the time of execution, it will not execute at all. The usage is the same as the normal addEvent.
Examples of this:
safeAddEvent(Player.addHealth, 3000, Player('Delusion'), 100)
safeAddEvent(Player.sendTextMessage, 1000, Player('Delusion'), MESSAGE_INFO_DESCR, 'Test')

Code:
Lua:
function safeAddEvent(f, interval, ...)
    local convertedArgs = {}

    for k, arg in ipairs({...}) do
        if type(arg) == 'userdata' then
            local convertedArg = {args = {arg:getId()}}
            if Player(arg) then
                convertedArg.constructor = Player
            elseif Monster(arg) then
                convertedArg.constructor = Monster
            elseif Npc(arg) then
                convertedArg.constructor = Npc
            elseif arg:isItem() then
                local parent = arg:getTopParent()
                if parent:isTile() then
                    convertedArg.args = {arg:getPosition(), arg:getId()}
                    convertedArg.constructor = function(pos, itemId)
                        local tile = Tile(pos)
                        if tile then
                            return tile:getItemById(itemId)
                        end
                    end
                end
            end
            convertedArgs[k] = convertedArg.constructor and convert or arg
        else
            convertedArgs[k] = arg
        end
    end

    local function callback()
        local parsedArgs = {}
        for k, arg in ipairs(convertedArgs) do
            if type(arg) == 'table' and arg.constructor and arg.args then
                local obj = arg.constructor(unpack(arg.args))
                if not obj then
                    return nil
                end
                parsedArgs[k] = obj
            else
                parsedArgs[k] = arg
            end
        end
        f(unpack(parsedArgs))
    end

    return addEvent(callback, interval)
end

Everything written on my phone, hopefully no syntax errors.
Thanks again for the contribution! I agree with mdwilliams, why this isn't the "new" addEvent?
 
This shouldn't be the implementation because you have no way of defining custom behavior when an object does not exist, it just simply returns. The original addEvent should be used by people who need that functionality and know what they're doing.
 
Updated main post, had an old variable name so the arguments would never get converted and used, now it should be safe to use.
 
did anyone create a pr on the tfs github with this? if not, please
 
Back
Top