Hamurlik Posted September 24, 2023 Share Posted September 24, 2023 (edited) Hello. I have this component, PostPostInit. Its functionality doesn't really matter, but basically it holds some functions that are then used in AddPrefabPostInitAny, so I can effectively change prefab post init while the world is already loaded. I use it for testing purposes ../modmain.lua Spoiler modimport("imports/postpostinit_import") ../imports/postpostinit_import.lua Spoiler local env = env GLOBAL.setfenv(1, GLOBAL) env.AddPrefabPostInitAny(function(inst) if inst == nil or not inst:IsValid() or TheWorld == nil or TheWorld.ismastersim ~= true then return end --Make sure we load the very very last inst:DoTaskInTime(0.1, function() if PostPostInit == nil then return end for index, init in ipairs(PostPostInit.inits) do if init.test(inst, init) then init.init(inst) end end end) end) env.AddPrefabPostInit("world", function(inst) if inst.ismastersim then inst:AddComponent("postpostinit") --For easy access. Remember it's only on mastersim global("PostPostInit") PostPostInit = inst.components.postpostinit end end) ../scripts/components/postpostinit.lua Spoiler --[[ How to use: local function TurnRed(inst) if inst.AnimState then inst.AnimState:SetMultColour(1,0,0,1) end end local function UnTurnRed(inst) if inst.AnimState then inst.AnimState:SetMultColour(1,1,1,1) end end local id = PostPostInit:AddInit({init = TurnRed, deinit = UnTurnRed, test = "prefab", prefab = "axe"}, 0, true) PostPostInit:RemoveInit(id) ]] local preset = { prefab = function(inst, init) return init.prefab == inst.prefab end, tag = function(inst, init) return inst:HasTag(init.tag) end, any = function(inst, init) return true end, player = function(inst, init) return inst:HasTag("player") end, } local PostPostInit = Class(function(self, inst) self.inst = inst if self.inst ~= TheWorld then print("CR3: Tried to assign PostPostInit to an instance that isn't TheWorld.") return end self.inits = {} self.id = 0 self.shard_sync = true end) function PostPostInit:DebugPrint() for i, v in ipairs(self.inits) do print("self.inits", i, v) for k, v in pairs(v) do print(i, k, v) end end end function PostPostInit:GiveId() self.id = self.id + 1 return self.id end function PostPostInit:FindInit(id, find_index) for i, v in ipairs(self.inits) do if v.id == id then return find_index and i or v end end return nil end function PostPostInit:AddInit(init_data, priority, apply_to_existing) local id = self:GiveId() local init = init_data init.priority = priority init.id = id init.should_apply = apply_to_existing init.should_remove = false if type(init.test) == "string" then init.test = preset[init.test] end table.insert(self.inits, init) self:RefreshInits() return id end function PostPostInit:RemoveInit(id) self:FindInit(id).should_remove = true self:RefreshInits() end function PostPostInit:SortInits() table.sort(self.inits, function(a, b) return a.priority > b.priority --Highest priority loads first, and may get overwritten by lower priority. end) end local function apply_to_ents(init, removing) for _, inst in pairs(Ents) do if inst ~= nil and inst:IsValid() then if init.test(inst, init) then if not init.should_remove and init.should_apply then if init.existing then init.existing(inst) else init.init(inst) end init.should_apply = false else if init.deinit then init.deinit(inst) end end end end end end function PostPostInit:RefreshInits(sent_from_shard) -- if not sent_from_shard then -- SendModRPCToShard(GetShardModRPC("CR3", "postpostinit_sync"), nil, self) -- end self:SortInits() for index, init in ipairs(self.inits) do if init.should_apply or init.should_remove then apply_to_ents(init) end if init.should_remove then table.remove(self.inits, index) end end end return PostPostInit The component is added to TheWorld, as you can see in postpostinit_import. It works perfectly fine and as intended on singleplayer worlds without caves, where the player is the master sim. I have not done much testing outside those conditions. The problem is that in servers with caves, overworld and caves have separate instances of the component, and I need them to be the same on both shards. My first thought was to add shard rpcs, however they cannot take functions as arguments, neither can DataDumper or json.encode. Functions are necessary for how this component works; I must be able to supply a completely unique function through the console at any time, and have it synced to caves as well. I'm stuck. How would you add such a thing? Edited September 25, 2023 by Hamurlik Link to comment https://forums.kleientertainment.com/forums/topic/151120-keeping-data-in-sync-in-between-shards/ Share on other sites More sharing options...
Hamurlik Posted September 24, 2023 Author Share Posted September 24, 2023 Here is a failed attempt with a shard rpc: modmain.lua AddShardModRPCHandler("CR3", "postpostinit_sync", function(sender_shard_id, dumped_data) if PostPostInit == nil or TheShard:GetShardId() == sender_shard_id then return end local data = loadstring(dumped_data)() for k, v in pairs(data) do print("data layer 1", k, v) if type(v) == "table" then for i, v in ipairs(v) do print("data layer 2", i, v) if type(v) == "table" then for k, v in pairs(v) do print("data layer 3", k, v) end end end end end PostPostInit.inits = data.inits PostPostInit.id = data.id PostPostInit:RefreshInits(true) end) postpostinit.lua function PostPostInit:RefreshInits(sent_from_shard) if not sent_from_shard then SendModRPCToShard( GetShardModRPC("CR3", "postpostinit_sync"), nil, DataDumper({ inits = self.inits, id = self.id }, nil, true) ) end self:SortInits() for index, init in ipairs(self.inits) do if init.should_apply or init.should_remove then apply_to_ents(init) end if init.should_remove then table.remove(self.inits, index) end end end And the error log that follows: [00:01:17]: data layer 1 id 1 [00:01:17]: data layer 1 inits table: 000000000BA0A990 [00:01:17]: data layer 2 1 table: 000000000BA0AA30 [00:01:17]: data layer 3 prefab axe [00:01:17]: data layer 3 should_apply true [00:01:17]: data layer 3 priority 1 [00:01:17]: data layer 3 should_remove false [00:01:17]: data layer 3 id 1 [00:01:17]: [string "../mods/Creative_Mode_3/scripts/components/..."]:109: attempt to call field 'test' (a nil value) As you can see, DataDumper got everything but the functions. Rpcs do not accept functions as a valid argument data type either, so not using DataDumper won't fix it. Link to comment https://forums.kleientertainment.com/forums/topic/151120-keeping-data-in-sync-in-between-shards/#findComment-1666584 Share on other sites More sharing options...
_zwb Posted September 27, 2023 Share Posted September 27, 2023 On 9/24/2023 at 10:26 PM, Hamurlik said: however they cannot take functions as arguments, Maybe you can define those functions as methods of the component you use? Link to comment https://forums.kleientertainment.com/forums/topic/151120-keeping-data-in-sync-in-between-shards/#findComment-1666916 Share on other sites More sharing options...
Hamurlik Posted September 27, 2023 Author Share Posted September 27, 2023 3 hours ago, _zwb said: Maybe you can define those functions as methods of the component you use? That wouldn't really do what I want it to do - see, the goal is to use the component from the console and be able to supply any function to it while the game is already running, not any predefined function already stored in the component Link to comment https://forums.kleientertainment.com/forums/topic/151120-keeping-data-in-sync-in-between-shards/#findComment-1666931 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