NIMBY Rails devblog 2025-07
1.17 Dynamic train braking
Trains are now capable of braking for the simulation-dynamic obstacles of (potential) red signals and other train tails. This has been implemented as a generic capability of placing a dynamic “speed bump waypoint” on the existing train path, rather than more specific coding for these conditions, so other “obstacles” could be added in the future. The system is also capable of speed-matching, not just stopping, by capping max speed to the target in front of it, and by disallowing positive acceleration as long as target is slower (this is not as a clear-cut rule as one might think, but it works out well in the end).
For the signal case, trains can only check the signal, they cannot assert a reservation. This means a go check can turn into no-go at any moment. This is because asserting reservations from signals needs to be coordinated, and this coordination forces the train into single thread mode. This is fine when it happens sparingly in the context of the whole simulation, like when a train reaches a signal. Surprisingly if this was a (more) realistic simulation it would also be fine, because under a realistic design a train approaching a signal at speed has a “last chance” to reserve it in advance, equivalent to the minimum braking distance. If it tries to do it later, it will run past the signal, making any further checking pointless, so it’s really equivalent to doing a single check. But existing saves are not set up to handle this kind of mechanics, at all, so it cannot be done, since they assume braking from any speed to 0 can be done instantly at any point in time. I will explore ideas to improve this further, but they will require additional player actions and construction.

For the train tail case, again I had to reach a compromise to support existing saves. The results don’t really look like railway operations at all, in my opinion, but trying to do something more realistic, like enforcing a distance of at least the minimum braking distance, would again have a big impact in existing saves, so it cannot be done. The current implementation looks a bit funny but it at least has the property of keeping trains running smoothly rather than the previous system of bumping into each other, so it is an improvement in the end.
1.17 ending soon, 1.18 started
The main motivation for the beta builds of 1.17 was testing NimbyScript in real saves, with a real simulation. It passed the test successfully, with a couple issues detected and fixed during the beta. One of them was potentially fatal, with some player PCs being unable to compile the scripts, but fortunately it was solved in the end. It was also intended to test the performance penalty of scripting, which has proven to be zero (at least for the simple scripts in 1.17). It was very important for this testing to be as controlled and limited as possible, for this reason the scripts are built-in and cannot be edited. Opening up editing for players and modders will for sure exhibit its own set of issues and performance impact but for now there’s a baseline of stability to build upon.
On top of the low level testing for stability and performance, which was successful, I also wanted to use this beta build period to develop a very important part of the scripting system: the binding interface. Bindings are how the scripts are “glued” to game objects, in all senses, from the technical details of how and when the player code will be invoked, to how the player picks scripts to run in the UI, and how these scripts are parametrized. This was a failure because I didn’t complete the implementation of this task, and I discarded my planned design.
I had what I believed was a solid design, but when I started to implement it, it turned out to be less well designed than I hoped. I made a weeklong pause at the end of the beta builds to correct the design and I think I got it right this time, but it’s also going to be more complex than I hoped. Therefore I decided to not implement it during the 1.17 beta, and move it to 1.18. The dynamics of the public beta period are about delivering fast build with fixes and small and medium features, and I believed I could fit script binding in this dynamic, but I was wrong.
Another compounding effect is that I also wanted at the same time to add new signal features. And I started doing so, with dynamic train braking (not exactly a signal feature but it requires signaling to be in a good state to work properly). After I finished and polished this feature, I realized something: I was preparing the game to go from “signaling is simplified and impossible to touch to not break saves” to “signaling is player-coded and essentially boundless in complexity”, with no in-between steps. It meant the baseline signaling would just be a compatibility layer of the existing path + balise system but written in NimbyScript, then players are supposed to code and/or install mods with signals from the workshop. Nothing wrong with the last part, but the first part seemed lacking to me. I’ve never had the chance to expand on the signal system. I was gearing up to implement a signal system-system, not a signal system, so to speak. Maybe the stock signals should be a bit more sophisticated, rather than fossilized v1.2 path signals? The existence of NimbyScript does not change the fact I roadmapped certain signal features, both public and private.
It would also help future script writers as a better example than the existing signals. Even if I document the APIs and data structures, the current signals are simple, they don’t do many things. A more complex example which is also built into the game as a default set of signals and scripts would be useful for scripters, and also try to set some interop standards. I really want to avoid a babel tower situation of signal systems, therefore a review of the system is required to make it easier to have multiple scripts affecting a single signal, or multiple signals properly collaborating over the same set of reservations, for example. So the goal of 1.18 is to expand the stock signal system, now implemented in NimbyScript, and more fleshed out scripting integration than the current 1.17 placeholder UI with no bindings and hardcoded script names.
In the end I decided to stop the 1.17 beta (apart from bugfixing, which is ongoing but about to end soon) and start 1.18 behind the scenes as a direct continuation of these developments. 1.17 beta will move to default soon and 1.18 internal development has already started.