On OpenRCT2’s Frontlines

In the previous essays we have analyzed OpenRCT2’s architecture, but who can give better insights than OpenRCT2’s developers themselves? We have asked the OpenRCT2’s developers questions relating to OpenRCT2’s architecture, the project itself and their experiences while developing for it.

Questions and answers

Q1: Could you tell us about some difficult architectural decisions that had to be made when developing OpenRCT2?

A1 - duncanspumpkin: “The main difficulties has always been maintaining the compatibility and feel of RCT2. In the early days of the project we also had to maintain memory compatibility. It meant that global variables had to be assigned the exact same memory address as vanilla. For some functions we had to patch the game on the fly which meant we also had to maintain calling convention (Chris Sawyer calling convention is a little unique). Its for these reasons the initial codebase was in C. It was much easier to handle the interfaces between the assembly and the OpenRCT2 code. You can still see that the majority of the codebase is written in almost C like code. After we had implemented the whole game and RCT2 no longer required to be in memory we gained the ability to change memory layout and slowly moved to C++. That is one of the main reasons why the codebase still has many global variables and restrictions. The main architectural differences are with loading/saving; window management; game actions. The loading and saving in RCT2 was pretty much a memcpy from a file into memory. As we want to change memory layout we have to read each individual field, convert it if required to new type, write into memory. In theory this is much safer than RCT2 but it has the trade off of a considerable increase in amount of code. Window management has moved to an intents system to decouple the ui from the base game. This allows the servers to no longer require processing of windows and such. Game actions is our replacement of Game commands. Game commands were very restrictive as to how much data could be stored in them and CS liked to pack his data in many different ways to get around that issue. We are very greatful that CS implemented the game commands like he did as it meant making things multiplayer was not too difficult. The game actions are much easier to understand and they are one of the few areas of the codebase that is actually written as C++. Although admittedly they aren’t the best examples of good code.”

A1 - Gymnasiast: “I haven’t been involved in architectural decisions much. I think that’s something that IntelOrca, Duncanspumpkin, janisozaur and ZehMatt can tell you more about. What I can say is that initially, OpenRCT2 didn’t really have much of an architecture. It simply implemented the RCT2 subroutines one by one as separate C functions, creating some huge files of mostly related functions in the process. But since then, there has been a lot of refactoring, including splitting the engine from the UI and slowly moving code to object-oriented C++ rather than just C functions.”

Q2: In what ways does OpenRCT2 deviate from the original RCT2 architecture and why?

A2 - Gymnasiast: “The above-mentioned split between engine and UI and the object-oriented approach are two important ones, I think. The original RCT2, being written in assembly, was mostly procedural and monolithic. We have done our changes to make the code more robust and easier to maintain and extend, and also to allow using the engine for other purposes than simply playing the game, like setting up a headless server with a minimum of overhead for example. It’s also possible to use the engine in other projects now.”

A2 - janisozaur: “The original architecture of RCT2, or rather the implementation detail thereof, code written in x86 asm turned out to be a blessing when porting the project to Linux.1 As there’s just a handful of calls to system or any external libraries from the game, I managed to create a process to host some memory at expected layouts and then load original binary there. From inside the asm code, there is no difference what the operating system is, as long as the memory layout is what the game expects. This attracted some more developers and was used for creating OS X builds in similar fashion.

Inside RCT2 virtually all the actions affecting game logic were sent to a single dispatch mechanism which then executed relevant handlers. This was used by other team members to create a multiplayer, akin to one found in OpenTTD or CS’s Locomotion, usually referred to as “lockstep simulation”. Initial implementation used to have permissions which had to be granted to each new client upon joining. With frequent crashes, disconnects and inherently long play sessions of multiplayer, this meant server hosters were left to either spend lots of time assigning permissions manually, default to allow new clients to build (and remove) things in park which invited vandalism, or password-protect their servers. I set out to create a system2 that would address all those issues at once and offer persistent permissions based on PKI. This was well-received and lives on in the code to this day.

One major improvement OpenRCT2 has over the tight coupling of RCT2 is how we split it to some sub-components.34 That work basically splits the project into ‘OpenRCT2 client’ and ‘libopenrct2’, the former provides user-facing part, latter has all the game logic in it. This allowed us to create lots of utilities inside the project relatively easy. Based on this work we were able to add Google Test-based test suite, benchmarks, format converters, headless clients…”

Q3: What are the main trade-offs that needed to be considered during development of OpenRCT2?

A3 - Gymnasiast: “Mostly ease of coding versus performance. We are lucky to have people like janisozaur and ZehMatt on the team, who are good at squeezing out more performance.”

Q4: Are there any parts of the architecture you would have designed differently in the original game?

A4 - Gymnasiast: “Chris Sawyer had good reasons to develop RCT and RCT2 in assembly: he was used to it and it made the game perform extremely well, even on computers that were very old for 90s standards. I have personally run RCT1 on a 1995 computer and RCT2 with half the minimum specified RAM. That’s no mean feat, and I think his decisions paid off. So I’d say he made the right call.”

Q5: In what way did your (work/study) background help with developing this project?

A5 - Gymnasiast: “Apart from general programming knowledge, not a whole lot. I never touched any C or C++ before this project, coming from a PHP background, and suddenly having to deal with memory management, string handling and other stuff that higher-level languages such as PHP and Java so kindly abstract away for you, that was quite a steep learning curve initially.”

A5 - janisozaur: “During my studies I majored in ‘Games and Computer Simulation’, but never really wanted to get into gamedev. I did follow it as my passion, though, as I often inspected new releases of emulators, game engines (such as VCMI, DesMuMe…) and was always into ways of improving their performance. These days I find a lot of inspiration in OpenRCT2 that helps me a lot in my day job. I can freely test out new concepts, coding techniques, tools on OpenRCT2 code and I can spend as long as I want or need on any given task.”

Q5: Do you have any tips for (new) developers wanting to contribute to open-source projects like OpenRCT2?

A5 - Gymnasiast: “Read the available documentation and join the chat, which almost all projects have. Ask any questions you have there. I like to think that we are a very friendly open source project and don’t mind explaining everything, but some are a little bit more strict and expect people to carefully read the documentation and rules in advance.

When picking out your first contribution to make, take a simple one. Some projects (like ours) have labelled some issues ‘good first issue’ or something similar. In other cases, you can ask on their development chat. My first contributions were simply bug reports.

My first code contribution was simply making two hardcoded strings translatable. It grew from there. I also got acquainted with the code base by just browsing through and sometimes modifying code, just to see what happened.”

  1. https://github.com/OpenRCT2/OpenRCT2/pull/1956 

  2. https://github.com/OpenRCT2/OpenRCT2/pull/3699 

  3. https://github.com/OpenRCT2/OpenRCT2/pull/5336 

  4. https://github.com/OpenRCT2/OpenRCT2/pull/5458