• 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+ Wall torches don't save their stat

jackl90

Member
Joined
Jul 25, 2017
Messages
249
Reaction score
12
Is there any way to save lamps or torches inside houses? I tried to add tile house to the walls, but it didn't work.
 
Solution
Alright, I got tired of having to back and forth slowly on here so I decided to just put it on my server to fix it. This seems to work for me just fine and i tested 2 types of lamps and different combinations to make sure it works.
Replace your lib file with this and it should be fine (remember to replace lampTransformIds with your own ids at the top of the script).
Lua:
wallLamps = {}
lampTransformIds, reverseLampTransformIds = { -- used for direct access to flip ids
    [3947] = 3948,
    [2039] = 2040
}, {}

for k, v in pairs(lampTransformIds) do
    reverseLampTransformIds[v] = k
end

function table.size(t)
    local size = 0
    for k, v in pairs(t) do
        size = size + 1
    end
    return size
end

function serialize(s)
    local...
When you use a wall lamp inside house and turn on the lamp for example, after server save, lamp back to turn off
Always have a way to do rs
You can try:
When server start, the map is loaded with lamp state in .otbm file. You can do a script to execute before server close and search all map for itens with itemID equal to lamp in On/Off state and store the info(position and state) to when server start, get lamp in positions and transform to stored state.
Give a try 😉
 
Always have a way to do rs
You can try:
When server start, the map is loaded with lamp state in .otbm file. You can do a script to execute before server close and search all map for itens with itemID equal to lamp in On/Off state and store the info(position and state) to when server start, get lamp in positions and transform to stored state.
Give a try 😉

Good idea, but idk how to do this script :/
 
Good idea, but idk how to do this script :/
Try this:

In your lib folder create the file: wall_lamps.lua and put this inside:
* Im using Lib folder like 0.4 By Zbizu *
Lua:
local startPos = {x = 0, y = 0, z = 7} --X, Y and Z values where will start searching for Lamps
local finalPos = {x = 50, y = 50, z = 7} --X, Y and Z values where will finish searching for Lamps
local idLampOn = 2051 --ItemId of Lamp in ON state
local idLampOff = 2050 --ItemId of Lamp in OFF state
local filePath = "lampsInMap.txt" --Full Path where .txt file will be saved with lamp states info

--Check all lamps in area and save in file the state
function saveWallLamps()
    local lampsInMap = {} --Initialize a empty table that will recive the info of lamps in map
   
    for _posZ = startPos.z, finalPos.z do --For startPosition Z to finalPosition Z
        for _posY = startPos.y, finalPos.y do --For startPosition Y to finalPosition Y
            for _posX = startPos.x, finalPos.x do--For startPosition X to finalPosition X
                local currentPos = Position({x = _posX, y = _posY, z = _posZ})
                local currentTile = Tile(currentPos) --Get tile on searched position X Y Z
                if currentTile then --If find a tile in position
                    local lampOn = currentTile:getItemById(idLampOn)  --try get item with ID of LampOn in currentTile
                    local lampOff = currentTile:getItemById(idLampOff)    --try get item with ID of LampOff in currentTile
                    if lampOn ~= nil then --If have item with id of LampOn in tile
                        table.insert(lampsInMap, {lampPos = currentTile:getPosition(), lampId = idLampOn}) --insert the position and state in table
                    elseif lampOff ~= nil then --Else If have item with id of LampOff in tile
                        table.insert(lampsInMap, {lampPos = currentTile:getPosition(), lampId = idLampOff})--insert the position and state in table
                    end
                end
            end
        end
    end
   
    if #lampsInMap > 0 then --If count of lamp infos in Table is more than 1
        local tableInStringFormat = dumpTable(lampsInMap) --turn table to string (don't sure if needed)
        local file = io.open(filePath, "w") --Open the file in FilePath with "w"(write) mode
        file:write(tableInStringFormat) --Write all table to file
        file:close() --Close the file
    end
   
    return true
end

--read file with lamps data and transform to stored state
function loadWallLamps()
    local file = io.open(filePath, "r") --Open the file in FilePath with "r"(read) mode
    if not file then --If file don't exist
        print("[ERRO] loadWallLamps FILE not found.") --Print erro in console
        return false --exit
    end
    local wallLampsData = file:read("*all")--Get all data from file and save in a variable
    file:close() --close the file
   
    local wallLampsTable = loadstring("return "..wallLampsData)() --transform the string variable in table
   
    if #wallLampsTable > 0 then --if have 1 or more index in table
        for i = 1, #wallLampsTable do -- for 1 index to all index in table
            local targetTile = Tile(wallLampsTable[i].lampPos) --get tile with position stored in table
            local targetLampOn = targetTile:getItemById(idLampOn) --try get a lamp on ON state in targetTile
            local targetLampOff = targetTile:getItemById(idLampOff) --try get a lamp on OFF state in targetTile
            local lampId = wallLampsTable[i].lampId --get the id of lamp stored in table(when server got closed)
            if targetLampOn ~= nil and targetLampOn.itemId ~= lampId then --if exist lamp with ON state in tile and the ID is ~= from stored in table
                targetLampOn:transform(lampId) --transform the item to stored lampId
            elseif targetLampOff ~= nil and targetLampOff.itemId ~= lampId then --if exist lamp with OFF state in tile and the ID is ~= from stored in table
                targetLampOff:transform(lampId)--transform the item to stored lampId
            end
        end
    end
   
    return true
end

--transform a table in string
function dumpTable(targetTable)
   if type(targetTable) == 'table' then
      local s = '{ '
      for k,v in pairs(targetTable) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dumpTable(v) .. ','
      end
      return s .. '} '
   else
      return tostring(targetTable)
   end
end

In data\globalevents\scripts\serversave.lua and change the serverSave function to:
Lua:
local function serverSave()
    if shutdownAtServerSave then
        Game.setGameState(GAME_STATE_SHUTDOWN)
        saveWallLamps()
    else
        Game.setGameState(GAME_STATE_CLOSED)
        saveWallLamps()

        if cleanMapAtServerSave then
            cleanMap()
        end

        Game.setGameState(GAME_STATE_NORMAL)
    end
end


In data\globalevents\scripts\startup.lua put this above last end:
Lua:
loadWallLamps()

I choosed to save as .txt file, but u can save how u want, just make sure to read back in right mode.

Do all this changes with the server closed, and just try. Worked nice here, im using TFS 1.2.
Remember to change the config in wall_lamps.lua on lib folder.
In large areas, this function will take some time to save the lamps, be carefull when you will use. I tested in 1000x1000x1 area and tooked ~1min of freezed server! HIGHLY recommend only use when server is in closed state.

I tryed to ident the code to be easy to read. Try learn how i did this.
If someone find a better way to do or improve, i will be glad if show me.
I Hope works.
 
This would have much better performance using an action script rather than iterating the entire map looking for the lamps.

In a lib file:
Lua:
wallLamps = {}
lampTransformIds, reverseLampTransformIds = { -- used for direct access to flip ids
    [1111] = 2222
}, {}

for k, v in pairs(lampTransformIds) do
    reverseLampTransformIds[v] = k
end

function table.size(t)
    local size = 0
    for k, v in pairs(t) do
        size = size + 1
    end
    return size
end

function serialize(s)
    local isTable = type(s) == 'table'
    local ret = {(isTable and "{" or nil)}
    local function doSerialize(s)
        if isTable then
            local size = table.size(s)
            local index = 0
            for k, v in pairs(s) do
                index = index + 1
                local key = (type(k) == 'string') and '"'..k..'"' or k
                local val = (type(v) == 'string') and '"'..v..'"' or v
                local comma = ((index < size) and ', ' or '')
                if type(v) == 'table' then
                    ret[#ret+1] = ('[%s] = {'):format(key)
                    doSerialize(v)
                    ret[#ret+1] = ('}%s'):format(comma)
                else
                    ret[#ret+1] = ('[%s] = %s%s'):format(key, val, comma)
                end
            end
        else
            ret[#ret+1] = s
        end
    end
    doSerialize(s)
    return (table.concat(ret) .. (isTable and "}" or ""))
end

function unserialize(str)
    local f = loadstring("return ".. str)
    return f and f() or nil
end

function serializePos(pos)
    return string.format('Position(%d, %d, %d)', pos.x, pos.y, pos.z)
end

function unserializePos(s)
    return Position(s:match("Position%((%d+), (%d+), (%d+)%)"))
end

function dumpLampStates()
    local file = io.open('lamp_states.lua', 'w')
    if file then
        file:write(serialize(wallLamps))
    end
end

function loadLampStates()
    local file = io.open('lamp_states.lua')
    wallLamps = unserialize(file:read('*a'))
    for serializedPos, state in pairs(wallLamps) do
        local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
        Tile(unserializePos(serializedPos)):getItemById(state):transform(flippedState)
    end
end

loadLampStates()

Action script (register each wall id to actions.xml, and add it to the lampTransformIds table in the lib file)
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local transformId = lampTransformIds[item:getId()] or reverseLampTransformIds[item:getId()]
    item:transform(transformId)
    wallLamps[serializePos(toPosition)] = transformId -- update lamp state
    dumpLampStates()
    return true
end
 
This would have much better performance using an action script rather than iterating the entire map looking for the lamps.

In a lib file:
Lua:
wallLamps = {}
lampTransformIds, reverseLampTransformIds = { -- used for direct access to flip ids
    [1111] = 2222
}, {}

for k, v in pairs(lampTransformIds) do
    reverseLampTransformIds[v] = k
end

function table.size(t)
    local size = 0
    for k, v in pairs(t) do
        size = size + 1
    end
    return size
end

function serialize(s)
    local isTable = type(s) == 'table'
    local ret = {(isTable and "{" or nil)}
    local function doSerialize(s)
        if isTable then
            local size = table.size(s)
            local index = 0
            for k, v in pairs(s) do
                index = index + 1
                local key = (type(k) == 'string') and '"'..k..'"' or k
                local val = (type(v) == 'string') and '"'..v..'"' or v
                local comma = ((index < size) and ', ' or '')
                if type(v) == 'table' then
                    ret[#ret+1] = ('[%s] = {'):format(key)
                    doSerialize(v)
                    ret[#ret+1] = ('}%s'):format(comma)
                else
                    ret[#ret+1] = ('[%s] = %s%s'):format(key, val, comma)
                end
            end
        else
            ret[#ret+1] = s
        end
    end
    doSerialize(s)
    return (table.concat(ret) .. (isTable and "}" or ""))
end

function unserialize(str)
    local f = loadstring("return ".. str)
    return f and f() or nil
end

function serializePos(pos)
    return string.format('Position(%d, %d, %d)', pos.x, pos.y, pos.z)
end

function unserializePos(s)
    return Position(s:match("Position%((%d+), (%d+), (%d+)%)"))
end

function dumpLampStates()
    local file = io.open('lamp_states.lua', 'w')
    if file then
        file:write(serialize(wallLamps))
    end
end

function loadLampStates()
    local file = io.open('lamp_states.lua')
    wallLamps = unserialize(file:read('*a'))
    for serializedPos, state in pairs(wallLamps) do
        local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
        Tile(unserializePos(serializedPos)):getItemById(state):transform(flippedState)
    end
end

loadLampStates()

Action script (register each wall id to actions.xml, and add it to the lampTransformIds table in the lib file)
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local transformId = lampTransformIds[item:getId()] or reverseLampTransformIds[item:getId()]
    item:transform(transformId)
    wallLamps[serializePos(toPosition)] = transformId -- update lamp state
    dumpLampStates()
    return true
end

First, thanks so much guys for help, and find a solution for my case...

Wel..
i created a file named lamp_states.lua located data/lib
in my file lib.lua i put there dofile('data/lib/lamp_states.lua')

in actions/scripts/others folder i created a file lamp_states.lua
and I registered
actions.xml
XML:
    <action itemid="5907" script="others/lamp_states.lua" />
    <action itemid="5908" script="others/lamp_states.lua" />

i got this console error

Code:
Lua Script Error: [Main Interface]
data/global.lua
cannot open data/lib/lamp_states.lua: No such file or directory
stack traceback:
        [C]: at 0x7ff792cb9200
        [C]: in function 'dofile'
        data/lib/lib.lua:3: in main chunk
        [C]: in function 'dofile'
        data/global.lua:1: in main chunk
[Warning - ScriptingManager::loadScriptSystems] Can not load data/global.lua
 
You sure you spelled the lib file correctly?
Did you put it in data/lib/core, or data/lib? Because the dofile checks data/lib, not data/lib/core.
Anyways I found an error, replace the entire loadLampStates with this instead:
Lua:
function loadLampStates()
    local file = io.open('lamp_states.lua')
    if file then
        wallLamps = unserialize(file:read('*a'))
        for serializedPos, state in pairs(wallLamps) do
            local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
            Tile(unserializePos(serializedPos)):getItemById(state):transform(flippedState)
        end
    else
        io.open('lamp_states.lua', 'w'):close() -- create file if it doesn't exist
    end
end
 
Last edited:
now 0 errors console, i tested 3 different locations, a file was created in the main folder

Lua:
{["Position(32354, 32198, 7)"] = 5908} ["Position(32358, 32198, 7)"] = 5907} ["Position(32358, 32198, 7)"] = 5907}
but after save server, everything is as before, i need do something on startup.lua?
 
Change this (inside dumpLampStates)
Lua:
local file = io.open('lamp_states.lua', 'w')
To
Lua:
local file = io.open('lamp_states.lua', 'w+')
 
Well, in this Position: [X: 32358] [Y: 32198] [Z: 7]. have a lamp, ItemID: [5908]., i used this lamp and trasform to ItemID: [5907]

the file in the main folder show this
{["Position(32358, 32198, 7)"] = 5907}
 
Replace this function, restart server, and show me the output:
Lua:
function loadLampStates()
    local file = io.open('lamp_states.lua')
    print('Loading lamp states')
    if file then
        wallLamps = unserialize(file:read('*a'))
        print('Previous data stored, size: '.. #wallLamps)
        for serializedPos, state in pairs(wallLamps) do
            local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
            local position = unserializePos(serializedPos)
            print(string.format('Restoring previous state of lamp at %s to id %d', position, flippedState))
            Tile(position):getItemById(state):transform(flippedState)
        end
    else
        print('Previous data not found, creating an empty file (no data will be loaded).')
        io.open('lamp_states.lua', 'w'):close() -- create file if it doesn't exist
    end
end
]
 
This would have much better performance using an action script rather than iterating the entire map looking for the lamps.

In a lib file:
Lua:
wallLamps = {}
lampTransformIds, reverseLampTransformIds = { -- used for direct access to flip ids
    [1111] = 2222
}, {}

for k, v in pairs(lampTransformIds) do
    reverseLampTransformIds[v] = k
end

function table.size(t)
    local size = 0
    for k, v in pairs(t) do
        size = size + 1
    end
    return size
end

function serialize(s)
    local isTable = type(s) == 'table'
    local ret = {(isTable and "{" or nil)}
    local function doSerialize(s)
        if isTable then
            local size = table.size(s)
            local index = 0
            for k, v in pairs(s) do
                index = index + 1
                local key = (type(k) == 'string') and '"'..k..'"' or k
                local val = (type(v) == 'string') and '"'..v..'"' or v
                local comma = ((index < size) and ', ' or '')
                if type(v) == 'table' then
                    ret[#ret+1] = ('[%s] = {'):format(key)
                    doSerialize(v)
                    ret[#ret+1] = ('}%s'):format(comma)
                else
                    ret[#ret+1] = ('[%s] = %s%s'):format(key, val, comma)
                end
            end
        else
            ret[#ret+1] = s
        end
    end
    doSerialize(s)
    return (table.concat(ret) .. (isTable and "}" or ""))
end

function unserialize(str)
    local f = loadstring("return ".. str)
    return f and f() or nil
end

function serializePos(pos)
    return string.format('Position(%d, %d, %d)', pos.x, pos.y, pos.z)
end

function unserializePos(s)
    return Position(s:match("Position%((%d+), (%d+), (%d+)%)"))
end

function dumpLampStates()
    local file = io.open('lamp_states.lua', 'w')
    if file then
        file:write(serialize(wallLamps))
    end
end

function loadLampStates()
    local file = io.open('lamp_states.lua')
    wallLamps = unserialize(file:read('*a'))
    for serializedPos, state in pairs(wallLamps) do
        local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
        Tile(unserializePos(serializedPos)):getItemById(state):transform(flippedState)
    end
end

loadLampStates()

Action script (register each wall id to actions.xml, and add it to the lampTransformIds table in the lib file)
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local transformId = lampTransformIds[item:getId()] or reverseLampTransformIds[item:getId()]
    item:transform(transformId)
    wallLamps[serializePos(toPosition)] = transformId -- update lamp state
    dumpLampStates()
    return true
end
Yeah, this is much better! Would never think of using it that way, thanks!
 
Replace this function, restart server, and show me the output:
Lua:
function loadLampStates()
    local file = io.open('lamp_states.lua')
    print('Loading lamp states')
    if file then
        wallLamps = unserialize(file:read('*a'))
        print('Previous data stored, size: '.. #wallLamps)
        for serializedPos, state in pairs(wallLamps) do
            local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
            local position = unserializePos(serializedPos)
            print(string.format('Restoring previous state of lamp at %s to id %d', position, flippedState))
            Tile(position):getItemById(state):transform(flippedState)
        end
    else
        print('Previous data not found, creating an empty file (no data will be loaded).')
        io.open('lamp_states.lua', 'w'):close() -- create file if it doesn't exist
    end
end
]
now nothing appear on output file.
 
no prints on console too,

all script lib here

Lua:
wallLamps = {}
lampTransformIds, reverseLampTransformIds = { -- used for direct access to flip ids
    [5907] = 5908
}, {}

for k, v in pairs(lampTransformIds) do
    reverseLampTransformIds[v] = k
end

function table.size(t)
    local size = 0
    for k, v in pairs(t) do
        size = size + 1
    end
    return size
end

function serialize(s)
    local isTable = type(s) == 'table'
    local ret = {(isTable and "{" or nil)}
    local function doSerialize(s)
        if isTable then
            local size = table.size(s)
            local index = 0
            for k, v in pairs(s) do
                index = index + 1
                local key = (type(k) == 'string') and '"'..k..'"' or k
                local val = (type(v) == 'string') and '"'..v..'"' or v
                local comma = ((index < size) and ', ' or '')
                if type(v) == 'table' then
                    ret[#ret+1] = ('[%s] = {'):format(key)
                    doSerialize(v)
                    ret[#ret+1] = ('}%s'):format(comma)
                else
                    ret[#ret+1] = ('[%s] = %s%s'):format(key, val, comma)
                end
            end
        else
            ret[#ret+1] = s
        end
    end
    doSerialize(s)
    return (table.concat(ret) .. (isTable and "}" or ""))
end

function unserialize(str)
    local f = loadstring("return ".. str)
    return f and f() or nil
end

function serializePos(pos)
    return string.format('Position(%d, %d, %d)', pos.x, pos.y, pos.z)
end

function unserializePos(s)
    return Position(s:match("Position%((%d+), (%d+), (%d+)%)"))
end

function dumpLampStates()
    local file = io.open('lamp_states.lua', 'w+')
    if file then
        file:write(serialize(wallLamps))
    end
end

function loadLampStates()
    local file = io.open('lamp_states.lua')
    print('Loading lamp states')
    if file then
        wallLamps = unserialize(file:read('*a'))
        print('Previous data stored, size: '.. #wallLamps)
        for serializedPos, state in pairs(wallLamps) do
            local flippedState = lampTransformIds[state] or reverseLampTransformIds[state]
            local position = unserializePos(serializedPos)
            print(string.format('Restoring previous state of lamp at %s to id %d', position, flippedState))
            Tile(position):getItemById(state):transform(flippedState)
        end
    else
        print('Previous data not found, creating an empty file (no data will be loaded).')
        io.open('lamp_states.lua', 'w'):close() -- create file if it doesn't exist
    end
end
 
Back
Top