• Vote in May Mapping Competition to select a winner!
  • 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

Asshole
Support Team
Joined
Feb 14, 2015
Messages
5,456
Best answers
540
Reaction score
3,316
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:

mdwilliams

(Michael)
Premium User
Joined
Nov 15, 2007
Messages
670
Best answers
3
Reaction score
158
Location
Santiago, Chile (Australian)
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?
 

oen432

Excellent OT User
Joined
Oct 3, 2014
Messages
649
Best answers
20
Reaction score
608
Location
Poland
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
289
Best answers
3
Reaction score
182
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

Intermediate OT User
Joined
Dec 1, 2010
Messages
265
Best answers
23
Reaction score
107
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
Infernum

Infernum

Asshole
Support Team
Joined
Feb 14, 2015
Messages
5,456
Best answers
540
Reaction score
3,316
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
Infernum

Infernum

Asshole
Support Team
Joined
Feb 14, 2015
Messages
5,456
Best answers
540
Reaction score
3,316
Updated main post, had an old variable name so the arguments would never get converted and used, now it should be safe to use.
 

Cassius Clay

Member
Joined
Jan 16, 2011
Messages
48
Best answers
0
Reaction score
6
I have been through many crashes with this function. excellent contribution
 
Top