I am the author of the server-side demo patch quoted above by Gig. This patch is not really in development anymore and I consider it to be pretty much final, since it is quite stable and I can't see what other features I could add (but I have some ideas about making an alternative patch, but that's another story).
About your question, as pointed out by andrewj, the code that writes and read demos are in the client-side part of the engine, and it's quite simple if you want to take a look.
But if you want to get a hang on how recording players states work in the engine, you can take a look at my patch since I've cleaned it up to the minimum and commented it as much as I could, more specifically the
sv_demo.c file which contains all the recording and playback code.
If you want to get the quintessence of the mechanisms, you can read this part:
https://github.com/lrq3000/openarena_engine_serversidedemos/blob/latest/code/server/sv_demo.c#L623Which pretty much sums it all:
- At the starting of the recording, some headers get created and outputted into a file (the demo file). Along the way, we also store the current state of every player (ie: the variables that defines various aspects of the players, like the position, mouse look, velocity, armor, ammos and weapons, etc.).
- Then,
at each iteration, we just
store the new state of every player. To be more specific, we don't store the whole values, but rather the difference between the previous frame's states and the current frame's players states, which is simply called the
Delta in the engine. To achieve that, there are various functions that will do this automatically for you (just search for "delta" in the engine's sourcecode).
- At the end of the recording, we put some footers (EOF flag) and close the file.
Now about the technical format:
In the engine, these "states" and various other informations about the demo are called "
messages". A message can contain only one information or a lot of information, depends on the format. For example: "demo_clientCommand" stores one command of one client at a time, while "demo_playerState" stores all players states for a frame (so you can get two or more demo_clientCommand messages, but only one demo_playerState message for one frame). In general, all players/entity states are stored in a single message (I guess for loop efficiency - demo_playerState, demo_entityState, demo_entityShared).
A message is composed of a constant byte header (a flag), which allows you to switch to the right processing chain, and then the data in a fixed format (a byte, then a string, then a long, etc...). In practice, you get the flag, but after you have to know in advance what will follow and decode the data in the right order, else your flow will get corrupted.
Warning: there are 3 major flags that can be confusing but are very different and need to be respected to the letter:
- demo_EOF: which means
End Of Flux, not End Of File! This ends the current message, so that you can continue to the next message (but you are still on the same frame).
- demo_endFrame: which ends a frame (no more message for this frame, so another way of thinking about it is to think that it ends all messages). This flag is very important as it tells the engine that it now has to commit the changes (because even if the players states are updated, the players won't move until this flag is met).
- demo_endDemo: which means that it's the final end of the demo (end of all frames).
These flags are those that I use inside my patch, I can't remember if they are standard, but anyway you will meet the same flags (albeit maybe with another name) in any demo recording.
If you want a good example, here is the main playback loop of my patch:
https://github.com/lrq3000/openarena_engine_serversidedemos/blob/latest/code/server/sv_demo.c#L1106This is clean example that will clearly show you how the processing chain works: you get a flag, then you branch to the right processing chain. Here I have stored every processing chain inside its own function, but in the vanilla engine's code, you will find that the processing chains are not so cleanly splitted, so you can expect to get very long if/else if/else branching where everything is done in a single function.
Recording and playback works the same, so that you can as easily save players states from a live game, or extract them from a demo.
Last note: my patch store a lot more informations than the standard client-side recording facility. For example, in my patch you can find about a dozen flags, when in the client-side recording facility you will find half (it was necessary for this patch to store more types of messages because the server-side engine does not have access to the game engine, contrarily to the client-side engine).
Last note2: if you are not used to this game's architecture, a big notion is the fact that the game is mainly split in 2: the engine, and the game code. The engine manage most of the stuff, but does not have access to most of the game logic, which is the part done by the game code. However, both parts can communicate together some kinds of information via "trap calls".
I know this can be confusing, but anyway for you it's not that important if you are only looking for the position of players, which you can access from both the engine or the game code. But it's still important to know if someday you want to go further.
I hope my explanations can be useful for you. Good luck for your project.