• 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+ [SOLVED] Can you make a spawned NPC stay in an owned house after restart?

Slatera

Active Member
Joined
Mar 6, 2009
Messages
521
Reaction score
46
Location
Sweden
Hello there, I've basically been working for 9+ hours trying to create my own version of a hireling, and I got it working completely except for one thing, the NPC spawned through my script obviously does not stay in the house after a restart of the server.

So I have a question, is there anyone who could send me in the right direction of what I need to do to make it so that NPCs spawned through "createNpc" scripts stay inside an owned house after a restart?

Using TFS 1.4, if that helps.
 
Solution
Hello there, I've basically been working for 9+ hours trying to create my own version of a hireling, and I got it working completely except for one thing, the NPC spawned through my script obviously does not stay in the house after a restart of the server.

So I have a question, is there anyone who could send me in the right direction of what I need to do to make it so that NPCs spawned through "createNpc" scripts stay inside an owned house after a restart?

Using TFS 1.4, if that helps.
data/scripts/saveNpcHouse.lua
Lua:
local NPCFile = "data/AutoHouseNPCs.lua"

local globalEvent = GlobalEvent("AutoHouseNPCs")

function globalEvent.onStartup()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs...

Idk if it helps
but if im not the masterPosition thing will let npc stay in the house instead of moving out anytime
 
Add a globalevent with startup function and depending on how you spawn the hirelings in the first place, save the coords, outfits etc in this startup script. Post your script that spawns the hireling and I could help out within a day or two.
 
Hello there, I've basically been working for 9+ hours trying to create my own version of a hireling, and I got it working completely except for one thing, the NPC spawned through my script obviously does not stay in the house after a restart of the server.

So I have a question, is there anyone who could send me in the right direction of what I need to do to make it so that NPCs spawned through "createNpc" scripts stay inside an owned house after a restart?

Using TFS 1.4, if that helps.
data/scripts/saveNpcHouse.lua
Lua:
local NPCFile = "data/AutoHouseNPCs.lua"

local globalEvent = GlobalEvent("AutoHouseNPCs")

function globalEvent.onStartup()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for _, npcInfo in pairs(npcs) do
                local npc = Game.createNpc(npcInfo.name, npcInfo.position)
                if npc then
                    npc:setMasterPos(npc:getPosition())
                    npcInfo.id = npc:getId()
                end
            end

            npcFile = io.open(NPCFile, "w+")
            if npcFile then
                npcFile:write(table.serialize(npcs))
                npcFile:close()
            end
        end
    end
    return true
end

globalEvent:register()

function Npc:saveInHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            table.insert(npcs, {name = self:getName(), position = self:getPosition(), id = self:getId()})
        else
            npcs = { {name = self:getName(), position = self:getPosition(), id = self:getId()} }
        end
        npcFile = io.open(NPCFile, "w+")
        npcFile:write(table.serialize(npcs))
        npcFile:close()
    end
end

function Npc:deleteFromHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for i = 1, #npcs do
                if npcs[i].id == self:getId() then
                    table.remove(npcs, i)
                    break
                end
            end
            npcFile = io.open(NPCFile, "w+")
            npcFile:write(table.serialize(npcs))
            npcFile:close()
            self:remove()
        end
    end
end
in the File data/AutoHouseNPCs.lua The npc will be saved and these will be loaded and spawned when the service starts.
How is this used?
Example:
data/scripts/talkactions/createNpcHouse.lua
Lua:
local talk = TalkAction("/createNpcHouse")

function talk.onSay(player, words, param)
    local position = player:getPosition()
    local npc = Game.createNpc(param, position)
    if npc then
        npc:setMasterPos(position)
        position:sendMagicEffect(CONST_ME_MAGIC_RED)
        npc:saveInHouse()
    else
        player:sendCancelMessage("There is not enough room.")
        position:sendMagicEffect(CONST_ME_POFF)
    end
    return false
end

talk:accountType(ACCOUNT_TYPE_GOD)
talk:separator(" ")
talk:register()

To eliminate an npc you use the method: deleteFromHouse
Lua:
npc:deleteFromHouse()
If you kill the npc by other means it will respawn when the server starts.

Note: I don't know if this method of saving npc to a file is the best, but it's a quick option you can try.
 
Solution
Thank you so much for the help! But it doesn't matter what I do to tinker with the code, I get this error:

Lua:
Lua Script Error: [Scripts Interface]
D:\Misc\Random Shit\Operation Svitjod\Brisingr\data\scripts\talkactions\createNpcHouse.lua:callback
data/lib/core/tables.lua:84: Can not serialize a table that has a metatable associated with it.
stack traceback:
        [C]: at 0x7ff7bc21b9d0
        [C]: in function 'error'
        data/lib/core/tables.lua:84: in function 'serialize'
        data/lib/core/tables.lua:94: in function 'serialize'
        data/lib/core/tables.lua:94: in function 'serialize'
        ...Operation Svitjod\Brisingr\data\scripts\saveNpcHouse.lua:42: in function 'saveInHouse'
        ...jod\Brisingr\data\scripts\talkactions\createNpcHouse.lua:9: in function <...jod\Brisingr\data\scripts\talkactions\createNpcHouse.lua:3>
 
data/scripts/saveNpcHouse.lua
Lua:
local NPCFile = "data/AutoHouseNPCs.lua"

local globalEvent = GlobalEvent("AutoHouseNPCs")

function globalEvent.onStartup()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for _, npcInfo in pairs(npcs) do
                local npc = Game.createNpc(npcInfo.name, npcInfo.position)
                if npc then
                    npc:setMasterPos(npc:getPosition())
                    npcInfo.id = npc:getId()
                end
            end

            npcFile = io.open(NPCFile, "w+")
            if npcFile then
                npcFile:write(table.serialize(npcs))
                npcFile:close()
            end
        end
    end
    return true
end

globalEvent:register()

function Npc:saveInHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            table.insert(npcs, {name = self:getName(), position = self:getPosition(), id = self:getId()})
        else
            npcs = { {name = self:getName(), position = self:getPosition(), id = self:getId()} }
        end
        npcFile = io.open(NPCFile, "w+")
        npcFile:write(table.serialize(npcs))
        npcFile:close()
    end
end

function Npc:deleteFromHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for i = 1, #npcs do
                if npcs[i].id == self:getId() then
                    table.remove(npcs, i)
                    break
                end
            end
            npcFile = io.open(NPCFile, "w+")
            npcFile:write(table.serialize(npcs))
            npcFile:close()
            self:remove()
        end
    end
end
in the File data/AutoHouseNPCs.lua The npc will be saved and these will be loaded and spawned when the service starts.
How is this used?
Example:
data/scripts/talkactions/createNpcHouse.lua
Lua:
local talk = TalkAction("/createNpcHouse")

function talk.onSay(player, words, param)
    local position = player:getPosition()
    local npc = Game.createNpc(param, position)
    if npc then
        npc:setMasterPos(position)
        position:sendMagicEffect(CONST_ME_MAGIC_RED)
        npc:saveInHouse()
    else
        player:sendCancelMessage("There is not enough room.")
        position:sendMagicEffect(CONST_ME_POFF)
    end
    return false
end

talk:accountType(ACCOUNT_TYPE_GOD)
talk:separator(" ")
talk:register()

To eliminate an npc you use the method: deleteFromHouse
Lua:
npc:deleteFromHouse()
If you kill the npc by other means it will respawn when the server starts.

Note: I don't know if this method of saving npc to a file is the best, but it's a quick option you can try.

god
 
data/scripts/saveNpcHouse.lua
Lua:
local NPCFile = "data/AutoHouseNPCs.lua"

local globalEvent = GlobalEvent("AutoHouseNPCs")

function globalEvent.onStartup()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for _, npcInfo in pairs(npcs) do
                local npc = Game.createNpc(npcInfo.name, npcInfo.position)
                if npc then
                    npc:setMasterPos(npc:getPosition())
                    npcInfo.id = npc:getId()
                end
            end

            npcFile = io.open(NPCFile, "w+")
            if npcFile then
                npcFile:write(table.serialize(npcs))
                npcFile:close()
            end
        end
    end
    return true
end

globalEvent:register()

function Npc:saveInHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            table.insert(npcs, {name = self:getName(), position = self:getPosition(), id = self:getId()})
        else
            npcs = { {name = self:getName(), position = self:getPosition(), id = self:getId()} }
        end
        npcFile = io.open(NPCFile, "w+")
        npcFile:write(table.serialize(npcs))
        npcFile:close()
    end
end

function Npc:deleteFromHouse()
    local npcFile = io.open(NPCFile, "r")
    if npcFile then
        local npcs = table.unserialize(npcFile:read("*all"))
        npcFile:close()
        if npcs then
            for i = 1, #npcs do
                if npcs[i].id == self:getId() then
                    table.remove(npcs, i)
                    break
                end
            end
            npcFile = io.open(NPCFile, "w+")
            npcFile:write(table.serialize(npcs))
            npcFile:close()
            self:remove()
        end
    end
end
in the File data/AutoHouseNPCs.lua The npc will be saved and these will be loaded and spawned when the service starts.
How is this used?
Example:
data/scripts/talkactions/createNpcHouse.lua
Lua:
local talk = TalkAction("/createNpcHouse")

function talk.onSay(player, words, param)
    local position = player:getPosition()
    local npc = Game.createNpc(param, position)
    if npc then
        npc:setMasterPos(position)
        position:sendMagicEffect(CONST_ME_MAGIC_RED)
        npc:saveInHouse()
    else
        player:sendCancelMessage("There is not enough room.")
        position:sendMagicEffect(CONST_ME_POFF)
    end
    return false
end

talk:accountType(ACCOUNT_TYPE_GOD)
talk:separator(" ")
talk:register()

To eliminate an npc you use the method: deleteFromHouse
Lua:
npc:deleteFromHouse()
If you kill the npc by other means it will respawn when the server starts.

Note: I don't know if this method of saving npc to a file is the best, but it's a quick option you can try.
Im curious why not database, anyway good job.
 
Hi there!
Can any of you share some informations about function table.unserialize and table.serialize? What's your implementation of these functions? I cannot find working function in TFS 1.5 downgrade (in global or data/lib/core).
Thanks in advance!
 
Hi there!
Can any of you share some informations about function table.unserialize and table.serialize? What's your implementation of these functions? I cannot find working function in TFS 1.5 downgrade (in global or data/lib/core).
Thanks in advance!
Those two methods convert a Lua table into a string, so that this string can be stored somewhere, like a file on a PC. Then we can retrieve this string and rebuild the same Lua table, and in this way we are simulating a type of database.

These default functions do not exist in TFS, but you can add them yourself.

global.lua
Lua:
function table.serialize(self, recursiveTable)
    local t = type(self)
    if t == 'nil' then
        return 'nil'
    elseif t == 'string' then
        return string.format('%q', self)
    elseif t == 'number' then
        return tostring(self)
    elseif t == 'boolean' then
        return self and 'true' or 'false'
    elseif t == 'table' then
        recursiveTable = recursiveTable or {}
        if table.contains(recursiveTable, self) then
            error("Can not serialize recursive tables.")
        end
        recursiveTable[#recursiveTable + 1] = self
        local stringTable = {"{"}
        for key, value in pairs(self) do
            stringTable[#stringTable + 1] = string.format("[%s]=%s,", table.serialize(key), table.serialize(value))
        end
        return string.format("%s}", table.concat(stringTable))
    elseif (getmetatable(self)) then
        error('Can not serialize a table that has a metatable associated with it.')
    else
        error("Can not serialize value of type '" .. t .. "'.")
    end
end

function table.unserialize(str)
    local func = loadstring("return " .. str)
    if type(func) == "function" then
        return func()
    end
end

There are many implementations, but this is one of them.
 
Only to know, by default its possible to create npcs inside house? 1.5 Nekiro 7.72. Idk why but I can summon a npc inside or Im doing something wrong
sure, you should only force it if necessary: Game.createNpc(param, position, extended, force)
 
@Sarah Wesker Thanks a lot, I'll try. I had something similar but deprecated function loadstring confused me.
@mano368 I can confirm, spawning npc with call like that:
Lua:
Game.createNpc("Npc Name", position, false, true)
creates npc in house and he will stay in place (I prefer hirelings not walking around).


(TFS 1.5 downgrade to 8.6)
 
how to make npc save on "shutdown"?
I used function globalEvent.onShutdown()
but it works strangely because this function is executed at the server startup.
 
Back
Top