Louis' archive

One Year of Descent 3 Open Source

One year ago, the engine source code of the 1999 6-axis shooter Descent 3 was released as Open-Source software under the GPLv3 license. This post collects personal notes about the community developments that followed this release, where the game stands today, and where it is headed. I hope you will enjoy the journey!

Early times

When the news broke and spread across the web, much attention was drawn to the project, which found headquarters on the Descent Developers Discord. Kevin Bentley, an original developer of Descent 3, spent time solidifying the community around a common fork and recruited a small team of volunteers to help merge Pull Requests (PR) coming in.

Quickly, people got the game building on Windows, then Mac and Linux. A continuous integration pipeline was set up to keep the game in a clean building state across PRs. The first days were pure chaos; in a week, about a hundred PRs were submitted, mostly build, CI, documentation, and code style fixes. It was relentless for the maintainers of the project, who had to figure out which contributions to accept, as well as the general direction to give to the project. Of course, some mistakes were made; the first one being to format the entire codebase using clang-format very early on. While this seems like a great idea at first, it had the unintended consequence of creating major conflicts with people’s forks, which were based on the non-formatted code. In particular, this slowed down porting Icculus’ patches, whose 2020 steam port has many improvements not included in the released code. There was also some back and forth on dependency management, caused by a lack of clear management and agreement between maintainers.

Towards a first release

After an intensive first few months, when everyone in the community had a stable build and could play the game on their favorite OS, it was time to get a first release going. For this first released open-source version of Descent 3, we set clear goals: full 64-bit compatibility, playable on Windows, Linux, and MacOS. This release would not have any gameplay or graphics improvements over the commercial game but would be a stable base for hackers to play with. Most dead code supporting outdated hardware was removed, and platform-specific code was reduced to a minimum. This 1.5 release was tagged in August, about 4 months after the code release. It was well received by the community, although some players had difficulties setting up the game. This is something we would focus on later on for the next version.

Piccu Engine

While a large part of the community was busy slowly discovering the newly released code, veteran Descent hacker InsanityBringer went on to build their fork which would become known as Piccu engine. InsanityBringer was already known as the author of the InjectD3 mod, which brings a lot of quality-of-life features to the aging D3 game, and became the de-facto standard for most players. This mod worked using DLL injection, which limited its ability to act on the game itself. With the full code source in hand, InsanityBringer quickly released major updates to the game’s controls and graphics. The stated goals of Piccu Engine were much different than the community port, tailoring a better experience for seasoned players, to keep full compatibility with the base game. In practice, this meant keeping full 32-bit support, and solely focusing on Windows support.

Ports, ports ports

Opening the game’s source also led to a variety of ports by different community members. The first notable one is the PortMaster version of Descent 3, made to run on various Linux handheld devices. At the time, the engine still used legacy OpenGL 1 calls for graphics, which is generally not compatible with modern handheld devices that have OpenGLES drivers. PortMaster’s general solution to this issue is using the GL4ES compatibility layer, translating GL1 calls to GL ES. This works pretty well, at the cost of a slight performance hit.

In the same vein, an experimental WebAssembly port was created, also leveraging GL4ES, because Emscripten does not emulate OpenGL 1.5 correctly either. This port was not very practical, because the required game assets are about 600MB, which is a lot to be transferred from a web server. This port could be revisited for better performance now that the engine uses OpenGL2 and SDL3! Some work could also be done on reducing or compressing game assets, reducing them to their bare minimum, so that a web server can transfer them in a reasonable time (bar the issues with uploading game assets).

Later, an Android port was created, using yet another method of managing game assets: the user needs to upload them to a web server running on the Android device, so they are copied to persistent storage. Controls on a phone/tablet are also an issue that has not been solved yet: a 6 axis shooter requires quite a lot of different buttons to be played, and the game assumes that players have access to a full keyboard, also using function keys. A phone has, well, a touchscreen, and not a lot of function keys. A full native port would need the whole interface to be thought through again, and on-screen buttons to be created, all of that without clogging too much the view. The Android port was also made possible by the implementation of ARM64 cross-compilation pipelines created for the PortMaster build.

There is still a lot to do when it comes to porting and hardware compatibility, but all of that is hugely facilitated by the SDL3 back-end, which offers compatibility with basically any platform one can think of.

Challenges

Improving and maintaining this new code, and managing the flow of incoming contributions came with a set of challenges, which may not be those one would expect at first!

Third-party management: C++ is one of the most widely used languages today that does not have a standard 3rd-party dependency management system. Depending on the project, you may find git submodules, CMake ExternalProject, raw binaries in the repository (??), or any of the nascent solutions at a unified package manager, such as Conan or VCPKG. And did I mention that every developer has a strong opinion about it and thinks their solution is the best? At the start, Descent 3’s only 3rd-party libraries were Zlib (embedded in the code), mvelib for video playback, and SDL for the Unix builds. Over time, we figured out that a logging library would be handy, as well as an HTTP library for downloading level maps online. After some wandering, we chose VCPKG as the main dependency management system, because of its cross-platform support, easy integration into CMake, and wide range of available libraries. This was not a tool I was personally acquainted with at the start but eventually built up familiarity. While I find Windows and MSVC integrations great, Linux support is a little more rough around the edges; system package requirements to run the libraries build are sometimes obscure and undocumented. However, the triplet system has proven very handy to manage cross-compilation to ARM64, and to produce MacOS Universal (x86+x64) builds. VCPKG loads as a toolchain in CMake and provides the targets for the required libraries. This also means that it is trivial for any Linux packager to swap out VCPKG for system packages, given that the versions are compatible: either use VCPKG to provide libraries, or let CMake’s FindPackage look on the system for the library.

Assets packaging: As only the source code for Descent 3 was released under an open-source license, assets are still proprietary. Players need to either get the game from an online shop (Steam, GoG) or install it from a CD and patch it to the 1.4 patch to get game assets. Not packaging the game with assets means that open-source ports need to implement a different logic to find them. Our first 1.5 release assumes that assets can be found in the same directory as the game executable, just like the commercial game does. This creates extra steps for the player, who needs to find the assets’ location from his commercial D3 installation and copy them over to the port’s directory. This operation may be unfriendly to some users, who’d hope for an easier setup. So, why not look for the base game installation to automatically find assets? Well, it’s not that easy; Steam lets the user select individually each game’s installation path and has no standard API to retrieve it. The same problem occurs for a CD installation as well. This is not a solved problem right now, but the current solution is looking at usual Steam/GoG installation paths for assets, as lets the user select the actual installation path from a GUI selection window otherwise.

Non challenges

Some things could have been challenging if done differently, but luckily were not.

Cross-platform support: the version of the engine code we were provided used CMake as the build generation system. This allowed the community to quickly get builds going on all platforms. The original code contained quite a lot of platform-specific code, using SDL1.2 for Linux, and DirectX for Windows. Icculus, who originally ported Descent 3 to Linux in 2000, generously contributed patches from his 2020 SDL2 port of the game to the GPL upstream. Having an SDL2 port meant that the Windows-specific DirectX code could be directly removed to get unified code for all I/O and graphics. SDL is a god-send for easy cross-platform support, and abstracts away all low-level OS primitives so we barely have any platform-specific code left yet. The game settings, which used to be stored in the Windows registry, are now stored in a portable configuration file on all operating systems.

Community buildup: unlike most open source projects starting from scratch, Descent 3 benefits from the support of an existing community, already experienced with the game and the series as a whole. New people keep coming in and contribute to the effort without any additional external communication effort. It’s always great to have people willing to help test patches at any moment!

Legacy code: The Descent 3 code we inherited was barely C++98, more like C with classes, using exclusively the C standard library. While it does not compare with modern C++20 practices, compatibility with current-day compilers is still great and did not cause too much trouble. One cannot dismiss how well 25-year-old C++ code holds up to this day! Overall, this legacy code is not a huge hassle to maintain and can be reworked iteratively. Some sections have been reworked to use STL containers, standard strings, and smart pointers, but this can be done gradually, without breaking the whole structure.

Where we stand now

After one year of contributions from the community, the game now builds as a 64-bit executable and runs as expected on Windows, MacOS (with a signed executable!), and many Linux distributions, using a unified SDL back-end for input and display. Video playback for the intro and cutscenes works on all platforms using the MVE video format (OGG format as used in the Unix Steam port cannot be played yet). Multiplayer has support for a new server tracker and IRC chat integration. The game is playable also on high-resolution displays, in windowed and fullscreen modes. And much more!

A new stable release is expected when a few more items are checked off the list, namely automatic and more user-friendly game file discovery, and improved gamepad and mouse support.

Looking forward

While a lot of improvements have been made so far on Descent 3, there is still a lot left to do. I list here the main areas where the Descent 3 community port could be improved.

Improved controller support: This is the last major item remaining for our 1.6 milestone. The current interface for controller support is old and crusty and has issues with modern controllers. Presets are mostly relevant to now outdated hardware and control selection is unintuitive when compared to modern standards. We could have better presets and default mappings for gamepads, using the SDL3 gamepad API that provides button semantic roles for many standard gamepads. Improved flight-stick support would also be nice, but requires hardware to test properly.

Level scripting: probably the biggest challenge out there. Descent 3 levels can execute scripts, that are binaries embedded in the game assets. These scripts can add logic around doors, triggers, etc. Scripts for levels from the main campaign have been released alongside the game engine code, so we can compile them and make sure they can be run. We build a custom HOG 1 file with a dynamic library for each level of the campaign: a .dll on Windows, .so on Linux/BSD, and .dylib on MacOS. This works well, except for user-made levels, which can also be scripted using the level editor. In this case, a Windows 32-bit DLL is generated and embedded in the level HOG file. As a consequence, only Windows 32-bit clients will be able to run the level script properly. The Descent 3 community port does not build 32 binaries anymore, which means that user-created scripts cannot be run anymore. As a side note, this is the main reason that the Piccu Engine port is still 32-bit only to this day. Being able to properly run user-created levels is critical from a game preservation standpoint, and it is currently the one major limitation that the 64-bit community port has. There is also a security concern associated with it: the online Descent 3 server can send levels to clients connecting to it, including the script DLL, which is therefore executed on the client. Yes, it sounded fine to run random binaries on people’s computers in ‘99, but today, that would be considered critical. The Descent 3 community port has disabled the execution of scripts from levels outside of the main campaign to prevent any exploit. Most of the user-created stages over the years were distributed without the script source code available, so they likely cannot be compiled anymore. We are left with a Win32 x86 binary blob that contains compiled C++ code, that we would need to somehow sandbox and run securely on any OS. Some suggestions have been made to make this possible, but nothing concrete has been done yet.

Level editor: The source code for D3Edit, the Descent 3 level editor was also made public; to my knowledge, nothing much has been made to improve the Descent 3 level edition experience starting from D3Edit, which is an old MVC app. We have a working CMake option to compile the editor on Windows, but the executable has not worked properly since the transition from DirectX to SDL2. It may be more valuable to the community to use the amazing Inferno Descent Editor and add the necessary primitives to make it fully compatible with Descent 3.

Unified Descent launcher & settings: Another idea that has been discussed before, is that now that all first 3 Descent games have open source ports, it would be nice to have a unified UI launcher that can look for game files and try to copy control configurations between games. This would also benefit installers such as PortMaster.

Competitive Descent: There is a small but dedicated player base for competitive Descent 3, that has long been waiting for improvements to the “less casual” part of the game. There are a lot of improvements to be made to the networking code, server deployment, configuration, and controls to make it a better experience overall. And why not organize a physical LAN someday?

Linux Distribution Packaging: After the first release of Descent 3 came out, Linux users started packaging the game for their distribution. To my knowledge, there is currently an OpenSUSE and NixOS package If you’re a packager and wish to ship the game for your distribution, feel free to do so!

Free Asset pack: Unlike the previous Descent games, Descent 3 does not have a shareware distributable version, so players cannot run the game at all without a complete set of proprietary assets. While this would be a high-effort task, for easier distribution and testing of the engine, people suggested creating new compatible assets from scratch, under a permissive license.

If you wish to help solve one of these, please reach out to us! We’re active on Discord for day-to-day discussions, as well as GitHub for more formal reporting.


  1. The HOG file format is a container holding game assets for Descent Games. Read more here: https://github.com/DescentDevelopers/Descent3/wiki/Descent-3-Internals#hog-files ↩︎