Page 1 of 1

Accessing Global on new save in multiplayer

Posted: Fri May 04, 2018 1:30 am
by Muppet9010
I made a mod that runs fine in single player and then tested it in multiplayer. While I thought I had obeyed the rules in the API data lifecycle, the mod worked fine, until I tried to rejoin a saved and reloaded MP game and got unsafe for multiplayer when loading/saving error.

I have tried different code combinations from looking at other mods and while there are no errors anymore the mod appears to fail to run the start_up() function properly on a multiplayer server as none of the global objects exists or the registered API commands. However, it works fine in a single player server

I originally didn't have any on_tick event as I just need to initialise and then react to events, however, I tried to use on_tick to get around the issue and at the same time added the extra "if not global.state.xxxx" into all function calls that the start_up() function calls. i.e. global.state.disable_satelite

Can anyone point where I have gone wrong?

The control.lua code is below

Code: Select all

require("prototypes.utility.utils")
require("constants")

require("prototypes.commands")
require("prototypes.orders")
require("prototypes.gui")

script.on_init(function() on_init() end)
script.on_load(function() on_load() end)
script.on_configuration_changed(function() on_init() end)
--script.on_event({defines.events.on_tick}, function() on_init() end)
script.on_event({defines.events.on_player_created}, function(event) on_player_created(event) end)
script.on_event({defines.events.on_gui_click}, function(event) on_gui_click(event) end)

function on_init()
	start_up()
	sisyphean.commands.register()
end 

function on_load()
	sisyphean.commands.register()
end

function start_up()
	generate_global_state()
	generate_global_structure()
	sisyphean.orders.initialise()
	sisyphean.order_item.initialise()
	disable_satelite_dialog()
end

function generate_global_state()
	if not global.state then global.state = {} end
	if not global.state.order_target then
		global.state.order_target = tonumber(settings.startup[sisyphean.setting_name.order_target].value)
	end
	if not global.state.started then 
		global.state.started = false
	end
	if not global.state.finished then 
		global.state.finished = false
	end
	if not global.state.orders_completed then
		global.state.orders_completed = 0
	end
	if not global.state.current_order_id then
		global.state.current_order_id = 0
	end
end

function generate_global_structure()
	if not global.orders then global.orders = {} end
end

function disable_satelite_dialog()
	if not global.state.disable_satelite then
		if remote.interfaces["silo_script"] then
			global.state.disable_satelite = true
			remote.call("silo_script","set_show_launched_without_satellite", false)
			remote.call("silo_script","set_finish_on_launch", false)        
		end
	end
end

function on_player_created(event)
	local player = game.players[event.player_index]
	sisyphean.gui.add_gui_for_player(player)
end

function on_gui_click(event)
	sisyphean.gui.gui_clicked(event)
end
The full mod is attached.
This was done on 0.16.39 client and server (2nd pc in headless mode).

As a side note, I am a c#, database and web developer and only have limited experience with LUA.

Re: Accessing Global on new save in multiplayer

Posted: Fri May 04, 2018 8:25 am
by bobingabout
I would need to examine this closer to see if there's any issues, but the one thing I do know right now, is that I dislike the fact that you're using an on_load.

Re: Accessing Global on new save in multiplayer

Posted: Fri May 04, 2018 12:33 pm
by eradicator
commands.add_command is safe to (==recommended to) call outside of any event, this makes it unconditional unless there's another mod trying to add the same command. As commands are like event handlers in that they're not part of the global state and thus not synced. (Unless you're actually dynamically adding/removing commands. Did only skim over the code).

Other than that...do you have any dynamic/conditional event handlers?

Re: Accessing Global on new save in multiplayer

Posted: Fri May 04, 2018 2:55 pm
by Muppet9010
Thank you for the suggestions, they got me thinking and I realised a few things (may be useful if someone else finds this thread):
- API Commands aren't saved into global and so have to be re-generated on each map load. This explained some of my failing tests.
- Remote interface command to affect the core game doesn't get saved between loads, so have to disable the silo script on each load.
- carefully review what I had in on_load and on_init in relation to global and game variables
- that when in-game you can't run mod functions via "/c" due to the scope and this also causes objects to appear empty via "/c". added logging to text file to confirm values in these cases.

My updated code below that doesn't require on_tick seems to work for my SP and MP testing with rejoining, save, loading and restarting the server from the save.

Code: Select all

require("prototypes.utility.utils")
require("constants")

require("prototypes.commands")
require("prototypes.orders")
require("prototypes.gui")



local function generate_global()
	global = global or {}
	if global.state == nil then global.state = {} end
	if global.state.order_target == nil then
		global.state.order_target = tonumber(settings.startup[sisyphean.setting_name.order_target].value)
	end
	if global.state.started == nil then 
		global.state.started = false
	end
	if global.state.finished == nil then 
		global.state.finished = false
	end
	if global.state.orders_completed == nil then
		global.state.orders_completed = 0
	end
	if global.state.current_order_id == nil then
		global.state.current_order_id = 0
	end
	if global.orders == nil then global.orders = {} end
end

local function start_up()
	sisyphean.order_item.initialise()
	sisyphean.commands.register()
	disable_satelite_dialog()
end

local function on_player_created(event)
	local player = game.players[event.player_index]
	sisyphean.gui.add_gui_for_player(player)
end

local function on_gui_click(event)
	sisyphean.gui.gui_clicked(event)
end

function disable_satelite_dialog()
	if remote.interfaces["silo_script"] then
		remote.call("silo_script","set_show_launched_without_satellite", false)
		remote.call("silo_script","set_finish_on_launch", false)        
	end
end


script.on_init(function() 
	generate_global()
	start_up()
end)
script.on_load(function() 
	start_up()
end)
script.on_configuration_changed(function() 
	generate_global()
	start_up()
end)
script.on_event({defines.events.on_player_created}, function(event) on_player_created(event) end)
script.on_event({defines.events.on_gui_click}, function(event) on_gui_click(event) end)
script.on_event(defines.events.on_rocket_launched, function(event)
	sisyphean.orders.rocket_launched_event(event.rocket, event.silo)
end)