how to events.on_load in MP without desync?

Place to get help with not working mods / modding interface.
Post Reply
AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

how to events.on_load in MP without desync?

Post 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!

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

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

Post 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

AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

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

Post 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!

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

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

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

Rseding91
Factorio Staff
Factorio Staff
Posts: 13204
Joined: Wed Jun 11, 2014 5:23 am
Contact:

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

Post 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.
If you want to get ahold of me I'm almost always on Discord.

User avatar
aubergine18
Smart Inserter
Smart Inserter
Posts: 1264
Joined: Fri Jul 22, 2016 8:51 pm
Contact:

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

Post 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.
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

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

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

User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

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

Post 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.
Last edited by Adil on Sun Sep 18, 2016 1:00 pm, edited 1 time in total.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.

AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

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

Post 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?
Last edited by AntiElitz on Sun Sep 18, 2016 12:56 pm, edited 2 times in total.

User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

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

Post 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.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.

AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

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

Post 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

AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

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

Post by AntiElitz »

RIP solution. Doesn't trigger when loading in Singleplayer. Still looking for an answer, but i fear there is none.

User avatar
IngoKnieto
Fast Inserter
Fast Inserter
Posts: 106
Joined: Mon Oct 03, 2016 9:29 am
Contact:

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

Post 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 :)

User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

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

Post 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.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.

User avatar
aubergine18
Smart Inserter
Smart Inserter
Posts: 1264
Joined: Fri Jul 22, 2016 8:51 pm
Contact:

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

Post 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
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

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

Post 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

User avatar
IngoKnieto
Fast Inserter
Fast Inserter
Posts: 106
Joined: Mon Oct 03, 2016 9:29 am
Contact:

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

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

User avatar
aubergine18
Smart Inserter
Smart Inserter
Posts: 1264
Joined: Fri Jul 22, 2016 8:51 pm
Contact:

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

Post 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 )
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.

User avatar
IngoKnieto
Fast Inserter
Fast Inserter
Posts: 106
Joined: Mon Oct 03, 2016 9:29 am
Contact:

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

Post 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 ;)

AntiElitz
Filter Inserter
Filter Inserter
Posts: 445
Joined: Sat Aug 29, 2015 11:37 pm
Contact:

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

Post by AntiElitz »

this will still cause desync in MP

Post Reply

Return to “Modding help”