About on_loaded event

Place to get help with not working mods / modding interface.
kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

About on_loaded event

Post by kendoctor »

there's no on_loaded event in api, but in on_load, you can not access game object

Code: Select all

local loaded = false 
function on_loaded(e)
 --- intialize what u want
 --- game object accessible
end 
script.on_nth_tick(1, 
function(e)
  if loaded then 
     script.on_nth_tick(1, nil) -- should use a mutiple handlers mechanism, here just demo
  else 
    on_loaded(e)
    loaded = true
 end 
end 
)
are there risks for using this method as on_loaded mechanism ?
Could we guarantee this is the first event triggered when game loaded ?

Qon
Smart Inserter
Smart Inserter
Posts: 2118
Joined: Thu Mar 17, 2016 6:27 am
Contact:

Re: About on_loaded event

Post by Qon »

kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
there's no on_loaded event in api, but in on_load, you can not access game object
Probably because of good reasons.
on_load
is meant for 3 specific reasons and only 3:
  • re-register conditional event handlers
  • re-setup meta tables
  • create local references to tables stored in the global table
If you are trying to do something else with some workaround then there's a possibility that you are approaching the problem from the wrong angle.
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
are there risks for using this method as on_loaded mechanism ?
In multiplayer all players clients must all have the same event handlers (I'm including non-on_event events) or you will get an error.
I did it wrong once and got a bug report: https://mods.factorio.com/mod/DoRobotsB ... d7715586b0
So when the second player joins he will execute

Code: Select all

script.on_nth_tick(1, function(e) end)
while the first player has already removed the nth_tick handler. So I believe your mod would crash immediately when it is used in multiplayer with more than 1 player. Unless all players join the exact same tick...
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
Could we guarantee this is the first event triggered when game loaded ?
I bet on_init() and on_configuration_changed() runs before all on_nth_tick at least. Maybe also some on_event events?

What are you trying to accomplish?

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

Code: Select all

script.on_nth_tick(1, function(e) end)
when server loaded in mp, there's no players, loaded should be immediately set to true when on_nth_tick triggered.
when players are joining, on_nth_tick will do nothing as i expected.
I bet on_init() and on_configuration_changed() runs before all on_nth_tick at least. Maybe also some on_event events?
yes, i know. maybe i should say "First event triggered when game on_load event ended"
What are you trying to accomplish?
Study more mechanism about factor modding.
Thx your reply.

Pi-C
Smart Inserter
Smart Inserter
Posts: 1651
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: About on_loaded event

Post by Pi-C »

kendoctor wrote: ↑
Sun Aug 09, 2020 1:35 pm

Code: Select all

script.on_nth_tick(1, function(e) end)
when server loaded in mp, there's no players, loaded should be immediately set to true when on_nth_tick triggered.
when players are joining, on_nth_tick will do nothing as i expected.
I did exactly that just recently: Registering an on_nth_tick event from script.on_load so that I could refresh player settings. It worked OK in single player, and I could also start a local multiplayer game as host. However, when I tried to connect to that game as client, the server wouldn't let me even join because of desyncs. So unless your mod is meant for only single player mode, you should not use that method!
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

Pi-C wrote: ↑
Sun Aug 09, 2020 1:48 pm
kendoctor wrote: ↑
Sun Aug 09, 2020 1:35 pm

Code: Select all

script.on_nth_tick(1, function(e) end)
when server loaded in mp, there's no players, loaded should be immediately set to true when on_nth_tick triggered.
when players are joining, on_nth_tick will do nothing as i expected.
I did exactly that just recently: Registering an on_nth_tick event from script.on_load so that I could refresh player settings. It worked OK in single player, and I could also start a local multiplayer game as host. However, when I tried to connect to that game as client, the server wouldn't let me even join because of desyncs. So unless your mod is meant for only single player mode, you should not use that method!
did you try not to register on_nth_tick in script.on_load ? register on_nth_tick in control stage

Qon
Smart Inserter
Smart Inserter
Posts: 2118
Joined: Thu Mar 17, 2016 6:27 am
Contact:

Re: About on_loaded event

Post by Qon »

kendoctor wrote: ↑
Sun Aug 09, 2020 3:43 pm
did you try not to register on_nth_tick in script.on_load ? register on_nth_tick in control stage
There's many parts to "control stage".
https://lua-api.factorio.com/latest/Data-Lifecycle.html
This also answers
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
Could we guarantee this is the first event triggered when game loaded ?
and will shed some light on
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
are there risks for using this method as on_loaded mechanism ?
if read carefully, once you fully understand the API.

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

Qon wrote: ↑
Sun Aug 09, 2020 5:05 pm
kendoctor wrote: ↑
Sun Aug 09, 2020 3:43 pm
did you try not to register on_nth_tick in script.on_load ? register on_nth_tick in control stage
There's many parts to "control stage".
https://lua-api.factorio.com/latest/Data-Lifecycle.html
This also answers
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
Could we guarantee this is the first event triggered when game loaded ?
and will shed some light on
kendoctor wrote: ↑
Sun Aug 09, 2020 11:48 am
are there risks for using this method as on_loaded mechanism ?
if read carefully, once you fully understand the API.
1. Yes, should be more clear, in control runtime stage.
2. there's no answers for when GAME LOADED which is the first event .
3. If you only read offcial doc, you will fully undestand. i think that's impossible.

anyway, thx your reply

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: About on_loaded event

Post by eradicator »

Qon wrote: ↑
Sun Aug 09, 2020 12:25 pm
In multiplayer all players clients must all have the same event handlers
To be even more precies in MP everyone has to have the same "game state". I suggest you (@OP) read up on what is and what isn't part of the gamestate. For event handlers as far as i know only the "this event has a handler/has no handler" distinction is part of the gamestate, not the actual handler. The lua state (local variables) on the other side are never part of the gamestate and they can differ as much as you want as long as they're not used to...affect the gamestate! ;)

As a rule of thumb, every time you think you need to change the gamestate in on_load you're doing something wrong and should review the problem instead of trying to get around the limitations of on_load. Otherwise your mod won't work in MP. I've never seen even a single problem that actually needed game state access in on_load.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

eradicator wrote: ↑
Sun Aug 09, 2020 7:10 pm
Qon wrote: ↑
Sun Aug 09, 2020 12:25 pm
In multiplayer all players clients must all have the same event handlers
To be even more precies in MP everyone has to have the same "game state". I suggest you (@OP) read up on what is and what isn't part of the gamestate. For event handlers as far as i know only the "this event has a handler/has no handler" distinction is part of the gamestate, not the actual handler. The lua state (local variables) on the other side are never part of the gamestate and they can differ as much as you want as long as they're not used to...affect the gamestate! ;)

As a rule of thumb, every time you think you need to change the gamestate in on_load you're doing something wrong and should review the problem instead of trying to get around the limitations of on_load. Otherwise your mod won't work in MP. I've never seen even a single problem that actually needed game state access in on_load.
I mentioned above, is, implementing on_loaded(not on_load) event in control runtime stage which means, on_load event already finished.

Qon
Smart Inserter
Smart Inserter
Posts: 2118
Joined: Thu Mar 17, 2016 6:27 am
Contact:

Re: About on_loaded event

Post by Qon »

kendoctor wrote: ↑
Sun Aug 09, 2020 8:21 pm
I mentioned above, is, implementing on_loaded(not on_load) event in control runtime stage which means, on_load event already finished.
As you wrote it in OP, no. But we already answered that. And we think that you are trying to do the wrong thing, and you should give up on it. Maybe if you told us what you are trying to accomplish we could help you get to your actual goal instead of you trying to get help with doomed workarounds.
It sounds like you are asking us the equivalent of how much shielding is "safe" in your nuclear bomb powered car... but you are also trying to hide what you are trying to accomplish. So if you have a secret mod that no-one can know about before it is released then it's a bit unproductive to come to the modding help forum ;) :|

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

Qon wrote: ↑
Sun Aug 09, 2020 9:38 pm
kendoctor wrote: ↑
Sun Aug 09, 2020 8:21 pm
I mentioned above, is, implementing on_loaded(not on_load) event in control runtime stage which means, on_load event already finished.
As you wrote it in OP, no. But we already answered that. And we think that you are trying to do the wrong thing, and you should give up on it. Maybe if you told us what you are trying to accomplish we could help you get to your actual goal instead of you trying to get help with doomed workarounds.
It sounds like you are asking us the equivalent of how much shielding is "safe" in your nuclear bomb powered car... but you are also trying to hide what you are trying to accomplish. So if you have a secret mod that no-one can know about before it is released then it's a bit unproductive to come to the modding help forum ;) :|
I just talk about the mechanism. Lots of programs has this pattern what i mentioned. But, factorio has some differences.
For example, In Brower, when on_load event triggered, you already can access all dom objects. here on_load equals on_loaded.
but in factroio, on_load just on_load , not on_loaded, you can not access very important object [game]. That's why i want to know if there's a way to achieve this.

Its a basic normal mechanism. But it will determine how u strcuture your codes and design the whole architecture .

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: About on_loaded event

Post by DaveMcW »

Yes, on_load is a confusing name because it is a powerful and useful event in other programs.

In Factorio it is a dangerous event with very limited abilities.

mrvn
Smart Inserter
Smart Inserter
Posts: 5704
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: About on_loaded event

Post by mrvn »

The problem is that on_load just means some client somewhere just restored the game state from serialized data, This happens when you load a single player game or every time a player joins a multiplayer game. And game state serializing doesn't save all the data (e.g. metatables). The event is strictly there to fix up what the serialization of gamestate didn't save.

The on_load must not change game state because then the game would behave differently when you save+load as opposed to when you just continue playing. Most criticlly the player in a multiplayer game and one just joining get different game state and desync.


Maybe what you are looking for is a migration scripts. That's more a on_loaded.

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

mrvn wrote: ↑
Wed Aug 12, 2020 1:04 pm
The problem is that on_load just means some client somewhere just restored the game state from serialized data, This happens when you load a single player game or every time a player joins a multiplayer game. And game state serializing doesn't save all the data (e.g. metatables). The event is strictly there to fix up what the serialization of gamestate didn't save.

The on_load must not change game state because then the game would behave differently when you save+load as opposed to when you just continue playing. Most criticlly the player in a multiplayer game and one just joining get different game state and desync.


Maybe what you are looking for is a migration scripts. That's more a on_loaded.
thx your reply. very appreciated.
I still have some questions to ask.
1. If in on_load, i just only do an assignment statement, for example, game_data = global.facto. does it mean that will not occur desync problems ?
2. in MP, you said, every playing joinin, will trigger on_load event, what's the exact actions on server already happened ? for example, player jion -> server save map ? -> downloading map -> catchup -> which point trigger on_load ?
3. in on_tick event, can i freely modify global data ? or need some precondictions to avoid desyncs?

mrvn
Smart Inserter
Smart Inserter
Posts: 5704
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: About on_loaded event

Post by mrvn »

1) If every time on_load triggers you always do game_data = global.facto and never assign anything else then that works fine (unless the game says you can't).

The important part is that after on_load all players must have the same game state. And only the newly joined player runs on_load afaik. The old player already did run it some time earlier. If after on_load all player have game_data = global.facto then there will be no desync.

2) I would assume it's before catchup. Because catchup could trigger any number of events that assume the metatables and others are already fixed.

3) Just make sure the on_tick modifies the data the same way on every client.

Things that don't work is for example using entities as key in global tables. They will differ after a save/load. Use entity.unit_number instead.
But that's not really anything to do with on_tick.

kendoctor
Inserter
Inserter
Posts: 34
Joined: Wed Aug 05, 2020 6:43 pm
Contact:

Re: About on_loaded event

Post by kendoctor »

mrvn wrote: ↑
Wed Aug 12, 2020 2:43 pm
1) If every time on_load triggers you always do game_data = global.facto and never assign anything else then that works fine (unless the game says you can't).

The important part is that after on_load all players must have the same game state. And only the newly joined player runs on_load afaik. The old player already did run it some time earlier. If after on_load all player have game_data = global.facto then there will be no desync.

2) I would assume it's before catchup. Because catchup could trigger any number of events that assume the metatables and others are already fixed.

3) Just make sure the on_tick modifies the data the same way on every client.

Things that don't work is for example using entities as key in global tables. They will differ after a save/load. Use entity.unit_number instead.
But that's not really anything to do with on_tick.
It seems i realized something which i guessed.
Let me give my understanding.

1. Game saved. With a gameState = N
2. One player A join -> download the save with gameState = N -> in his client on_load event triggered => Pass gameState into script => This is point
3. Player A going to play -> triggered M*event -> gameState changed to X, at this time another player B join
4. Player B join -> download the save with gameState = N -> in his client on_load event triggered => Pass gameState into script => The same point with player A at this step. But Playe A and Player B NOW has different gameState X and N
5. Player B begin to catch up, excuting all M*event in his client to reach gameState to X
6. But Player A still going, when Player B catch up, When Player A's gameState = Z, Player B also reached Z, Player B Joined.
7. Player A and Player B in his client , excuting the same events From gameState = N to gameState Z. No desync happned.
8. What will cause desyncs in this catchup period? Even excuting the same events?
9. Give an example: Player A, in on_event1 called random(), this changed gameState, but it did not changed sequences of events and amounts. At the end, when Player A and Player B exuting the same events in the same sequences, gameState will not be equal. random() has different seed in different client at different time.
10. All above just I deduced. If this was true, that will be the basis to avoid the desync problems.

Qon
Smart Inserter
Smart Inserter
Posts: 2118
Joined: Thu Mar 17, 2016 6:27 am
Contact:

Re: About on_loaded event

Post by Qon »

Avoid desyncs by avoiding to do anything the manual tells you to not do, and don't do anything that gives defferent clients different states for the same tick. You don't have to figure out what clients are doing what etc, because then you are doing something wrong. You don't think about manipulating gamestates in clients, only about doing deterministic changes to the abstract singular gamestate, even i multiplayer. The game itself will sync correctly it as long as you do your part correct.

The random() function depends on map seed and is deterministic. There's also non-map seed dependent random() which is also deterministic. Read the manual for info.

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

Re: About on_loaded event

Post by Rseding91 »

An interesting read :)

This topic came up the other day in #mod-making in Discord and I realized something simple: You can avoid any (mod-maker-created) desync by following these 3 steps:

* Delete any on_load event handler from the code
* Delete any code which relied on the on_load event handler
* Store all of your variables in the 'global' table

Really it's that simple. If you can't delete all of the code which you had in on_load it means you probably are using meta tables, conditional event handlers, or local references to stuff in the 'global' table. For every other need you think on_load solves; it doesn't and isn't going to solve it - there are other ways to solve those issues - better ways.

Factorio modding does not require you use any of the things which make on_load needed; all of what on_load exists for is optional stuff. If you can't do any one of those 3 things then you'll most likely have to sit and try to understand all of how it can go wrong (it can go wrong very quickly very easily).

Dunno if that's helpful to anyone :) It was an interesting thought to me - as i've spent time over the years locking down ways people can use on_load wrong.
If you want to get ahold of me I'm almost always on Discord.

mrvn
Smart Inserter
Smart Inserter
Posts: 5704
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: About on_loaded event

Post by mrvn »

kendoctor wrote: ↑
Wed Aug 12, 2020 3:13 pm
mrvn wrote: ↑
Wed Aug 12, 2020 2:43 pm
1) If every time on_load triggers you always do game_data = global.facto and never assign anything else then that works fine (unless the game says you can't).

The important part is that after on_load all players must have the same game state. And only the newly joined player runs on_load afaik. The old player already did run it some time earlier. If after on_load all player have game_data = global.facto then there will be no desync.

2) I would assume it's before catchup. Because catchup could trigger any number of events that assume the metatables and others are already fixed.

3) Just make sure the on_tick modifies the data the same way on every client.

Things that don't work is for example using entities as key in global tables. They will differ after a save/load. Use entity.unit_number instead.
But that's not really anything to do with on_tick.
It seems i realized something which i guessed.
Let me give my understanding.

1. Game saved. With a gameState = N
2. One player A join -> download the save with gameState = N -> in his client on_load event triggered => Pass gameState into script => This is point
3. Player A going to play -> triggered M*event -> gameState changed to X, at this time another player B join
4. Player B join -> download the save with gameState = N -> in his client on_load event triggered => Pass gameState into script => The same point with player A at this step. But Playe A and Player B NOW has different gameState X and N
5. Player B begin to catch up, excuting all M*event in his client to reach gameState to X
6. But Player A still going, when Player B catch up, When Player A's gameState = Z, Player B also reached Z, Player B Joined.
7. Player A and Player B in his client , excuting the same events From gameState = N to gameState Z. No desync happned.
8. What will cause desyncs in this catchup period? Even excuting the same events?
9. Give an example: Player A, in on_event1 called random(), this changed gameState, but it did not changed sequences of events and amounts. At the end, when Player A and Player B exuting the same events in the same sequences, gameState will not be equal. random() has different seed in different client at different time.
10. All above just I deduced. If this was true, that will be the basis to avoid the desync problems.
The game computes the CRC of the game state I think every tick, or every N ticks. The server sends those CRC to the client and the client can then compare his CRC to the one from the server during catchup.

Anything that causes a desync during normal play will also cause one during catchup. catchup is nothing special apart from updates not being displayed on the client.

mrvn
Smart Inserter
Smart Inserter
Posts: 5704
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: About on_loaded event

Post by mrvn »

Rseding91 wrote: ↑
Wed Aug 12, 2020 6:26 pm
An interesting read :)

This topic came up the other day in #mod-making in Discord and I realized something simple: You can avoid any (mod-maker-created) desync by following these 3 steps:

* Delete any on_load event handler from the code
* Delete any code which relied on the on_load event handler
* Store all of your variables in the 'global' table

Really it's that simple. If you can't delete all of the code which you had in on_load it means you probably are using meta tables, conditional event handlers, or local references to stuff in the 'global' table. For every other need you think on_load solves; it doesn't and isn't going to solve it - there are other ways to solve those issues - better ways.

Factorio modding does not require you use any of the things which make on_load needed; all of what on_load exists for is optional stuff. If you can't do any one of those 3 things then you'll most likely have to sit and try to understand all of how it can go wrong (it can go wrong very quickly very easily).

Dunno if that's helpful to anyone :) It was an interesting thought to me - as i've spent time over the years locking down ways people can use on_load wrong.
Insufficient to avoid all desyncs. For example using entities as keys in tables will crash and burn. Including using player. You need to use entity.unit_number or player.player_index. Or sorting entities by their object instead of unit_number. There are some other ways than on_load to cause desyncs.

But I agree with the sentiment: Don't use on_load. If you have to, think again and don't use on_load. Clever things like metatables are just not worth the risk of desyncs because you didn't get the code 100% right.

Post Reply

Return to β€œModding help”