• 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!

Lua Boss area entrance - crash

Unknown Soldier

Mapping a map
Joined
Oct 30, 2010
Messages
294
Solutions
11
Reaction score
665
Hello,

recently, after some chanegs in my script, I have noticed some crashes. Crash happens ocassionally while using the following script, so entering the boss area, usually after a fight or at the very end of it, or few seconds after player leaves the arena and enters it again. I blame addEvent, but have no crashlog yet, can somebody just take a look at this poorly written script and find possible cause? Maybe for somebody it would be an obvious thing, if not I will try to get crashlog. TFS 1.4.2.

Thanks in advance

Lua:
local bossConfig = {
    [50074] = {  -- ActionID
        requiredLevel = 400,
        minPlayersRequired = 1,
        fromPos = {x = 2446, y = 1720, z = 9}, --position needed for reward chest to be removed
        toPos = {x = 2473, y = 1743, z = 9},
        boss = "Embertacle",
        playerStorage = 60095, -- storage counting the time, number of hours between fights
        playerQuestStorage = 60084, --storage, which will allow player to enter the room
        teleportPosition = Position(2451, 1738, 9),
        centerRoomPosition = Position(2460, 1732, 9),
        northRange = 18,
        eastRange = 18,
        southRange = 18,
        westRange = 18,
        exit = Position(2457, 1734, 7),
        bossPosition = Position(2460, 1732, 9),
        time = 2, --fight time in minutes, 0.1 = 6 seconds
        timebetweenfights = 0.0005, --in hours, 20 as default, 0.1 = 6 minutes
        playerPositions = {
            [1] = Position(2464, 1732, 7),
            [2] = Position(2464, 1733, 7),
            [3] = Position(2464, 1734, 7),
            [4] = Position(2464, 1735, 7),
            [5] = Position(2464, 1736, 7)
        }
    }
}

local function resetBoss_embertacle(bossConfig, bossId)
    local spectators = Game.getSpectators(bossConfig.centerRoomPosition,false,true,bossConfig.westRange,bossConfig.eastRange,bossConfig.northRange,bossConfig.southRange)
    for i = 1, #spectators do
        spectators[i]:teleportTo(bossConfig.exit)
        spectators[i]:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Time's up.")
    end
end

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if item.itemid == 1946 then
        local bossConfig = bossConfig[item:getActionId()]
        if not bossConfig then
            return false
        end
    local spectators2 = Game.getSpectators(bossConfig.centerRoomPosition,false,true,bossConfig.westRange,bossConfig.eastRange,bossConfig.northRange,bossConfig.southRange)
        if #spectators2 >= 1 then
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The arena is occupied. Please wait for your turn.")
            return true
        else
            --removing all "old" creatures from arena
            local monsters = {}
            for x = bossConfig.fromPos.x, bossConfig.toPos.x do
                for y = bossConfig.fromPos.y, bossConfig.toPos.y do
                    for z = bossConfig.fromPos.z, bossConfig.toPos.z do
                        local v = getTopCreature({x=x, y=y, z=z})
                         if v.type == 1 then
                            return
                        elseif v.type == 2 then
                            table.insert(monsters, v.uid)
                        end
                    end
                end
            end
            for i = 1, #monsters do
                doRemoveCreature(monsters[i])
            end
  
            --removing reward chest
            iterateArea(
                function(position)
                    local tile = Tile(position)
                    if not tile then
                        return
                    end
                    local items = tile:getItems()

                    if items then
                        for i = 1, #items do
                            local checkitem = items[i]
                            if checkitem:getId() == 18472 then
                                local item = items[i]
                                item:remove()
                            end
                        end
                    end
                end,
                Position(bossConfig.fromPos.x, bossConfig.fromPos.y, bossConfig.fromPos.z),
                Position(bossConfig.toPos.x, bossConfig.toPos.y, bossConfig.toPos.z)
            )

            stopEvent(resetBossEvent_embertacle)
            local errorMsg
            local rPlayers = {}

            for index, ipos in pairs(bossConfig.playerPositions) do
                local playerTile = Tile(ipos):getTopCreature()
                if playerTile then
                    if playerTile:isPlayer() then
                        if playerTile:getLevel() >= bossConfig.requiredLevel then
                            if playerTile:getStorageValue(bossConfig.playerStorage) <= os.time() then
                                if playerTile:getStorageValue(bossConfig.playerQuestStorage) >= 1 then
                                    table.insert(rPlayers, playerTile:getId())
                                else
                                    errorMsg = "One or more players have not acomplished required quest progress yet."
                                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, errorMsg)
                                    return true
                                end
                            else
                                local cur_time, cur_storage = os.time(), player:getStorageValue(bossConfig.playerStorage)
                          
                                if cur_storage > cur_time then
                                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can enter again in " .. os.date("!%H hours %M minutes and %S seconds", cur_storage - cur_time) ..".")
                                else
                                    errorMsg ="One or more players have already entered in the last  " .. bossConfig.timebetweenfights .. " hours."
                                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, errorMsg)
                                    return true
                                end
                                return true
                            end
                        else
                            errorMsg = "All the players need to be level " .. bossConfig.requiredLevel .. " or higher."
                            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, errorMsg)
                            return true
                        end
                    end
                end
            end

 
                if (#rPlayers >= bossConfig.minPlayersRequired) then
                for _, pid in pairs(rPlayers) do
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
                        rplayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, ("You have " .. bossConfig.time .. " minutes to kill " .. bossConfig.boss .. "."))
                        for j = 1, 5 do
                            bossConfig.playerPositions[j]:sendMagicEffect(CONST_ME_POFF)
                        end
                        rplayer:teleportTo(bossConfig.teleportPosition)
                        rplayer:setStorageValue(bossConfig.playerStorage, os.time() + (bossConfig.timebetweenfights * 60 * 60)) --should be 20*60*60 for 20 hours
                        bossConfig.teleportPosition:sendMagicEffect(CONST_ME_ENERGYAREA)
                        rplayer:setDirection(DIRECTION_NORTH)
                    end
                end
                local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
                resetBossEvent_embertacle = addEvent(resetBoss_embertacle, bossConfig.time * 60 * 1000, bossConfig, monster and monster.uid or 0)
            else
                if not errorMsg then
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, ("You need at least %u player."):format(bossConfig.minPlayersRequired))
                else
                    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, errorMsg)
                end
                return true
            end
        end
    end

    item:transform(item.itemid == 1946 and 1945 or 1946)

    return true
end
 
Last edited:
Maybe for somebody it would be an obvious thing, if not I will try to get crashlog
Get crashlog. It will be much easier with name of Lua function that crashed server.

This part may get bugged, but NOT crash, if for some reason boss cannot spawn [tile is blocked by something]:
Lua:
                for _, pid in pairs(rPlayers) do
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
                        rplayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, ("You have " .. bossConfig.time .. " minutes to kill " .. bossConfig.boss .. "."))
                        for j = 1, 5 do
                            bossConfig.playerPositions[j]:sendMagicEffect(CONST_ME_POFF)
                        end
                        rplayer:teleportTo(bossConfig.teleportPosition)
                        rplayer:setStorageValue(bossConfig.playerStorage, os.time() + (bossConfig.timebetweenfights * 60 * 60)) --should be 20*60*60 for 20 hours
                        bossConfig.teleportPosition:sendMagicEffect(CONST_ME_ENERGYAREA)
                        rplayer:setDirection(DIRECTION_NORTH)
                    end
                end
                local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
                resetBossEvent_embertacle = addEvent(resetBoss_embertacle, bossConfig.time * 60 * 1000, bossConfig, monster and monster.uid or 0)
You should first spawn boss:
Lua:
local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
then check if it's spawned, Game.createMonster returns Monster or nil, if it failed to spawn ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L4479-L4504 ):
Lua:
if monster then
-- addEvent
-- teleport players
else
-- send error to player "contact with GM"
end
and then addEvent and teleport players into room.

Also this check is wrong:
Lua:
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
to check if rplayer is Player, you just need to check, if it's not nil, function Player(pid) returns Player or nil ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L8135-L8170 )
Lua:
                    local rplayer = Player(pid)
                    if rplayer then
calling isPlayer on nil will throw Lua error (again, not crash).
 
Get crashlog. It will be much easier with name of Lua function that crashed server.

This part may get bugged, but NOT crash, if for some reason boss cannot spawn [tile is blocked by something]:
Lua:
                for _, pid in pairs(rPlayers) do
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
                        rplayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, ("You have " .. bossConfig.time .. " minutes to kill " .. bossConfig.boss .. "."))
                        for j = 1, 5 do
                            bossConfig.playerPositions[j]:sendMagicEffect(CONST_ME_POFF)
                        end
                        rplayer:teleportTo(bossConfig.teleportPosition)
                        rplayer:setStorageValue(bossConfig.playerStorage, os.time() + (bossConfig.timebetweenfights * 60 * 60)) --should be 20*60*60 for 20 hours
                        bossConfig.teleportPosition:sendMagicEffect(CONST_ME_ENERGYAREA)
                        rplayer:setDirection(DIRECTION_NORTH)
                    end
                end
                local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
                resetBossEvent_embertacle = addEvent(resetBoss_embertacle, bossConfig.time * 60 * 1000, bossConfig, monster and monster.uid or 0)
You should first spawn boss:
Lua:
local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
then check if it's spawned, Game.createMonster returns Monster or nil, if it failed to spawn ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L4479-L4504 ):
Lua:
if monster then
-- addEvent
-- teleport players
else
-- send error to player "contact with GM"
end
and then addEvent and teleport players into room.

Also this check is wrong:
Lua:
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
to check if rplayer is Player, you just need to check, if it's not nil, function Player(pid) returns Player or nil ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L8135-L8170 )
Lua:
                    local rplayer = Player(pid)
                    if rplayer then
calling isPlayer on nil will throw Lua error (again, not crash).
Thank you for tips, valuable things, I will update the script.
I will be back as soon as I have crashlog, but usually it takes over an hour to reproduce it, so might be a few days.
 
Get crashlog. It will be much easier with name of Lua function that crashed server.

This part may get bugged, but NOT crash, if for some reason boss cannot spawn [tile is blocked by something]:
Lua:
                for _, pid in pairs(rPlayers) do
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
                        rplayer:sendTextMessage(MESSAGE_EVENT_ADVANCE, ("You have " .. bossConfig.time .. " minutes to kill " .. bossConfig.boss .. "."))
                        for j = 1, 5 do
                            bossConfig.playerPositions[j]:sendMagicEffect(CONST_ME_POFF)
                        end
                        rplayer:teleportTo(bossConfig.teleportPosition)
                        rplayer:setStorageValue(bossConfig.playerStorage, os.time() + (bossConfig.timebetweenfights * 60 * 60)) --should be 20*60*60 for 20 hours
                        bossConfig.teleportPosition:sendMagicEffect(CONST_ME_ENERGYAREA)
                        rplayer:setDirection(DIRECTION_NORTH)
                    end
                end
                local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
                resetBossEvent_embertacle = addEvent(resetBoss_embertacle, bossConfig.time * 60 * 1000, bossConfig, monster and monster.uid or 0)
You should first spawn boss:
Lua:
local monster = Game.createMonster(bossConfig.boss, bossConfig.bossPosition)
then check if it's spawned, Game.createMonster returns Monster or nil, if it failed to spawn ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L4479-L4504 ):
Lua:
if monster then
-- addEvent
-- teleport players
else
-- send error to player "contact with GM"
end
and then addEvent and teleport players into room.

Also this check is wrong:
Lua:
                    local rplayer = Player(pid)
                    if rplayer:isPlayer() then
to check if rplayer is Player, you just need to check, if it's not nil, function Player(pid) returns Player or nil ( https://github.com/otland/forgottenserver/blob/1.4/src/luascript.cpp#L8135-L8170 )
Lua:
                    local rplayer = Player(pid)
                    if rplayer then
calling isPlayer on nil will throw Lua error (again, not crash).
Okay, finally got it to crash. No idea if it is related to mentioned script, I have doubts.

But how do I get the detailed text file that can usually be seen in other threads? I have already saved the .dmp file.
 
The one part I'm skeptical of is this
Lua:
            --removing reward chest
            iterateArea(
                function(position)
                    local tile = Tile(position)
                    if not tile then
                        return
                    end
                    local items = tile:getItems()

                    if items then
                        for i = 1, #items do
                            local checkitem = items[i]
                            if checkitem:getId() == 18472 then
                                local item = items[i]
                                item:remove()
                            end
                        end
                    end
                end,
                Position(bossConfig.fromPos.x, bossConfig.fromPos.y, bossConfig.fromPos.z),
                Position(bossConfig.toPos.x, bossConfig.toPos.y, bossConfig.toPos.z)
            )
When searching through items to be deleted, I would loop through the items backwards, going top down, instead of bottom to top.
Because as you remove items, the top of the stack reduces.. and might throw some kind of error / crash if the item no longer exists to be checked.
 
The one part I'm skeptical of is this
Lua:
            --removing reward chest
            iterateArea(
                function(position)
                    local tile = Tile(position)
                    if not tile then
                        return
                    end
                    local items = tile:getItems()

                    if items then
                        for i = 1, #items do
                            local checkitem = items[i]
                            if checkitem:getId() == 18472 then
                                local item = items[i]
                                item:remove()
                            end
                        end
                    end
                end,
                Position(bossConfig.fromPos.x, bossConfig.fromPos.y, bossConfig.fromPos.z),
                Position(bossConfig.toPos.x, bossConfig.toPos.y, bossConfig.toPos.z)
            )
When searching through items to be deleted, I would loop through the items backwards, going top down, instead of bottom to top.
Because as you remove items, the top of the stack reduces.. and might throw some kind of error / crash if the item no longer exists to be checked.
Thank you for reply. Well, to be honest I have no idea how could I reverse that order. But looking at the error window, it made me thinking, that if it is not about the thing you mention, this could be related to reward bag item, which is container, and to which I gave decayto="0" attibute in items.xml. Perhaps this might be the issue if I am correctly thinking that it is related to some item removal action. And what's more, crash happens in - I'd say - random moment, not directly in the moment of triggering the script, sometimes while afking, so this might be just disappearing reward bag either from the ground or players backpack. I am gonna check this.
 
When searching through items to be deleted, I would loop through the items backwards, going top down, instead of bottom to top.
It's removing Item* objects from game (only mark them 'to remove' at 'cleanup' [at end of Dispatcher event]), not elements of Lua table local items = tile:getItems(). This table is not modified in Lua, only after :remove() on item it points to Item* that is 'to be removed' (or is really removed from RAM, in rare cases).
It would be bugged, if it was like [without using variable to keep list]:
Lua:
                        for i = 1, #tile:getItems() do
                            local checkitem = tile:getItems()[i]
it would skip 1 item after every removed item.

Variable local item is useless. It can be:
Lua:
                    if items then
                        for i = 1, #items do
                            local checkitem = items[i]
                            if checkitem:getId() == 18472 then
                                checkitem:remove()
                            end
                        end
                    end
 
It's removing Item* objects from game (only mark them 'to remove' at 'cleanup' [at end of Dispatcher event]), not elements of Lua table local items = tile:getItems(). This table is not modified in Lua, only after :remove() on item it points to Item* that is 'to be removed' (or is really removed from RAM, in rare cases).
It would be bugged, if it was like [without using variable to keep list]:
Lua:
                        for i = 1, #tile:getItems() do
                            local checkitem = tile:getItems()[i]
it would skip 1 item after every removed item.

Variable local item is useless. It can be:
Lua:
                    if items then
                        for i = 1, #items do
                            local checkitem = items[i]
                            if checkitem:getId() == 18472 then
                                checkitem:remove()
                            end
                        end
                    end
Yeah true. I'm still used to a lot of 0.4 stuff, and it's carried over.
I'd always loop through the tile using stackpos.. and it was super annoying. 😂
 
@gesior @Xikini
Are you able to suggest where to look for, having only that screenshot I posted earlier? Or give a hint how to get more detailed information about the crash, if something more detailed is even possible?

I'll be slowly eliminating another possible reasons I think of, based on the debug, but it will be slow, I would appreciane any hints.

PS. Removed decayto from reward bag, server crashed anyway. Missed shot.
 
Back
Top