Historical note: this file describes the old monolithic GameClient path that
has since been replaced by ClientApp, Connection, Playback, UI, and related
helpers. Keep it as source archaeology, not as current implementation
documentation.

Here's my attempt to explain how various things work in a more central
location.

Demo playback: The client types the command "demo demoname" in the
console and presses enter.  This results in GameClient::onKeyEvent()
calling m_console.executeCommand("demo demoname"), which ultimately
calls a ClientConsoleCommand::execute() which passes the arguments
along to the GameClient::Cmd_demo() function.  After checking that
there are enough arguments, setConnectionState(DISCONNECTED) is
called.  The demo file is opened as a binary file, and m_demos.play()
is called with the demo file as an argument.  Demos is a pretty
useless class, it's really dumb, but whatever, I don't feel like
modifying it right now since it works.  Ok, assuming the file opened
properly, we now prepare to play the demo.  We read in the first part
of the message, which is supposed to be the version.  we make sure the
demo is the correct version -- if not, I guess the program quits?
that's kind of stupid (added to todo.txt).  We read in the next part
of the demo, which should contain the map name, and then we call
loadWorld() to load the map and set the _localid to tag2, which I
presume is the player who we're supposed to follow.  Indeed, we then
set cl_follow to the _localid.  Then we read in the next part to
&gs(), which I guess is supposed to be the gamestate -- so this is the
first "real" part of the demo file.  We pop some queues called _ldc
and _demomsgs, update m_gameTimer and m_lastDemoTime according to the
game_time() now in the gs(), and setConnectionState(PLAYBACK).
m_lastDemoTime is only used for the "demoscale" variable, as it turns
out.  _ldc is a queue of DemoChunks, which contain a GameState dcgs
and two unsigneds, tag1 and tag2, while _demomsgs consists of pairs of
strings and unsigneds, the text of the message and the time at which
it appears, respectively.
It's now clear to me where the problem is -- we read in everything at
once in GameClient::recvData(), and set done=true, but don't check
that done=true, which is stupid.
That wasn't the only problem.  More importantly, the _demoInLastGs had
to be set, both after reading the first GameState in Cmd_demo(), and
it needs to be read from and updated in recvData().  Ok, I think
that's fixed now.

GameClient::doNextFrame() is only ever actually used for executing
autoexec.cfg and uaconfig.cfg.  It pushes the command to
m_commandQueue, which is flushed each frame in GameClient::onFrame()
between sending input to the server and checking for replies from
master server.

Ways endMatch() can be called:
- in DuelMode::init(), if gmc().matchInProgress()==true
- in DuelMode::handleDisconnect(), if the guy leaving was playing
- in DuelMode::handleRequest(), if an existing player wants to spec
- in DuelMode::handleMatchEnd()
- similarly for FFAMode
- in GameServer::mapRestart(), m_pGMC->endMatch()
- in TrigDefault, if debugmap is set

It'd be really nice to have a streamlined way to define entities,
items, etc., that isn't hardcoded into the engine.  
