Jump to content

Refs, Prefabs OnSave or Devs, save us all!


Recommended Posts

OnSave always returning nil in GetPersistData(). What I'm doing wrong?

Code in prefab file

local function onsave(inst, data)
	if inst.pokemons then
		data.saved=true
		for k, v in pairs(inst.pokemons) do
		print("Saving GUID "..v.GUID)
			if data.pokemons == nil then
				data.pokemons = { v.GUID }
			else
				table.insert(data.pokemons, v.GUID)
			end
		end
		dumptable(data.pokemons)
		
		return data.pokemons
	end
end

Modified EntityScript:GetPersistData() for debugging purpose

function EntityScript:GetPersistData()
    local references = nil
    local data = nil
    for k,v in pairs(self.components) do
        if v.OnSave then
            local t, refs = v:OnSave()
            if type(t) == "table" then
                if t and next(t) and not data then
                    data = {}
                end
                if t and data then
                    data[k] = t
                end
            end

            if refs then
                if not references then
                    references = {}
                end
                for k,v in pairs(refs) do
                    
                    table.insert(references, v)
                end
            end
        end
    end

    if self.OnSave then
        if not data then
            data = {}
        end

        local refs = self.OnSave(self, data)
		if self.prefab=="trainerred" then
			print("OnSave GUIDs ")
			print(refs)
			print(dumptable(refs))
		end

        if refs then
            if not references then
                references = {}
            end
            for k,v in pairs(refs) do
				if self.prefab=="trainerred" then
					print("Ref GUIDs "..v)
				end
                table.insert(references, v)
            end
        end

        
    end
	
    if (data and next(data)) or references then
        return data, references
    end
end

Log

[00:03:15]: DoRestart:	true	
[00:03:16]: Saving GUID 112230	
[00:03:16]: 	K: 	1	 V: 	112230	
[00:03:16]: OnSave GUIDs 	
[00:03:16]: nil	
[00:03:16]: 
[00:03:16]: Serializing user: session/47A546BD92005A26/KU_dAYvwZw3_/0000000003

 

Link to comment
Share on other sites

Still nil

[00:03:11]: DoRestart:    true    
[00:03:12]: Saving GUID 114814    
[00:03:12]:     K:     1     V:     114814    
[00:03:12]: OnSave GUIDs     
[00:03:12]: nil    
[00:03:12]:
[00:03:12]: Serializing user: session/395F1B81BACCF1F2/KU_dAYvwZw3_/0000000003

Edited by popitup
nil
Link to comment
Share on other sites

3 minutes ago, popitup said:

Still nil

[00:03:11]: DoRestart:    true    
[00:03:12]: Saving GUID 114814    
[00:03:12]:     K:     1     V:     114814    
[00:03:12]: OnSave GUIDs     
[00:03:12]: nil    
[00:03:12]:
[00:03:12]: Serializing user: session/395F1B81BACCF1F2/KU_dAYvwZw3_/0000000003

You're gonna need to post your prefab that is trying to save. There isn't enough information in order to determine the true cause of the issue.

Link to comment
Share on other sites

local MakePlayerCharacter = require "prefabs/player_common"


local assets = {
    Asset("SCRIPT", "scripts/prefabs/player_common.lua"),
}


-- Custom starting items
local start_inv = {

    "trainercap",
    "pokeball",

}

-- When the character is revived from human
local function onbecamehuman(inst)
    -- Set speed when reviving from ghost (optional)
    inst.components.locomotor:SetExternalSpeedMultiplier(inst, "trainerred_speed_mod", 1)
end

local function onbecameghost(inst)
    -- Remove speed modifier when becoming a ghost
   inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "trainerred_speed_mod")
end

-- When loading or spawning the character
local function onload(inst,data)
    inst:ListenForEvent("ms_respawnedfromghost", onbecamehuman)
    inst:ListenForEvent("ms_becameghost", onbecameghost)

    if inst:HasTag("playerghost") then
        onbecameghost(inst)
    else
        onbecamehuman(inst)
    end
    if data and data.pokemons then
        print("Pokemons GUIDs loaded")
        inst.datapokemons=data.pokemons
    end
end

-- This initializes for both the server and client. Tags can be added here.
local common_postinit = function(inst)
    -- Minimap icon
    inst.MiniMapEntity:SetIcon( "trainerred.tex" )
    inst:AddTag("TrainerRed")
end
local function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end
local function onsave(inst, data)
    if inst.pokemons then
        data.saved=true
        for k, v in pairs(inst.pokemons) do
        print("Saving GUID "..v.GUID)
            if data.pokemons == nil then
                data.pokemons = { v.GUID }
            else
                table.insert(data.pokemons, v.GUID)
            end
        end
        dumptable(data.pokemons)
        
        return data.pokemons
    end
end

local function OnLoadPostPass(inst, newents, savedata)
    print("OnLoadPostPass")
    dumptable(savedata)
    if savedata ~= nil and savedata.pokemons ~= nil then
        
        for k, v in pairs(savedata.pokemons) do
        print("Loading pokemons")
            local pokemon = newents[v]
            if pokemon ~= nil then
                pokemon.entity.trainer=inst
            end
        end
    end
end
-- This initializes for the server only. Components are added here.
local master_postinit = function(inst)
    -- choose which sounds this character will play
    inst.soundsname = "wilson"
    
    -- Uncomment if "wathgrithr"(Wigfrid) or "webber" voice is used
    -- inst.talker_path_override = "dontstarve_DLC001/characters/"
    
    -- Can eat monster meat
    inst.components.eater.strongstomach = false
    
    -- Stats    
    inst.components.health:SetMaxHealth(150)
    inst.components.hunger:SetMax(150)
    inst.components.sanity:SetMax(200)
    
    -- Damage multiplier (optional)
    inst.components.combat.damagemultiplier = 0.5
    
    -- Hunger rate (optional)
    inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE
    inst.OnSave = onsave
    --inst.OnLoadPostPass = OnLoadPostPass --doesn't work anyway
    inst.OnLoad = onload
    inst.OnNewSpawn = onload
    
end

return MakePlayerCharacter("trainerred", prefabs, assets, common_postinit, master_postinit, start_inv)

Just changed back "return data.pokemons" after trying "return data"

Edit: I'm trying to save references to mobs. But their GUIDs don't persist because of problem that I described.

Edited by popitup
Link to comment
Share on other sites

5 minutes ago, popitup said:

Just changed back "return data.pokemons" after trying "return data"

How are you 'storing' the pokemon on the instance? I don't see that anywhere. If you're using 'table.insert(inst.pokemons, X)' then you could try:

for k, v in ipairs(inst.pokemons) do

instead of

for k, v in pairs(inst.pokemons) do

That is really the only thing I see wrong with your code, aside from the returning of data.pokemons; which you should return data because other data could be in the data table.

Link to comment
Share on other sites

This code isn't for storing. It's for labeling mobs as pokemon in the wild.

Just now, Aquaterion said:

What exactly would be the use of returning a value after saving, if you dont mind me asking?

The only use of returning value is make GUIDs persistent.

Currently I'm using saved data in this piece of code (since OnLoadPostPass doesn't fire at all so I commented it)

local function trainerred_postinit(inst)
		inst:DoTaskInTime( 1, function()
			if inst.datapokemons then	
				for k, v in pairs(inst.datapokemons) do
					local pokemon = GLOBAL.Ents[v]
					if pokemon ~= nil then
						pokemon.entity.trainer=inst
					end
				end
			end
		end )
end 
AddPrefabPostInit("trainerred", trainerred_postinit)

 I labeling them for multiple purpose. One of them is forbid to attack master.

Edited by popitup
Link to comment
Share on other sites

13 minutes ago, popitup said:

This code isn't for storing. It's for labeling mobs as pokemon in the wild.

The only use of returning value is make GUIDs persistent.

Currently I'm using saved data in this piece of code (since OnLoadPostPass doesn't fire at all so I commented it)


local function trainerred_postinit(inst)
		inst:DoTaskInTime( 1, function()
			if inst.datapokemons then	
				for k, v in pairs(inst.datapokemons) do
					local pokemon = GLOBAL.Ents[v]
					if pokemon ~= nil then
						pokemon.entity.trainer=inst
					end
				end
			end
		end )
end 
AddPrefabPostInit("trainerred", trainerred_postinit)

 I labeling them for multiple purpose. One of them is forbid to attack master.

How are you populating 'inst.pokemons'?? You have to populate that table before you can save anything from it.

Edited by Kzisor
Changed to, to from, stupid auto correct.
Link to comment
Share on other sites

local function addpokemon(trainer, pokemon)
    if trainer.pokemons==nil then
        trainer.pokemons={}
    end
    table.insert(trainer.pokemons,pokemon)
end 

pokemon is just instance of regular mob.

 

Useful topic:

OK if it doesn't work with prefab then I'll try save GUIDs with component.

Edited by popitup
Link to comment
Share on other sites

13 minutes ago, popitup said:

local function addpokemon(trainer, pokemon)
    if trainer.pokemons==nil then
        trainer.pokemons={}
    end
    table.insert(trainer.pokemons,pokemon)
end 

 

Did you try using ipairs instead of pairs in the onsave function?

Link to comment
Share on other sites

Yeap, still nothing. So what should happen that returning value become nil even if I returning a simple number.

So calling OnSave from EntityScript:GetPersistData()

local refs = self.OnSave(self, data)

return nil even for this code

local function onsave(inst, data)
	if inst.pokemons then
		data.saved=true
		for k, v in ipairs(inst.pokemons) do
		print("Saving GUID "..v.GUID)
			if data.pokemons == nil then
				data.pokemons = { v.GUID }
			else
				table.insert(data.pokemons, v.GUID)
			end
		end
		dumptable(data.pokemons)
		
		return 2345555
	end
	return 2345555
end

I'm a bit confused.

Edited by popitup
Link to comment
Share on other sites

local function OnSave(inst, data)
	local pokemons = {}
	for k,v in pairs(inst.pokemons) do
		pokemons[k] = {}
		pokemons[k].GUID = v.GUID
	end
	data.pokemons = pokemons
end

local function OnLoad(inst, data)
	if data and data.pokemons then
		inst.pokemons = data.pokemons
	end
end

local master_postinit = function(inst)
	inst.pokemons = {}
    inst.OnSave = OnSave
    inst.OnLoad = OnLoad 
end

Don't know if it's work for you or not. But above code works on my Warly mod: https://steamcommunity.com/sharedfiles/filedetails/?id=607654103

Link to comment
Share on other sites

Thanks for replies. Solution was found.

So I made dummy prefab and spawning it once per world

local assets = {
    
}

local function addguid(inst,guid,ownerid)
	table.insert(inst.guids,guid)
	inst.ownerlist[guid]=ownerid
end

local function onsave(inst, data)
	print("Dummy: onsave")
	local cleanguids={}
	local cleanownerlist={}
	for k, v in ipairs(inst.guids) do
		if Ents[v] then
			table.insert(cleanguids,v)
			cleanownerlist[v]=inst.ownerlist[v]
		end
	end
	if #cleanguids>0 then
		data.guids=cleanguids
		data.ownerlist=cleanownerlist
		print(dumptable(data.ownerlist))
		return data.guids
	end
end

local function OnLoadPostPass(inst, newents, data)	
	print("Dummy: OnLoadPostPass")
	for k, v in pairs(Ents) do
		--print(k)
	end
	if data and data.guids and data.ownerlist then
		for k, v in pairs(data.guids) do
            local poke = newents[v]
			print("Dummy: loading GUID "..v)
            if poke ~= nil then
				print("Dummy: owner "..data.ownerlist[v])
				--poke.entity.trainerid=data.ownerlist[v]
                print("Dummy: Exist "..poke.entity.name)
				print("Dummy: newGUID "..poke.entity.GUID)
				if Ents[poke.entity.GUID] then
					print("Dummy: labeling it")
					Ents[poke.entity.GUID].trainerid=data.ownerlist[v]
					inst:addguid(poke.entity.GUID,data.ownerlist[v])
				end
            end
        end
	end
end

local function fn()
    local inst = CreateEntity()
	inst.entity:AddTransform()
    inst.entity:AddNetwork()
	
    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end
	inst.ownerlist={}
	inst.guids={}
	inst.addguid = addguid
	inst.OnSave = onsave
    inst.OnLoadPostPass = OnLoadPostPass
	
	GUIDsHelper=inst
    return inst
end

return Prefab("guiddummy", fn, assets)

In modmain

GLOBAL.GUIDsHelper=nil
local function trainer_postinit(inst)
		inst:DoTaskInTime( 0, function()
			for k, v in pairs(GLOBAL.Ents) do
				if v.trainerid and v.trainerid==inst.userid then
					print(v.name.." labeled")
					v.trainer=inst
				end
			end
			if GLOBAL.TheWorld.ismastersim and not GLOBAL.GUIDsHelper then
				print("Dummy spawned")
				GLOBAL.GUIDsHelper=GLOBAL.SpawnPrefab("guiddummy")
			end
		end )
end 
AddPlayerPostInit(trainer_postinit)

There is no easy way to store references with players prefab. Hope devs will do something about it.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...