Introspection

Alliteration is cool, but not particularly practical per prevalent perception.

Reading, or listening to Masters of Doom, gave me an impression that .plan files are mostly used as to-do/have-done lists as a matter of personal housekeeping. Not being particularly keen on sifting through the entirety of John Carmack’s .plan archive in search of golden bits of wisdom, I decided to employ the practice for my own use, but promptly forgot about the original source. Last week, I started going through more online material regarding the networking architecture of both the Q3A and the Source engines and stumbled upon PDF-ied releases of the aforementioned .plan files on Fabien Sanglard’s page. Turns out, JDC later used those files as a sort of a precursor to a personal blog, mostly, it seems, in a futile attempt to stifle the unbearable influx of mail. Prose is much nicer to read than lists of abbreviated thoughts, which is only proven by my devouring those pdfs as opposed to skimming them as I did with his early .plans. While I don’t particularly feel like expanding on my thoughts for my personal use only, updating this blog with more technical info on a more regular basis than just with random streams of consciousness or boring essays may only bring more (useful) life into it.

After resolving to finish some basic 3D courses in order to be ready for modelling work for CHP, I completely lost any desire to work on it. A conjunction of different events in 2021 made me very skeptical of any added value I could produce and of my ability to finish it. That in itself is ironic, considering that back then I already were saying that I’m in it all for the process; however, parts of it were, and will yet be, unbearable for me to deal with alone. When the time comes - potentially after the tech demo is released - I will have to sit down and seriously think through all of the challenges that I will not want to face on my own if I were to commit myself to finishing the project. While audio work turned out more interesting than initially expected, I just don’t have the know-how or the patience required to produce 3D assets of sufficient quality. AI is a godsend when it comes to concept art, but actually preparing the assets will require actually sitting down and doing the hard work. As much as I am an opponent of prioritising graphical fidelity over gameplay or technical aspects, I cannot deny anymore that it is a driving, or discouraging, factor for many people when evaluating a game. This will have to be addressed.

Ever since coming back with the beta releases of Godot 4.0, I decided to attempt to at least complete the tech demo with as minimal a scope as possible. Trimming down the features and desired characteristics turned out to be a fairly simple process and let me more cleanly define all of the milestones that sound most promissing as effective motivation boosters when reached. Unsurprisingly feature creep has already reared its ugly head back at me again, but it is still rather managable; the same cannot however be said about the prototype of the final game, that I planned to work on next. For now, the tech demo will feature a simple arena shooter with a number of key weapons known as the Holy Trinity - the rocket launcher, the lightning gun and the railgun. All three have already been implemented and while the assets are lacking here and there, using low-poly placeholders feels perfectly suitable for this stage. With a basic prototype of a level editor done too, the work now focuses on implementing networking.

The scene replication framework introduced in Godot 4.0 along with RPCs feels alien and not focused on performance. This is obviously not a reasonable stance to end up at - some research should have been conducted to verify the exact architecture and performance of it - but NIH has struck me down and I decided to delve into the black magiks of Q3/Source networking. Replicating it in my game in G4 turned out more complicated than expected, mostly due to a completely different data flow than what I originally intended to use. Q3/Source use a concept of a half-dumb terminal for a client, where the game running on the client side is concerned with two things - collecting and transmitting input data to the server and simulating the game world and predicting its course while waiting for an official confirmation from the server. The server meanwhile has a complete authority over the exact state of the game - it collects all the input data from clients, runs the simulation and transmits snapshots of all of the changes back to the clients. Polling of the inputs and generation and transmission of the snapshots happen independently at steady paces defined by the command rate and the update rate. Those, along with rate, can be adjusted client-side to limit the bandwidth required to run the game smoothly or improve latency and reduce the impact of prediction errors.

My initial idea with CustomSerializables/Interactibles was to also sync their state with the clients regularly, so points for decent intuitive understanding of prior work there. However, all of the input/command data was supposed to be turned into events transmitted to the server and handled by it immediately, instead of effectively limiting and bunching it up for bandwidth reasons. While both concepts are not very far removed from each other, detaching handling of input from actually issuing it adds a very unintuitive layer of abstraction that requires a rework to data/event pipelines and the structure of the scenes in Godot. Understanding this issue of granurality gave me a more thorough understanding of what I am actually trying to accomplish. It is rather hard to give up on the idea of any sort of authority given to the client, but at the end of the day, with it being a necessary evil, Carmack did that with QW/Q2/Q3A and still, to this day, this seems likely to be the optimal way of handling state synchronization while limiting latency and increasing responsiveness. Boring and uninspired by today’s standards, but definitely close to perfect.

Valve announced that CS2 will be released soon(tm) and will be running on Source 2. It promised sub-tick updates for moving, shooting and throwing. I thought they are simply doing some pseudo-optimization trickery to improve responsiveness without actually detaching the engine from the aforementioned model of simulation, but I could not figure out any way for them to reasonably implement a system not tied to fixed-rate tick-based handling from the original installment. Carmack once again comes to the rescue as it turns out this is not a novel idea and he tried and tested it in QuakeWorld in 1996. On one hand it is nice to be given a hint instead of a solution, as was the case with most of the information I gathered from the Internet so far on the topic, but on the other, stuff like this gives me an identity crisis. There are far more talented and resourceful people who worked or are working on this stuff and yet here I am, trying to follow in their steps to eventually, hopefully, surpass them. Hard not to come to a conclusion nothing of the sort will happen with how things are going so far. Nevertheless, I am committed to doing The Right Thing and trying to figure this stuff out on my own as much as possible, the way I feel it should look like, producing an elegant, but performant and not-too-resource-hungry codebase. The final effect will not be as groundbreaking as any of the Quakes ever were, nor as enticing as them to be played, but hopefully it will at least grant me enough experience and knowledge to do more interesting stuff in the future.

I have finished preparing a GDExtension wrapper for GameNetworkingSockets and implemented very barebones chatting functionality. It is now time to properly transition my part of the engine to the structure more suited for handling input data as envisioned by Carmack. A lot of stuff will be done in few commits as it is hardly separable: structure of messages, message handling and pipelines, event creation, transmission and synchronization, snapshot creation, delta compression and handling on the client, prediction, lag compensation, etc. All the good stuff I was eager to start on; now, however, more aware of the complexity of it all, procrastinating by reading the old .plan files of the great visionary of the modern video game age.