NIMBY Rails devblog 2020-11

The Early Access release date (almost)

Originally I planned to release the Early Access just before the final sales season of 2020 started, so at the end of October, but I didn’t manage to have the game ready in time. Instead I continued working towards an EA version without a fixed date other than “when it’s ready”, knowing I was very close to achieving my feature and product milestones anyway. After the work put in November, which has completed most of the remaining EA development tasks, I am now confident in setting a new release date for the second half of January 2021. I will use the extra time to put in more polish, do more testing, and do all the non-development related tasks like remaking the trailer and contacting press, youtubers and streamers. Also I want to avoid December, which is the worst month of the year to release a game (unless you are CDPR :).

Steamworks mod uploader for mod authors

Continuing with the mod work from October, I added a mod uploader for Steamworks.

This tool will allow mod developers to upload new mods and new versions of their mods, without having to use any external programs.

The basic idea of mod uploading is to first develop a local mod in your PC, as described in the mod guide, and once you are confident of its quality, use the tool to create a Steamworks mod and upload your local mod as its contents. At a later time it is also possible to select a previously created Steamworks mod and upload a new version based on your local mod, using the same interface. If you are interested in more details of the process check the Steamworks uploader section in the mod guide.

Mods in multiplayer

One of the major “But how?” remaining tasks was to correctly enable mods in multiplayer. I want to support it, and as seamless as possible, without forcing players joining a server to subscribe and download new mods the server host decided to enable for their game. First, I believe forced mod subscription is rude and I wouldn’t like a game doing that. Second, there are major problems related to the potentially unbound texture sizes. Some players (and their GPUs) will be fine with, say, mods that add thousands of new train models to the game. But other players won’t care for that and/or cannot run it. Sending the textures via the network protocol without subscribing was a less rude alternative, but it still won’t solve the problem of players on less powerful PCs, plus it could amount to a lot of data and slowdowns.

In the end I reached a design compromise. Mod data is divided in two parts: rules and textures. Rules are “numbers”, including data like the size of the train cars or their speed. The game is perfectly capable of running untextured trains with the full set of rules. And when compressed into the multiplayer protocol serialization, they are very small. So now, when a client joins a server, the server sends the full set of rules for the mods enabled in the game, but it does not send the textures. The game will render those trains with placeholder textures. The client player can then look at the mods list and decide if they want to subscribe to the mods to download the missing textures.

Translation mods

The second must-have mod type for the EA launch is translations mods. These are now implemented, and they are developed and uploaded just like train mods. Once the user subscribes to a translation mod in the Workshop, it will show up in the Options dialog:

The mod guide is not yet updated for this new kind of mod, I will make sure it’s documented in the coming weeks, and offer a full example translation.

Enhanced Unicode support

Translation mods highlighted an already existing problem in the game UI. While the font renderer I was using for the map labels was powerful enough to support Unicode glyphs rendered and cached on the fly, with a dynamic atlas, the UI renderer required a pre-baked atlas, which obviously was never going to work with Asian fonts. So I replaced the UI text renderer with the map text renderer, and now both are based on a fork of the superb fontstash.

This in turn allows me to bundle as many fonts as I want to support Unicode ranges, and I believe it will be usable for many languages. Some others will get some glyph rendering, while lacking important features to properly display their scripts, like shaping, combining diacritics, or RTL text. Still, I believe the situation is much improved now.

Single player / multiplayer save compatibility

Some critical fixes were landed to enable seamless handover of saves between single player and multiplayer. When hosting a game server, it will be possible to continue any saved game, with no distinction of single or multiplayer. Additionally both the host and also the clients who connect to them will be allowed to save the game locally. And these saved games can then be continued in single player or multiplayer, with no restrictions.

Multiplayer UI

NIMBY Rails multiplayer works with a classic client-server scheme, but it’s running on top of the Steam P2P relay network. Players will pick the role of host or client. When a player is host, they are actually hosting a game server in their game session, from their PC, and directly serving to the client players using their Internet connection (via the mentioned Steam P2P network).

Additionally, at this point, the only supported multiplayer game mode is fully cooperative. There are almost no limits to what a client player can do when joining a server.

For these two reasons I believe the game must offer host players a set of tools to control access to their hosting sessions. The game implements an ACL, with roles similar to those of firewalls (allowed, denied, pending review), plus a couple of rules to implement default actions for users not already in your private ACL.

And in the same vein, there concept of a public game is not directly supported. Instead the game makes heavy use of, and delegates often, to Steam own tools for friend lists and inviting.

Client side

Let’s imagine you are a player who wants to join a game session in some host, as a client. You can select the “Join Game” option from the main menu, and then are presented with this dialog:

The first panel shows your steam friends, and what is their current Steam and NIMBY Rails status. If a player is currently hosting a game, or is inside a game hosted by another player, the “Request join” button is enabled for them. This button will trigger the popup shown in the next section. If the player is in-game but not inside a multiplayer session, the game allows you to directly host a game yourself and automatically invite them (more info on inviting in the next section).

The “Strangers” input allows you to paste a join code for any host, even for players who are not in your friends list. These players may veto or ignore your join request.

The last button opens your Steam Friends list, in case you prefer to chat and request a game session join from the Steam interface.

Server side

Conversely, players hosting a game can use the Steam invite feature.

This inserts an invite in the chat you have with your friend:

And then they will also be prompted in-game, for extra verification, since joining a game will quit their current one, be it single player or multiplayer.

If your friend is not playing the game, it is even possible for Steam to automatically launch the game and ask them from the main menu to accept the invite.

Host players have extra tools while playing the game, in the form of the server management dialog:

The first tab, “Invite”, is similar to the Join dialog, except here the goal is to invite friends and strangers. This time the action presented when you select a friend is to send them a Steam invite, while at the same time adding them to your ACL as an allowed player. You can also share your join code with friends and strangers alike, but keep in mind it does not automatically allow them to join your game, bypassing your join rules. You may still need to enable them in the Access list.

The “Connected” tab shows some stats for the connected players. It’s mostly internal data I need to diagnose problems, but the net stats option shows useful information to hosters, so they can keep an eye on the bandwidth usage and packet loss. You can pretty much ignore the ping, since the game has a fixed, deliberate one second lag for the train simulation. Hey, it’s trains on rails, not CS:GO :)

The “Access” tab allows you to pick the default access rules, and shows the ACL. The ACL is just a list of players that have been allowed to join, denied to join, or who triggered the “ask me” rule and you ignored. From this list you can decide what level of access you grant to those players. An “allow” access will always allow the player to join, and a “deny” access will always deny them. Being in the ACL overrides the first two rules.

These two rules are probably what most players will bother with, rather than vetoing every single player via the ACL, and the screenshot shows their default state. Each rule has 3 possible behaviors: ignore (if not allowed already, the player won’t be able to join and you won’t be notified), always allow (the player joins immediately, unless they are denied), and “ask me”, which triggers this message:

This may sound complicated, but as I mentioned, I believe the default rules will be okay for most players. Only players that want to run semi-public servers (which is probably not a good idea at this point) and make heavy use of join codes will need the ACL. But it’s there for anybody to use.

In conclusion, the multiplayer UI/UX is built around the idea of cooperatively playing with trusted friends, and using the Steam features around invites and join requests where possible.