TDM Release Mechanics

From The DarkMod Wiki
Jump to navigationJump to search

This article describes the process of creating TDM releases.


Before even starting to build a package, make sure you have all the following tools ready.


The tdm_package tool inspects your working copy of assets SVN, and generates PK4 archives from it.

Prebuilt Windows executable can be downloaded from TDM server: link

The source code is located in source code SVN in tdm_update subdirectory. Build instructions:

  • Windows: Open tdm_update.sln, build Release configuration
  • Linux: Run scons BUILD="release"

As the result, packager executable tdm_package.??? will be created in bin subdirectory.

If the packager tool is modified, update the prebuilt executable on TDM server. Contact admin if you don't have SSH access for it.

TODO: update this section when #5076 is done.


Zipsync command-line tool is used to integrate a freshly generated set of pk4 archives into the database of releases. More details provided in article Tdm_installer_and_zipsync#Command-line_tool.

Prebuilt Windows executable can be downloaded here: link

The source code is located in source code SVN in tdm_installer/zipsync subdirectory. Unlike all the other TDM projects, this one does not use artefacts from ThirdParty directory. Hence, you need to install and use conan directly if you want to build it.

Build instructions:

  • Get packages: conan install . -if build -s build_type=RelWithDebInfo --build=outdated
  • Build: conan build . -bf build

The binaries should be ready in build/RelWithDebInfo. In case of MSVC, the CMake-generated solution will be somewhere nearby.

If the zipsync tool is modified, update the prebuilt executable on TDM server. Contact admin if you don't have SSH access for it.


Tdm_installer is GUI application, used to install/update the game on player's machine. More details provided in article Tdm installer and zipsync#tdm_installer.

Prebuilt executables can be obtained from Downloads section on the website.

The source code is located in source code SVN in tdm_installer subdirectory. It can be built with CMake in pretty standard way. For example:

  • Create build directory, switch to it.
  • Run cmake .. to generate project or makefile.
  • Windows: Open generated solution, build RelWithDebInfo Win32 configuration.
  • Linux: Run make to finish build.

You only need to build 32-bit executable on Windows: it will work fine on 64-bit OS too. For Linux, build both 64-bit and 32-bit executables separately.

In order to build 32-bit executable on 64-bit Linux, run cmake with additional parameters:

  • cmake .. -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_C_FLAGS=-m32

If tdm_installer is modified, update all prebuilt executables on TDM server.

For the self-update to work, each executable must be packaged into a special zip archive:

  • zipsync hashzip tdm_installer.exe
  • zipsync hashzip tdm_installer.linux64
  • zipsync hashzip tdm_installer.linux32

The resulting zip archives should be uploaded onto the TDM server (contact admin for access). Note that tdm_installer automatically updates itself to the executable on the server: if you put a bad build, make sure to fix it quickly!

Development Phase

Development phase takes the majority of time. During this time, day-to-day development happens on SVN trunk, so all commits end up there. Stuff gets broken, stuff gets fixed, stuff gets evaluated, some might be ripped out again. See also SVN article.

Dev Builds

New development build/release is created roughly every 2-4 weeks (might depend on the amount of changes) from trunk. Even though these builds are unstable and untested, and may include features will be heavily changed of even removed by the time of the next major release, there are many reasons to provide them:

  • Beta-testers are able to bisect over development history to find when an issue started happening.
  • External programmers can get assets compatible with recent source code trunk.
  • Curious players can test new features and provide early feedback.

The rest of this chapter describes how to create such a dev build. Note: mostly the same steps are done when building beta releases and major releases.

Compile Game Binaries

The assets SVN repo contains TDM executable files, which are copied into PK4 archives by the packager. They are rarely up-to-date, so the very first step is to build new executables from the source code and commit them to the assets repo.

Ensure you have clean and most recent revision of darkmod_src repo. The detailed build instructions are provided in TDM Compilation Guide, and the process of updating executables is explained in SVN#Executables Update. Just a few remainders:

  • Better explicitly delete all executables before building. For major releases, better delete build directory and build everything from scratch.
  • Always build Release configuration.
  • Build binaries for all officially supported platforms: Linux and Windows. Deprecated 32-bit executables are not built, except for the major releases and the last beta.
  • Don't forget to save debug symbols (.pdb and .debug files) somewhere when you commit executables, so that you don't accidentally overwrite them later!
  • Note that shaders (glprogs directory) are also copied to assets repo when you build TDM, so don't forget to commit them too.

The easiest way to build executables for Windows and Linux at once is to use Windows machine with Linux virtual machine. Detailed setup instructions for virtual machine are provided in VirtualBox and VMWare articles.

Better choose fairly popular and old version of Linux, for instance a five-year-old Ubuntu LTS (still using 16.04 in 2021). Due to how GLIBC symbols versioning works, sometimes TDM executable built on a new Linux won't start on an old one.

The debug symbols take quite a lot of space, but are indispensible for analyzing crashes on players' machines. So release manager must keep them safe on a local disk, and probably share them with other programmers on some cloud storage. For major releases, Windows debug symbols are even committed to the assets repo (see below).

Release Manifest

The "manifest" is a huge list of all the files that are nominated to go into the release package. Not all files in SVN are supposed to be released (like test files, test maps, other broken or unfinished stuff), so a white list is needed. Aside from that, some rules are needed to decide how files are divided among pk4 archives.

Here are the files related to manifest:

  • devel/manifests/base.txt: an explicit list of files which are packaged regardless of any other conditions.
  • devel/manifests/darkmod_maps.txt: sequence of include/exclude commands with regexes.
  • devel/manifests/darkmod.txt: full manifest file generated from the previous two files using tdm_package.
  • devel/manifests/darkmod_pk4s.txt: set of regex-powered rules describing which files go into which pk4 archive.

Build Manifest

The manifest file devel/manifests/darkmod.txt is generated by the tdm_package application. I run it with the following command:

  • tdm_package --create-manifest --darkmoddir=G:\TheDarkMod\darkmod >update_manifest.log

Here darkmoddir argument points to your working copy of assets SVN (it should be clean and up-to-date).

After the packager finishes, review the packager log file and the changes in the file devel/manifests/darkmod.txt. If everything looks good, commit the updated manifest to the assets repo. If not, then adjust some files (e.g. the maps file) and try again. Commit your adjustments to SVN of course.

Include/Exclude From Manifest

The main place to specify what should be included into release is darkmod_maps.txt file. As a rare exception, some files are specified in base.txt. But it is very small, and should probably be merged into the maps file in the future. Here is how maps file works.

In the first step, all files in specific folders are included:

# Include all these files (but without parsing them like maps), each
# statement will include files (from SVN) in that folder:
INCLUDE video/
INCLUDE xdata/

As seen in the above example, the sharp character # is used to denote comments. Put that at the beginning of a line to disable the statement.

As the next step, the algorithm will exclude certain files matching the regular expressions in EXCLUDE statements like this:

EXCLUDE dds/darkmod/test

In 95% of the cases it's enough to just specify the path of the files you want to exclude (use forward slashes), but you can do more fancy stuff like this:

EXCLUDE models/md5/chars/undead/revenant/.*.md5anim
EXCLUDE ^(dds/)?models/md5/chars/undead/revenant

The first line will exclude all MD5ANIM files in the revenant folder. The second line will exclude all files in models/md5/chars/undead/revenant and the ones in dds/models/md5/chars/undead/revenant (the dds/ part is marked to be optional).

In summary, all files that are INCLUDE'd as denoted above and afterwards manage to get through the hundreds of lines of EXCLUDE filters will end up in the manifest file.

Distribute files into PK4s

Each of the thousands of files in the manifest needs to be sorted into the correct PK4. This is done by defining a rules in the file devel/manifests/darkmod_pk4s.txt. Each of the lines there defines a PK4 and which files go into it:

# Miscellaneous stuff (GL Progs, Script, Language Files, Rope Arrow)
tdm_base01.pk4: ^glprogs, ^script, ^strings, sound\.wav, _emptyname\.wav, ^models/md5/environments, ^dds/models/md5/environments, ...

Again, lines starting with the sharp # character denote comments.

Leftmost is the PK4 filename, followed by a colon character. To the right of the colon a list comma-separated patterns is defined, whereas each pattern is interpreted as regular expression. As with the darkmod_maps.txt you don't need to know very much about regular expressions to define those rules, it's usually enough to write the folder names and use wildcards like .*.md5anim

Note that a file will not get into the release package if no rule covers it in the darkmod_pk4s.txt, even if it is present in the full manifest. The packager prints a warning in such case when pk4 files are created.

Create Package

After the manifest is ready, we can finally use packager to pack working copy of assets SVN into a set of pk4 files. Make sure your working copy is clean beforehand: unversioned files will be ignored, but local modifications will get into the package!

  • tdm_package --create-package --darkmoddir=G:/TheDarkMod/darkmod --outputdir=G:/TheDarkMod/tmp_package >create_package.log

Here darkmoddir specifies path to SVN working copy, and outputdir specifies path to an empty directory where pk4 files will be generated.

Be sure to look through the log file! If some file is not covered by pk4 distribution rules, you will see the corresponding message there.

Add Release

All versions of TDM are stored in one zipsync database, which looks like a big tree of differential packages. Read Tdm_installer_and_zipsync#Version_conventions carefully and decide 1) how to name the new version, and 2) which previously released version should serve as a parent for the new version.

First of all, you need the parent version somewhere on local disk (in clean state: without any additional files). If you don't have it, you can download it via tdm_installer:

  1. Create new directory. You can optionally copy some tdm_XXX.pk4 files to it to reduce download time.
  2. Copy tdm_installer to the directory, start it.
  3. On the first page, check "Bitwise exact zips" advanced setting! Also check "Get custom version".
  4. On the second page, choose the parent version.
  5. Optional: After installation is complete, you can delete .zipsync directory and all files with extensions other than pk4 and zip.

Note: It is much easier to save all versions you create on the local disk, so that you can use them as parent versions later.

Rename the directories with new version and with parent version: the directory name must match the name of the version. Let's say parent version is dev01234-8765, and new version is dev12345-9876.

Run analysis on the new version (remove -j0 unless data is stored on SSD):

  • zipsync analyze -r G:\tdmrel\dev12345-9876 -cn -j0 *.pk4 *.zip

Then create a differential package:

  • zipsync diff -r G:\tdmrel\dev12345-9876 -s G:\tdmrel\dev01234-8765\manifest.iniz -o G:\tdmrel\dev12345-9876_from_dev01234-8765

Detailed explanation of how to use zipsync command line tool is provided in Tdm installer and zipsync.

Now you need to connect to TDM server via SSH. For simplicity, I recommend storing exact copy of the whole zipsync database locally. In order to push your modifications, you can log to TDM server using WinSCP and use Commands:Synchronize with the properly set Direction/Target.

Look at the directory structure in the database and put the new differential package appropriately, e.g. into dev/210/dev12345-9876_from_dev01234-8765. Then open tdm_installer.ini in text editor, find appropriate place for the new version, and add it there, e.g.:

[Version dev12345-9876]

When you are sure that you edited it properly, synchronize your changes to the server. Immediately after that try to switch some of your TDM installations to the new version using tdm_installer (with "Bitwise exact zips" flag). If something does not work for you, fix the problem as soon as possible.

Changelog and Announcement

The current tradition for changelog is:

  • Changelog for all the dev builds after the last major release is in the second post of this forum thread.
  • When beta phase is started, a separate thread is created for it, and changelog of all dev builds is moved to there.
  • Changelog for beta releases is added to the beta testing thread. One post should contain changelog for all dev builds and beta releases in chronological order.
  • In the message with changelog, post link to the changelog for previous versions (making a linked list of forum posts).

Use SVN log over both repos to write changelog. Try to write in language which users can understand, whenever possible. Post as much links as you can: links to bugtracker as especially welcome, or links to feature discussion thread. Some very minor things can be omitted in changelog, but don't miss anything which can break something or cause any type of negative consequences. Most likely you will often read this changelog yourself =)

When changelog is ready, post short message that new version is available in the appropriate forum thread.

Dev Build: Recap

Here are all steps for creating a new dev build:

  1. #Compile Game Binaries
  2. #Build Manifest
  3. #Create Package
  4. #Add Release to zipsync database.
  5. Write #Changelog and Announcement.

Beta Phase

At some moment the development phase is over, and beta phase starts. The main goal of beta phase is to create a new major release, which will be used by most players for the next year or so. Of course, it is preceded by lengthy public beta testing.

Agree on Schedule

The beta phase starts with branching off the release, and typically lasts for 1-2 months after that. There is usually a rough plan about when to hold beta phase and when to release, but as time comes close, it's better to discuss it with team members again. Team members are supposed to wrap up their pending changes and commit them to the trunk before beta phase starts. About two weeks are long enough of a time span for people to get everything into SVN.

Libraries and License

A few weeks before branching, it is a good idea to revise the set of third-party libraries used. Starting from TDM 2.08, it should be easy to check it thanks to the new conventions on third-party stuff (see also Libraries and Dependencies). Some minor code pieces get embedded directly into the source, try to recall all of them.

It is necessary to update the LICENSE.txt file in SVN repositories if something is not up-to-date. Note that you should commit the same updated file into both source code and assets repos.

Update Script Reference

Another thing which needs update prior to branching is the script reference. It can be regenerated using the tdm_gen_script_event_doc command in game console:

  • tdm_gen_script_event_doc tdm_events.script d3script
  • tdm_gen_script_event_doc mediawiki

The files will be created in the FM directory. Copy mediawiki output to the wiki article TDM Script Reference. Copy script output to script/tdm_events.script file in the assets repo and commit.

Branching off

Once the deadline arrives, create the release branch in both repos:

Create release branch.png
  • Update your working copy of assets repo (darkmod).
  • Update working copy of source code repo (darkmod_src) too.
  • Right-click the darkmod_src folder to create the branch
    • Select SVN > Branch/Tag...
    • A new dialog appears asking for a URL. Enter a URL like this: https://<darkmod_server_here>/svn/darkmod_src/branches/releaseX.YY where X.YY is replaced by whatever version the next release will be.
    • You'll probably want to switch to the new branch once it is created, so make sure the check button at the bottom of the dialog is active.
    • Hit OK to let the SVN server create the branch.
  • Do the same for darkmod repository, using the URL scheme https://<darkmod_server_here>/svn/darkmod/branches/releaseX.YY

At this point there is new "release" branch in each of the two repos. The release branch and trunk live independently from each other: each working copy is attached to one of these branches, and commits done to one branch do not change the state of the other. All the beta releases and the final major release will be packaged from the release branch, thus it is important to keep it stable and only commit there the changes which are necessary for the release. Trunk will continue to evolve after the beta phase ends, so it is in principle possible to commit there some work which is not intended for release (although not desirable).

The release manager has to establish the workflow for developers, in order to avoid confusion with changes and branches. The typical workflow is: everyone commits his changes to trunk and notifies release manager that changes are intended for release, release manager merges the necessary changes to release branch when needed. If developer knows well how to deal with branches and commits merging in SVN, he can also merge his commits to release branch himself.

While trunk is technically independent from release branch, it is desirable to freeze or at least cool down all development not intended for the upcoming release. The reason for that is that during beta phase it is necessary to merge commits between branches a lot. If you merge 100% of commits from trunk to release, then beta phase can last forever or some major bug can slip into release at the last moment. On the other hand, the more unmerged commits you have, the more likely merge conflicts become. Refactoring, files renaming, and massive changes over the codebase are strongly discouraged.

Tracy and dbghelp.dll

The game uses Tracy for in-game profiling since 2.10. Tracy is capable of collecting call stacks, and uses some new functions from dbghelp.dll to achieve that. These functions require version 6.2 of dbghelp.dll, but Windows 7 has only version 6.1 preinstalled by default. As the result, TDM does not boot on a fresh Windows 7 machine. See issue.

The current workaround is to add #undef TRACY_HAS_CALLSTACK at the end of ThirdParty/artefacts/tracy/include/client/TracyCallstack.h on release branch. Unfortunately, this has to be done again for every major release, since we don't want to lose callstacks on trunk.

Publish Beta Release

Beta release is created and published the same way as dev build. The main difference is that dev builds are created on SVN trunk, and beta releases are created on the release branch. So make sure your SVN working copy is switched to release branch both in the source code repo and in the assets repo. Another difference between dev builds and beta releases is naming TDM versions in zipsync database.

Once again, the steps are:

  1. #Compile Game Binaries
  2. #Build Manifest
  3. #Create Package
  4. #Add Release to zipsync database.
  5. Write #Changelog and Announcement.

The announcement differs greatly when beta phase starts. Usually release manager creates a dedicated forum thread in public subforum, like e.g. Beta Testing 2.09. This thread should contain changelog of beta releases. Changelog of dev builds should be moved here too. I usually reserve three forum post immediately: the first one on how to install and what is beta about, the second one giving high-level overview of changes and new cvars/commands, and the third one with detailed changelog.

Testing Cycle

After beta release is published, testers update to it and post their bug reports. Developers investigate them and fix whatever they can. In typical workflow, the fixes are committed to the trunk (both for darkmod and darkmod_src repos), and the release manager merges them into the release branch.

One point to keep in mind is that SVN does not have real branches: a branch is merely a copy of the whole directory. As the result, SVN does not have proper branch merging either: it simply takes the selected commits as patches and reapplies them onto the working copy (more like cherry-picking). This is not a big problem in our context, since usually you don't want to merge everything: only a nontrivial subset of commits from trunk will be merged into the release branch. In order to avoid confusion, keep an eye on svn:mergeinfo property: it should list the commits which were merged, so that you don't merge them again.

Here is how I merge commits from trunk to release branch using TortoiseSVN:

  • Make sure your working copy is switched to the release branch and is clean from local modifications.
  • Now right-click the SVN root directory and choose SVN > Merge.
  • Select Merge a range of revisions.
  • As URL to merge from use the address of the trunk.
  • In the field Revision range to merge, click the Show Log button right next to the entry field. You will see log of commits in trunk, with the already merged commits greyed out. You can select one commit, or hold Ctrl to select an arbitrary subset of commits. You can press Ctrl+C when you finish selection, then paste the list of commits to a text file: later you can post it to developers so that they understand what was merged.
  • Hit Next, then Merge.
  • Now that the changes are incorporated into your working copy, test and review them briefly and commit to release branch.

After some number of fixes has been committed and merged to release branch, repeat again from section #Publish Beta Release. Publish new beta release, wait for feedback, fix, merge fixes to release branch, publish new release, etc.

Major Release

At some moment beta phase ends, and the next major release is created from the latest state of the release branch.


The common approach is to turn the latest beta release (called "release candidate") into the major release without even rebuilding/repackaging it. In such case you can take the full package of release candidate and proceed directly to #Add Release section: determine how major release is named (e.g. release210), its parent version (it is always the previous major release), create differential package, upload it to server and update tdm_installer.ini. When editing tdm_installer.ini, move the default=1 line from the previous major release to the new one.

The next thing is saving Windows debug symbols. You must have saved them when you built executables which are in the final package. If you did not save them, then you cannot simply publish the existing package, and you have to start over from section #Compile Game Binaries. Compress Windows debug symbols (TheDarkMod.pdb and TheDarkModx64.pdb) with 7z, put them into devel/release/debugging/X.YY in assets repo. Commit them to both to trunk and to release branch (commit + merge).

Exact state of SVN repos should be saved for every major release. So create a tag tags/X.YY in both SVN repos from the state of the release branch that you used to create release from.

While 32-bit executables are deprecated, we still provide them as a separate download for the latest major version. Thus, you should take 32-bit executables TheDarkMod.exe and thedarkmod.x86 and pack them into If you did not build 32-bit executables previously, build them exactly from the same source code which was used to build 64-bit executables. Put them at _aux/XYY/ in the zipsync database (where X.YY is the TDM version).

Update Website

Be sure to update the following pages on the website to reflect the latest release version:

To generate the latest source code package for the pages above:

Also update the link to 32-bit executables. It should point to --- that's where it was put inside the zipsync database.

Update Wiki

The wiki contains one page for each release. You can find them all in the Category:What's New category.

First update the current release page:

  • Change the "roadmap" link on the current release to a "changelog" link by swapping "roadmap_page.php?version_id=XX" with "changelog_page.php?version=XX".
  • Add the {{released|2.01|2014-01-30}} Template on top of the current release page with the correct date.

Then add a new page for the next release. To do this, add a link to it in this template: Template:Whatsnew by editing it. After saving the template, a link to the new release version page appears in the list and in the category. Edit this page and insert some text into it (you can copy it from an older release page).

Also insert a link to the current roadmap on the bugtracker, you can find out the version_id by looking at the roadmap page.

Forum Announcement

Post thread about new version on the News & Announcements subforum.

Back to Development Phase

After the dust of release settles, it's time to start the next release cycle and return to development phase.

  • Make sure all changes from release branch are also present in trunk, since you will unlikely return to the release branch in future.
  • Switch working copies of both repos back to trunk.
  • Open framework/Licensee.h in the source code repo, and update TDM_VERSION_MAJOR, TDM_VERSION_MINOR, ENGINE_VERSION for the next major release. Commit the change.
  • Go to Manage section of bugtracker, choose The Dark Mod project. Click "Edit" for the just released version, set it to "Released" state. Add new version if not yet present.

Hotfix Release

Ideally, players use the major release for about a year, until the next major release comes out. But in the real world, we often discover some issues which cannot/shouldn't be delayed until the next major release. In such case, it is possible to create a special release, which is historically called "hotfix" release. It is something like a "patch release" from semantic versioning: a new version which is almost the same as the major release, but with a few small fixes added.

The hotfix release is created from the previous release branch. For instance, if 2.09 is the last published major release and 2.10 is currently in development, then hotfix release 2.09a is created from release2.09 branch (in both SVN repos). Review Changelog or SVN commits since the last major release, and choose the changes which should be added in the hotfix. Discuss them with team members. Proper beta phase lasts months, and you don't want to waste much time on hotfix release, so be very careful and critical about the changes you include:

  • Included commits should be done some time ago, so that at least developers have already tested them.
  • Included commits must not break savegame format, i.e. they must not change any calls to idSaveGame/idRestoreGame objects.
  • Included commits should be small and have little risk of breaking something.
  • Do not include commits which provide a new feature: new features are for new major release.

Usually, commits are included in the following cases:

  • Fixing game crashes, race conditions, data corruption. Especially the ones which players bump into.
  • Fixing wrong usage of OpenGL API. Or adding workarounds for bad OpenGL drivers. Same for OpenAL and other machine-dependent stuff.
  • Fixing interaction with outside projects (FM database, DarkRadiant, tdm_installer, etc.) in case it has been broken.

Note that unlike typical convention, we guarantee full savegame compatibility between the hotfix release and its predecessor. The game writes SVN revision into every savegame and checks that it is the same when it loads game, complaining if it does not match. To silence this warning, go to the method RevisionTracker::GetSavegameRevision and make it return the hardcoded number: the revision reported by the original major release. Commit this change to branch (but not to trunk): now all executables created from this branch will use the same revision number when writing and reading savegames. Don't forget to test that savegames created in original release load properly in hotfix release and vice versa.

When everyone agrees on the set of included commits, merge them all to the release branch.

Now do the typical steps for creating a new release:

  1. #Compile Game Binaries
  2. #Build Manifest
  3. #Create Package
  4. #Add Release to zipsync database.
  5. Write #Changelog and Announcement.

Use release branch. Name the added version appropriately in zipsync database (e.g. release209a). Write announcement on public forums with changelog describing the merged commits, and instructions how to get hotfix (like this one).

After a very brief public beta-testing, move the default=1 line from the original release to the hotfix release in tdm_installer.ini. Now everyone who runs tdm_installer without explicitly specifying version will get the hotfix release.

Now do the ordinary finalization steps:

  1. Publish 32-bit executables.
  2. Commit 7zipped Windows PDB files to devel/release/debugging, replacing the files from original release.
  3. Create SVN tags in both repos.
  4. Update website: replace link to 32-bit executables and source code archive.
  5. Write announcements, so that people know they should update.

Of course, you can provide more than one hotfix release for one major release: 2.09a, 2.09b, 2.09c, etc. Also note that the original release is not lost: players can even install it if they want.