• 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,631
Solutions
559
Reaction score
3,846
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:

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,488
Solutions
49
Reaction score
1,587
Location
Poland
GitHub
Oen44
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.
 

Ochman

Advanced OT User
Joined
Feb 27, 2016
Messages
339
Solutions
5
Reaction score
238
I think you misunderstood his question.
He asks why it's not in addEvent directly. I think it's common EAFP or LBYL problem.
 

zxmatzx

Advanced OT User
Joined
Dec 1, 2010
Messages
308
Solutions
26
Reaction score
156
Location
Brazil
GitHub
Mateuso8
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?
 
OP
OP
Infernum

Infernum

Senator
Joined
Feb 14, 2015
Messages
5,631
Solutions
559
Reaction score
3,846
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.
 
OP
OP
Infernum

Infernum

Senator
Joined
Feb 14, 2015
Messages
5,631
Solutions
559
Reaction score
3,846
Updated main post, had an old variable name so the arguments would never get converted and used, now it should be safe to use.
 

Lurk

Active Member
Joined
Dec 4, 2017
Messages
336
Reaction score
48
did anyone create a pr on the tfs github with this? if not, please
 
Top