CulturedCait Posted December 29, 2025 Report Share Posted December 29, 2025 Hi guys! I'm working on a new script and I was wondering if there is a way to execute Engine.SetSimRate / Engine.GetSimRate from JS trigger script? It works if executed directly from the console, but using these functions inside JS produces following error: ERROR: Error in timer on entity 1, IID103, function DoAction: TypeError: Engine.SetSimRate is not a function This is my test trigger: Trigger.prototype.TestDelay = () => { Engine.SetSimRate(20); } const trg = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); trg.DoAfterDelay(5000,"TestDelay", {enabled: true}); I'm on release-a27.1 6a576. Do you have an idea how to change simulation speed from within javascript? Is it even possible using trigger mechanics? Thank you. Quote Link to comment Share on other sites More sharing options...
Vantha Posted December 30, 2025 Report Share Posted December 30, 2025 That's not possible, unfortunately. Essentially, the engine creates different JS script interfaces for different purposes - one for the GUI (scripts in gui/), one for the simulation (scripts in simulation/ and trigger scripts in maps/scenarios/) and one for map generation (scripts in maps/random/). And those Engine functions are always registered in specific contexts. Engine.QueryInterface, for example, is obviously only available in simulation context, since that's were simulation components and interfaces are. Similarly, Engine.SetSimRate and Engine.GetSimRate are only registered in the GUI scripting context, meaning you can only call them from GUI scripts. What exactly are you trying to achieve? There are some possible workarounds because the engine allows you to communicate between contexts and modifying the engine to register SetSimRate and GetSimRate in simulation scripting context too is quite straightforward actually. Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted December 30, 2025 Author Report Share Posted December 30, 2025 Hi Vantha, thanks for detailed explanation. All is clear now. What I wanted to achieve is to programmatically speed up the game in the beginning to the value of~ x20 (just temporarily) to override default Map Setup setting. Reason is to perform some JS asynchronous calculations much faster eliminating internal tick bottleneck. Then go back to the default speed value few moments after. What you described as a workaround sounds very promising! Can you please explain little more how to communicate between different contexts to register SetSimRate? Thank you! Quote Link to comment Share on other sites More sharing options...
phosit Posted December 30, 2025 Report Share Posted December 30, 2025 Trigger-scripts are executed in the simulation js-interface. I took a look at the tutorial and saw that it uses Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification(...); to send a message to the gui. The gui receives the messages in "session/messages.js". Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted December 30, 2025 Author Report Share Posted December 30, 2025 Hi phosit, I checked PushNotification and it seems to be limited to several possible message types. Which of course makes sense from the usage perspective... Does it mean the only way to achieve what I wanted would be to mod messages.js by adding new message type that registers function executing SetSimRate? Did I get your idea right? Thanks. Quote Link to comment Share on other sites More sharing options...
phosit Posted December 31, 2025 Report Share Posted December 31, 2025 Yes, I think that is correct. But @Vantha has more knowledge. Quote Link to comment Share on other sites More sharing options...
Vantha Posted December 31, 2025 Report Share Posted December 31, 2025 Yes, notifications are the best way to communicate stuff like this from the simulation to the GUI (and it works just as @phosit said). 23 hours ago, CulturedCait said: What I wanted to achieve is to programmatically speed up the game in the beginning to the value of~ x20 (just temporarily) to override default Map Setup setting. Reason is to perform some JS asynchronous calculations much faster eliminating internal tick bottleneck. Then go back to the default speed value few moments after. What kind of calculations? A "tick" in the GUI happens for each rendered frame, so the tick rate won't go up if you increase the sim rate (in fact, it'll probably go down a bit, since the game is busier with the simulation and has less time for rendering). Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted December 31, 2025 Author Report Share Posted December 31, 2025 4 hours ago, phosit said: Yes, I think that is correct. But @Vantha has more knowledge. Well, I wanted to avoid creating a mod. Actually I just wanted to make scripted map in plain "vanilla" 0 A.D. SetSimRate, as helpful as it would be for automating few things, is not that critical in the end. I can always invoke the command from the console. Thank you. 3 hours ago, Vantha said: What kind of calculations? A "tick" in the GUI happens for each rendered frame, so the tick rate won't go up if you increase the sim rate (in fact, it'll probably go down a bit, since the game is busier with the simulation and has less time for rendering). Ok. This is very long story, but to the details what I'm working on... I'll try to explain. I've always wanted to know which types of units and formations are best in a given case. Also, how my formation should look like considering shape and direction vector of the enemy during tactical clash (in second stage of the project my idea would be also to find precise polynomials that should represent formation lines that should be used). On the one hand these are pointless questions (what we're dealing with are differential equations... that's why some say that strategy is rather an art than something you can calculate). But for a long time it's been bugging me and I had some ideas how to crack this problem with genetic algorithms. Or at least to try finding approximate or partial answers in some cases. Since I found some time during the Christmas I developed very tiny genetic algorithm framework in JS that operates only on 0 AD triggers in asynchronous way. Since the objective function in my problem is the result of single combat, and GA requires to perform many of such clashes for single generation, this is "the lengthy calculation" I was talking about. GA has to wait for each of such "entities" (i.e. clash of two armies) results to pick parameter candidates for next generation. That's why I was interested in SetSimRate to speed up the process a little. As you can see there are many asynchronous calculations (actually thousands...) that need to be processed sequentially. Speeding up simulation helps much. I know what you say. You could always grab units statistics, calculations etc from the code and write a program in faster environment to achieve similar results. But I wanted to utilize exact 0 A.D. engine as it is. There would be many mistakes in this process if I did otherwise. Maybe it would be easier, and you wouldn't have to resort to some quirks, but since scripting support in 0 A.D. is very comprehensive it is possible to achieve all I described above. GA's are very slow by definition so technology doesn't matter that much. The algorithm itself is the bottleneck. This is of course very lengthy project. Absolutely nothing to brag about, more like a hobby actually  Hope this now sheds little light where my question came from. Now, to simulate how SetSimRate helps, just try to invoke something simple that spawns itself each 100ms in a sequence using DoAfterDelay. You can assume this "100ms" is aforementioned sequenced lengthy atomic operation that you don't know the result yet. You'll be notified of the result later when it finishes, and you'll utilize the result later to spawn next iteration "somehow". testTrigger.DoAfterDelay(100, .....) You'll see the difference if you speed up simulation to x20. This "100" would become ~5 in real life time. Maybe not really 5, but much faster. Quote Link to comment Share on other sites More sharing options...
Stan` Posted Thursday at 13:56 Report Share Posted Thursday at 13:56 Have you seen ->Â https://gitea.wildfiregames.com/0ad/0ad/wiki/GettingStartedReinforcementLearning Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted Thursday at 17:05 Author Report Share Posted Thursday at 17:05 Nope. Very interesting. This would be precisely what I'm looking for. Thank you. Quote Link to comment Share on other sites More sharing options...
Vantha Posted Friday at 10:46 Report Share Posted Friday at 10:46 17 hours ago, CulturedCait said: Nope. Very interesting. This would be precisely what I'm looking for. Thank you. All clear now? (it would be possible to run the game headlessly (with no rendering) and to force it to execute turns a quickly as it can) Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted Friday at 22:30 Author Report Share Posted Friday at 22:30 I checked the python repo and it picked my interest. Particularly headless option you mentioned would be very useful. I have question regarding this cmd: pyrogenesis --rl-interface=0.0.0.0:6000 --autostart-nonvisual --mod=rl-scenarios --mod=public Is there any api documentation regarding how to communicate with this interface? I.e. what should you send to this port to execute some part of the game (assuming I understand this interface correctly, sorry I didn't have much time to examine python scripts in detail..). In my case what I needed would be to actually load given map remotely, and save this map after period of time. Quote Link to comment Share on other sites More sharing options...
Stan` Posted Saturday at 08:31 Report Share Posted Saturday at 08:31 In that python example they wrote a client with the endpoints IIRC You can run arbitrary JS with /evaluate and /step to make the simulation progress. Regarding saving the map itself I'm not sure you can do it one go might have to replay the map and save it after. You can load any map with that endpoint. If you're on Windows you might have some issues with python not going as fast as it can. Probably some issue with flushing. Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted Saturday at 12:02 Author Report Share Posted Saturday at 12:02 OK! Thank you guys. I'll investigate and let you know if I encounter any obstacles. Cheers. Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted Monday at 15:21 Author Report Share Posted Monday at 15:21 OK, I've completed environment setup. I managed to expose SetEngineRate, WriteJSONFile and incorporate other useful APIs to JS triggers according to your suggestions. To make it work creating a very tiny mod was necessary. However -autostart-nonvisual seems not to work as intended. According to readme: -autostart-nonvisual disable any graphics and sounds Apparently it ignores all other settings and runs the game with hardcoded options like if it was tailored to --rl-interface only. This is the command I run the game. It includes my custom map with all algorithms, and mod name with exposed functions. pyrogenesis -mod="public" -mod="train" -autostart-victory="endless" -autostart="skirmishes/test_sparta" -autostart-disable-replay -quickstart Typically map starts automatically, then training is performed, and after one generation script closes the game due to high memory usage. Then bash script runs it again indefinitely to iterate etc. Expected output in my case after pyrogenesis start is something like: (....) TIMER| session/setup.xml: 82.173 us TIMER| session/sprites.xml: 381.305 us TIMER| session/styles.xml: 72.086 us TIMER| session/session.xml: 31.4006 ms GAME STARTED, ALL INIT COMPLETE WARNING: Beginning from JSON file WARNING: Trigger.js: Trigger "TestLearningCommand" has been registered before. Aborting... WARNING: Load WARNING: currentIteration: 9 WARNING: entitiesToDo: 0 WARNING: entitiesDone: 250 WARNING: crossover chance: 50% WARNING: mutation chance: 40% WARNING: 512 (....) However if I just add "-autostart-nonvisual", then map isn't being loaded, and some other scripts or computations are being performed instead (look after "GAME STARTED, ALL INIT COMPLETE): bash:~/.local/share/0ad/mods/user/simulation$ pyrogenesis -mod="public" -mod="train" -autostart-victory="endless" -autostart="skirmishes/test_sparta" -autostart-disable-replay -quickstart -autostart-nonvisual TIMER| InitVfs: 197.269 us FILES| Main log written to '/home/przemek/.config/0ad/logs/mainlog.html' FILES| Interesting log written to '/home/przemek/.config/0ad/logs/interestinglog.html' TIMER| CONFIG_Init: 1.27741 ms WARNING: Init GAME STARTED, ALL INIT COMPLETE Turn 0 (200)... Turn 1 (200)... Turn 2 (200)... Turn 3 (200)... Turn 4 (200)... Turn 5 (200)... Turn 6 (200)... (...) This may be a bug if (=>readme) is accurate. Is it possible to disable screen and sound in a different way? Thank you!  Quote Link to comment Share on other sites More sharing options...
Vantha Posted Monday at 18:37 Report Share Posted Monday at 18:37 It works for me just as expected. Have you tested it with other maps? If it works with other maps, the issue probably lies with your trigger script. Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted 21 hours ago Author Report Share Posted 21 hours ago Yes, I checked other maps. Same result. Always executes "Turn 0 (200).." script etc ignoring the map. Doesn't matter if mods are used or not. If I remove --autostart-nonvisual everything is OK and maps are loaded normally. Maybe my version of the game is the issue? ~/.local/share/0ad/mods/user/simulation$ pyrogenesis -autostart="skirmishes/tarim_basin_2p" -quickstart --autostart-nonvisual TIMER| InitVfs: 160.709 us FILES| Main log written to '/home/przemek/.config/0ad/logs/mainlog.html' FILES| Interesting log written to '/home/przemek/.config/0ad/logs/interestinglog.html' TIMER| CONFIG_Init: 2.53454 ms FILES| Replay written to '/home/przemek/.local/share/0ad/replays/0.27.1/2026-01-06_0007' GAME STARTED, ALL INIT COMPLETE Turn 0 (200)... Turn 1 (200)... Turn 2 (200)... Turn 3 (200)... Turn 4 (200)... Turn 5 (200)... Turn 6 (200)... Turn 7 (200)... Turn 8 (200)... Turn 9 (200)... Turn 10 (200)... ~/.local/share/0ad/mods/user/simulation$ pyrogenesis -autostart="scenarios/polynesia" -quickstart --autostart-nonvisual TIMER| InitVfs: 194.705 us FILES| Main log written to '/home/przemek/.config/0ad/logs/mainlog.html' FILES| Interesting log written to '/home/przemek/.config/0ad/logs/interestinglog.html' TIMER| CONFIG_Init: 3.4274 ms FILES| Replay written to '/home/przemek/.local/share/0ad/replays/0.27.1/2026-01-06_0008' GAME STARTED, ALL INIT COMPLETE Turn 0 (200)... Turn 1 (200)... Turn 2 (200)... Turn 3 (200)... Turn 4 (200)... Â Quote Link to comment Share on other sites More sharing options...
Stan` Posted 21 hours ago Report Share Posted 21 hours ago Maybe it's more strict in non visual although it should not, what if you try to figure out why it's registering it twice ? Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted 21 hours ago Author Report Share Posted 21 hours ago While quick searching the repository I encountered this:Â https://gitea.wildfiregames.com/0ad/0ad/src/commit/b77ee3c5dcde432787b0bdf0386fbf766e19f25f/source/ps/Replay.cpp#L279 It's part of the "Replay" method and looks precisely as part of the output I got. Is it possible that --autostart-nonvisual is designed only for replays and you simply can't start the match from scratch when this parameter is present? Quote Link to comment Share on other sites More sharing options...
Stan` Posted 21 hours ago Report Share Posted 21 hours ago Every game is a replay. Replays are just a list of turns which sometimes have a list of commands. The debug is there so you know it's actually running. Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted 21 hours ago Author Report Share Posted 21 hours ago I see. I'll try to run same command on windows build to see if it works there. That should hopefully give me the answer if the problem is my version of the game or something else. Quote Link to comment Share on other sites More sharing options...
Stan` Posted 21 hours ago Report Share Posted 21 hours ago 18 hours ago, CulturedCait said: WARNING: Trigger.js: Trigger "TestLearningCommand" has been registered before. Aborting... Why not fix this? Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted 20 hours ago Author Report Share Posted 20 hours ago 44 minutes ago, Stan` said: Why not fix this? Nope, this is not the issue. Script works correctly. It was just redundancy but didn't affect anything. I removed "TestLearningCommand" so you could see it had nothing to do with this matter. Problem is that --autostart-nonvisual does *something*, but certainly not what user selected with "-autostart". Here are some proofs: Â --autostart-nonvisual causes high CPU usage and uses all cores. While regular game uses only one thread (~6-7%). My script should generate "debug.json" after start, and about each minute performs the save game. With --autostart-nonvisual nothing happens. Even if we assumed --autostart-nonvisual suppressed all warnings, but still performed map and scripts, I should expect to JSON files and savegame to be created. --autostart-nonvisual uses only ~250Mb of memory. My script after start uses ~1G and after 15min consumes around 5G. It's combat between ~300units performed each 5sec. Screen below pertains --autostart-nonvisual: Certainly pyrogenesis performs some heavy computations on all cores. And selected map is not being run. Â While this screen below is without -autostart-nonvisual. One core used, JSON files produced, output produced, and all works as intended: Â I checked on windows and it's same story. The only difference is that if you use --autostart-nonvisual, "Turn (....)" output is not being produced at all, program exits immediately spawning another process. Rest is the same. Spawned process *does something* on all cores, but not running the map that it was asked. Â Quote Link to comment Share on other sites More sharing options...
Stan` Posted 19 hours ago Report Share Posted 19 hours ago Okay let's adress points in order then  1 hour ago, CulturedCait said: Nope, this is not the issue. Script works correctly. It was just redundancy but didn't affect anything. I removed "TestLearningCommand" so you could see it had nothing to do with this matter. Alright, just wanted to make sure it was not causing any weird side effects  The difference between non visual and visual is that it runs the game as fast as possible, that's why it's using so much processing power. If your script expects the game to run normally from a time standpoint that's maybe where it's going wrong. That's also why I suggested the RL interface, as you can you can run step to control things a bit more finely. If you ran the profiler2 you'd see what it was spending time on https://gitea.wildfiregames.com/0ad/0ad/wiki/Profiler2 https://gitea.wildfiregames.com/0ad/0ad/wiki/EngineProfiling  1 hour ago, CulturedCait said: checked on windows and it's same story. The only difference is that if you use --autostart-nonvisual, "Turn (....)" output is not being produced at all, program exits immediately spawning another process. Rest is the same. Spawned process *does something* on all cores, but not running the map that it was asked. Yeah the terminal is not able to capture the game's output, you need something like dbgview. It's very likely it's running in the background still.  Quote Link to comment Share on other sites More sharing options...
CulturedCait Posted 18 hours ago Author Report Share Posted 18 hours ago That explains a lot. Hmm.. Weird idea, but is it possible that in nonvisual mode "OnInitGame" is not invoked?? This is my entry point: { let learnTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); learnTrigger.RegisterTrigger("OnDeserialized", "TestLearningLoad", { "enabled": true }); learnTrigger.RegisterTrigger("OnInitGame", "TestLearningStart", { "enabled": true }); learnTrigger.RegisterTrigger("OnPlayerCommand", "TestLearningCommand", { "enabled": true }); ////learnTrigger.DoAfterDelay(1000, "TestLearningSpeed", { "enabled": true }); } In single run my script ends in about 20 minutes,then Engine.Exit is issued to close the game. If it works much faster in -nonvisual mode, then we should expect it to finish in less than 20 minutes. What I did was to test it again with -autostart-nonvisual and just waited. Although it didn't produce save games and JSON files, we could explain this is somehow forbidden in this mode. But considering it has still not finished after 30 minutes, maybe the whole thing is just that the script is not triggering in -nonvisual mode?? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.