Jump to content

Custom widget crashes on cave enabled server


Recommended Posts

Here's the offending code, this is a character specific widget that keeps track of childspawner children (displaying how many are inside the spawner). It is a simple text widget:

AddClassPostConstruct("widgets/statusdisplays", function(self)
	local player = self.owner
	if player.prefab=="mychar" then
		local beeCounter = require "widgets/beecounter"
		self.beeCounter = beeCounter(player)
		
		player:DoPeriodicTask(0, function(inst)
			if inst.components.childspawner then
				local children = tostring(inst.components.childspawner.childreninside)
				self.beeCounter:Update("Bees: "..children)
			end
		end)  
	end
end)

The problems start with the line "if inst.components.childspawner then" - this line only exists because if I let the next line execute on a caveĀ enabled server, the game crashes. For whatever reason, at this point the player does not have defined components (for the childspawner at least). However it goes deeper than that, as if I move that DoPeriodicTask function into the character itself, running the code to update the widget causes a disconnect from the server. This may be because I'm not sure what "self" is in the context of the AddClassPostConstruct closure parameter, so I'm not correctly referencing it when I try to move the code out of modmain.lua, however I'm more than a little surprised that the periodic task doesn't start working once the player is spawned in and fully set up - shouldn't they get the component at some point and the if state begin evaluating as true? The component works, things related to it function in game, just this widget fails and I have no idea why. I should mention that this works perfectly on a non-cave enabled server (and does so in several of the different configurations I've tried).

Any help would be appreciated.

Thanks!

Edited by Feldoth
Link to comment
Share on other sites

Welcome to the forums!

When you host a game without caves, there's only one process running, which includes the world simulation and your character. However, hosting a world with caves runs 3 separate processes: the overworld simulation, the caves simulation, and your character. In this case, you are a client which happens to be hosting the game instead of being the host, hence why you need the remote console to execute commands. Component logic is usually handled only by the server. So what happens is that the code is valid for the host (as in player-hosted game with overworld only or the separate simulations for either overworld or caves), but the client (a pure client or the third process in a multi-shard setup) is missing the component and crashing.

The TL;DR is that you'll probably want to write something like

if not GLOBAL.TheWorld.ismastersim then return end

before handling components, and remember to set all_clients_require_mod to false in your modinfo.lua whenever possible.

As for the rest: I haven't tried anything with widgets, so I have no idea. Sorry!

Link to comment
Share on other sites

Thanks for your help, I was able to get this working by using net_variables - your comment about the server handling component logic got me on the right track. Here's what I came up with - it feels like I might not have done everything exactly right, but it is working and might be useful to someone else:

AddClassPostConstruct("widgets/statusdisplays", function(self)

	local player = self.owner
	if player.prefab=="mychar" then
	
		local beeCounter = require "widgets/beecounter"
		player.beeCounter = beeCounter(player)
		
		player.beeCounter:Update("")
		
		if not player.components.childspawner then
			-- Update bee counter for cave enabled servers
			self.netBeeTotal = GLOBAL.net_ushortint(player.GUID, "beeTotal", "mychar_bee_update")
			self.netBeeTotal:set(0)			
			
			player:ListenForEvent("guude_bee_update", function(inst)
				inst.beeCounter:Update("Bees: "..self.netBeeTotal:value())
			end)
		else
			-- Update bee counter for non-cave servers
			player:DoPeriodicTask(0, function(inst)
				inst.beeCounter:Update("Bees: "..player.components.childspawner.childreninside)
			end)
		end
	end
end)

I used the old method for non-cave servers as that avoided an issue I was having with duplicate id's for my net_var (it was getting added twice since the server and player are the same thing on non-cave servers - so I decided to just not use it on non-cave servers).

The other half of this is in my character's prefab script:

local master_postinit = function(player)

	--snip
	
	player.netBeeTotal = net_ushortint(player.GUID, "beeTotal", "mychar_bee_update")
	player.netBeeTotal:set(0)

	player:DoPeriodicTask(1, function(inst)
		inst.netBeeTotal:set(inst.components.childspawner.childreninside)
	end)
	
	--snip

end

That adds the variable on the server, and updates it with the current childspawner count every second, which in turn causes the event to fire on the client, which updates the display widget.

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...