Recent News
- 0.2.9.3.1 released
- New Tools on the Block
- Build 0.2.9.3_beta_z3199 available
- Build 0.2.8.3.7_alpha_z1898 available
- Build 0.2.9.3_rc_z3199 available
- Build 0.2.9.3_rc_z3198 available
- Build 0.2.9.3_alpha_z3570 available
- Build 0.2.9.3_beta_z3198 available
- Build 0.4.0_alpha_z6401 available
- Build 0.2.9.3_beta_z3195 available
- Benefits of a Framerate Limit
- 0.2.9.3.0 released
- macOS: Monterey now required
- All news ...
- All releases ...
- All blog entries ...
- All builds (release, beta, alpha...) ...
- All LTS releases ...
New Tools on the Block
Oct 8, 2026
We have some new utilities in the source tree! The motivation of these additions was to make AI aided development safer, but of course, they have the side effect of being pretty useful for humans as well.
Unit Tests using doctest
In the directory src/test, we now have a couple of unit tests for base classes and even integration style tests. They use the doctest framework, a single header library included in src/thirdparty/doctest/doctest.h.
Tests get compiled into the standalone executable src/unit_tests. Run it without parameters to run all tests, check out -h to learn how to list tests and run single tests should you be interested in that. IDE integrations make use of these options.
doctest uses macros to structure tests, because there is basically no other sane way. A test can be a simple sequence of instructions with some assertions CHECK(...), but a very
cool thing is that you can define sub-cases with a common setup section on top:
TEST_CASE("ePlayer basics")
{
GIVEN("existsing ePlayer")
{
// fetch player 0
ePlayer& player = *ePlayer::PlayerConfig(0);
THEN("player has a name")
{
CHECK(0 != strlen(player.Name()));
}
THEN("there are at least four players")
{
CHECK(uMAX_PLAYERS >= 4);
}
THEN("ids assigned")
{
for (int i = 0; i < uMAX_PLAYERS; ++i)
{
CHECK(ePlayer::PlayerConfig(i)->ID() == i);
}
}
}
}
This will run the test case three times, each time executing the fetch call, then one of the THEN blocks. Then sadly exit, you cannot define common tear down code. That will have to happen in destructors; check out the new header tools/tDefer.h and its existing uses in tests, that helps a little.
There are two very neat things about this:
- If you run your debugger the right way, you can debug a single sub-case, and you won’t be bothered with breakpoint hits from parts of the test you are not interested in.
- Strip out the code and only leave the macros, what do you get? You get a human readable specification. They suggest you use the macros
GIVENfor setup,WHENfor an action, andTHENfor the checks, and call that Behavior Driven Development (BDD).
What MAY work, I have not tested it, but will: For a new feature, you just write the part with the macros. Then you let the AI fill in the blanks of the test, then you let the AI build the implementation.
What was a bit surprising to me: We do have a lot of static data, lists that only can exist once, lists that objects automatically insert themselves to, and global functions. You’d think such things make code untestable, but that is not true; it makes it impossible to run tests in multi-threaded mode, that is all. As long as your test cleans up and leaves the system in the state it started in, there is no problem at all. Even the static timer function is not an obstacle; there is now simply a function to advance time a bit. (It still is shoddy design, mind.)
Friendly Build Script
batch/test_builds.sh can configure and build several different configurations inside build, directly in the source folder. build is of course in .gitignore. What is built is controlled by command line arguments, how it is built by environment variables. It makes
an effort to keep builds for different configurations apart. It defaults to moderately strict
compiler flags, with warnings as errors, very little output if there are no errors, and building and running the tests.
Possible command line arguments:
client_debug: client in debug mode with code coverageserver_debug: server in debug mode with code coveragedebug: both of the aboveclientandserver: same in optimized modeminimal: client with optional (but by default active) things disabledall: all of the abovefull: all of the above, but if you have both installed, for gcc and clang.clean: clear all build directories
Most important environment variables, run with -h for the rest:
CXX: The C++ compiler to useVERBOSE=1: Full outputCOVERAGE=1: Builds human readable code coverage reportFORCE_RECONFIGURE=1: Runsconfigureagain (automatic in most cases)
The idea behind the script is to provide a simple and definitive command to do development builds with. It should be easy to wire it into all kinds of workflows.
No matter what the actual configuration is, the two debug builds get linked to the shorter build/test_vs_server_debug and build/test_vs_client_debug, so IDE configurations can work with hardcoded paths to those.
Code Coverage
As already mentioned above, we now support building with code coverage collection. It defaults to off, the configure parameter --enable-coverage enables it. We use gcov style output and the lcov tool to generate HTML reports. For clang, llvm-gcov is used for conversion.
We do not blindly chase high code coverage. That would be silly. lcov does not even count entire source files that are not covered at all. You can increase coverage by deleting non-covered code. You can write tests that cover a lot of code, but don’t do any testing; one could, for example, just play back a debug recording.
Instead, just use the coverage results to check whether your tests do indeed cover the code you want to test.
Codium/Visual Studio Code
.vscode has been added to .gitignore, so you can now put your configuration there without having to constantly take care to not commit it. A sample configuration, making use of the build script, is provided in .vscode.example. Link it and leave it, or copy it and modify to your needs.
What the sample configuration provides:
- Include specifications, so code completion works
- Build and debug configuration
- Code coverage and test runner configuration
For all this to work, you need some extensions, they are in the suggested extensions configuration of the sample. The official C++ extension is in there, obviously. Additionally:
- TestMate C++: Has adapters for the doctest based tests. You can run all tests to see which failed, see those failures integrated into the source code editor, and you can run and debug individual test cases. You can have the test executable monitored for changes so tests get rerun in the background every time you build.
- Coverage Gutters: I have not used it as much so far… on demand, you can let it show on the left-hand margin which code lines are covered and which are not. This requires that you run the build script with
COVERAGE=1.
Doxygen
We had this the whole time, but new now, thanks to newly discovered capabilities and filters, we generate a Main Page and classes are sorted into Topics, which makes navigation much easier. Building the documentation and uploading it somewhere using the build pipeline is a job for the future.
AI Guidance Files
Littered throughout the project, there are now AGENTS.md files, giving AI coding tools a short overview of the directory contents so they do not have to re-scan them every time, wasting tokens. The top level and src/test ones contain some human written instructions so they stick to the rules.
For your own fun side projects, use them as you see fit. If you want to contribute to the main repository, however, keep in mind that human developer enjoyment still very much is something we value. Anything you cook up with AI will be read by a human and checked whether it can be maintained by a human, so don’t even think about submitting 5000 line vibe coded diffs. We don’t have any hard limit… think about it like that, do you understand what your AI generated code does? Enough so you could change it yourself if requirements change? Then it is probably fine.
In case that scares you off: On the bright side, AI already has proven itself useful. Writing many of the automated tests, it uncovered a couple of bugs (diligently writing the tests around them, of course, because it was instructed to write tests that document the Status Quo). And 0.2.9.3.1 is an AI generated security fix against an (unpublished!) AI generated exploit, submitted by arcsinx.
In case you are curious, the AGENTS.md files were generated with a depth search by my COMPLETELY SELF DEVELOPED skill collection “Mossy Slop” (GitHub/GitLab), the mossy-scan skill.
Ok, the real origin story is that at work, we got a day of AI instructions from some consultant, and were told to use his framework. However, it burned through so many tokens that I told my AI to clone it, with token conservation in mind. And it did. And it worked, sort of. Most of the skills I actually used so far, I really have rewritten by hand.
Devcontainers
Not strictly part of any big change in the source, apart from adding .devcontainers to .gitignore: It is very much recommended to run your AI agents in a docker container without access to any important credentials. Here is my configuration, likely in heavy flux.
Some small adaptions in the source do cater to devcontainer users: The build script tries to detect if it is running in a container and if so, chooses different directory names. Container builds are likely incompatible with host builds.
Have fun,
Z-Man