-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
625 lines (582 loc) · 62.9 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>p1x’s knowledge base</title>
<link rel="related" href="https://kb.p1x.pw" />
<id>https://kb.p1x.pw</id>
<author>
<name>p1x</name>
</author>
<subtitle></subtitle>
<generator>blades</generator>
<updated>2022-05-24T14:31:47Z</updated>
<entry>
<title>Content locations</title>
<link rel="alternate" href="https://kb.p1x.pw/locations/" />
<id>https://kb.p1x.pw/locations</id>
<summary>Where Sauerbraten loads/stores content from/to.</summary>
<content><p>Sauerbraten uses overlaying to enable loading content from different locations. I say locations because not only can you tell Sauer to use certain directories, it can also use .zip files directly!</p>
<p>There are four types of content locations:</p>
<ul>
<li>zip archives (added using the <code>addzip</code> command)</li>
<li>the user directory (only one, defined using <code>-q</code> CLI flag)</li>
<li>package directories (zero or more, added using <code>-k</code> CLI flag)</li>
<li>the installation root directory</li>
</ul>
<h2>Content</h2>
<p>In sauer, pretty much everything except the game binary and libraries qualifies as “content”: maps, textures, models, scripts, demos, screenshots, configuration files, cubescript files, shaders, everything. Yes, you could have your config.cfg loaded from a .zip file! (You really shouldn’t though: it would be impossible to change any settings…)</p>
<h2>Loading</h2>
<p>A content file is loaded by first looking for it inside all zip archives (in the order they were added), then in the user directory, then inside all package directories (in the order they were added), and lastly in the installation directory.</p>
<h2>Writing</h2>
<p>Sauerbraten only ever writes files to your user directory. (Unless there is no user directory configured, then it writes to the installation directory. This should only be the case if you explicitly changed the launch script, see below.)</p>
<p>That way, you should <em>in theory</em> never need to reinstall, provided you never touch the original content files in the installation directory. Deleting the user directory would reset everything to the state of a fresh installation.</p>
<p>This means you can not override the contents of a .zip file by placing an updated version of a file in your user directory! Zip archives are always preferred when loading content.</p>
<h1>Zip Archives</h1>
<p>The easiest way to add content to your Sauerbraten installation is to add it directly as .zip file. For this to work, the contents of the .zip has to be structured the same way as the installation directory, i.e. a map .ogz needs to be in packages/base/ inside the archive. Most content you find online is already correctly packaged. Just put <code>addzip C:\Users\Tom\Downloads\my-awesome-shiny-inky-models.zip</code> into your autoexec.cfg and restart Sauer.</p>
<h1>User Directory</h1>
<p>The path to the user directory is set by the OS-specific launch script, for example sauerbraten.bat on Windows, by passing a <code>-q<path></code> command-line argument to the game binary. By default, sauerbraten.bat uses <code>$HOME\My Games\Sauerbraten</code>. <code>$HOME</code> is (unintuitively) <em>not</em> substituted with the path to your user folder, for example <code>C:\Users\Tom</code>, but with the path to your “My Documents” folder, for example <code>C:\Users\Tom\My Documents</code>, so the actual location becomes <code>C:\Users\Tom\My Documents\My Games\Sauerbraten</code>.</p>
<p>On Linux, the launch script is called sauerbraten_unix and it uses <code>$HOME/.sauerbraten</code> as user directory. On macOS, the script is at Sauerbraten.app/Contents/MacOS/sauerbraten, and uses <code>$HOME/Library/Application Support/Sauerbraten</code>. (On these systems, <code>$HOME</code> is substituted with what you think it is, your home directory.)</p>
<p>By letting the launch script define the user content directory, you are very flexible with your setup. For example, you can have several Sauer installations that each use a different user directory by opening the sauerbraten.bat file and changing the path. (This is very much advised for parallel installations of different editions of Sauer, since Sauer overwrites config.cfg every time you quit and configs are not compatible between editions.)</p>
<h1>Package Directories</h1>
<p>To load content from additional directories, you can pass <code>-k<path></code> to the game binary. Content is only loaded from, never written to a package directory. If the path you specify contains <code>/packages/bla</code>, only files starting with that prefix are tried to be loaded from that package directory.</p>
</content>
<updated>2022-05-24T00:00:00Z</updated>
</entry>
<entry>
<title>Demo Files</title>
<link rel="alternate" href="https://kb.p1x.pw/demo-files/" />
<id>https://kb.p1x.pw/demo-files</id>
<summary>What demos are, how to use them, and how they work under the hood.</summary>
<content><p>Sauerbraten offers a way to record and replay matches (as do many other games, especially first-person shooters). In Sauer, these replays are called demos and stored as .dmo files. Demos are usually recorded by the server, showing the server’s view of game events and timing, but there are client mods that allow you to record your own POV as well.</p>
<h2><code>/recorddemo</code> & <code>/stopdemo</code></h2>
<p>By default, vanilla servers do not record demos, unless enabled for the next match (see below). However, most popular servers are configured to record demos automatically. Server operators can set <code>autorecorddemo 1</code> in server-init.cfg to do so.</p>
<p>For servers that do not automatically record demos, privileged users can enable recording of the next game using <code>/recorddemo 1</code>. To disable recording for the next match, use <code>/recorddemo 0</code>. To stop recording the current match, there is <code>/stopdemo</code>. The privilege required to use these commands depends on the server; by default, only admins are allowed to use them, and server operators can enable the commands for master and auth by setting <code>restrictdemos 0</code> in their server-init.cfg.</p>
<h2><code>/getdemo</code> & <code>/listdemos</code></h2>
<p>Servers will announce at the start of the game if they are recording the game. When you play e.g. forge and see “recording demo”, you know you can wait until the game is over and the next map is loaded (could be reissen), and then call <code>/getdemo [F]</code> to download and save the replay of the game that ended last, i.e. the match on forge. The filename (<code>F</code>) is optional, but useful; if not provided, the current date and time will be used.</p>
<p>You can use <code>/listdemos</code> to get an overview of the demos the server will let you download. (Usually, only the last 5 or so matches are kept for you to download.) Use <code>/getdemo N [F]</code> where N is the number of a demo listed by <code>/listdemos</code> to fetch that particular game’s replay.</p>
<h2><code>/demo</code> & <code>/seekdemo</code></h2>
<p>To watch the match recorded in a demo file, you use the <code>/demo</code> command. It will automatically go through the files you downloaded using <code>/getdemo</code> if you press TAB after typing <code>/demo</code> and SPACE. While the game runs, you are a spectator with the special client number “-1”. Use <code>/gamespeed N</code> to make the game run slower or faster (<code>N</code> is in percent, so 100 is normal speed, 50 is half speed, 1000 is super fast).</p>
<p>There is also <code>/seekdemo -MM:SS</code> to skip forward to when there were <code>MM</code> minutes and <code>SS</code> seconds remaining. (<code>/seekdemo MM:SS</code> works as well and will seek to when <code>MM</code> minutes and <code>SS</code> seconds had been played, but that’s decidedly less useful.)</p>
<h2>Client-side Demo recording</h2>
<p>Since demo files are basically just captures of the network packets sent a server (see below), it’s not too difficult to record a player’s POV of the game by recording the network packets arriving at the client instead. (Some network packets that originate at a client, like N_SHOT, aren’t echoed from the server to the source client, and so have to be injected individually, but it works.) Thomas’ <a href="https://github.com/tpoechtrager/wc-ng">wc-ng</a> as well as <a href="https://mmm.page/p1x.braten">p1xbraten</a> have client-side demo recording built in.</p>
<h3>wc-ng</h3>
<p>wc-ng has the commands <code>/recordclientdemo</code> and <code>/stopclientdemo</code>. They work very similar to the commands to manage server-side demo recording:</p>
<ul>
<li><code>/recordclientdemo F</code>: schedules a recording of the next match (<code>F</code> is the file name to record to)</li>
<li><code>/stopclientdemo</code>: stops an ongoing recording of the current match</li>
</ul>
<p>wc-ng also embeds some game meta data into client-side demo recordings and let’s you search your demo files for player names for example. Documentation is a bit scarce, but you can find out more in <a href="https://github.com/tpoechtrager/wc-ng/blob/e9201821b81bc36d155fa8e6eb44d9ff373f257a/doc/WC_README.html">WC_README.html</a>. As far as I know, it’s not possible in wc-ng to start recording immediately (even though the code for it seems to be there).</p>
<h3>p1xbraten</h3>
<p>In p1xbraten, I also added the <code>/recordclientdemo</code> and <code>/stopclientdemo</code> commands. <code>/stopclientdemo</code> works just like in wc-ng, but <code>/recordclientdemo</code> is a bit different:</p>
<ul>
<li><code>/recordclientdemo 0|1|2</code>: 1 schedules, 0 cancels demo recording for the next match; 2 starts recording a demo immediately</li>
<li><code>/stopclientdemo</code> command: stops an ongoing recording</li>
</ul>
<p>p1xbraten uses the current date, time, map and mode to name client-side demos automatically.</p>
<h2>.dmo file format</h2>
<p>The Sauerbraten demo file format is closely related to the network protocol. All communication between the server and the client is based on many individual packets of data. Usually, replays are recorded by the server, meaning demo files are created on the server.</p>
<p>Demo files use compression (gzip) to reduce file size. (In fact, you can just run <code>gunzip --suffix=dmo awsome_game.dmo</code> to get the raw bytes Sauerbraten will process when playing back the file.) If you decompress a .dmo file, you find at the beginning of the file the “demo header” telling Sauerbraten what to expect in the remaining bytes:</p>
<ul>
<li>first, it contains the magic bytes “SAUERBRATEN_DEMO” so Sauer can be sure there’s demo data contained in the gzip stream,</li>
<li>then there is the demo file format version (so Sauer can inform the user (in case a newer version was used to record this file) that it will not be able to read the stream correctly),</li>
<li>and finally, the network protocol version number that was used while recording is included (for the same reason as above).</li>
</ul>
<p>After the header, the file simply contains all network packets the server would have sent to a spectator of the live match (in chronological order). Each packet is prefixed with a small header that has three fields, holding information required to replay the game correctly:</p>
<ul>
<li>the time when the packet was sent (relative to the start of the game)</li>
<li>the channel the packet was sent on (0 or 1, read more <a href="/np-connecting-to-server/">here</a>)</li>
<li>the size of the packet (so the code know how many bytes to read before trying to parse the packet)</li>
</ul>
<p>To replay the match, the game loop continously looks at the next packet’s timestamp and when the game clock advances to the time the packet was originally sent, it will interpret it as if it just came from a real server.</p>
</content>
<updated>2022-01-12T00:00:00Z</updated>
</entry>
<entry>
<title>Projectile spread</title>
<link rel="alternate" href="https://kb.p1x.pw/spread/" />
<id>https://kb.p1x.pw/spread</id>
<summary>How exactly Sauer spreads the chaingun and shotgun rays.</summary>
<content><p>Today in the Sauerworld Discord (<a href="https://discord.gg/j3kyxtj">join here</a>), we talked about the chaingun (aka minigun, aka machine gun) and shotgun damage and how their rays spread around your crosshair. Games like Counter-Strike aim for realistic projectile spread induced by the weapon’s recoil: your crosshair (and with it, projectile vectors) tend to move upwards the longer you burst-fire, and players learn to correct for it by moving the crosshair down. Some weapons in some games also have projectiles spreading outwards around your crosshair, with the spread usually increasing the longer you hold the trigger.</p>
<p>Sauerbraten, in contrast to most other shooter games you may know, has pretty basic projectile spread mechanics. And since Sauer is open-source, we can take a look behind the scenes to understand the intricacies of those mechanics! The following C++ code is taken directly from Sauerbraten’s <a href="https://sourceforge.net/p/sauerbraten/code/6205/tree/src/fpsgame/weapon.cpp#l118">source code (<code>src/fpsgame/weapon.cpp</code>)</a>:</p>
<pre><code class="language-c++">void offsetray(const vec &from, const vec &to, int spread, float range, vec &dest)
{
vec offset;
do offset = vec(rndscale(1), rndscale(1), rndscale(1)).sub(0.5f);
while(offset.squaredlen() > 0.5f*0.5f);
offset.mul((to.dist(from)/1024)*spread);
offset.z /= 2;
dest = vec(offset).add(to);
if(dest != from)
{
vec dir = vec(dest).sub(from).normalize();
raycubepos(from, dir, dest, range, RAY_CLIPMAT|RAY_ALPHAPOLY);
}
}
</code></pre>
<p>Some things to note before we dive into this function:</p>
<ul>
<li>the function calculates the offset for a single ray (from the straight line, which would be used for example a rifle projectile)</li>
<li>it’s the only function to calculate ray offset (= projectile spread) in the source code, which means the overall spread calculation is the same for all weapons that have spreading projectiles (currently, chaingun, shotgun and pistol)</li>
<li>however, it takes a <code>spread</code> argument, so the output is not neccessarily the same for all weapons</li>
<li>the function is called with the same <code>spread</code> argument for every ray of a weapon, meaning <strong>chaingun spread does not increase with time</strong></li>
</ul>
<p>So what does the code do, exactly? Let’s begin by looking at the function’s input (its arguments) and output:</p>
<p>The first two arguments tell the function <code>from</code> where <code>to</code> where in the map the player is shooting. Then there are the <code>spread</code> and <code>range</code> arguments, which are taken from the weapon’s defined settings. (These weapon settings are set in the source code, and can’t be changed in-game. Other weapon settings would be how much damage a ray deals and how long it takes to reload.) The last argument, <code>dest</code>, is a variable that will hold the destination of the offset ray after the function ran and is actually the output of the function. (In other programming languages, this would be the function’s return value.)</p>
<p><img src="https://www.sauerworld.org/wp-content/uploads/2020/10/screenshot_1766047.png" alt="chaingun shooting at flag carrier" /></p>
<p>From a high-level point of view, the function calculates a single ray’s destination vector by preparing an offset vector which it adds to the vector pointing from the player to the target, i.e. offsetting the ray from the vector connecting <code>from</code> and <code>to</code>. It returns the vector pointing from <code>from</code> to the offset target as the <code>dest</code> vector.</p>
<p>Let’s go through the function step-by-step:</p>
<pre><code class="language-c++">vec offset;
do offset = vec(rndscale(1), rndscale(1), rndscale(1)).sub(0.5f);
while(offset.squaredlen() > 0.5f*0.5f);
</code></pre>
<p>The first three lines prepare a vector variable with three coordinates (x, y and z) and try random values between -0.5 and 0.5 for each coordinate, until it finds a vector where x<sup>2</sup> + y<sup>2</sup> + z<sup>2</sup> is greater than 0.25. This basically means the offset vector can point in any direction, but its magnitude is limited to a sphere of radius 0.5. Although this explicitly prevents the case that x<sup>2</sup> + y<sup>2</sup> + z<sup>2</sup> = 0, it does not mean that this function will never produce a ray that goes exactly straight: the offset vector might point parallel to the direction of the shot, so offsetting the ray will only make it point behind or in front of the original target! You might get lucky and get a straight shot even with your chaingun!</p>
<pre><code class="language-c++">offset.mul((to.dist(from)/1024)*spread);
</code></pre>
<p>The next line makes it so that long range shots are offset more than short range ones. It uses the shot distance (<code>to.dist(from)</code>), scales it by a magic factor of 1/1024, and then scales it again by the weapon’s spread setting (currently 100 for chaingun, 400 for shotgun, 50 for pistol). The entire offset vector is then scaled by the result of all that scaling of the shot distance.</p>
<pre><code class="language-c++">offset.z /= 2;
</code></pre>
<p>This line is very interesting: <code>z</code> is the up-down axis in Sauer (if you jump, your z coordinate increases, if you fall down like a noob on reissen, it decreases). The <code>/= 2</code> bit means the z component is halved. We will get back to what this means for us later!</p>
<pre><code class="language-c++">dest = vec(offset).add(to);
</code></pre>
<p>This part simply defines <code>dest</code> as the position where the offset ray ends (for now), by adding the offset vector to the position vector of the target of the shot.</p>
<pre><code class="language-c++">if(dest != from)
{
...
}
</code></pre>
<p>The next bit ensures that the calculated ray doesn’t end where it starts, for reasons I am not sure why. It might have to do with Sauer’s spawn kill protection, but it’s really just my best guess here. For simplicity, let’s assume that will never be the case, so the code inside the braces will be executed next.</p>
<pre><code class="language-c++">vec dir = vec(dest).sub(from).normalize();
raycubepos(from, dir, dest, range, RAY_CLIPMAT|RAY_ALPHAPOLY);
</code></pre>
<p>The last two lines of code move the destination (= end) point of the ray along the ray until it collides with something in the world, for example the wall or (ideally) an enemy’s player model. This is done by calculating a normalized vector <code>dir</code> of the ray’s direction from its start (<code>from</code>) and end (<code>dest</code>) vectors, and then relying on the engine to set <code>dest</code> to the point where this vector <code>dir</code>, starting at <code>from</code> intersects with something that would stop a projectile. Essentially, this makes sure the ray doesn’t end in front of the player or goes through her model without hitting or has the ray end somewhere beside the player in the middle of the air.</p>
<p>Now back to why <code>offset.z /= 2</code> is so interesting here: For you as a player, this line means <strong>shots are more accurate when you are at the same height as your target</strong>!</p>
<p>If you’re not sure why, think about the sphere of possible offset vectors around the target: when the offset vector’s z component is reduced by the <code>/= 2</code> operation, the height of the sphere of possible offsets around the target is reduced, so it’s no longer a sphere, really, but more of a pumpkin! At the very end, what matters is the 2D projection of this pumpkin towards the players camera (since the depth component of the offset vector in relation to the player’s camera is irrelevant [the end point of the ray is recalculated after offsetting the shot]). Seen from eye level (that is, perfectly horizontal), the surface area of the sphere of possible offset vectors got smaller by compressing it along the z-axis, but seen from above, it’s still the same size! So the more “from above” a player’s perspective onto the target is, the less they benefit from this height compression of the possible offset vectors. The greater the difference in z-height between the start and end of the shot (i.e. the player and their target), the less likely a ray is to be close enough in the center to count as a hit!</p>
</content>
<updated>2020-10-20T00:00:00Z</updated>
</entry>
<entry>
<title>Quick-start Guide for New Players</title>
<link rel="alternate" href="https://kb.p1x.pw/notas-guide/" />
<id>https://kb.p1x.pw/notas-guide</id>
<summary>by notas</summary>
<content><p>Resources:</p>
<ul>
<li>brightskins: <a href="http://sauerleague.org/index.php/skins-snoutx10k">http://sauerleague.org/index.php/skins-snoutx10k</a></li>
<li>gameclock, ammobar, better mousefeel and more <a href="http://comed.sauerworld.org/">http://comed.sauerworld.org/</a></li>
</ul>
<p>Useful commands that aren’t in GUI (type in chat with / in front):</p>
<ul>
<li><code>texreduce</code> (= <code>picmip</code> in Quake), 0-12 (ex: <code>/texreduce 4</code>)</li>
<li><code>hudgun</code> (= <code>drawgun</code>), 1|0 (ex: <code>/hudgun 0</code>)</li>
<li><code>/maxfps 0</code> sets uncapped, or set to any value (ex: <code>/maxfps 288</code>)</li>
</ul>
<p>Join the Sauerworld Discord server: <a href="https://discord.gg/SNheTc8">https://discord.gg/SNheTc8</a> (use #find-games)</p>
<p>Peak hours are 10 - 18:00 EST (15:00 to 23:00 GMT), after 18:00 EST you can usually find duels in discord but no team games other than public ictf</p>
<p>For items duel, try to find a server using the competitive HB mod - HB testing, sp4nk’s PCP, and [sQ] servers all have it and are open, while w00p.cc and sp4nk’s g4ngb4ng have it but you’ll need an established player to unlock them.</p>
<p>Note that because hit detection in Sauerbraten is clientside a ping of 100 is perfectly fine, and up to 200 is still playable albeit bad.</p>
<p>Try messaging notas, Redon, or Lokio if you need help or just post in #sauercom in Sauerworld Discord</p>
<p>PS: if you are kicked randomly from the server EFFIC_BITCHES it’s because the server is run by a fuckwit</p>
<hr />
<p>The above text is a short introduction notas put together originally for people watching his stream. It’s supposed to be helpful for new players, especially those already familiar with the genre of Arena FPS.</p>
<p>I just formatted and reposted it here for preservation purposes.</p>
</content>
<updated>2019-02-18T00:00:00Z</updated>
</entry>
<entry>
<title>Network Protocol: The Position Packet</title>
<link rel="alternate" href="https://kb.p1x.pw/np-position-packet/" />
<id>https://kb.p1x.pw/np-position-packet</id>
<summary>Explains the position packet and how its contents are compressed and interpreted.</summary>
<content><p>Sauerbraten uses this type of packet to communicate client positions between client(s) and server. A client sends its position to the server every 33 milliseconds, which corresponds to 30 “frames” per second. The server replays packets of clients to all other clients at the same rate.</p>
<h1>Schema</h1>
<p>Here’s what the <code>N_POS</code> packet looks like (byte by byte):</p>
<pre><code>N_POS
CN
state
flags
x.1 x.2 [x.3] y.1 y.2 [y.3] z.1 z.2 [z.3]
dir.1 dir.2 roll
vel.1 [vel.2] veldir.1 veldir.2
[fall.1 [fall.2] [falldir.1 falldir.2]]
</code></pre>
<h4><code>N_POS</code></h4>
<p>This just indicates to the receiver that this packet is a position packet.</p>
<h4><code>CN</code></h4>
<p>Client number of the player whose position is described in the packet (can be > 128 to describe a bot’s position).</p>
<h4><code>state</code></h4>
<p>Describes the parts of the player’s state that are relevant to animating the player model; the bits from low to high are:</p>
<ul>
<li>3 bits “physical” state (falling, sliding down a slope, moving/standing on floor, etc…)</li>
<li>1 bit life sequence (changes at every respawn)</li>
<li>2 bits move (<code>0b01</code> = forward, <code>0b11</code> = backward, <code>0b00</code> = none)</li>
<li>2 bits strafe (<code>0b01</code> = left, <code>0b11</code> = right, <code>0b00</code> = none)</li>
</ul>
<h4><code>flags</code></h4>
<p>Flags that indicate what optional packets to expect; the bits from low to high are:</p>
<ul>
<li>3 bits indicating the presence <code>x.3</code>, <code>y.3</code>, <code>z.3</code> respectively</li>
<li>1 bit indicating whether there is a <code>vel.2</code> byte</li>
<li>3 bits describing the player’s fall:
<ul>
<li>first bit indicates presence of the entire <code>[fall.1 [fall.2] falldir.1 falldir.2]</code> block</li>
<li>second bit indicates whether <code>fall.2</code> is present</li>
<li>third bit is set when the two <code>falldir</code> bytes are present: when they are not sent in the position packet, it is assumed that the fall vector is pointing straight down (this way “normal” falling down saves the <code>falldir</code> bytes)</li>
</ul>
</li>
<li>1 bit indicating whether the player is standing in game clip material</li>
</ul>
<h4><code>x.1</code> through <code>z.3</code></h4>
<p>Describe the player’s position in the world (third bytes are only present when the values exceed 2 bytes, see <code>flags</code>).</p>
<h4><code>dir.1 dir.2</code></h4>
<p>Contain (compressed) yaw and pitch of the player’s view/“camera” (i.e. the direction the player is looking in):</p>
<ul>
<li>all 16 bits (<code>dir.1 | dir.2 << 8</code>) make up an integer value <code>dir</code></li>
<li><code>dir mod 360</code> is the yaw value in degrees</li>
<li><code>(dir / 360) - 90</code> is the pitch value in degrees (-90° to 90°; -90° ~ looking straight down; 90° ~ looking straight up)</li>
</ul>
<h4><code>roll</code></h4>
<p>The player’s roll value (with +90 offset like pitch: a <code>0x00</code> byte (= 0) means -90° roll, and <code>0x5A</code> (= 90) means the view is horizontal / 0° roll).</p>
<h4><code>vel.1 [vel.2]</code></h4>
<p>Magnitude of the velocity vector, i.e. the speed the player is moving at.</p>
<h4><code>veldir.1 veldir.2</code></h4>
<p>Yaw and pitch of the velocity vector (compressed like <code>dir.1 dir.2</code>).</p>
<h4><code>fall.1 [fall.2]</code></h4>
<p>Magnitude of the fall vector, i.e. the speed the player is falling at.</p>
<h4><code>falldir.1 falldir.2</code></h4>
<p>Yaw and pitch of the fall vector (compressed like <code>dir.1 dir.2</code>); only sent when it’s not pointing straight down (x ≠ 0 or y ≠ 0 or z > 0).</p>
<h1>Example</h1>
<p>Here’s an example of a position packet (bytes printed as their decimal representations):</p>
<pre><code>4 1 17 16 11 39 102 35 207 27 134 112 90 132 151 126 35
4 → N_POS
1 → CN
17 → state (17 = 0b00010001 ~ player moves forward, physical
state: falling)
16 → flags (16 = 0b00010000 ~ fall vector is included in the
packet)
11 39 → x (9995 ~ 624.6875)
102 35 → y (9062 ~ 566.375)
207 27 → z (7119 ~ 444.9375)
134 112 → direction of camera vector (28806 ~ 6° yaw, -9.984° pitch)
90 → roll of camera (0°)
132 → magnitude of the velocity vector
151 126 → direction of velocity vector (32407 ~ 7° yaw, 0.019° pitch)
35 → magnitude of the fall vector
</code></pre>
<p>So in this case, the player is falling (in the air) and at the same time moving forwards. The 0.019° velocity pitch indicates an almost horizontal pitch, possibly shortly before reaching the peak of a jump.</p>
</content>
<updated>2014-03-28T00:00:00Z</updated>
</entry>
<entry>
<title>Network Protocol: Connecting to a Server</title>
<link rel="alternate" href="https://kb.p1x.pw/np-connecting-to-server/" />
<id>https://kb.p1x.pw/np-connecting-to-server</id>
<summary>Detailed explanation of how a client connects to a server and joins the game.</summary>
<content><p>Have you ever wondered what’s going on under the hood when you play a game of efficiency ctf, with all that action of players shooting, jumping, dying, picking up flags, and spectators chatting? Here’s the explanation! Read on and learn how Sauerbraten’s networking works.</p>
<p>Sauerbraten’s networking code is based on the <a href="http://enet.bespin.org/">ENet library</a>. Without going into too much detail, ENet is a layer on top of <a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a> offering (optional) sequential and/or reliable transmission of packets (similar to <a href="https://en.wikipedia.org/wiki/Transmission_Control_Protocol">TCP</a>), the concept of using multiple channels on one connection, and more.</p>
<p><em>For this article, we assume a simple connect to a vanilla server with default config, nothing fancy like auth-on-connect or server passwords will be covered in-depth.</em></p>
<h2>Channels</h2>
<p>Sauerbraten uses 3 channels for communication between server and client:</p>
<ul>
<li>channel 0 is for transmitting position data (that is, where in the map is a client?)</li>
<li>channel 1 is for game events, like a player shooting, a player using a teleport, a player joining the game, chat messages, and much more</li>
<li>channel 2 is used in “coop edit” mode to send maps (<code>/sendmap</code> and <code>/getmap</code>)</li>
</ul>
<h2>Requesting a CN and server information</h2>
<p>When you try to connect to a server (for example by clicking on it in the server browser), your client will establish a connection to the server over ENet. The server will notice that you want to connect and will add you as a client and assign you a client number (CN). The server will then reply with a “server info” packet, which looks something like this (when you display the decimal values of the bytes):</p>
<pre><code>1 0 128 3 1 129 184 252 162 0 0 108 111 99 97 108 32 116 101 115 116 32
115 101 114 118 101 114 32 50 0 0
</code></pre>
<p>Here’s a short listing of the parts that make up a “server info” packet, followed by a more detailed explanation:</p>
<pre><code>1 → N_SERVINFO
0 → cn given to you by the server
128 3 1 → protocol number (3 + 256 = 259)
129 184 252 162 0 → session ID (184 + 64512 + 10616832 + 0 = 10681528)
0 → not password-protected
108 111 ... 50 0 → server description ("local test server 2")
0 → server auth domain ("")
</code></pre>
<h4><code>N_SERVINFO</code></h4>
<p>The <code>N_SERVINFO</code> byte is part of a list of network message codes that is known to the client and the server (you can find that list in <code>src/fpsgame/game.h</code>). <code>N_SERVINFO</code> just happens to have <code>1</code> as its byte representation; it doesn’t really matter what value it is, as long as the client and server both know the meaning of it.</p>
<h4><code>cn</code></h4>
<p>The next number is the CN the server assigned to your connection. If you are the first client to connect to a server, it is usually <code>0</code>. Connecting clients get the lowest free number (not a requirement, but it is this way in the vanilla server and in all server mods).</p>
<h4><code>protocol number</code></h4>
<p>Next up is the protocol number (<code>259</code> in collect edition). Don’t get confused by the weird three byte representation; it will be explained later.</p>
<h4><code>session ID</code></h4>
<p>The session ID is always generated, but only actually used when the server requires you to provide a password in order to connect. The password you type in is hashed together with this session ID and your CN, and only the hash is then sent to the server and compared to the server’s hash.</p>
<h4><code>passwort-protected indicator</code></h4>
<p>The following byte can be either <code>0</code> or <code>1</code>, and indicates if the server requires you to provide a password or not.</p>
<h4><code>server description</code></h4>
<p>The server description (C/C++ strings always end with a <code>0x00</code> byte). This is the same description that you can also read in your server browser.</p>
<h4><code>server auth domain</code></h4>
<p>Last comes the server’s auth domain for which a connecting client has to provide an auth key in order to be let in (an empty string signals the server does not use auth-on-connect; however, it does not mean the server doesn’t use local auth domains at all).</p>
<h3>Note regarding integer compression</h3>
<p>Notice how the protocol number (<code>259</code> in collect edition) consists of three bytes (also, the CN was only one byte even though it is a <code>uint32</code> internally and should be four bytes): this is because Sauerbraten uses an encoding for integer numbers that saves bandwitdh:</p>
<ul>
<li>values less than <code>128</code> and greater than <code>-127</code> (i.e. fit into one byte) will simply be sent as one byte</li>
<li>values that would fit into two bytes are sent as two bytes, preceeded by a <code>0x80</code> byte (<code>128</code> in decimal)</li>
<li>all other values will be sent as their normal four bytes, preceeded by a <code>0x81</code> byte (<code>129</code> in decimal)</li>
</ul>
<p>This works well for Sauerbraten even though big numbers need five bytes instead of just four, since most numbers sent are small and only take up one byte or two bytes (three after encoding).</p>
<h2>Joining the game</h2>
<p>After receiving a CN, Session ID and the server information, the client replies with a <code>N_CONNECT</code> packet, which looks like this:</p>
<pre><code>0 124 121 101 115 86 73 58 112 105 120 0 1 0 0 0
0 → N_CONNECT
124 121 ... 120 0 → player name ("|yesVI:pix")
1 → player model ID
0 → connect password hash ("")
0 → auth domain ("")
0 → auth name ("")
</code></pre>
<h4><code>N_CONNECT</code></h4>
<p><code>N_CONNECT</code> (like <code>N_SERVINFO</code>) tells the server what kind of packet this is so it knows what parts will come next.</p>
<h4><code>player name</code></h4>
<p>The next bytes up to a <code>0x00</code> byte are the player’s name.</p>
<h4><code>player model ID</code></h4>
<p>Up next is the ID of the player model the client uses, so that other clients know what player model to show for that player.</p>
<h4><code>connect password hash</code></h4>
<p>Next comes a string (all bytes up to <code>0x00</code>) containing the hashed connect password the user entered, in case the <code>N_SERVINFO</code> packet told the client that this server is password protected.</p>
<h4><code>auth domain</code> and <code>auth name</code></h4>
<p>Last come the auth domain (which is the same as the server auth domain the server sent in the <code>N_SERVINFO</code> packet) as a string and the name corresponding to the auth key the client found for the specified auth domain. For more information on the auth system in Sauerbraten, read the article on <a href="/auth/">Sauerbraten’s Auth Mechanism</a>.</p>
<p>Personal note: I find <code>N_CONNECT</code> to be a very confusing name for this packet: on the ENet level, you are already connected when you get the <code>N_SERVINFO</code> packet. “<code>N_JOIN</code>” would be a more apt name I think.)</p>
<h2>Welcoming the client</h2>
<p>When the server receives the <code>N_CONNECT</code> packet, it checks for a number of things:</p>
<ul>
<li>is the password correct? (if needed)</li>
<li>if not, is the server in private mode maybe?</li>
<li>is the auth key valid? (if needed)</li>
<li>is there a free spot (server not full)?</li>
<li>is the IP that connected banned?</li>
</ul>
<p>If everything is OK, it prepares the client’s on-server representation for playing and then sends a welcome packet back to the client that joined.</p>
<p>The server then welcomes the client by sending a number of packets. Here is a (small) example of that list of packets making up the “welcome packet“:</p>
<pre><code>2 22 114 101 105 115 115 101 110 0 5 0 33 87 58 1 3 1 -1 61 0 103 111
111 100 0 -1 17 0 0 100 100 100 1 2 20 20 10 10 20 0 37 1 0 12 1 0 3
100 100 100 1 2 16 4 3 8 15 0 -1 3 1 67 111 111 107 105 101 0 101 118
105 108 0 1
2 → N_WELCOME
22 → N_MAPCHANGE
114 101 ... 110 0 → map name ("reissen")
5 → game mode (effic)
0 → whether or not the server has representations
for items (quad, health boost, etc); 0 means it
already has them
33 → N_TIMEUP
87 → seconds left to play
58 → N_CURRENTMASTER
1 → veto
3 → a CN
1 → the privilege of client with CN 3
-1 → end of privileges list
61 → N_SETTEAM
0 → CN of the client whose team to set (= client
receiving this packet)
103 111 111 100 0 → team name ("good")
-1 → end of N_SETTEAM packet
17 → N_SPAWNSTATE
0 → CN of the client the spawn state date belongs to
(= client receiving this packet)
0 → client's life sequence (changes at every
respawn)
100 → client's health
100 → maximum health the player can have (can
change when health boost is picked up)
100 → armour
1 → armour type (pre-defined constant: 0 = blue,
1 = green, 2 = yellow)
2 → weapon the player is currently using (pre-
defined: 2 = minigun)
20 → shotgun ammo
20 → minigun ammo
10 → rocket launcher ammo
10 → rifle ammo
20 → grenades ammo
0 → pistol ammo
37 → N_RESUME
1 → CN of another player
0 → player 1's current state (0 = alive, ...)
12 → frags
1 → flags player 1 scored
0 → time left for player 1's quad damage, if he
has it
3 → player 1's life sequence (changes at every
respawn)
100 → player 1's health
100 → maximum health the player can have (can change
when health boost is picked up)
100 → armour
1 → armour type (pre-defined constant: 0 = blue,
1 = green, 2 = yellow)
2 → weapon the player is currently using (pre-
defined: 2 = minigun)
16 → shotgun ammo
4 → minigun ammo
3 → rocket launcher ammo
8 → rifle ammo
15 → grenades ammo
0 → pistol ammo
-1 → end of N_RESUME packet
3 → N_INITCLIENT
1 → CN of another client
67 111 ... 101 0 → name of client with above CN ("Cookie")
101 118 105 108 0 → team name of client with above CN ("evil")
1 → player model ID of client with above CN
</code></pre>
<p>Basically, every time you see a network message code (<code>N_*</code>), a new game packet begins:</p>
<ul>
<li><code>N_WELCOME</code> tells the client to close the server browser in case it is open</li>
<li><code>N_MAPCHANGE</code> tells the client what map to load and what mode is played on that map</li>
<li><code>N_TIMEUP</code> tells the client how many seconds are left to play (so the clock is set correctly when joining mid-game)</li>
<li><code>N_CURRENTMASTER</code> is followed by the master mode currently set on the server and a list of CNs of players with higher than normal privilege (this list can be empty; the end is marked by a <code>-1</code>)</li>
<li><code>N_SETTEAM</code> tells the client what team the player was put into</li>
<li><code>N_SPAWNSTATE</code> tells the client what state to use when the player spawns</li>
<li><code>N_RESUME</code> tells the client to start the countdown and is followed by a list of the current game state of each client already connected to the server (again, ends when <code>-1</code> is read)</li>
<li>now, one or more <code>N_INITCLIENT</code> packets follow, each describing a client’s name, team and player model (if more than one client are connected, there will be multiple <code>N_INITCLIENT</code> packets)</li>
</ul>
<p>After the server sent the above packet to the new client, it will notify all other clients of the newly connected client by sending a <code>N_INITCLIENT</code> packet to each of them containing the info of the new client. In case the new client was automatically set to spectator (e.g. when the server is locked), all other clients will also receive a <code>N_SPECTATOR</code> packet.</p>
<p>At the end of all this, the client is now doing all the stuff it has to do in order to get “up-to-speed” with the other players, i.e. load the map, put itself into the correct team, set the local player’s spawn state, etc. As far as the server is concerned, the client is now treated exactly the same as all the other clients, because the process of joining the current game is now finished.</p>
</content>
<updated>2014-03-17T00:00:00Z</updated>
</entry>
<entry>
<title>Screen Recording</title>
<link rel="alternate" href="https://kb.p1x.pw/screen-recording/" />
<id>https://kb.p1x.pw/screen-recording</id>
<summary>About the different methods of recording Sauerbraten gameplay.</summary>
<content><p>In order to be able to show off how good you are in front of your friends, or to show the world something interesting, or to just record yourself playing for fun, you need to record the screen output rendered by Sauerbraten. To do this, there are different methods; here they are explained:</p>
<h1>Recording</h1>
<h2>The built-in <code>/movie</code> command</h2>
<p>Sauerbraten has a built-in command to record the frames it renders and write them to a file: <code>/movie</code>. It is however of little use: it generates huge .avi files containing all the individual frames, without any compression at all. Nevertheless, some people might want this, and big files might not be a problem, so here is some explanation on how to use <code>/movie</code>.</p>
<p>Before you start recording, you should set a few things up:</p>
<ul>
<li><code>/moviesound 1|0</code> specifies wether sound will be included in the recorded .avi file</li>
<li><code>/moview 1920</code> sets the width of the recording in pixels, replace 1920 with e.g. 1280 for 720p recording</li>
<li><code>/movieh 1080</code> sets the height of the recording in pixels, again, replace 1080 with 720 to record in 720p</li>
<li><code>/moviefps 30</code> sets the frame rate at which recording will take place; 30 will suffice if you do not intend to make slow-motion clips (if you do, read on, it’s explained further down)</li>
</ul>
<p>If you have set up all that, start a recording with <code>/movie asdf</code>, with <code>asdf</code> being the name of the file the recorded frames are saved to (asdf.avi in this case). To stop recording, just type <code>/movie</code>.</p>
<h2>External screen recording software</h2>
<p>Since the <code>/movie</code> command produces big files and is kind of clumsy, it makes sense to use an external screen recording software. For Windows, there is FRAPS, a widely-used and well-known program. There is probably a lot of information out there on how to record games with it already, so Google can probably help you. For Linux, <a href="http://www.maartenbaert.be/simplescreenrecorder/">Simple Screen Recorder</a> works well using the OpenGL recording feature (experimental at the time of writing, but known to work with Sauer). However, due to the way Sauerbraten is launched, you need to know a few things. Here’s a short guide:</p>
<ol>
<li>Tick the “Record OpenGL” option</li>
<li>Click on “OpenGL settings”</li>
<li>As command, use <code>/path/to/sauerbraten/bin_unix/linux_64_client -q~/.sauerbraten -k/path/to/sauerbraten</code> (with the correct path to your Sauerbraten folder of course)</li>
<li>Set “Frame rate” to 30 (or higher if you want)</li>
<li>Unselect “Scale video”</li>
<li>Tick “Record cursor”</li>
<li>Click “Continue”</li>
<li>Next, select file name and codecs (MP4 as container, H.264 as video codec, AAC as audio codec is fine)</li>
<li>Before clicking “Continue”, start Sauerbraten</li>
<li>Set the desired resolution, and set <code>/maxfps 30</code> (or whatever you used in SSR). Maybe use windowed mode to be able to easily switch between Sauer and SSR</li>
<li>Close Sauerbraten using the menu, so it can save your settings</li>
<li>In SSR, click “Continue”</li>
<li>When you are ready to record, click “Start recording” in SSR</li>
</ol>
<h1>Demo playback</h1>
<p>In case you do not want to record yourself playing live, you can use Sauerbraten’s demo file functionality. You can get demo files from most online servers, either using the <code>/listdemos</code> and <code>/getdemo</code> commands, or by downloading them from the website advertised on the server.</p>
<p>Here are some useful commands for demo playback:</p>
<ul>
<li><code>/demo <file.dmo></code>: starts demo playback</li>
<li><code>/gamespeed 10..1000</code>: controls gamespeed; sounds stay the same however (chain gun sound playback rate is adjusted of course); 100 is default (think of it as percent)</li>
<li><code>/pausegame 1|0</code>: pauses/resumes game</li>
<li><code>/thirdperson 0..2</code>: 0 means first person view, 1 is fixed third person view (you see your character from behind), 2 is a freely rotating third person view (you can’t aim with your mouse anymore)</li>
</ul>
<p>A smart way to use the <code>/gamespeed</code> command is to do <code>/gamespeed 50</code> (or something like that), then after recording speeding up video by 2 times, except for scenes you want to be slow-mo. Using this method you don’t have a problem with low frame rates in slowed down scenes, but on the other hand sounds will need fixing probably.</p>
<p>The <a href="http://ogros.org/forum/viewtopic.php?f=77&t=1733">Wahn/Wonder Client</a> offers an easy way of jumping to a certain time in a demo: <code>/jumpto <mm:[ss]></code> will jump to <code>mm</code> minutes (and <code>ss</code> seconds) left to play (note: you can’t jump backwards in demos).</p>
<p>If you are using a vanilla client, it might be a good idea to make binds for changing the game speed to allow fast-forwarding, for example <code>gamespeed 1000</code> on <code>RIGHT</code>, <code>gamespeed 50</code> on <code>LEFT</code>, <code>gamespeed 100</code> on <code>UP</code>, or to do something like <code>/bind RIGHT [gamespeed 1000; onrelease gamespeed 100]</code>.</p>
<p>You can find more about demos on <a href="http://ogros.org/tips-a-tricks/51-all-about-demos.html">ogros.org</a>.</p>
</content>
<updated>2013-11-12T00:00:00Z</updated>
</entry>
<entry>
<title>Settings for professional-grade Gaming</title>
<link rel="alternate" href="https://kb.p1x.pw/pro-settings/" />
<id>https://kb.p1x.pw/pro-settings</id>
<summary>Settings that can help you improve your gameplay.</summary>
<content><p>Here’s two shots of the same scene, one using maxed-out graphics options with default settings, the other using gameplay-optimized graphics and settings (open these images in new tabs to compare):</p>
<p><a href="https://i.imgur.com/Mg8WvLl.jpg"><img src="https://i.imgur.com/Mg8WvLl.jpg" alt="pretty" /></a></p>
<p><a href="https://i.imgur.com/pha36Zc.png"><img src="https://i.imgur.com/pha36Zc.png" alt="ugly" /></a></p>
<p>Nevertheless, graphics aren’t the only thing you can change in Sauerbraten, and other settings may be at least equally important, for example a mouse sensitivity you are comfortable with and key bindings that make sense to you.</p>
<h1>Graphics settings</h1>
<p>Sauerbraten can be set up to look really nice, which is great and a lot of fun to look at. Luckily, it can also be set up to facilitate serious gameplay, which is about seeing things as fast as possible so that you can react to them as fast as possible.</p>
<h2>GFX</h2>
<p>You can/should turn off everything except the “fix t-joints” setting in the gfx tab of the options menu. Keep in mind that however beautiful graphical effects look, they are a distraction to your eye. How ugly you want to play the game is up to you of course. Especially water will look really bad, and it’s probably a good idea to keep the water refraction on, to be able to better see into water from above.
You can keep textures and animation on high quality, it won’t do much to your frame rate or your concentration.</p>
<h2>Frame rate</h2>
<p>Turn off vsync.
Next, find out your monitor’s refresh rate (probably 60Hz, but check for yourself in your OS’ settings). Now do <code>/maxfps 60</code> (<code>60</code> being your monitor’s refresh rate) and test if you can get stable 60 fps while playing as usual (<code>/showfps 1</code> in case you can’t see your frame rate in the bottom right corner of the screen). A good map to test this is eternal_valley. Make sure to stand somewhere high so you can overlook a good part of the map. If you’re feeling fancy, test xenon from a high position, and if that’s not enough, try kopenhagen from a high viewing point (try flying around as spectator). If you have stable 60 fps, add another 60 and try the same with <code>/maxfps 120</code>. Do this until you found a multiple of your monitors refresh rate that still gives you a stable framerate.</p>
<p>Why bother doing this? A stable frame rate will result in the same amount of frames being calculated in between each refreshing cycle of the monitor. This gives you about the same amount of “change” with each new frame. This technique uses the maximum possible frame rate while making sure frame changes are smooth (due to multiples of the refresh rate). Would you set <code>maxfps</code> to, say, <code>150</code>, your monitor (assuming a refresh rate of 60Hz) would show you 1 calculated frame with the first cycle and 2 (drawn as one) in the next. Your eyes prefer the constant amount of “change” you get with the method explained above. For more info on this, read about <a href="http://en.wikipedia.org/wiki/Screen_tearing">tearing</a>.</p>
<h2>Custom player model skins</h2>
<p>A huge help for your eyes when looking for enemies are custom skins for the playermodels. Most widely used are the <a href="http://mdsauer.blogspot.de/p/snout-skins.html">IronsnoutX10K skins by greenadiss</a>, they are also used in the screenshot above. They are much brighter than the default ones, and you can get them in pink and yellow, too. In combination with using these skins you should enable forcing matching player models (see below) and use the Ironsnout model yourself, to ensure all enemies are shown with the new bright skins.</p>
<p>On <a href="https://mdsauer.blogspot.de/">greenadiss’ modding site</a> you can also find skins for the Ogro playermodel as well as flag skins, transparent hudguns and some custom crosshairs.</p>
<h1>Mouse acceleration</h1>
<p>A lot of professional players prefer mouse acceleration to be disabled. This will give you 1:1 responsiveness from the distance your mouse travels on the table to the amount you turn in game; regardless of what speed you move your mouse at. Normally, the OS interprets fast mouse movement different from slow mouse movement (using an acceleration factor) and accelerates cursor movement for you. This is nice when clicking around on 2D GUIs, but it can be very confusing in a 3D game. Hence, most players want it off.</p>
<p>On instructions on how to turn off mouse acceleration in windows, use Google. It’s kind of complicated and involves registry changes. On Linux (using X server) it can be done on-demand using the command <code>xset m 0 0</code>. Note that it will go back to the default value when X restarts, so you should probable write a script and use your desktop’s autostart feature, or remember to execute the command every time you want to play. On OS X, I have no idea, but Google probably knows where to look.</p>
<h1>Miscellaneous commands</h1>
<p>These settings, again, support professional gameplay. Some of these may will have a bigger effect on your playing than the GFX settings we did before. Some of these can be set in the GUI, but for consistency’s sake they are all written out here.</p>
<p>Type them into the chat console, preceeded by a <code>/</code>, e.g. <code>/fov 120</code>.</p>
<ul>
<li><code>fov 120</code> (or anything else higher than <code>100</code>, the default value): allows you to see more of your surroundings (“field of view”)</li>
<li><code>maxroll 0</code>: disables the rolling camera effect when starting or ending a sideways movement</li>
<li><code>zoominvel 0</code>: disables the zoom-in animation, thus making zoom instant</li>
<li><code>zoomoutvel 0</code>: same as before, for the zoom-out animation</li>
<li><code>hudgunsway 0</code>: disables hudgun swaying animation; resulting in less distraction for the eye from moving enemies</li>
<li><code>muzzleflash 0</code>: disables the flashing animation when shooting</li>
<li><code>muzzlelight 0</code>: disables the lighting effect caused by the muzzle flash</li>
<li><code>forceplayermodels 1</code>: all players are shown as using the model you use; makes it easier to train your aiming on one kind of target</li>
<li><code>fullbrightmodels 200</code>: makes player models shine bright</li>
<li><code>texreduce 12</code>: reduces texture quality; think of it as blurring each texture until there is only one main color left, making the map quite ugly but also making it easier to see playermodels (this can make it harder to navigate in maps you don’t know well, so if you have problems, turn it off (<code>texreduce 0</code>) or to a lower level (e.g. <code>texreduce 6</code>).)</li>
<li><code>bind MOUSE2 togglezoom</code>: binds zoom to right mouse button; you’ll have to get used to jumping with the space bar</li>
<li><code>bind MOUSE2 [deaths = (getdeaths); togglezoom; onrelease [if (<= (getdeaths) $deaths) [togglezoom]]]</code>: same as before, but zooms out when you release the right mouse button; useful for quickscoping; the <code>getdeaths</code> stuff is in there because Sauerbraten automatically zooms out when you die, so when you die holding the right mouse button, releasing it would zoom in…</li>
</ul>
</content>
<updated>2013-10-14T00:00:00Z</updated>
</entry>
<entry>
<title>The Auth Command</title>
<link rel="alternate" href="https://kb.p1x.pw/auth-command/" />
<id>https://kb.p1x.pw/auth-command</id>
<summary>Explains what the /auth command does, and how it does it.</summary>
<content><p>Sauerbraten’s auth mechanism is often misunderstood. This section explains what <code>/auth</code> does, and how it does it.</p>
<p>On a vanilla server (and most modded servers), there are 3 privilege levels: “admin”, “auth” and “master” (or 4 if you count “unprivileged” as a level of privilege). <code>admin</code> can do everything on a server, including server configuration things like setting the server’s bot limit. <code>admin</code>, <code>auth</code> and <code>master</code> can do the usual gameplay stuff like change mode, map and mastermode, move players around from team to team or put them into the group of spectators, pause the game, kick people, and so on. <code>auth</code> is a little bit higher than <code>master</code> though, in that <code>auth</code> can kick <code>master</code>, but not the other way around. Also, you can only get <code>auth</code> privileges using the <code>/auth</code> command and a working auth key, while you can get <code>master</code> using <code>/setmaster 1</code> too.
Players with admin privilege are shown orange on the score board, masters are green, and players with auth are purple (but were green, just like masters, in older Sauerbraten versions).</p>
<p>For those looking to write a server mod: note that <code>/auth</code>-functionality is dictated by the master server at <code>master.sauerbraten.org</code>: If your server does not allow people to use their global auth keys to kick cheaters etc., your server will get banned from the master server and will no longer show up in the server browser.</p>
<h1>What it looks like</h1>
<p>First of all, for <code>/auth</code> to do anything, you need an authkey in your <code>auth.cfg</code>. An authkey consists of 3 parts and is used in <code>auth.cfg</code> like this:</p>
<pre><code>authkey <name> <key> [domain/description]
</code></pre>
<ul>
<li><code>authkey</code> is a cubescript command which adds the key to a list of keys in memory at startup.</li>
<li><code>name</code> is the name for which the key (pair) was generated.</li>
<li><code>key</code> is actually the private key of a key pair. It’s a 192 bit key, represented as a 48-characters long hexadecimal string.</li>
<li>The last part is optional, and its defininition is a little blurry. The source code refers to it as <code>desc</code>, but domain is a more accurate name for it. More on why it is used later.</li>
</ul>
<h1>Different methods of authentication</h1>
<p>A lot of people don’t know, but <code>/auth</code> can not only be used to authenticate a player globally (the game server asks the master server at master.sauerbraten.org if it recognizes the player), but also locally (the game server itself recognizes the user). This means a player can have many auth keys in their auth.cfg, and when using <code>/auth</code>, the game server could first try to ask for a “local” authkey. Some hopmod servers use this method. The wahnmod servers (oo, DM, PSL) did not use this method, but gave trusted users and admins their privileges after they logged in using the <code>/setmaster "name/password"</code> method. This method was part of their mod; in a vanilla server, <code>/setmaster</code> can only be used with one password (the <code>adminpassword</code> server variable), and gives you admin privileges (apart from <code>/setmaster 1</code> of course, which gives you master privileges when allowed).</p>
<p>This local authentication method is also the reason why the last part of an authkey is needed, and why <code>domain</code> is a good word for it: the server tells the client which key to use for authenticating. When authenticating globally (at the master server, through the game server), it will tell the client to use the key with description <code>""</code> (an empty string), because global auth keys do not have the last part (hence optional).</p>
<h1>The internals</h1>
<p>When you do <code>/auth</code> in your client, what actually happens is a lot more complicated than sending your key to the server. In fact, the key is never sent to the server, and it would be wrong to do so, since it is supposed to stay secret. Sending it to the server would mean the server admin could log it and use it to pretend to be you on a different server.</p>
<p>Instead, the auth mechanism relies on <a href="http://en.wikipedia.org/wiki/Public-key_cryptography"><em>public-key cryptography</em></a>. Whenever someone gives you an authkey, he has actually generated 2 keys, or a key pair, consisting of a public key and a private key. The private key is the one you get. Think of it as a key to a really strong padlock. The other part, the public key, although being called “key” is actually more like the padlock your key fits into.</p>
<p>The person who generated the keys will of course be able to keep the private key, too, or even give it away to someone else, but that would not be in their interest, since they generated the key pair to authenticate the player (i.e. be sure it’s them, not someone else). Global auth keys are generated by whoever currently runs master.sauerbraten.org (probably eihrul, the main developer at the moment). The master server has the public keys of all the global auth keys in existence, except those that have been revoked for abuse.</p>
<p>Back to <code>/auth</code> in your client. What happens under the hood is this (assuming the server does not use local auth, only global auth):</p>
<pre><code class="language-">1. Client → I want to authenticate! → Server
← OK, you'll need your global auth key for that. ←
→ Found it, my name for that key is 'player1'. →
2. Server → I need an auth challenge for 'player1', please! → Master
← Tell him to decrypt this for me: <encrypted bytes>. ←
3. Client ← Decrypt these please: <encrypted bytes>. ← Server
→ Here you go: <decrypted bytes>. →
4. Server → This is what he sent me: <decrypted bytes>. → Master
← Yup, looks good. He really is 'player1'. ←
5. Client ← OK, Master confirmed it. You have auth privileges! ← Server
</code></pre>
<p>Here is a detailed explanation of each of the five steps:</p>
<ol>
<li>The client asks the server for auth privileges. The server replies by telling the client the auth domain of the auth key that will be needed to authenticate (in this case, the global domain). The client checks if it has an auth key matching the domain. If it finds a fitting key, it tells the server what its “auth name” for that domain is. If it does not find one, <code>/auth</code> didn’t work and the process of authenticating has failed and is over.</li>
<li>The server asks the master server to generate a challenge to pass on to the client and provides the auth name it received from the client. The master server looks for a public key belonging to that auth name. If there is none, it will tell the server, which in turn will tell the client that authentication has failed. If there is a public key matching the name, the master server then creates a challenge by encrypting a seed (basically random bytes) with the public key of <code>player1</code>. It also stores the seed (the unencrypted random bytes) locally.</li>
<li>The server forwards the challenge to the client. The client decrypts the bytes it got sent using the private key. Since the clear text is random, the client doesn’t know yet if his key worked or not. Only the master server will know if the key worked.</li>
<li>The server forwards the client’s repsonse to the master server. The master server compares the bytes he stored with the bytes the server forwarded to him. When they are the same, the client must have the correct private key, and the master server can be sure that the client trying to authenticate really is who he claims to be (unless the auth key was stolen of course…).</li>
<li>The server now gives the client <code>auth</code> privileges.</li>
</ol>
</content>
<updated>2013-10-08T00:00:00Z</updated>
</entry>
</feed>