abrash-black-book/70-08.md
2013-12-30 20:26:41 +11:00

115 lines
8.3 KiB
Markdown

From the beginning, Quake was conceived as a client-server app,
specifically so that it would be possible to have persistent servers
always running on the Internet, independent of whether anyone was
playing on them at any particular time, as a step toward the long-term
goal of persistent worlds. Also, client-server architectures tend to be
more flexible and robust than peer-to-peer, and it is much easier to
have players come and go at will with client-server. Quake is
client-server from the ground up, and even in single-player mode,
messages are passed through buffers between the client code and the
server code; it's quite likely that the client and server would have
been two processes, in fact, were it not for the need to support DOS.
Client-server turned out to be the right decision, because Quake's
ability to support persistent, come-and-go-as-you-please Internet
servers with up to 16 people has been instrumental in the game's high
visibility in the press, and its lasting popularity.
However, client-server is not without a cost, because, in its pure form,
latency for clients consists of the round trip from the client to the
server and back. (In Quake, orientation changes instantly on the client,
short-circuiting the trip to the server, but all other events, such as
motion and firing, must make the round trip before they happen on the
client.) In peer-to-peer games, maximum latency can be just the cost of
the one-way trip, because each client is running a simulation of the
game, and each peer sees its own actions instantly. What all this means
is that latency is the downside of client-server, but in many other
respects client-server is very attractive. So the big task with
client-server is to reduce latency.
As of the release of QTest1, the first and last prerelease of Quake,
John had smoothed net play considerably by actually keeping the client's
virtual time a bit earlier than the time of the last server packet, and
interpolating events between the last two packets to the client's
virtual time. This meant that events didn't snap to whatever packet had
arrived last, and got rid of considerable jerking and stuttering.
Unfortunately, it actually increased latency, because of the retarding
of time needed to make the interpolation possible. This illustrates a
common tradeoff, which is that reduced latency often makes for rougher
play.
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *Reduced latency also often makes for more frustrating play. It's actually not hard to reduce the latency perceived by the player, but many of the approaches that reduce latency introduce the potential for paradoxes that can be quite distracting and annoying. For example, a player may see a rocket go by, and think they've dodged it, only to find themselves exploding a second later as the difference of opinion between his simulation and the other simulation is resolved to his detriment.*
------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Worse, QTest1 was prone to frequent hitching over all but the best
connections, because it was built around reliable packet delivery (TCP)
provided by the operating system. Whenever a packet didn't arrive, there
was a long pause waiting for the retransmission. After QTest1, John
realized that this was a fundamentally wrong assumption, and changed the
code to use unreliable packet delivery (UDP), sending the relevant
portion of the full state every time (possible only because the PVS can
be used to cull most events in a level), and letting the game logic
itself deal with packets that didn't arrive. A reliable sideband was
used as well, but only for events like scores, not for gameplay state.
However, this was a good example of Carmack's Law: John did not rewrite
the net code to reflect this new fundamental assumption, and wound up
with 8,000 lines of messy code that took right up until Quake shipped to
debug. For QuakeWorld, John did rewrite the net code from scratch around
the assumption of unreliable packet delivery, and it wound up as just
1,500 lines of clean, bug-free code.
In the long run, it's cheaper to rewrite than to patch and modify!
So as of shipping Quake, multiplayer performance was quite smooth, but
latency was still a major issue, often in the 250 to 400 ms range for
modem players. QuakeWorld attacked this in two ways. First, it reduced
latency by around 50 to 100 ms with a server change. The Quake server
runs 10 or 20 times a second, batching up inputs in between ticks, and
sending out results after the tick. By contrast, QuakeWorld servers run
immediately whenever a client sends input, knocking up to 50 or 100 ms
off response time, although at the cost of a greater server processing
load. (A similar anti-latency idea that wasn't implemented in QuakeWorld
is having a separate thread that can send input off to the server as
soon as it happens, instead of incurring up to a frame of latency.)
The second way in which QuakeWorld attacks latency is by not
interpolating. The player is actually predicted well ahead of the latest
server packet (after all, the client has all the information needed to
move the player, unless an outside force intervenes), giving very
responsive control. The rest of the world is drawn as of the latest
server packet; this is jerkier than Quake, again showing that smoothness
is often a tradeoff for latency. The player's prediction may, of course,
result in a minor paradox; for example, if an explosion turns out to
have knocked the player sideways, the player's location may suddenly
jump without warning as the server packet arrives with the correct
location. In the latest version of QuakeWorld, the other players are
predicted as well, with consequently more frequent paradoxes, but
smoother, more convincing motion. Platforms and doors are still not
predicted, and consequently are still pretty jerky. It is, of course,
possible to predict more and more objects into the future; it's a
tradeoff of smoothness and perceived low latency for the frustration of
paradoxes—and that's the way it's going to stay until most people are
connected to the Internet by something better than modems.
#### Quake 2 {#Heading20}
I can't talk in detail about Quake 2 as a game, but I can describe some
interesting technology features. The Quake 2 rendering engine isn't
going to change that much from Quake; the improvements are largely in
areas such as physics, gameplay, artwork, and overall design. The most
interesting graphics change is in the preprocessing, where John has
added support for radiosity lighting; that is, the ability to put a
light source into the world and have the light bounced around the world
realistically. This is sometimes terrific—it makes for great glowing
light around lava and hanging light panels—but in other cases it's less
spectacular than the effects that designers can get by placing lots of
direct-illumination light sources in a room, so the two methods can be
used as needed. Also, radiosity is *very* computationally expensive,
approximately as expensive as BSPing. Most of the radiosity demos I've
seen have been in one or two rooms, and the order of the problem goes up
tremendously on whole Quake levels. Here's another case where the PVS is
essential; without it, radiosity processing time would be
O(polygons^2^), but with the PVS it's
O(polygons\*average\_potentially\_visible\_polygons), which is over an
order of magnitude less (and increases approximately linearly, rather
than as a squared function, with greater-level complexity).