Serpens Posted October 6, 2016 Share Posted October 6, 2016 (edited) Why is it not possible to save an instance in an component? I made a component that is meant to save and load some imponrtant stuff and is assigned to the world: Spoiler local RememberStuff = Class(function(self, inst) self.inst = inst self.stuff1 = {} self.stuff2 = {} self.stuff3 = {} self.stuff4 = {} self.stuff5 = {} self.stuff6 = {} self.stuff7 = {} self.stuff8 = {} self.stuff9 = {} self.stuff10 = {} end) function RememberStuff:OnSave() return { stuff1 = self.stuff1, stuff2 = self.stuff2, stuff3 = self.stuff3, stuff4 = self.stuff4, stuff5 = self.stuff5, stuff6 = self.stuff6, stuff7 = self.stuff7, stuff8 = self.stuff8, stuff9 = self.stuff9, stuff10 = self.stuff10, } end function RememberStuff:OnLoad(data) self.stuff1 = data and data.stuff1 or {} self.stuff2 = data and data.stuff2 or {} self.stuff3 = data and data.stuff3 or {} self.stuff4 = data and data.stuff4 or {} self.stuff5 = data and data.stuff5 or {} self.stuff6 = data and data.stuff6 or {} self.stuff7 = data and data.stuff7 or {} self.stuff8 = data and data.stuff8 or {} self.stuff9 = data and data.stuff9 or {} self.stuff10 = data and data.stuff10 or {} end return RememberStuff But as soon as I save any instance in one of the stuff variables, the game will crash as soon as the game is saved (after one day or when leaving the game) with the error: [string "scripts/dumper.lua"]:112: Cannot dump userdata (Pathfinder (1562C950) - unknown) In this case I just made directly in the component function: self.stuff2 = inst Spoiler [00:00:34]: [string "scripts/dumper.lua"]:112: Cannot dump userdata (Pathfinder (1562C950) - unknown) LUA ERROR stack traceback: =[C]:-1 in (global) error (C) <-1--1> scripts/dumper.lua:112 in () ? (Lua) <98-113> value = Pathfinder (1562C950) var = nil i = 8 =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> value = 100034 - world (valid:true) numidx = 1 key = Pathfinder val = Pathfinder (1562C950) =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> value = table: 3D9DE9E0 numidx = 1 key = stuff2 val = 100034 - world (valid:true) =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> value = table: 3D9DE1E8 numidx = 1 key = rememberstuff val = table: 3D9DE9E0 =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> value = table: 3D9DD040 numidx = 1 key = persistdata val = table: 3D9DE1E8 =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> value = table: 3D38D460 numidx = 1 key = map val = table: 3D9DD040 =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:225 in (global) DataDumper (Lua) <77-245> value = table: 3D38D460 varname = return fastmode = true ident = nil defined = table: 3D9DF5E8 dumplua = function - scripts/dumper.lua:214 string_format = function - =[C]:-1 type = function - =[C]:-1 string_dump = function - =[C]:-1 string_rep = function - =[C]:-1 tostring = function - =[C]:-1 pairs = function - =[C]:-1 table_concat = function - =[C]:-1 keycache = table: 3D9DF548 strvalcache = table: 3D9DF570 out = table: 3D9DF610 closure_cnt = 0 fcts = table: 3D9DFBD8 test_defined = function - scripts/dumper.lua:116 make_key = function - scripts/dumper.lua:127 scripts/mainfunctions.lua:653 in (global) SaveGame (Lua) <535-664> isshutdown = nil cb = nil save = table: 3D38D460 nument = 3027 saved_ents = table: 3D38D4D8 references = table: 3D38D500 new_refs = nil ground = 100034 - world (valid:true) PRETTY_PRINT = false scripts/saveindex.lua:339 in (method) SaveCurrent (Lua) <328-340> self = data = table: 1664F178 current_slot = 3 onsavedcb = nil isshutdown = nil slotdata = table: 1664F330 scripts/components/autosaver.lua:85 in (field) fn (Lua) <59-87> inst = 100037 - forest_network (valid:true) taskid = 1 snapshot = nil scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207> self = running = table: 33E9FEC0 waitingfortick = table: 33EA00F0 tasks = table: 33EA0258 waking = table: 3D38D0C8 attime = table: 33E9FF10 hibernating = table: 33EA0028 tick = 116 k = PERIODIC 100037: 1.000000 v = true already_dead = false scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377> tick = 116 scripts/update.lua:166 in () ? (Lua) <150-223> dt = 0.033333335071802 tick = 116 i = 116 [00:00:34]: [string "scripts/dumper.lua"]:112: Cannot dump userdata (Pathfinder (1562C950) - unknown) LUA ERROR stack traceback: =[C]:-1 in (global) error (C) <-1--1> scripts/dumper.lua:112 in () ? (Lua) <98-113> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:151 in () ? (Lua) <141-159> =(tail call):-1 in () (tail) <-1--1> scripts/dumper.lua:225 in (global) DataDumper (Lua) <77-245> scripts/mainfunctions.lua:653 in (global) SaveGame (Lua) <535-664> scripts/saveindex.lua:339 in (method) SaveCurrent (Lua) <328-340> scripts/components/autosaver.lua:85 in (field) fn (Lua) <59-87> scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207> scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377> scripts/update.lua:166 in () ? (Lua) <150-223> I already had such error messages when I tried to use this "dump" command myself for arrays. So I just made my own dump-like function. But in this case I can't use my own dump function...So what to do now? How can I save instances in an component? Or do I have to report this as a bug, cause the dump function is stupid ? Edited October 6, 2016 by Serpens Link to comment Share on other sites More sharing options...
DarkXero Posted October 7, 2016 Share Posted October 7, 2016 You almost never save entities. Game does it if they persist. You either do it like components do (example, herd): function Herd:OnSave() local data = {} for k, v in pairs(self.members) do if data.members == nil then data.members = { k.GUID } else table.insert(data.members, k.GUID) end end return data, data.members end function Herd:LoadPostPass(newents, savedata) if savedata.members ~= nil then for k, v in pairs(savedata.members) do local member = newents[v] if member ~= nil then self:AddMember(member.entity) end end end end They save the GUIDs. LoadPostPass runs after the entities and the world are loaded, and the ids assigned. newents holds all the world's loaded entities, so the function gets the entity by using the saved GUID as key. Or like how Wendy does it: local function OnSave(inst, data) if inst.abigail ~= nil then data.abigail = inst.abigail:GetSaveRecord() end end local function OnLoad(inst, data) if data.abigail ~= nil and inst.abigail == nil then local abigail = SpawnSaveRecord(data.abigail) if abigail ~= nil then if inst.migrationpets ~= nil then table.insert(inst.migrationpets, abigail) end abigail.SoundEmitter:PlaySound("dontstarve/common/ghost_spawn") abigail:LinkToPlayer(inst) end end end With GetSaveRecord and SpawnSaveRecord. But really, keep it simple. WHY are you saving THE WORLD (the Pathfinder component)? Absurd. DO NOT save the world yourself. Put the particular data you want to save in a table, and save it, then retrieve it when loading. Link to comment Share on other sites More sharing options...
Serpens Posted October 7, 2016 Author Share Posted October 7, 2016 (edited) The test case to which the log belongs is simply stuff2=inst: Spoiler local RememberStuff = Class(function(self, inst) self.inst = inst self.stuff1 = {} self.stuff2 = inst self.stuff3 = {} self.stuff4 = {} self.stuff5 = {} self.stuff6 = {} self.stuff7 = {} self.stuff8 = {} self.stuff9 = {} self.stuff10 = {} end) function RememberStuff:OnSave() return { stuff1 = self.stuff1, stuff2 = self.stuff2, stuff3 = self.stuff3, stuff4 = self.stuff4, stuff5 = self.stuff5, stuff6 = self.stuff6, stuff7 = self.stuff7, stuff8 = self.stuff8, stuff9 = self.stuff9, stuff10 = self.stuff10, } end function RememberStuff:OnLoad(data) self.stuff1 = data and data.stuff1 or {} self.stuff2 = data and data.stuff2 or {} self.stuff3 = data and data.stuff3 or {} self.stuff4 = data and data.stuff4 or {} self.stuff5 = data and data.stuff5 or {} self.stuff6 = data and data.stuff6 or {} self.stuff7 = data and data.stuff7 or {} self.stuff8 = data and data.stuff8 or {} self.stuff9 = data and data.stuff9 or {} self.stuff10 = data and data.stuff10 or {} end return RememberStuff This is just a test case everyone can easily replicate and where I can be sure, that I made no other mistakes. The main goal is to place at first game start a firepit somewhere and everytime a new player spawns, this firepit gets some fuel. So I have to save this firepit somewhere. I will try this GUID thing, thank you Edited October 7, 2016 by Serpens Link to comment Share on other sites More sharing options...
Serpens Posted October 16, 2016 Author Share Posted October 16, 2016 (edited) On 7.10.2016 at 8:00 PM, DarkXero said: You either do it like components do (example, herd): function Herd:OnSave() local data = {} for k, v in pairs(self.members) do if data.members == nil then data.members = { k.GUID } else table.insert(data.members, k.GUID) end end return data, data.members end function Herd:LoadPostPass(newents, savedata) if savedata.members ~= nil then for k, v in pairs(savedata.members) do local member = newents[v] if member ~= nil then self:AddMember(member.entity) end end end end They save the GUIDs. LoadPostPass runs after the entities and the world are loaded, and the ids assigned. newents holds all the world's loaded entities, so the function gets the entity by using the saved GUID as key. This is now my rememberstuff component: local RememberStuff = Class(function(self, inst) self.inst = inst self.stuff1 = {} self.stuff2 = {} self.stuff3 = {} self.stuff4 = {} self.stuff5 = {} self.stuff6 = {} self.stuff7 = {} self.stuff8 = {} self.stuff9 = {} self.stuff10 = {} end) function RememberStuff:OnSave() local data = {} for i=1,10 do data["stuff"..i] = {} if type(self["stuff"..i])=="table" then for key,thing in pairs(self["stuff"..i]) do if type(thing)=="table" and thing.GUID then -- if it is an entity, only save the GUID! table.insert(data["stuff"..i],thing.GUID) else table.insert(data["stuff"..i],thing) end end end end return data end function RememberStuff:OnLoad(data) self.stuff1 = data and data.stuff1 or {} self.stuff2 = data and data.stuff2 or {} self.stuff3 = data and data.stuff3 or {} self.stuff4 = data and data.stuff4 or {} self.stuff5 = data and data.stuff5 or {} self.stuff6 = data and data.stuff6 or {} self.stuff7 = data and data.stuff7 or {} self.stuff8 = data and data.stuff8 or {} self.stuff9 = data and data.stuff9 or {} self.stuff10 = data and data.stuff10 or {} end function RememberStuff:LoadPostPass(newents, data) for i=1,10 do -- now revert GUIDs back to entities if type(self["stuff"..i])=="table" then for key,thing in pairs(self["stuff"..i]) do -- data was already saved in self in the onload function if type(thing)=="number" and newents[thing]~=nil then -- if thing is GUID, this will be true self["stuff"..i][key] = newents[thing].entity -- replace the GUID with the entity else print("LoadPostPass: type: "..type(thing).." thing: "..tostring(thing).."ent: "..tostring(newents[thing])) end end end end end return RememberStuff Everything works, except getting the old entity with helpf of the GUID. The GUID is saved and loaded succesfully, but newents[thing] is always nil and I don't get why ... The lenght of newents is ~ 3000 so it seems tho be the correct table... Edited October 16, 2016 by Serpens Link to comment Share on other sites More sharing options...
DarkXero Posted October 17, 2016 Share Posted October 17, 2016 LoadPostPass doesn't "load the entities". The game saves and loads the entities. What LoadPostPass does is hook up the old GUIDs so they remain constant. Which you are doing incorrectly because you are forgetting my "stuff_references" table. local RememberStuff = Class(function(self, inst) self.inst = inst self.stuff1 = {} self.stuff2 = {} self.stuff3 = {} self.stuff4 = {} self.stuff5 = {} self.stuff6 = {} self.stuff7 = {} self.stuff8 = {} self.stuff9 = {} self.stuff10 = {} end) function RememberStuff:OnSave() local data = {} local stuff_references = {} for i = 1, 10 do local data_stuff = {} local self_stuff = self["stuff"..i] for key, thing in pairs(self_stuff) do if type(thing) == "table" and thing.GUID then -- we need to pass the GUID references to the game table.insert(stuff_references, thing.GUID) -- save a table with flag and GUID where entity should be table.insert(data_stuff, { is_GUID = true, GUID = thing.GUID }) else table.insert(data_stuff, thing) end end data["stuff"..i] = data_stuff end if next(stuff_references) == nil then -- stuff_references is empty, make it nil stuff_references = nil end return data, stuff_references end function RememberStuff:OnLoad(data) self.stuff1 = data and data.stuff1 or {} self.stuff2 = data and data.stuff2 or {} self.stuff3 = data and data.stuff3 or {} self.stuff4 = data and data.stuff4 or {} self.stuff5 = data and data.stuff5 or {} self.stuff6 = data and data.stuff6 or {} self.stuff7 = data and data.stuff7 or {} self.stuff8 = data and data.stuff8 or {} self.stuff9 = data and data.stuff9 or {} self.stuff10 = data and data.stuff10 or {} end function RememberStuff:LoadPostPass(newents, data) -- OnLoad runs before LoadPostPass -- we saved GUIDs in data -- therefore, stuff1 loaded the tables with { is_GUID, GUID } where there should be an entity instead -- therefore, we just need to swap for i = 1, 10 do local self_stuff = self["stuff"..i] for key, thing in pairs(self_stuff) do if type(thing) == "table" then if thing.is_GUID then local thingy = newents[thing.GUID] if thingy then -- swap temp table for entity self_stuff[key] = thingy.entity end end end end end end return RememberStuff If your entities don't get saved by the game because you put "inst.persists = false" in your prefab, you can consider: local RememberStuff = Class(function(self, inst) self.inst = inst self.stuff1 = {} self.stuff2 = {} self.stuff3 = {} self.stuff4 = {} self.stuff5 = {} self.stuff6 = {} self.stuff7 = {} self.stuff8 = {} self.stuff9 = {} self.stuff10 = {} end) function RememberStuff:OnSave() local data = {} for i = 1, 10 do local data_stuff = {} local self_stuff = self["stuff"..i] for key, thing in pairs(self_stuff) do if type(thing) == "table" and thing.GUID then table.insert(data_stuff, { is_SaveRecord = true, SaveRecord = thing:GetSaveRecord() }) else table.insert(data_stuff, thing) end end data["stuff"..i] = data_stuff end return data end function RememberStuff:OnLoad(data) self.stuff1 = data and data.stuff1 or {} self.stuff2 = data and data.stuff2 or {} self.stuff3 = data and data.stuff3 or {} self.stuff4 = data and data.stuff4 or {} self.stuff5 = data and data.stuff5 or {} self.stuff6 = data and data.stuff6 or {} self.stuff7 = data and data.stuff7 or {} self.stuff8 = data and data.stuff8 or {} self.stuff9 = data and data.stuff9 or {} self.stuff10 = data and data.stuff10 or {} end function RememberStuff:LoadPostPass(newents, data) for i = 1, 10 do local self_stuff = self["stuff"..i] for key, thing in pairs(self_stuff) do if type(thing) == "table" then if thing.is_SaveRecord then local thingy = SpawnSaveRecord(thing.SaveRecord, newents) if thingy then self_stuff[key] = thingy end end end end end end return RememberStuff Link to comment Share on other sites More sharing options...
Serpens Posted October 17, 2016 Author Share Posted October 17, 2016 (edited) Great it works, thanks Since I want to keep the previous keys (named) and I forgot that in my previous code, I changed the part of the save function to use the key (does work, just if someone else is also interested): for key, thing in pairs(self_stuff) do if type(thing) == "table" and thing.GUID then -- we need to pass the GUID references to the game table.insert(stuff_references, thing.GUID) -- save a table with flag and GUID where entity should be data_stuff[key] = { is_GUID = true, GUID = thing.GUID } else data_stuff[key] = thing end end Edited October 17, 2016 by Serpens Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now