|
|
1.1.1.3 ! root 1: /* ! 2: Copyright (C) 1997-2001 Id Software, Inc. ! 3: ! 4: This program is free software; you can redistribute it and/or ! 5: modify it under the terms of the GNU General Public License ! 6: as published by the Free Software Foundation; either version 2 ! 7: of the License, or (at your option) any later version. ! 8: ! 9: This program is distributed in the hope that it will be useful, ! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! 12: ! 13: See the GNU General Public License for more details. ! 14: ! 15: You should have received a copy of the GNU General Public License ! 16: along with this program; if not, write to the Free Software ! 17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ! 18: ! 19: */ 1.1 root 20: 21: #include "server.h" 22: 23: /* 24: ============================================================================= 25: 26: Encode a client frame onto the network channel 27: 28: ============================================================================= 29: */ 30: 1.1.1.2 root 31: #if 0 32: 33: // because there can be a lot of projectiles, there is a special 34: // network protocol for them 35: #define MAX_PROJECTILES 64 36: edict_t *projectiles[MAX_PROJECTILES]; 37: int numprojs; 38: cvar_t *sv_projectiles; 39: 40: qboolean SV_AddProjectileUpdate (edict_t *ent) 41: { 42: if (!sv_projectiles) 43: sv_projectiles = Cvar_Get("sv_projectiles", "1", 0); 44: 45: if (!sv_projectiles->value) 46: return false; 47: 48: if (!(ent->svflags & SVF_PROJECTILE)) 49: return false; 50: if (numprojs == MAX_PROJECTILES) 51: return true; 52: 53: projectiles[numprojs++] = ent; 54: return true; 55: } 56: 57: void SV_EmitProjectileUpdate (sizebuf_t *msg) 58: { 59: byte bits[16]; // [modelindex] [48 bits] xyz p y 12 12 12 8 8 [entitynum] [e2] 60: int n, i; 61: edict_t *ent; 62: int x, y, z, p, yaw; 63: int len; 64: 65: if (!numprojs) 66: return; 67: 68: MSG_WriteByte (msg, numprojs); 69: 70: for (n=0 ; n<numprojs ; n++) 71: { 72: ent = projectiles[n]; 73: x = (int)(ent->s.origin[0]+4096)>>1; 74: y = (int)(ent->s.origin[1]+4096)>>1; 75: z = (int)(ent->s.origin[2]+4096)>>1; 76: p = (int)(256*ent->s.angles[0]/360)&255; 77: yaw = (int)(256*ent->s.angles[1]/360)&255; 78: 79: len = 0; 80: bits[len++] = x; 81: bits[len++] = (x>>8) | (y<<4); 82: bits[len++] = (y>>4); 83: bits[len++] = z; 84: bits[len++] = (z>>8); 85: if (ent->s.effects & EF_BLASTER) 86: bits[len-1] |= 64; 87: 88: if (ent->s.old_origin[0] != ent->s.origin[0] || 89: ent->s.old_origin[1] != ent->s.origin[1] || 90: ent->s.old_origin[2] != ent->s.origin[2]) { 91: bits[len-1] |= 128; 92: x = (int)(ent->s.old_origin[0]+4096)>>1; 93: y = (int)(ent->s.old_origin[1]+4096)>>1; 94: z = (int)(ent->s.old_origin[2]+4096)>>1; 95: bits[len++] = x; 96: bits[len++] = (x>>8) | (y<<4); 97: bits[len++] = (y>>4); 98: bits[len++] = z; 99: bits[len++] = (z>>8); 100: } 101: 102: bits[len++] = p; 103: bits[len++] = yaw; 104: bits[len++] = ent->s.modelindex; 105: 106: bits[len++] = (ent->s.number & 0x7f); 107: if (ent->s.number > 255) { 108: bits[len-1] |= 128; 109: bits[len++] = (ent->s.number >> 7); 110: } 111: 112: for (i=0 ; i<len ; i++) 113: MSG_WriteByte (msg, bits[i]); 114: } 115: } 116: #endif 1.1 root 117: 118: /* 119: ============= 120: SV_EmitPacketEntities 121: 122: Writes a delta update of an entity_state_t list to the message. 123: ============= 124: */ 125: void SV_EmitPacketEntities (client_frame_t *from, client_frame_t *to, sizebuf_t *msg) 126: { 127: entity_state_t *oldent, *newent; 128: int oldindex, newindex; 129: int oldnum, newnum; 130: int from_num_entities; 131: int bits; 132: 1.1.1.2 root 133: #if 0 134: if (numprojs) 135: MSG_WriteByte (msg, svc_packetentities2); 136: else 137: #endif 138: MSG_WriteByte (msg, svc_packetentities); 1.1 root 139: 140: if (!from) 141: from_num_entities = 0; 142: else 143: from_num_entities = from->num_entities; 144: 145: newindex = 0; 146: oldindex = 0; 147: while (newindex < to->num_entities || oldindex < from_num_entities) 148: { 149: if (newindex >= to->num_entities) 150: newnum = 9999; 151: else 152: { 153: newent = &svs.client_entities[(to->first_entity+newindex)%svs.num_client_entities]; 154: newnum = newent->number; 155: } 156: 157: if (oldindex >= from_num_entities) 158: oldnum = 9999; 159: else 160: { 161: oldent = &svs.client_entities[(from->first_entity+oldindex)%svs.num_client_entities]; 162: oldnum = oldent->number; 163: } 164: 165: if (newnum == oldnum) 166: { // delta update from old position 167: // because the force parm is false, this will not result 168: // in any bytes being emited if the entity has not changed at all 1.1.1.2 root 169: // note that players are always 'newentities', this updates their oldorigin always 170: // and prevents warping 171: MSG_WriteDeltaEntity (oldent, newent, msg, false, newent->number <= maxclients->value); 1.1 root 172: oldindex++; 173: newindex++; 174: continue; 175: } 176: 177: if (newnum < oldnum) 178: { // this is a new entity, send it from the baseline 1.1.1.2 root 179: MSG_WriteDeltaEntity (&sv.baselines[newnum], newent, msg, true, true); 1.1 root 180: newindex++; 181: continue; 182: } 183: 184: if (newnum > oldnum) 185: { // the old entity isn't present in the new message 186: bits = U_REMOVE; 187: if (oldnum >= 256) 188: bits |= U_NUMBER16 | U_MOREBITS1; 189: 190: MSG_WriteByte (msg, bits&255 ); 191: if (bits & 0x0000ff00) 192: MSG_WriteByte (msg, (bits>>8)&255 ); 193: 194: if (bits & U_NUMBER16) 195: MSG_WriteShort (msg, oldnum); 196: else 197: MSG_WriteByte (msg, oldnum); 198: 199: oldindex++; 200: continue; 201: } 202: } 203: 204: MSG_WriteShort (msg, 0); // end of packetentities 1.1.1.2 root 205: 206: #if 0 207: if (numprojs) 208: SV_EmitProjectileUpdate(msg); 209: #endif 1.1 root 210: } 211: 212: 213: 214: /* 215: ============= 216: SV_WritePlayerstateToClient 217: 218: ============= 219: */ 220: void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, sizebuf_t *msg) 221: { 222: int i; 223: int pflags; 224: player_state_t *ps, *ops; 225: player_state_t dummy; 226: int statbits; 227: 228: ps = &to->ps; 229: if (!from) 230: { 231: memset (&dummy, 0, sizeof(dummy)); 232: ops = &dummy; 233: } 234: else 235: ops = &from->ps; 236: 237: // 238: // determine what needs to be sent 239: // 240: pflags = 0; 241: 242: if (ps->pmove.pm_type != ops->pmove.pm_type) 243: pflags |= PS_M_TYPE; 244: 245: if (ps->pmove.origin[0] != ops->pmove.origin[0] 246: || ps->pmove.origin[1] != ops->pmove.origin[1] 247: || ps->pmove.origin[2] != ops->pmove.origin[2] ) 248: pflags |= PS_M_ORIGIN; 249: 250: if (ps->pmove.velocity[0] != ops->pmove.velocity[0] 251: || ps->pmove.velocity[1] != ops->pmove.velocity[1] 252: || ps->pmove.velocity[2] != ops->pmove.velocity[2] ) 253: pflags |= PS_M_VELOCITY; 254: 255: if (ps->pmove.pm_time != ops->pmove.pm_time) 256: pflags |= PS_M_TIME; 257: 258: if (ps->pmove.pm_flags != ops->pmove.pm_flags) 259: pflags |= PS_M_FLAGS; 260: 261: if (ps->pmove.gravity != ops->pmove.gravity) 262: pflags |= PS_M_GRAVITY; 263: 264: if (ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0] 265: || ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1] 266: || ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2] ) 267: pflags |= PS_M_DELTA_ANGLES; 268: 269: 270: if (ps->viewoffset[0] != ops->viewoffset[0] 271: || ps->viewoffset[1] != ops->viewoffset[1] 272: || ps->viewoffset[2] != ops->viewoffset[2] ) 273: pflags |= PS_VIEWOFFSET; 274: 275: if (ps->viewangles[0] != ops->viewangles[0] 276: || ps->viewangles[1] != ops->viewangles[1] 277: || ps->viewangles[2] != ops->viewangles[2] ) 278: pflags |= PS_VIEWANGLES; 279: 280: if (ps->kick_angles[0] != ops->kick_angles[0] 281: || ps->kick_angles[1] != ops->kick_angles[1] 282: || ps->kick_angles[2] != ops->kick_angles[2] ) 283: pflags |= PS_KICKANGLES; 284: 285: if (ps->blend[0] != ops->blend[0] 286: || ps->blend[1] != ops->blend[1] 287: || ps->blend[2] != ops->blend[2] 288: || ps->blend[3] != ops->blend[3] ) 289: pflags |= PS_BLEND; 290: 291: if (ps->fov != ops->fov) 292: pflags |= PS_FOV; 293: 294: if (ps->rdflags != ops->rdflags) 295: pflags |= PS_RDFLAGS; 296: 297: if (ps->gunframe != ops->gunframe) 298: pflags |= PS_WEAPONFRAME; 299: 300: pflags |= PS_WEAPONINDEX; 301: 302: // 303: // write it 304: // 305: MSG_WriteByte (msg, svc_playerinfo); 306: MSG_WriteShort (msg, pflags); 307: 308: // 309: // write the pmove_state_t 310: // 311: if (pflags & PS_M_TYPE) 312: MSG_WriteByte (msg, ps->pmove.pm_type); 313: 314: if (pflags & PS_M_ORIGIN) 315: { 316: MSG_WriteShort (msg, ps->pmove.origin[0]); 317: MSG_WriteShort (msg, ps->pmove.origin[1]); 318: MSG_WriteShort (msg, ps->pmove.origin[2]); 319: } 320: 321: if (pflags & PS_M_VELOCITY) 322: { 323: MSG_WriteShort (msg, ps->pmove.velocity[0]); 324: MSG_WriteShort (msg, ps->pmove.velocity[1]); 325: MSG_WriteShort (msg, ps->pmove.velocity[2]); 326: } 327: 328: if (pflags & PS_M_TIME) 329: MSG_WriteByte (msg, ps->pmove.pm_time); 330: 331: if (pflags & PS_M_FLAGS) 332: MSG_WriteByte (msg, ps->pmove.pm_flags); 333: 334: if (pflags & PS_M_GRAVITY) 335: MSG_WriteShort (msg, ps->pmove.gravity); 336: 337: if (pflags & PS_M_DELTA_ANGLES) 338: { 339: MSG_WriteShort (msg, ps->pmove.delta_angles[0]); 340: MSG_WriteShort (msg, ps->pmove.delta_angles[1]); 341: MSG_WriteShort (msg, ps->pmove.delta_angles[2]); 342: } 343: 344: // 345: // write the rest of the player_state_t 346: // 347: if (pflags & PS_VIEWOFFSET) 348: { 349: MSG_WriteChar (msg, ps->viewoffset[0]*4); 350: MSG_WriteChar (msg, ps->viewoffset[1]*4); 351: MSG_WriteChar (msg, ps->viewoffset[2]*4); 352: } 353: 354: if (pflags & PS_VIEWANGLES) 355: { 356: MSG_WriteAngle16 (msg, ps->viewangles[0]); 357: MSG_WriteAngle16 (msg, ps->viewangles[1]); 358: MSG_WriteAngle16 (msg, ps->viewangles[2]); 359: } 360: 361: if (pflags & PS_KICKANGLES) 362: { 363: MSG_WriteChar (msg, ps->kick_angles[0]*4); 364: MSG_WriteChar (msg, ps->kick_angles[1]*4); 365: MSG_WriteChar (msg, ps->kick_angles[2]*4); 366: } 367: 368: if (pflags & PS_WEAPONINDEX) 369: { 370: MSG_WriteByte (msg, ps->gunindex); 371: } 372: 373: if (pflags & PS_WEAPONFRAME) 374: { 375: MSG_WriteByte (msg, ps->gunframe); 376: MSG_WriteChar (msg, ps->gunoffset[0]*4); 377: MSG_WriteChar (msg, ps->gunoffset[1]*4); 378: MSG_WriteChar (msg, ps->gunoffset[2]*4); 379: MSG_WriteChar (msg, ps->gunangles[0]*4); 380: MSG_WriteChar (msg, ps->gunangles[1]*4); 381: MSG_WriteChar (msg, ps->gunangles[2]*4); 382: } 383: 384: if (pflags & PS_BLEND) 385: { 386: MSG_WriteByte (msg, ps->blend[0]*255); 387: MSG_WriteByte (msg, ps->blend[1]*255); 388: MSG_WriteByte (msg, ps->blend[2]*255); 389: MSG_WriteByte (msg, ps->blend[3]*255); 390: } 391: if (pflags & PS_FOV) 392: MSG_WriteByte (msg, ps->fov); 393: if (pflags & PS_RDFLAGS) 394: MSG_WriteByte (msg, ps->rdflags); 395: 396: // send stats 397: statbits = 0; 398: for (i=0 ; i<MAX_STATS ; i++) 399: if (ps->stats[i] != ops->stats[i]) 400: statbits |= 1<<i; 401: MSG_WriteLong (msg, statbits); 402: for (i=0 ; i<MAX_STATS ; i++) 403: if (statbits & (1<<i) ) 404: MSG_WriteShort (msg, ps->stats[i]); 405: } 406: 407: 408: /* 409: ================== 410: SV_WriteFrameToClient 411: ================== 412: */ 413: void SV_WriteFrameToClient (client_t *client, sizebuf_t *msg) 414: { 415: client_frame_t *frame, *oldframe; 416: int lastframe; 417: 418: //Com_Printf ("%i -> %i\n", client->lastframe, sv.framenum); 419: // this is the frame we are creating 420: frame = &client->frames[sv.framenum & UPDATE_MASK]; 421: 422: if (client->lastframe <= 0) 423: { // client is asking for a retransmit 424: oldframe = NULL; 425: lastframe = -1; 426: } 427: else if (sv.framenum - client->lastframe >= (UPDATE_BACKUP - 3) ) 428: { // client hasn't gotten a good message through in a long time 429: // Com_Printf ("%s: Delta request from out-of-date packet.\n", client->name); 430: oldframe = NULL; 431: lastframe = -1; 432: } 433: else 434: { // we have a valid message to delta from 435: oldframe = &client->frames[client->lastframe & UPDATE_MASK]; 436: lastframe = client->lastframe; 437: } 438: 439: MSG_WriteByte (msg, svc_frame); 440: MSG_WriteLong (msg, sv.framenum); 441: MSG_WriteLong (msg, lastframe); // what we are delta'ing from 442: MSG_WriteByte (msg, client->surpressCount); // rate dropped packets 443: client->surpressCount = 0; 444: 445: // send over the areabits 446: MSG_WriteByte (msg, frame->areabytes); 447: SZ_Write (msg, frame->areabits, frame->areabytes); 448: 449: // delta encode the playerstate 450: SV_WritePlayerstateToClient (oldframe, frame, msg); 451: 452: // delta encode the entities 453: SV_EmitPacketEntities (oldframe, frame, msg); 454: } 455: 456: 457: /* 458: ============================================================================= 459: 460: Build a client frame structure 461: 462: ============================================================================= 463: */ 464: 465: byte fatpvs[65536/8]; // 32767 is MAX_MAP_LEAFS 466: 467: /* 468: ============ 469: SV_FatPVS 470: 471: The client will interpolate the view position, 472: so we can't use a single PVS point 473: =========== 474: */ 475: void SV_FatPVS (vec3_t org) 476: { 477: int leafs[64]; 478: int i, j, count; 479: int longs; 480: byte *src; 481: vec3_t mins, maxs; 482: 483: for (i=0 ; i<3 ; i++) 484: { 485: mins[i] = org[i] - 8; 486: maxs[i] = org[i] + 8; 487: } 488: 489: count = CM_BoxLeafnums (mins, maxs, leafs, 64, NULL); 490: if (count < 1) 491: Com_Error (ERR_FATAL, "SV_FatPVS: count < 1"); 492: longs = (CM_NumClusters()+31)>>5; 493: 494: // convert leafs to clusters 495: for (i=0 ; i<count ; i++) 496: leafs[i] = CM_LeafCluster(leafs[i]); 497: 498: memcpy (fatpvs, CM_ClusterPVS(leafs[0]), longs<<2); 499: // or in all the other leaf bits 500: for (i=1 ; i<count ; i++) 501: { 502: for (j=0 ; j<i ; j++) 503: if (leafs[i] == leafs[j]) 504: break; 505: if (j != i) 506: continue; // already have the cluster we want 507: src = CM_ClusterPVS(leafs[i]); 508: for (j=0 ; j<longs ; j++) 509: ((long *)fatpvs)[j] |= ((long *)src)[j]; 510: } 511: } 512: 513: 514: /* 515: ============= 516: SV_BuildClientFrame 517: 518: Decides which entities are going to be visible to the client, and 519: copies off the playerstat and areabits. 520: ============= 521: */ 522: void SV_BuildClientFrame (client_t *client) 523: { 524: int e, i; 525: vec3_t org; 526: edict_t *ent; 527: edict_t *clent; 528: client_frame_t *frame; 529: entity_state_t *state; 530: int l; 531: int clientarea, clientcluster; 532: int leafnum; 533: int c_fullsend; 534: byte *clientphs; 535: byte *bitvector; 536: 537: clent = client->edict; 538: if (!clent->client) 539: return; // not in game yet 540: 1.1.1.2 root 541: #if 0 542: numprojs = 0; // no projectiles yet 543: #endif 544: 1.1 root 545: // this is the frame we are creating 546: frame = &client->frames[sv.framenum & UPDATE_MASK]; 547: 1.1.1.2 root 548: frame->senttime = svs.realtime; // save it for ping calc later 549: 1.1 root 550: // find the client's PVS 551: for (i=0 ; i<3 ; i++) 552: org[i] = clent->client->ps.pmove.origin[i]*0.125 + clent->client->ps.viewoffset[i]; 553: 554: leafnum = CM_PointLeafnum (org); 555: clientarea = CM_LeafArea (leafnum); 556: clientcluster = CM_LeafCluster (leafnum); 557: 558: // calculate the visible areas 559: frame->areabytes = CM_WriteAreaBits (frame->areabits, clientarea); 560: 561: // grab the current player_state_t 562: frame->ps = clent->client->ps; 563: 564: 565: SV_FatPVS (org); 566: clientphs = CM_ClusterPHS (clientcluster); 567: 568: // build up the list of visible entities 569: frame->num_entities = 0; 570: frame->first_entity = svs.next_client_entities; 571: 572: c_fullsend = 0; 573: 574: for (e=1 ; e<ge->num_edicts ; e++) 575: { 576: ent = EDICT_NUM(e); 577: 578: // ignore ents without visible models 579: if (ent->svflags & SVF_NOCLIENT) 580: continue; 581: 582: // ignore ents without visible models unless they have an effect 583: if (!ent->s.modelindex && !ent->s.effects && !ent->s.sound 584: && !ent->s.event) 585: continue; 586: 587: // ignore if not touching a PV leaf 588: if (ent != clent) 589: { 590: // check area 591: if (!CM_AreasConnected (clientarea, ent->areanum)) 592: { // doors can legally straddle two areas, so 593: // we may need to check another one 594: if (!ent->areanum2 595: || !CM_AreasConnected (clientarea, ent->areanum2)) 596: continue; // blocked by a door 597: } 598: 599: // beams just check one point for PHS 600: if (ent->s.renderfx & RF_BEAM) 601: { 602: l = ent->clusternums[0]; 603: if ( !(clientphs[l >> 3] & (1 << (l&7) )) ) 604: continue; 605: } 606: else 607: { 608: // FIXME: if an ent has a model and a sound, but isn't 609: // in the PVS, only the PHS, clear the model 610: if (ent->s.sound) 611: { 612: bitvector = fatpvs; //clientphs; 613: } 614: else 615: bitvector = fatpvs; 616: 617: if (ent->num_clusters == -1) 618: { // too many leafs for individual check, go by headnode 619: if (!CM_HeadnodeVisible (ent->headnode, bitvector)) 620: continue; 621: c_fullsend++; 622: } 623: else 624: { // check individual leafs 625: for (i=0 ; i < ent->num_clusters ; i++) 626: { 627: l = ent->clusternums[i]; 628: if (bitvector[l >> 3] & (1 << (l&7) )) 629: break; 630: } 631: if (i == ent->num_clusters) 632: continue; // not visible 633: } 634: 635: if (!ent->s.modelindex) 636: { // don't send sounds if they will be attenuated away 637: vec3_t delta; 638: float len; 639: 640: VectorSubtract (org, ent->s.origin, delta); 641: len = VectorLength (delta); 642: if (len > 400) 643: continue; 644: } 645: } 646: } 647: 1.1.1.2 root 648: #if 0 649: if (SV_AddProjectileUpdate(ent)) 650: continue; // added as a special projectile 651: #endif 652: 1.1 root 653: // add it to the circular client_entities array 654: state = &svs.client_entities[svs.next_client_entities%svs.num_client_entities]; 655: if (ent->s.number != e) 656: { 657: Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); 658: ent->s.number = e; 659: } 660: *state = ent->s; 661: 662: // don't mark players missiles as solid 663: if (ent->owner == client->edict) 664: state->solid = 0; 665: 666: svs.next_client_entities++; 667: frame->num_entities++; 668: } 669: } 670: 671: 672: /* 673: ================== 674: SV_RecordDemoMessage 675: 676: Save everything in the world out without deltas. 677: Used for recording footage for merged or assembled demos 678: ================== 679: */ 680: void SV_RecordDemoMessage (void) 681: { 682: int e; 683: edict_t *ent; 684: entity_state_t nostate; 685: sizebuf_t buf; 686: byte buf_data[32768]; 687: int len; 688: 689: if (!svs.demofile) 690: return; 691: 692: memset (&nostate, 0, sizeof(nostate)); 693: SZ_Init (&buf, buf_data, sizeof(buf_data)); 694: 695: // write a frame message that doesn't contain a player_state_t 696: MSG_WriteByte (&buf, svc_frame); 697: MSG_WriteLong (&buf, sv.framenum); 698: 699: MSG_WriteByte (&buf, svc_packetentities); 700: 1.1.1.2 root 701: e = 1; 702: ent = EDICT_NUM(e); 703: while (e < ge->num_edicts) 1.1 root 704: { 705: // ignore ents without visible models unless they have an effect 1.1.1.2 root 706: if (ent->inuse && 707: ent->s.number && 708: (ent->s.modelindex || ent->s.effects || ent->s.sound || ent->s.event) && 709: !(ent->svflags & SVF_NOCLIENT)) 710: MSG_WriteDeltaEntity (&nostate, &ent->s, &buf, false, true); 1.1 root 711: 1.1.1.2 root 712: e++; 713: ent = EDICT_NUM(e); 1.1 root 714: } 715: 716: MSG_WriteShort (&buf, 0); // end of packetentities 717: 718: // now add the accumulated multicast information 719: SZ_Write (&buf, svs.demo_multicast.data, svs.demo_multicast.cursize); 720: SZ_Clear (&svs.demo_multicast); 721: 722: // now write the entire message to the file, prefixed by the length 723: len = LittleLong (buf.cursize); 724: fwrite (&len, 4, 1, svs.demofile); 725: fwrite (buf.data, buf.cursize, 1, svs.demofile); 726: } 727:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.