Transport Fever 2

Transport Fever 2

Create your own game world!
Give your game a personal touch and change it to your liking. Create, share and install mods. Customize the game with new landscapes, vehicles, stations, assets and more.
Chaos 27 Dec, 2021 @ 2:05pm
how to determine running thread of load() method
Hello,

Per the mod API documentation [1], the load() method is called both from the engine thread and the GUI thread. It seems that this is the method where a mod should initialize its state and get ready to do work.

But, how can the load() method detect if it is running on the GUI thread or the engine thread?

I have tried checking if api.gui is defined (it is in neither thread)
I have tried checking if api.engine is defined (it is in both threads)
I have tried setting a flag in guiInit() method, but this method is called late, after the first load() invokation on both GUI and engine threads.

There should be a simple flag that can be checked, but I can't find it.

Thanks in advance for any help.

EDIT: found it. see answer #4 below

[1] https://transportfever2.com/wiki/doku.php?id=modding:gamescripts
Last edited by Chaos; 31 Dec, 2021 @ 12:00pm
< >
Showing 1-11 of 11 comments
lollus 15 30 Dec, 2021 @ 2:18pm 
The first call is in a worker thread, all subsequent calls are in the GUI thread.
Last edited by lollus; 14 Jan, 2022 @ 12:09am
kamykaze13 5 30 Dec, 2021 @ 6:46pm 
If you need to check for the thread in load(), you should be able to try:
if api.engine then -- engine stuff here end if api.gui then -- gui stuff here end
Chaos 30 Dec, 2021 @ 8:23pm 
Originally posted by kamykaze13:
If you need to check for the thread in load(), you should be able to try:
if api.engine then -- engine stuff here end if api.gui then -- gui stuff here end

Thanks, but this does not work. api.engine is defined when load() is first called in either thread, and api.gui is not.
Last edited by Chaos; 30 Dec, 2021 @ 9:40pm
Chaos 30 Dec, 2021 @ 9:35pm 
For posterity, and since I'll be googling this problem in a few years, no doubt, I found another construct to work, with the caveat that it relies on the undocumented behaviour that the game.interface.sendScriptEvent property is not defined in the engine thread's game.interface object:

local state = nil function data() return { load = function(data) if not state then if not game.interface.sendScriptEvent then print("Do Engine Thread Init Here") if not data then print("This is the first time this mod is used on this gamesave") end else print ("Do GUI Thread Init Here") end end state = data or { mycounter = 1 } end, save = function() return state end, } end

DEVS: If this is how you intend mod initialization to work, please document it.
Last edited by Chaos; 30 Dec, 2021 @ 9:38pm
kamykaze13 5 31 Dec, 2021 @ 5:08am 
Originally posted by Chaos:
Originally posted by kamykaze13:
If you need to check for the thread in load(), you should be able to try:
if api.engine then -- engine stuff here end if api.gui then -- gui stuff here end

Thanks, but this does not work. api.engine is defined when load() is first called in either thread, and api.gui is not.

Ah, I see. Now I got it. Then yes, this doesn't work.
These gamescripts are rather confusing at times with their threads and what runs where and whatnot. :steamhappy:
VacuumTube 32 9 Jan, 2022 @ 8:56am 
I have written a getCurrentThread() function some time ago, because I was messing around with the different threads, too. It can be tested with CommonAPI console.
https://github.com/Vacuum-Tube/Advanced_Statistics_1/blob/main/res/scripts/advanced_statistics/thread.lua

However, in the init process, keep in mind, it's more difficult because not all things exist from the start. For example, if the game script files are read in the first time, api is only {}.

The gamescript is indeed confusing, because the same file will create 2 instances in the game in different threads (engine+gui). However, the load function is the only one that is called in both. I have already written about this and other issues to UG.
https://www.transportfever.net/index.php?thread/16887-game-script-save-function-unerkl%C3%A4rliches-verhalten/&postID=342719&highlight=gamescript#post342719
Chaos 9 Jan, 2022 @ 10:33pm 
Originally posted by VacuumTube:
I have written a getCurrentThread() function some time ago, because I was messing around with the different threads, too. It can be tested with CommonAPI console.
https://github.com/Vacuum-Tube/Advanced_Statistics_1/blob/main/res/scripts/advanced_statistics/thread.lua

However, in the init process, keep in mind, it's more difficult because not all things exist from the start. For example, if the game script files are read in the first time, api is only {}.

The gamescript is indeed confusing, because the same file will create 2 instances in the game in different threads (engine+gui). However, the load function is the only one that is called in both. I have already written about this and other issues to UG.
https://www.transportfever.net/index.php?thread/16887-game-script-save-function-unerkl%C3%A4rliches-verhalten/&postID=342719&highlight=gamescript#post342719

Thanks for your response.

Is your getCurrentThread() function any more complex than I suggested above, ie checking if game.interface.sendScriptEvent is not nil ?

In my experiments I found that the save method is also called in the GUI thread one time, after the engine load() and initial GUI load(). If the GUI save() call returns a different state than was load()'ed, there is an assertion failure due to the gui and engine state being different. However, I think the GUI thread save() is not called after this check is done.
VacuumTube 32 11 Jan, 2022 @ 11:06am 
No it also uses game.interface.sendScriptEvent. At first I used game.gui but this does not exist before the gui is loaded.

The function can check other game threads, too.

Excactly, the init behavior is a bit confusing. I also wrote that in the linked forum post.
My results:
LOAD Script Thread table: 000001872F5A8A20 SAVE Script Thread LOAD Gui Thread table: 000001872F5B2700 SAVE Script Thread SAVE Gui Thread
The first load is in script thread to read state data from savegame. Nil if mod is loaded the first time.
The same is done for gui thread. For whatever reason save is called 2x in script thread.

I don't know why the Assertion for being the same Save result exists, but it really came in my way. Because I wanted to load only parts of state (settings), the rest was not important to recover because it is recalculated. The rest also included a init flag which should NOT be recovered with init=true from the savegame.

Meanwhile, in the load the new state needs to be saved but maybe also other things. This is done 5x per second.
So the load function does basically 2 different tasks.
Chaos 12 Jan, 2022 @ 11:42am 
Originally posted by VacuumTube:
I don't know why the Assertion for being the same Save result exists, but it really came in my way. Because I wanted to load only parts of state (settings), the rest was not important to recover because it is recalculated. The rest also included a init flag which should NOT be recovered with init=true from the savegame.

Meanwhile, in the load the new state needs to be saved but maybe also other things. This is done 5x per second.
So the load function does basically 2 different tasks.

Hi, and thanks for your response.

The need to persist only part of the state is probably quite common among mods. It sounds like you only persist settings, but some mods might need to persist other state which cannot be re-generated. I ran into this issue also. Here's how I solved it:

I noticed that the serializing process follows these steps:

  1. make gameInfo bar invisible (visibilityChange gui event with param = false)
  2. engine thread receives handleEvent() with any pending events
  3. engine thread runs save()
  4. serializing to disk happens
  5. visibilityChange event in gui thread is fired
  6. engine thread receives handleEvent() with any pending events
  7. game continues

Using this observed behaviour; my code does the following:

  1. in GUI event handler, on visibilityChange of the gameInfo, sends an event to the engine thread to communicate that serializing has started/ended
  2. in engine thread, in handleEvent, a serializing flag is set when the above event is received
  3. In save(), return only state that should persist if the serializing flag is set; otherwise return the full state as normal.


save = function() if serializing then return { refs = state.refs, } end return state end,

I'm hoping to release two mods I'm working on soonish, which use this technique.
lollus 15 13 Jan, 2022 @ 2:46pm 
Are you sure you need all that? I don't know what you are trying to achieve, coz you haven't told us, but I would try harder and keep things simple.
Last edited by lollus; 14 Jan, 2022 @ 12:55am
Chaos 14 Jan, 2022 @ 3:34pm 
Originally posted by lollus:
Are you sure you need all that? I don't know what you are trying to achieve, coz you haven't told us, but I would try harder and keep things simple.

I'm trying to initialize my new script mod correctly during the first load() call, but with @VacuumTube's help, I think I have it figured out. Thank you for your feedback!
< >
Showing 1-11 of 11 comments
Per page: 1530 50

Date Posted: 27 Dec, 2021 @ 2:05pm
Posts: 11