Page 1 of 1

how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 3:10 am
by AntiElitz
So when I load a game with my mod, i want to check if some configurations in the config changed. So i registered an event for on_load to check if the config changed. However the on_load cannot edit global variables(cause they are not loaded yet). So i got a workaround here to make it update on the first tick instead.

Code: Select all

function events.on_load(event)
	trigger_on_load = true
end

function events.on_tick()
	if trigger_on_load then 
		init.initialize()
		trigger_on_load = nil
	end
end

function init.initialize()
	step_check_config_changed() 
end
This Code is working wonderfull in Singleplayer, but causes a instant desync in MP for joining players because the variable trigger_on_load is not global and therefor diffrent for each players. but i cannot define a global in on_load. So how am i supposed update the config-data when loading the game while keeping MP compatibility? THX for the help!

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 3:27 am
by Nexela
The correct way to do it is in on_configuration_changed or possibly via migration scripts in the migration folder

the dirtiest way would be to register and un-register a custom event in the tick handler (register in on_load and un_register after the tick handler has run) -- I do this for SP testing so I don't have to increment version and change folder names everywhere

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 3:43 am
by AntiElitz
on_configuration_changed does only fire on game or mod update, not on every load. What i actually want to check is if the user changed the options in my mod folders config file. This is not a mod update tho and does not get capture by on_configuration_changed.

Code: Select all

the dirtiest way would be to register and un-register a custom event in the tick handler (register in on_load and un_register after the tick handler has run) -- I do this for SP testing so I don't have to increment version and change folder names everywhere
Could you post an example of that? I get the idea but i don't fully understand without code i guess :/ . TX for your help!

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 4:22 am
by Nexela
Using stdlib events makes this easier :) (if you are not using the tick handler you can just register on_tick and unregister on_tick inside of on_tick)
Also not 100% sure how this would work in MP
pseudo code below

Code: Select all

local my_event = script.generate_event_name()

script.on_load(function ()
script.on_event(my_event, function()
update code here
script.on_event(my_event, nil) --nil the event to remove it
end)
end)

script.on_event(defines.events.on_tick, function(event)
game.raise_event(my_event)
blah blah blah rest of your tick code here.
)

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 5:13 am
by Rseding91
If you do any of the things listed here it will desync when used in MP and or break replays in single player.

Just FYI.

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 5:23 am
by aubergine18
Rseding91 wrote:If you do any of the things listed here it will desync when used in MP and or break replays in single player.
Would it be worth the devs doing a few youtube vids or something explaining how multiplayer works and the differences between MP and SP, and explaining how the desych happens and how to avoid it?

For example, when I was looking through the EvoGUI code I noticed that things like using string format %f could cause desynch because different operating systems output slightly different results. And I saw in another mod that if you make a local reference to math.random that can cause problems too. Then there's all the config change stuff and proper comprehension of the events like on_init, on_load and on_configuration_changed.

It would be useful to have a resource that modders can look at to get a better understanding of how to write desynch-free code.

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 5:57 am
by Nexela
Rseding91 wrote:If you do any of the things listed here it will desync when used in MP and or break replays in single player.
Just FYI.
Nexela wrote: Also not 100% sure how this would work in MP
Guess I can change that to being 100% sure it doesn't work in mp. But as I said it was only something I did for single player testing/devolping where desyncs and replay don't matter.

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 11:03 am
by Adil
AntiElite wrote:However the on_load cannot edit global variables(cause they are not loaded yet).
Where did that came from?
Modifying "global." is the sole purpose of on_load,(edited) It's game objects that are only a dead weight at this stage.

The desync comes from "trigger_on_load = true ". This line defines local variable, when second player connects, onload is fired only for him, so other players don't have the same variable, and on next tick it is polled for its value.

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 12:20 pm
by AntiElitz
Adil wrote:
AntiElite wrote:However the on_load cannot edit global variables(cause they are not loaded yet).
Where did that came from?
Modifying "global." is the sole purpose of on_load, It's game objects that are only a dead weight at this stage.

The desync comes from "trigger_on_load = true ". This line defines local variable, when second player connects, onload is fired only for him, so other players don't have the same variable, and on next tick it is polled for its value.
If I use global here, it tells me its not possible in on_load

Code: Select all

function events.on_load(event)
	global.triggerinitialize = true
end
Error while running mod-UPS++::on_load().

Detected modifications to the 'global' table:
CRC before: 1095740016
CRC after: 2444180806
I just want to run a function the tick after loading a game ... not after a player joining ... not before the entitys are loaded ... and without any desync - I've never thought this is gonna be so difficult :D


Rseding91 wrote:If you do any of the things listed here it will desync when used in MP and or break replays in single player.
Just FYI.
So what can i do instead to reach my goal Rseding91?

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 1:19 pm
by Adil
AntiElite wrote: If I use global here, it tells me its not possible in on_load
Well, indeed global cannot be changed during load, the desync explanation from above post is still correct though.

AntiElite wrote:I just want to run a function the tick after loading a game ... not after a player joining ... not before the entitys are loaded ... and without any desync - I've never thought this is gonna be so difficult
Well, for player1 the game is loaded on the tick1 and your function is run on tick1+1, for player2 its the tick2 and tick2+2, how do you expect it be easy not to desync if on each tick script calculated by each player's machine is supposed to arrive in the same state?

If you want changes in some config file to take effect without migrations then just don't store their data in "global." , make that data lua-global (as in, not "local"), lua script files are run on each game load.

Code: Select all

--this is control lua
--these are config variables
local var1=2
var2=3
--this initializes a bit more of config variables
require "config.lua"
--here go functions that use the above variables
...
This will require every player to have same values of config however. And no game objects can be involved.

If you for some reason absolutely need the api-functionality in the config, then you're out of luck.

Re: how to events.on_load in MP without desync?

Posted: Sun Sep 18, 2016 2:17 pm
by AntiElitz
I found out how i can implement what I needed! on_player_joined is fired also in SP when loading a game, but ist fired for everybody in multiplayer as well - so there is no desync. So i just count the number of players on_join and if it's only 1 player online, that will probably a load (unless this is a headless server, but that is fair enought)

Code: Select all

function events.on_player_joined_game(event)
	local player_online_count = 0
	for key, player in pairs(game.players) do
		if player.connected then
			player_online_count = player_online_count + 1
			if player_online_count >=2 then break end
		end
	end
	if player_online_count < 2 then global.trigger.initialize = true end --will trigger initialization in the mainloop only when first player joins
end

Re: how to events.on_load in MP without desync?

Posted: Thu Sep 22, 2016 5:40 pm
by AntiElitz
RIP solution. Doesn't trigger when loading in Singleplayer. Still looking for an answer, but i fear there is none.

Re: how to events.on_load in MP without desync?

Posted: Mon Oct 03, 2016 2:05 pm
by IngoKnieto
AntiElite wrote: ... So i got a workaround here to make it update on the first tick instead.

Code: Select all

function events.on_load(event)
	trigger_on_load = true
end

function events.on_tick()
	if trigger_on_load then 
		init.initialize()
		trigger_on_load = nil
	end
end

function init.initialize()
	step_check_config_changed() 
end
How exactly do you run this code? Is it in the control.lua of your mod?

I am basically looking for the same thing: I want to run some code when the game is loaded in SP. However your example isn't working for me; when I put it in my control.lua, it throws this exception: attempt to index global 'events' (a nil value)
Probably I am missing something, any help appreciated :)

Re: how to events.on_load in MP without desync?

Posted: Mon Oct 03, 2016 9:27 pm
by Adil

Code: Select all

events={}
--previous snip pasted here
--then register the handlers in the following manner
game.on_event(defines.events.on_tick, events.on_tick() ) 
However, I'd like to recommend once again: rethink your ways if you're in need of this hack.

Re: how to events.on_load in MP without desync?

Posted: Mon Oct 03, 2016 9:29 pm
by aubergine18
About knowing if the game is MP or SP mode, you might like to lend your voice to this topic: viewtopic.php?f=28&t=33264

Re: how to events.on_load in MP without desync?

Posted: Mon Oct 03, 2016 9:59 pm
by Nexela
Adil wrote:

Code: Select all

events={}
--previous snip pasted here
--then register the handlers in the following manner
game.on_event(defines.events.on_tick, events.on_tick() ) 
However, I'd like to recommend once again: rethink your ways if you're in need of this hack.
Yes this hack will not work in MP and shouldn't be used in a released version. About the only thing this is good for is single player development resetting recipes/code etc instead of always starting new worlds or increment version.

Better config handling will be coming in 0.15

Re: how to events.on_load in MP without desync?

Posted: Wed Oct 05, 2016 6:56 pm
by IngoKnieto
Thank you for all your replies, but I can't get this to work.

This is the complete code from my control.lua:

Code: Select all

events={}
--previous snip pasted here
function events.on_load(event)
   trigger_on_load = true
end

function events.on_tick()
   if trigger_on_load then
      init.initialize()
      trigger_on_load = nil
   end
end

function init.initialize() 
	local player = game.players[event.player_index]
	player.print("loaded game")
end

--then register the handlers in the following manner
script.on_event(defines.events.on_tick, events.on_tick() ) 
script.on_event(defines.events.on_load, events.on_load() ) 
But it still throws the error attempt to index global 'init' (a nil value) in the line function init.initialize. What am I missing?
I am still beginning to learn to understand LUA please be patient 8-)

Currently I just use this for my personal testing - I want the game to reload base prototypes that I changed with the mod. I understand that migrations would be the best way to do this, I plan to implement it in a later step...

Re: how to events.on_load in MP without desync?

Posted: Wed Oct 05, 2016 7:17 pm
by aubergine18

Code: Select all

-- use local to make it clear if something is local variable or function

local trigger_on_load = false

local init   = {} -- you forgot to define this
local events = {}


function events.on_load()
   trigger_on_load = true
end

function events.on_tick()
   if trigger_on_load then
      init.initialize()
      trigger_on_load = nil
   end
end

function init.initialize()
   local player = game.players[event.player_index]
   player.print("loaded game")
end

-- if you put () after a function it runs immediately
-- instead just pass in the function reference

-- wrong:
-- script.on_event(defines.events.on_tick, events.on_tick() )

-- correct:
   script.on_event(defines.events.on_tick, events.on_tick )
--                                                       ^ removed ()


-- on_load is a special type of event

-- wrong:
-- script.on_event(defines.events.on_load, events.on_load() )

-- correct:
   script.on_load( events.on_load )

Re: how to events.on_load in MP without desync?

Posted: Wed Oct 05, 2016 8:16 pm
by IngoKnieto
Huuray it works :) Thank you very much for your detailed explanations Aubergine!

I ended up with this code in my control.lua, which very nicely updates recipes or technologies from base game, which I changed in my mod:

Code: Select all

local trigger_on_load = false
local events = {}

script.on_load(
	function()
		trigger_on_load = true
	end
)

function events.on_tick()
   if trigger_on_load then
	  initMod()
      trigger_on_load = nil
   end
end

function initMod()
	for _, force in pairs(game.forces) do
		force.reset_recipes()
		force.reset_technologies()
	end
end

script.on_event(defines.events.on_tick, events.on_tick )
And most important: I learned some LUA ;)

Re: how to events.on_load in MP without desync?

Posted: Thu Oct 06, 2016 7:19 pm
by AntiElitz
this will still cause desync in MP