Changes between Version 5 and Version 6 of ioquake3


Ignore:
Timestamp:
Jun 24, 2011 10:44:00 AM (13 years ago)
Author:
jpawlick
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ioquake3

    v5 v6  
     1= IOQuake3 =
    12The following is what I've learned about the ioquake3 engine, which I hope will be helpful to you guys while I'm away.
     3
     4FIXME: Currently, I'm using this page as more of a "hey guys, here's the sit" rather than an actual public page. We can clean it up later.
    25
    36== General Terms & Tips ==
     
    1215We call an iteration of a game-driving loop a "frame". Because the client and the server are on different machines, client frames and server frames may operate at completely different frequencies. Quagent AI is called every single server frame (unlike normal Quake bot AI). This is unlikely to cost much computationally on a modern machine, since they are far more powerful than machines made in the early 90s.
    1316
    14 == Server, Client, Interface ==
    15 TODO: put text here.
     17== Server, Client ==
     18Quake is a multiplayer game, so it runs on multiple machines and struggles to keep everything consistent. Clients do prediction and interpolation, while the server periodically gives authoritative snapshots of the game state. Honestly I don't know too much about this: I gave Xu a book that covers it a little.
     19
     20This might be a problem for our model loading bug - since events that result in the loading of a new model occur on the server, but model loading takes place on the client, we'll have to find a clever way to realize a model is missing in the client (though for all I know, it calls "ModelMissing()" or something...)
    1621
    1722== The Virtual Machine ==
    18 TODO: put text here.
     23To protect users from malicious mods, Quake has a virtual machine that runs large sections of its code. In fact, most of our modifications are to virtual machine code. On both the server and the client, there is some code running inside the VM, and some code running outside it.
    1924
    2025Generally, code inside the following folders are available inside the VM:
    2126 * game
    2227 * cgame
    23  * client
     28 * client?
     29 * ui?
    2430while any code inside the following folders are strictly outside the VM:
    2531 * botlib
    2632some code is shared:
    27  * qcommon
    28  * tools?
     33 * qcommon (some of it?)
    2934and the rest I don't know about.
    3035
    3136=== Getting In and Out of the VM ===
    32 TODO: put text here.
     37So say you want to call something inside the VM from a function outside it. I have no idea how to do that, but can point you at VM_Call in the qcommon/vm_* files.
     38
     39Going the other way is easier, however. You need to add a "trap function" that when called, actually drops out of the VM, reinterprets any memory pointers, and does whatever you need it to. Adding a trap function is a bit of a pain, especially if you're doing it to botlib (if so you'll have to change the function export structure: just grep -i "Stupidly" and you'll find all the changes I made for one of our botlib traps: trap_BotMoveStupidlyInDirection). Adding traps like we did for reading/writing sockets is much easier, though: just follow the step-by-step instructions below and copy the style of the existing entries there. Typically our edits are at the bottom of the files, but if you search "quagents" (case-insensitive) you'll turn then up quickly.
    3340
    3441==== Adding a Trap Function ====
     
    4754
    4855All Quagent behavior runs through an AINode. An AINode is a function assigned to the bs->ainode pointer in bot_state_t, as you can see in the large switch statement.
     56
     57Generally all our modifications to ai_main.c are at the bottom of that file.
     58
     59=== !CommandQueue ===
     60As you know, the Quagents store their commands into a command queue that is actually an array 1024 commands long. There are two array indexes that are used to point into this array: bs->finalcommand and bs->currentcommand. If finalcommand is ever greater than currentcommand, there are no orders on the queue. When they are equal (pointing to the same command), that command is the only command. Otherwise, finalcommand points at the last command and currentcommand points at the next to be executed. Most commands are [[AtomicActions|atomic]], but some will take some duration of time. All manipulation of the queue itself is done in AIExit_Quagents and AIEnter_Quagents in game/ai_quagentsnodes.c.
     61
     62The queue is an array of "quagent_command_t"s, which are a struct that contains command state information. Because it must be statically sized, it is as large as the state information of the largest command ([[rb|rb]]). It has generic spaces for information - by convention, these values are in the same order as ProtocolZero parameters. So for example, if I issue a "n ro 4 180 0", the command for that will have:
     63{{{
     64command = QCMD_RO
     65id = 4
     66valf1 = 180
     67valf2 = 0
     68}}}
     69These values are assigned as part of BotAIQuagentsParseCommand's routine. After the command is executed in one frame, the Quagent has rotated a few degrees, and valf1 is updated to be however much is left to go (so, say, valf1 = 166). This way, if the user issues another command that interrupts the rotate, when the rotate eventually resumes it will only rotate the remaining amount. So typically the valf* track parameters, but in some cases they've been used as state machines.
    4970
    5071=== AINodes ===
     
    7596
    7697==== Thread Safety ====
    77 Thread safety is not a concern because Quake is single-threaded. If a user sends a command while an AINode is midway through execution, it will not be read until the next AI frame. You are guaranteed that the state of the command queue is constant through the execution of an AINode. For this reason, it is safe to make multiple calls to trap_Com_QuagentsWriteStr, as the batch rangefinder does.
     98Thread safety is not a concern because Quake is single-threaded. If a user sends a command while an AINode is midway through execution, it will not be read until the next AI frame. You are guaranteed that the state of the command queue is constant through the execution of an AINode. (That is, until you return, no function anywhere can interrupt you.) For this reason, it is safe to make multiple calls to trap_Com_QuagentsWriteStr, as the batch rangefinder does.
    7899
    79 === ai_main.c ===
    80 TODO: put text here.
     100== The Director ==
     101I should have all the framework we want in place for the GOD / Director functionality. Try connecting to Quagents on port 6002 and typing stuff. Currently, it'll just echo every command to stdout. I think pretty much anything we want a "configuration file" for, we should build through the director instead. Here's the points:
     102 * Assuming we implement all the commands, anything a config file can do, we could do by piping it in command form into the director with an easy script (that we could even configure to run on startup or something). Example:
     103{{{
     104cat config.txt | telnet localhost 6002
     105}}}
     106 * If we put more general options here, we can do things a config file can't, such as change the configuration on the fly. Sure, it's not a huge feature, but it might be nice to add more items to the world as it ran, or something like that.
     107 * The major drawback is that it's not blindingly obvious how to buffer commands the user issues as soon as Quake starts up (say, before the map is loaded). Then again, I'd be surprised if people choose not to just start Quake using the +map option. At least, that's what everyone I know did in the previous Quagents. Actually, I take this back, maybe. We just have it wait on the "hasn't had a read call yet" bit.
     108
     109If you want to edit director effects, check out the !BotHandleDirectorCommand(char* cmdbuff) function in game/ai_main.c. Currently, it just echoes any director commands to stdout, but it gets called once per command with the command string in cmdbuff (see the comments). So you'll need to add stuff to parse out the commands from whatever format they are in and then evaluate them. I decided it might be nice to keep all the director command functions in ai_directorcommands.c and ai_directorcommands.h. Hopefully that won't be a problem.