doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Game_network.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "Game_local.h"
33 
34 /*
35 ===============================================================================
36 
37  Client running game code:
38  - entity events don't work and should not be issued
39  - entities should never be spawned outside idGameLocal::ClientReadSnapshot
40 
41 ===============================================================================
42 */
43 
44 // adds tags to the network protocol to detect when things go bad ( internal consistency )
45 // NOTE: this changes the network protocol
46 #ifndef ASYNC_WRITE_TAGS
47  #define ASYNC_WRITE_TAGS 0
48 #endif
49 
50 idCVar net_clientShowSnapshot( "net_clientShowSnapshot", "0", CVAR_GAME | CVAR_INTEGER, "", 0, 3, idCmdSystem::ArgCompletion_Integer<0,3> );
51 idCVar net_clientShowSnapshotRadius( "net_clientShowSnapshotRadius", "128", CVAR_GAME | CVAR_FLOAT, "" );
52 idCVar net_clientSmoothing( "net_clientSmoothing", "0.8", CVAR_GAME | CVAR_FLOAT, "smooth other clients angles and position.", 0.0f, 0.95f );
53 idCVar net_clientSelfSmoothing( "net_clientSelfSmoothing", "0.6", CVAR_GAME | CVAR_FLOAT, "smooth self position if network causes prediction error.", 0.0f, 0.95f );
54 idCVar net_clientMaxPrediction( "net_clientMaxPrediction", "1000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of milliseconds a client can predict ahead of server." );
55 idCVar net_clientLagOMeter( "net_clientLagOMeter", "1", CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT | CVAR_ARCHIVE, "draw prediction graph" );
56 
57 /*
58 ================
59 idGameLocal::InitAsyncNetwork
60 ================
61 */
63  int i, type;
64 
65  for ( i = 0; i < MAX_CLIENTS; i++ ) {
66  for ( type = 0; type < declManager->GetNumDeclTypes(); type++ ) {
68  }
69  }
70 
71  memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
72  memset( clientPVS, 0, sizeof( clientPVS ) );
73  memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
74 
75  eventQueue.Init();
77 
79  localClientNum = 0; // on a listen server SetLocalUser will set this right
80  realClientTime = 0;
81  isNewFrame = true;
83 }
84 
85 /*
86 ================
87 idGameLocal::ShutdownAsyncNetwork
88 ================
89 */
91  entityStateAllocator.Shutdown();
92  snapshotAllocator.Shutdown();
95  memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
96  memset( clientPVS, 0, sizeof( clientPVS ) );
97  memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
98 }
99 
100 /*
101 ================
102 idGameLocal::InitLocalClient
103 ================
104 */
105 void idGameLocal::InitLocalClient( int clientNum ) {
106  isServer = false;
107  isClient = true;
108  localClientNum = clientNum;
110 }
111 
112 /*
113 ================
114 idGameLocal::InitClientDeclRemap
115 ================
116 */
117 void idGameLocal::InitClientDeclRemap( int clientNum ) {
118  int type, i, num;
119 
120  for ( type = 0; type < declManager->GetNumDeclTypes(); type++ ) {
121 
122  // only implicit materials and sound shaders decls are used
123  if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
124  continue;
125  }
126 
127  num = declManager->GetNumDecls( (declType_t) type );
128  clientDeclRemap[clientNum][type].Clear();
129  clientDeclRemap[clientNum][type].AssureSize( num, -1 );
130 
131  // pre-initialize the remap with non-implicit decls, all non-implicit decls are always going
132  // to be in order and in sync between server and client because of the decl manager checksum
133  for ( i = 0; i < num; i++ ) {
134  const idDecl *decl = declManager->DeclByIndex( (declType_t) type, i, false );
135  if ( decl->IsImplicit() ) {
136  // once the first implicit decl is found all remaining decls are considered implicit as well
137  break;
138  }
139  clientDeclRemap[clientNum][type][i] = i;
140  }
141  }
142 }
143 
144 /*
145 ================
146 idGameLocal::ServerSendDeclRemapToClient
147 ================
148 */
150  idBitMsg outMsg;
151  byte msgBuf[MAX_GAME_MESSAGE_SIZE];
152 
153  // if no client connected for this spot
154  if ( entities[clientNum] == NULL ) {
155  return;
156  }
157  // increase size of list if required
158  if ( index >= clientDeclRemap[clientNum][type].Num() ) {
159  clientDeclRemap[clientNum][(int)type].AssureSize( index + 1, -1 );
160  }
161  // if already remapped
162  if ( clientDeclRemap[clientNum][(int)type][index] != -1 ) {
163  return;
164  }
165 
166  const idDecl *decl = declManager->DeclByIndex( type, index, false );
167  if ( decl == NULL ) {
168  gameLocal.Error( "server tried to remap bad %s decl index %d", declManager->GetDeclNameFromType( type ), index );
169  return;
170  }
171 
172  // set the index at the server
173  clientDeclRemap[clientNum][(int)type][index] = index;
174 
175  // write update to client
176  outMsg.Init( msgBuf, sizeof( msgBuf ) );
177  outMsg.BeginWriting();
179  outMsg.WriteByte( type );
180  outMsg.WriteLong( index );
181  outMsg.WriteString( decl->GetName() );
182  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
183 }
184 
185 /*
186 ================
187 idGameLocal::ServerRemapDecl
188 ================
189 */
190 int idGameLocal::ServerRemapDecl( int clientNum, declType_t type, int index ) {
191 
192  // only implicit materials and sound shaders decls are used
193  if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
194  return index;
195  }
196 
197  if ( clientNum == -1 ) {
198  for ( int i = 0; i < MAX_CLIENTS; i++ ) {
199  ServerSendDeclRemapToClient( i, type, index );
200  }
201  } else {
202  ServerSendDeclRemapToClient( clientNum, type, index );
203  }
204  return index;
205 }
206 
207 /*
208 ================
209 idGameLocal::ClientRemapDecl
210 ================
211 */
213 
214  // only implicit materials and sound shaders decls are used
215  if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
216  return index;
217  }
218 
219  // negative indexes are sometimes used for NULL decls
220  if ( index < 0 ) {
221  return index;
222  }
223 
224  // make sure the index is valid
225  if ( clientDeclRemap[localClientNum][(int)type].Num() == 0 ) {
226  gameLocal.Error( "client received decl index %d before %s decl remap was initialized", index, declManager->GetDeclNameFromType( type ) );
227  return -1;
228  }
229  if ( index >= clientDeclRemap[localClientNum][(int)type].Num() ) {
230  gameLocal.Error( "client received unmapped %s decl index %d from server", declManager->GetDeclNameFromType( type ), index );
231  return -1;
232  }
233  if ( clientDeclRemap[localClientNum][(int)type][index] == -1 ) {
234  gameLocal.Error( "client received unmapped %s decl index %d from server", declManager->GetDeclNameFromType( type ), index );
235  return -1;
236  }
238 }
239 
240 /*
241 ================
242 idGameLocal::ServerAllowClient
243 ================
244 */
245 allowReply_t idGameLocal::ServerAllowClient( int numClients, const char *IP, const char *guid, const char *password, char reason[ MAX_STRING_CHARS ] ) {
246  reason[0] = '\0';
247 
248  if ( serverInfo.GetInt( "si_pure" ) && !mpGame.IsPureReady() ) {
249  idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07139" );
250  return ALLOW_NOTYET;
251  }
252 
253  if ( !serverInfo.GetInt( "si_maxPlayers" ) ) {
254  idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07140" );
255  return ALLOW_NOTYET;
256  }
257 
258  if ( numClients >= serverInfo.GetInt( "si_maxPlayers" ) ) {
259  idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07141" );
260  return ALLOW_NOTYET;
261  }
262 
263  if ( !cvarSystem->GetCVarBool( "si_usepass" ) ) {
264  return ALLOW_YES;
265  }
266 
267  const char *pass = cvarSystem->GetCVarString( "g_password" );
268  if ( pass[ 0 ] == '\0' ) {
269  common->Warning( "si_usepass is set but g_password is empty" );
270  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "say si_usepass is set but g_password is empty" );
271  // avoids silent misconfigured state
272  idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07142" );
273  return ALLOW_NOTYET;
274  }
275 
276  if ( !idStr::Cmp( pass, password ) ) {
277  return ALLOW_YES;
278  }
279 
280  idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07143" );
281  Printf( "Rejecting client %s from IP %s: invalid password\n", guid, IP );
282  return ALLOW_BADPASS;
283 }
284 
285 /*
286 ================
287 idGameLocal::ServerClientConnect
288 ================
289 */
290 void idGameLocal::ServerClientConnect( int clientNum, const char *guid ) {
291  // make sure no parasite entity is left
292  if ( entities[ clientNum ] ) {
293  common->DPrintf( "ServerClientConnect: remove old player entity\n" );
294  delete entities[ clientNum ];
295  }
296  userInfo[ clientNum ].Clear();
297  mpGame.ServerClientConnect( clientNum );
298  Printf( "client %d connected.\n", clientNum );
299 }
300 
301 /*
302 ================
303 idGameLocal::ServerClientBegin
304 ================
305 */
306 void idGameLocal::ServerClientBegin( int clientNum ) {
307  idBitMsg outMsg;
308  byte msgBuf[MAX_GAME_MESSAGE_SIZE];
309 
310  // initialize the decl remap
311  InitClientDeclRemap( clientNum );
312 
313  // send message to initialize decl remap at the client (this is always the very first reliable game message)
314  outMsg.Init( msgBuf, sizeof( msgBuf ) );
315  outMsg.BeginWriting();
317  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
318 
319  // spawn the player
320  SpawnPlayer( clientNum );
321  if ( clientNum == localClientNum ) {
322  mpGame.EnterGame( clientNum );
323  }
324 
325  // send message to spawn the player at the clients
326  outMsg.Init( msgBuf, sizeof( msgBuf ) );
327  outMsg.BeginWriting();
329  outMsg.WriteByte( clientNum );
330  outMsg.WriteLong( spawnIds[ clientNum ] );
332 }
333 
334 /*
335 ================
336 idGameLocal::ServerClientDisconnect
337 ================
338 */
339 void idGameLocal::ServerClientDisconnect( int clientNum ) {
340  int i;
341  idBitMsg outMsg;
342  byte msgBuf[MAX_GAME_MESSAGE_SIZE];
343 
344  outMsg.Init( msgBuf, sizeof( msgBuf ) );
345  outMsg.BeginWriting();
347  outMsg.WriteBits( ( spawnIds[ clientNum ] << GENTITYNUM_BITS ) | clientNum, 32 ); // see GetSpawnId
349 
350  // free snapshots stored for this client
351  FreeSnapshotsOlderThanSequence( clientNum, 0x7FFFFFFF );
352 
353  // free entity states stored for this client
354  for ( i = 0; i < MAX_GENTITIES; i++ ) {
355  if ( clientEntityStates[ clientNum ][ i ] ) {
356  entityStateAllocator.Free( clientEntityStates[ clientNum ][ i ] );
357  clientEntityStates[ clientNum ][ i ] = NULL;
358  }
359  }
360 
361  // clear the client PVS
362  memset( clientPVS[ clientNum ], 0, sizeof( clientPVS[ clientNum ] ) );
363 
364  // delete the player entity
365  delete entities[ clientNum ];
366 
367  mpGame.DisconnectClient( clientNum );
368 
369 }
370 
371 /*
372 ================
373 idGameLocal::ServerWriteInitialReliableMessages
374 
375  Send reliable messages to initialize the client game up to a certain initial state.
376 ================
377 */
379  int i;
380  idBitMsg outMsg;
381  byte msgBuf[MAX_GAME_MESSAGE_SIZE];
382  entityNetEvent_t *event;
383 
384  // spawn players
385  for ( i = 0; i < MAX_CLIENTS; i++ ) {
386  if ( entities[i] == NULL || i == clientNum ) {
387  continue;
388  }
389  outMsg.Init( msgBuf, sizeof( msgBuf ) );
390  outMsg.BeginWriting( );
392  outMsg.WriteByte( i );
393  outMsg.WriteLong( spawnIds[ i ] );
394  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
395  }
396 
397  // send all saved events
398  for ( event = savedEventQueue.Start(); event; event = event->next ) {
399  outMsg.Init( msgBuf, sizeof( msgBuf ) );
400  outMsg.BeginWriting();
402  outMsg.WriteBits( event->spawnId, 32 );
403  outMsg.WriteByte( event->event );
404  outMsg.WriteLong( event->time );
406  if ( event->paramsSize ) {
407  outMsg.WriteData( event->paramsBuf, event->paramsSize );
408  }
409 
410  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
411  }
412 
413  // update portals for opened doors
414  int numPortals = gameRenderWorld->NumPortals();
415  outMsg.Init( msgBuf, sizeof( msgBuf ) );
416  outMsg.BeginWriting();
418  outMsg.WriteLong( numPortals );
419  for ( i = 0; i < numPortals; i++ ) {
421  }
422  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
423 
425 }
426 
427 /*
428 ================
429 idGameLocal::SaveEntityNetworkEvent
430 ================
431 */
432 void idGameLocal::SaveEntityNetworkEvent( const idEntity *ent, int eventId, const idBitMsg *msg ) {
433  entityNetEvent_t *event;
434 
435  event = savedEventQueue.Alloc();
436  event->spawnId = GetSpawnId( ent );
437  event->event = eventId;
438  event->time = time;
439  if ( msg ) {
440  event->paramsSize = msg->GetSize();
441  memcpy( event->paramsBuf, msg->GetData(), msg->GetSize() );
442  } else {
443  event->paramsSize = 0;
444  }
445 
447 }
448 
449 /*
450 ================
451 idGameLocal::FreeSnapshotsOlderThanSequence
452 ================
453 */
454 void idGameLocal::FreeSnapshotsOlderThanSequence( int clientNum, int sequence ) {
455  snapshot_t *snapshot, *lastSnapshot, *nextSnapshot;
456  entityState_t *state;
457 
458  for ( lastSnapshot = NULL, snapshot = clientSnapshots[clientNum]; snapshot; snapshot = nextSnapshot ) {
459  nextSnapshot = snapshot->next;
460  if ( snapshot->sequence < sequence ) {
461  for ( state = snapshot->firstEntityState; state; state = snapshot->firstEntityState ) {
462  snapshot->firstEntityState = snapshot->firstEntityState->next;
463  entityStateAllocator.Free( state );
464  }
465  if ( lastSnapshot ) {
466  lastSnapshot->next = snapshot->next;
467  } else {
468  clientSnapshots[clientNum] = snapshot->next;
469  }
470  snapshotAllocator.Free( snapshot );
471  } else {
472  lastSnapshot = snapshot;
473  }
474  }
475 }
476 
477 /*
478 ================
479 idGameLocal::ApplySnapshot
480 ================
481 */
482 bool idGameLocal::ApplySnapshot( int clientNum, int sequence ) {
483  snapshot_t *snapshot, *lastSnapshot, *nextSnapshot;
484  entityState_t *state;
485 
486  FreeSnapshotsOlderThanSequence( clientNum, sequence );
487 
488  for ( lastSnapshot = NULL, snapshot = clientSnapshots[clientNum]; snapshot; snapshot = nextSnapshot ) {
489  nextSnapshot = snapshot->next;
490  if ( snapshot->sequence == sequence ) {
491  for ( state = snapshot->firstEntityState; state; state = state->next ) {
492  if ( clientEntityStates[clientNum][state->entityNumber] ) {
493  entityStateAllocator.Free( clientEntityStates[clientNum][state->entityNumber] );
494  }
495  clientEntityStates[clientNum][state->entityNumber] = state;
496  }
497  memcpy( clientPVS[clientNum], snapshot->pvs, sizeof( snapshot->pvs ) );
498  if ( lastSnapshot ) {
499  lastSnapshot->next = nextSnapshot;
500  } else {
501  clientSnapshots[clientNum] = nextSnapshot;
502  }
503  snapshotAllocator.Free( snapshot );
504  return true;
505  } else {
506  lastSnapshot = snapshot;
507  }
508  }
509 
510  return false;
511 }
512 
513 /*
514 ================
515 idGameLocal::WriteGameStateToSnapshot
516 ================
517 */
519  int i;
520 
521  for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
522  msg.WriteFloat( globalShaderParms[i] );
523  }
524 
525  mpGame.WriteToSnapshot( msg );
526 }
527 
528 /*
529 ================
530 idGameLocal::ReadGameStateFromSnapshot
531 ================
532 */
534  int i;
535 
536  for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
537  globalShaderParms[i] = msg.ReadFloat();
538  }
539 
540  mpGame.ReadFromSnapshot( msg );
541 }
542 
543 /*
544 ================
545 idGameLocal::ServerWriteSnapshot
546 
547  Write a snapshot of the current game state for the given client.
548 ================
549 */
550 void idGameLocal::ServerWriteSnapshot( int clientNum, int sequence, idBitMsg &msg, byte *clientInPVS, int numPVSClients ) {
551  int i, msgSize, msgWriteBit;
552  idPlayer *player, *spectated = NULL;
553  idEntity *ent;
554  pvsHandle_t pvsHandle;
555  idBitMsgDelta deltaMsg;
556  snapshot_t *snapshot;
557  entityState_t *base, *newBase;
558  int numSourceAreas, sourceAreas[ idEntity::MAX_PVS_AREAS ];
559 
560  player = static_cast<idPlayer *>( entities[ clientNum ] );
561  if ( !player ) {
562  return;
563  }
564  if ( player->spectating && player->spectator != clientNum && entities[ player->spectator ] ) {
565  spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
566  } else {
567  spectated = player;
568  }
569 
570  // free too old snapshots
571  FreeSnapshotsOlderThanSequence( clientNum, sequence - 64 );
572 
573  // allocate new snapshot
574  snapshot = snapshotAllocator.Alloc();
575  snapshot->sequence = sequence;
576  snapshot->firstEntityState = NULL;
577  snapshot->next = clientSnapshots[clientNum];
578  clientSnapshots[clientNum] = snapshot;
579  memset( snapshot->pvs, 0, sizeof( snapshot->pvs ) );
580 
581  // get PVS for this player
582  // don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
583  numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
584  pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
585 
586 #ifdef _D3XP
587  // Add portalSky areas to PVS
588  if ( portalSkyEnt.GetEntity() ) {
589  pvsHandle_t otherPVS, newPVS;
590  idEntity *skyEnt = portalSkyEnt.GetEntity();
591 
592  otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() );
593  newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS );
594  pvs.FreeCurrentPVS( pvsHandle );
595  pvs.FreeCurrentPVS( otherPVS );
596  pvsHandle = newPVS;
597  }
598 #endif
599 
600 #if ASYNC_WRITE_TAGS
601  idRandom tagRandom;
602  tagRandom.SetSeed( random.RandomInt() );
603  msg.WriteLong( tagRandom.GetSeed() );
604 #endif
605 
606  // create the snapshot
607  for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
608 
609  // if the entity is not in the player PVS
610  if ( !ent->PhysicsTeamInPVS( pvsHandle ) && ent->entityNumber != clientNum ) {
611  continue;
612  }
613 
614  // add the entity to the snapshot pvs
615  snapshot->pvs[ ent->entityNumber >> 5 ] |= 1 << ( ent->entityNumber & 31 );
616 
617  // if that entity is not marked for network synchronization
618  if ( !ent->fl.networkSync ) {
619  continue;
620  }
621 
622  // save the write state to which we can revert when the entity didn't change at all
623  msg.SaveWriteState( msgSize, msgWriteBit );
624 
625  // write the entity to the snapshot
627 
628  base = clientEntityStates[clientNum][ent->entityNumber];
629  if ( base ) {
630  base->state.BeginReading();
631  }
632  newBase = entityStateAllocator.Alloc();
633  newBase->entityNumber = ent->entityNumber;
634  newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
635  newBase->state.BeginWriting();
636 
637  deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
638 
639  deltaMsg.WriteBits( spawnIds[ ent->entityNumber ], 32 - GENTITYNUM_BITS );
640  deltaMsg.WriteBits( ent->GetType()->typeNum, idClass::GetTypeNumBits() );
642 
643  // write the class specific data to the snapshot
644  ent->WriteToSnapshot( deltaMsg );
645 
646  if ( !deltaMsg.HasChanged() ) {
647  msg.RestoreWriteState( msgSize, msgWriteBit );
648  entityStateAllocator.Free( newBase );
649  } else {
650  newBase->next = snapshot->firstEntityState;
651  snapshot->firstEntityState = newBase;
652 
653 #if ASYNC_WRITE_TAGS
654  msg.WriteLong( tagRandom.RandomInt() );
655 #endif
656  }
657  }
658 
660 
661  // write the PVS to the snapshot
662 #if ASYNC_WRITE_PVS
663  for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
664  if ( i < numSourceAreas ) {
665  msg.WriteLong( sourceAreas[ i ] );
666  } else {
667  msg.WriteLong( 0 );
668  }
669  }
670  gameLocal.pvs.WritePVS( pvsHandle, msg );
671 #endif
672  for ( i = 0; i < ENTITY_PVS_SIZE; i++ ) {
673  msg.WriteDeltaLong( clientPVS[clientNum][i], snapshot->pvs[i] );
674  }
675 
676  // free the PVS
677  pvs.FreeCurrentPVS( pvsHandle );
678 
679  // write the game and player state to the snapshot
680  base = clientEntityStates[clientNum][ENTITYNUM_NONE]; // ENTITYNUM_NONE is used for the game and player state
681  if ( base ) {
682  base->state.BeginReading();
683  }
684  newBase = entityStateAllocator.Alloc();
685  newBase->entityNumber = ENTITYNUM_NONE;
686  newBase->next = snapshot->firstEntityState;
687  snapshot->firstEntityState = newBase;
688  newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
689  newBase->state.BeginWriting();
690  deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
691  if ( player->spectating && player->spectator != player->entityNumber && gameLocal.entities[ player->spectator ] && gameLocal.entities[ player->spectator ]->IsType( idPlayer::Type ) ) {
692  static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->WritePlayerStateToSnapshot( deltaMsg );
693  } else {
694  player->WritePlayerStateToSnapshot( deltaMsg );
695  }
696  WriteGameStateToSnapshot( deltaMsg );
697 
698  // copy the client PVS string
699  memcpy( clientInPVS, snapshot->pvs, ( numPVSClients + 7 ) >> 3 );
700  LittleRevBytes( clientInPVS, sizeof( int ), sizeof( clientInPVS ) / sizeof ( int ) );
701 }
702 
703 /*
704 ================
705 idGameLocal::ServerApplySnapshot
706 ================
707 */
708 bool idGameLocal::ServerApplySnapshot( int clientNum, int sequence ) {
709  return ApplySnapshot( clientNum, sequence );
710 }
711 
712 /*
713 ================
714 idGameLocal::NetworkEventWarning
715 ================
716 */
717 void idGameLocal::NetworkEventWarning( const entityNetEvent_t *event, const char *fmt, ... ) {
718  char buf[1024];
719  int length = 0;
720  va_list argptr;
721 
722  int entityNum = event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 );
723  int id = event->spawnId >> GENTITYNUM_BITS;
724 
725  length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "event %d for entity %d %d: ", event->event, entityNum, id );
726  va_start( argptr, fmt );
727  length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
728  va_end( argptr );
729  idStr::Append( buf, sizeof(buf), "\n" );
730 
731  common->DWarning( buf );
732 }
733 
734 /*
735 ================
736 idGameLocal::ServerProcessEntityNetworkEventQueue
737 ================
738 */
740  idEntity *ent;
741  entityNetEvent_t *event;
742  idBitMsg eventMsg;
743 
744  while ( eventQueue.Start() ) {
745  event = eventQueue.Start();
746 
747  if ( event->time > time ) {
748  break;
749  }
750 
752 
753  if( !entPtr.SetSpawnId( event->spawnId ) ) {
754  NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
755  } else {
756  ent = entPtr.GetEntity();
757  assert( ent );
758 
759  eventMsg.Init( event->paramsBuf, sizeof( event->paramsBuf ) );
760  eventMsg.SetSize( event->paramsSize );
761  eventMsg.BeginReading();
762  if ( !ent->ServerReceiveEvent( event->event, event->time, eventMsg ) ) {
763  NetworkEventWarning( event, "unknown event" );
764  }
765  }
766 
767  entityNetEvent_t* freedEvent = eventQueue.Dequeue();
768  assert( freedEvent == event );
769  eventQueue.Free( event );
770  }
771 }
772 
773 /*
774 ================
775 idGameLocal::ServerSendChatMessage
776 ================
777 */
778 void idGameLocal::ServerSendChatMessage( int to, const char *name, const char *text ) {
779  idBitMsg outMsg;
780  byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
781 
782  outMsg.Init( msgBuf, sizeof( msgBuf ) );
783  outMsg.BeginWriting();
785  outMsg.WriteString( name );
786  outMsg.WriteString( text, -1, false );
788 
789  if ( to == -1 || to == localClientNum ) {
790  mpGame.AddChatLine( "%s^0: %s\n", name, text );
791  }
792 }
793 
794 /*
795 ================
796 idGameLocal::ServerProcessReliableMessage
797 ================
798 */
799 void idGameLocal::ServerProcessReliableMessage( int clientNum, const idBitMsg &msg ) {
800  int id;
801 
802  id = msg.ReadByte();
803  switch( id ) {
806  char name[128];
807  char text[128];
808 
809  msg.ReadString( name, sizeof( name ) );
810  msg.ReadString( text, sizeof( text ) );
811 
812  mpGame.ProcessChatMessage( clientNum, id == GAME_RELIABLE_MESSAGE_TCHAT, name, text, NULL );
813 
814  break;
815  }
817  int index = msg.ReadLong();
818  bool team = msg.ReadBits( 1 ) != 0;
819  mpGame.ProcessVoiceChat( clientNum, team, index );
820  break;
821  }
823  mpGame.WantKilled( clientNum );
824  break;
825  }
827  mpGame.DropWeapon( clientNum );
828  break;
829  }
831  mpGame.ServerCallVote( clientNum, msg );
832  break;
833  }
835  bool vote = ( msg.ReadByte() != 0 );
836  mpGame.CastVote( clientNum, vote );
837  break;
838  }
839 #if 0
840  // uncomment this if you want to track when players are in a menu
842  bool menuUp = ( msg.ReadBits( 1 ) != 0 );
843  break;
844  }
845 #endif
847  entityNetEvent_t *event;
848 
849  // allocate new event
850  event = eventQueue.Alloc();
852 
853  event->spawnId = msg.ReadBits( 32 );
854  event->event = msg.ReadByte();
855  event->time = msg.ReadLong();
856 
857  event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
858  if ( event->paramsSize ) {
859  if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
860  NetworkEventWarning( event, "invalid param size" );
861  return;
862  }
863  msg.ReadByteAlign();
864  msg.ReadData( event->paramsBuf, event->paramsSize );
865  }
866  break;
867  }
868  default: {
869  Warning( "Unknown client->server reliable message: %d", id );
870  break;
871  }
872  }
873 }
874 
875 /*
876 ================
877 idGameLocal::ClientShowSnapshot
878 ================
879 */
880 void idGameLocal::ClientShowSnapshot( int clientNum ) const {
881  int baseBits;
882  idEntity *ent;
883  idPlayer *player;
884  idMat3 viewAxis;
885  idBounds viewBounds;
886  entityState_t *base;
887 
889  return;
890  }
891 
892  player = static_cast<idPlayer *>( entities[clientNum] );
893  if ( !player ) {
894  return;
895  }
896 
897  viewAxis = player->viewAngles.ToMat3();
898  viewBounds = player->GetPhysics()->GetAbsBounds().Expand( net_clientShowSnapshotRadius.GetFloat() );
899 
900  for( ent = snapshotEntities.Next(); ent != NULL; ent = ent->snapshotNode.Next() ) {
901 
902  if ( net_clientShowSnapshot.GetInteger() == 1 && ent->snapshotBits == 0 ) {
903  continue;
904  }
905 
906  const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
907 
908  if ( !entBounds.IntersectsBounds( viewBounds ) ) {
909  continue;
910  }
911 
912  base = clientEntityStates[clientNum][ent->entityNumber];
913  if ( base ) {
914  baseBits = base->state.GetNumBitsWritten();
915  } else {
916  baseBits = 0;
917  }
918 
919  if ( net_clientShowSnapshot.GetInteger() == 2 && baseBits == 0 ) {
920  continue;
921  }
922 
923  gameRenderWorld->DebugBounds( colorGreen, entBounds );
924  gameRenderWorld->DrawText( va( "%d: %s (%d,%d bytes of %d,%d)\n", ent->entityNumber,
925  ent->name.c_str(), ent->snapshotBits >> 3, ent->snapshotBits & 7, baseBits >> 3, baseBits & 7 ),
926  entBounds.GetCenter(), 0.1f, colorWhite, viewAxis, 1 );
927  }
928 }
929 
930 /*
931 ================
932 idGameLocal::UpdateLagometer
933 ================
934 */
935 void idGameLocal::UpdateLagometer( int aheadOfServer, int dupeUsercmds ) {
936  int i, j, ahead;
937  for ( i = 0; i < LAGO_HEIGHT; i++ ) {
938  memmove( (byte *)lagometer + LAGO_WIDTH * 4 * i, (byte *)lagometer + LAGO_WIDTH * 4 * i + 4, ( LAGO_WIDTH - 1 ) * 4 );
939  }
940  j = LAGO_WIDTH - 1;
941  for ( i = 0; i < LAGO_HEIGHT; i++ ) {
942  lagometer[i][j][0] = lagometer[i][j][1] = lagometer[i][j][2] = lagometer[i][j][3] = 0;
943  }
944  ahead = idMath::Rint( (float)aheadOfServer / 16.0f );
945  if ( ahead >= 0 ) {
946  for ( i = 2 * Max( 0, 5 - ahead ); i < 2 * 5; i++ ) {
947  lagometer[i][j][1] = 255;
948  lagometer[i][j][3] = 255;
949  }
950  } else {
951  for ( i = 2 * 5; i < 2 * ( 5 + Min( 10, -ahead ) ); i++ ) {
952  lagometer[i][j][0] = 255;
953  lagometer[i][j][1] = 255;
954  lagometer[i][j][3] = 255;
955  }
956  }
957  for ( i = LAGO_HEIGHT - 2 * Min( 6, dupeUsercmds ); i < LAGO_HEIGHT; i++ ) {
958  lagometer[i][j][0] = 255;
959  if ( dupeUsercmds <= 2 ) {
960  lagometer[i][j][1] = 255;
961  }
962  lagometer[i][j][3] = 255;
963  }
964 }
965 
966 /*
967 ================
968 idGameLocal::ClientReadSnapshot
969 ================
970 */
971 void idGameLocal::ClientReadSnapshot( int clientNum, int sequence, const int gameFrame, const int gameTime, const int dupeUsercmds, const int aheadOfServer, const idBitMsg &msg ) {
972  int i, typeNum, entityDefNumber, numBitsRead;
973  idTypeInfo *typeInfo;
974  idEntity *ent;
975  idPlayer *player, *spectated;
976  pvsHandle_t pvsHandle;
977  idDict args;
978  const char *classname;
979  idBitMsgDelta deltaMsg;
980  snapshot_t *snapshot;
981  entityState_t *base, *newBase;
982  int spawnId;
983  int numSourceAreas, sourceAreas[ idEntity::MAX_PVS_AREAS ];
984  idWeapon *weap;
985 
987  UpdateLagometer( aheadOfServer, dupeUsercmds );
989  common->Printf( "lagometer: UploadImage failed. turning off net_clientLagOMeter\n" );
990  net_clientLagOMeter.SetBool( false );
991  }
992  }
993 
994  InitLocalClient( clientNum );
995 
996  // clear any debug lines from a previous frame
998 
999  // clear any debug polygons from a previous frame
1001 
1002  // update the game time
1003  framenum = gameFrame;
1004  time = gameTime;
1005  previousTime = time - msec;
1006 
1007  // so that StartSound/StopSound doesn't risk skipping
1008  isNewFrame = true;
1009 
1010  // clear the snapshot entity list
1012 
1013  // allocate new snapshot
1014  snapshot = snapshotAllocator.Alloc();
1015  snapshot->sequence = sequence;
1016  snapshot->firstEntityState = NULL;
1017  snapshot->next = clientSnapshots[clientNum];
1018  clientSnapshots[clientNum] = snapshot;
1019 
1020 #if ASYNC_WRITE_TAGS
1021  idRandom tagRandom;
1022  tagRandom.SetSeed( msg.ReadLong() );
1023 #endif
1024 
1025  // read all entities from the snapshot
1026  for ( i = msg.ReadBits( GENTITYNUM_BITS ); i != ENTITYNUM_NONE; i = msg.ReadBits( GENTITYNUM_BITS ) ) {
1027 
1028  base = clientEntityStates[clientNum][i];
1029  if ( base ) {
1030  base->state.BeginReading();
1031  }
1032  newBase = entityStateAllocator.Alloc();
1033  newBase->entityNumber = i;
1034  newBase->next = snapshot->firstEntityState;
1035  snapshot->firstEntityState = newBase;
1036  newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
1037  newBase->state.BeginWriting();
1038 
1039  numBitsRead = msg.GetNumBitsRead();
1040 
1041  deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
1042 
1043  spawnId = deltaMsg.ReadBits( 32 - GENTITYNUM_BITS );
1044  typeNum = deltaMsg.ReadBits( idClass::GetTypeNumBits() );
1045  entityDefNumber = ClientRemapDecl( DECL_ENTITYDEF, deltaMsg.ReadBits( entityDefBits ) );
1046 
1047  typeInfo = idClass::GetType( typeNum );
1048  if ( !typeInfo ) {
1049  Error( "Unknown type number %d for entity %d with class number %d", typeNum, i, entityDefNumber );
1050  }
1051 
1052  ent = entities[i];
1053 
1054  // if there is no entity or an entity of the wrong type
1055  if ( !ent || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber || spawnId != spawnIds[ i ] ) {
1056 
1057  if ( i < MAX_CLIENTS && ent ) {
1058  // SPAWN_PLAYER should be taking care of spawning the entity with the right spawnId
1059  common->Warning( "ClientReadSnapshot: recycling client entity %d\n", i );
1060  }
1061 
1062  delete ent;
1063 
1064  spawnCount = spawnId;
1065 
1066  args.Clear();
1067  args.SetInt( "spawn_entnum", i );
1068  args.Set( "name", va( "entity%d", i ) );
1069 
1070  if ( entityDefNumber >= 0 ) {
1071  if ( entityDefNumber >= declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
1072  Error( "server has %d entityDefs instead of %d", entityDefNumber, declManager->GetNumDecls( DECL_ENTITYDEF ) );
1073  }
1074  classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
1075  args.Set( "classname", classname );
1076  if ( !SpawnEntityDef( args, &ent ) || !entities[i] || entities[i]->GetType()->typeNum != typeNum ) {
1077  Error( "Failed to spawn entity with classname '%s' of type '%s'", classname, typeInfo->classname );
1078  }
1079  } else {
1080  ent = SpawnEntityType( *typeInfo, &args, true );
1081  if ( !entities[i] || entities[i]->GetType()->typeNum != typeNum ) {
1082  Error( "Failed to spawn entity of type '%s'", typeInfo->classname );
1083  }
1084  }
1085  if ( i < MAX_CLIENTS && i >= numClients ) {
1086  numClients = i + 1;
1087  }
1088  }
1089 
1090  // add the entity to the snapshot list
1092  ent->snapshotSequence = sequence;
1093 
1094  // read the class specific data from the snapshot
1095  ent->ReadFromSnapshot( deltaMsg );
1096 
1097  ent->snapshotBits = msg.GetNumBitsRead() - numBitsRead;
1098 
1099 #if ASYNC_WRITE_TAGS
1100  if ( msg.ReadLong() != tagRandom.RandomInt() ) {
1101  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "writeGameState" );
1102  if ( entityDefNumber >= 0 && entityDefNumber < declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
1103  classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
1104  Error( "write to and read from snapshot out of sync for classname '%s' of type '%s'", classname, typeInfo->classname );
1105  } else {
1106  Error( "write to and read from snapshot out of sync for type '%s'", typeInfo->classname );
1107  }
1108  }
1109 #endif
1110  }
1111 
1112  player = static_cast<idPlayer *>( entities[clientNum] );
1113  if ( !player ) {
1114  return;
1115  }
1116 
1117  // if prediction is off, enable local client smoothing
1118  player->SetSelfSmooth( dupeUsercmds > 2 );
1119 
1120  if ( player->spectating && player->spectator != clientNum && entities[ player->spectator ] ) {
1121  spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
1122  } else {
1123  spectated = player;
1124  }
1125 
1126  // get PVS for this player
1127  // don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
1128  numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
1129  pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
1130 
1131 #ifdef _D3XP
1132  // Add portalSky areas to PVS
1133  if ( portalSkyEnt.GetEntity() ) {
1134  pvsHandle_t otherPVS, newPVS;
1135  idEntity *skyEnt = portalSkyEnt.GetEntity();
1136 
1137  otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() );
1138  newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS );
1139  pvs.FreeCurrentPVS( pvsHandle );
1140  pvs.FreeCurrentPVS( otherPVS );
1141  pvsHandle = newPVS;
1142  }
1143 #endif
1144 
1145  // read the PVS from the snapshot
1146 #if ASYNC_WRITE_PVS
1147  int serverPVS[idEntity::MAX_PVS_AREAS];
1148  i = numSourceAreas;
1149  while ( i < idEntity::MAX_PVS_AREAS ) {
1150  sourceAreas[ i++ ] = 0;
1151  }
1152  for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1153  serverPVS[ i ] = msg.ReadLong();
1154  }
1155  if ( memcmp( sourceAreas, serverPVS, idEntity::MAX_PVS_AREAS * sizeof( int ) ) ) {
1156  common->Warning( "client PVS areas != server PVS areas, sequence 0x%x", sequence );
1157  for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1158  common->DPrintf( "%3d ", sourceAreas[ i ] );
1159  }
1160  common->DPrintf( "\n" );
1161  for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1162  common->DPrintf( "%3d ", serverPVS[ i ] );
1163  }
1164  common->DPrintf( "\n" );
1165  }
1166  gameLocal.pvs.ReadPVS( pvsHandle, msg );
1167 #endif
1168  for ( i = 0; i < ENTITY_PVS_SIZE; i++ ) {
1169  snapshot->pvs[i] = msg.ReadDeltaLong( clientPVS[clientNum][i] );
1170  }
1171 
1172  // add entities in the PVS that haven't changed since the last applied snapshot
1173  for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
1174 
1175  // if the entity is already in the snapshot
1176  if ( ent->snapshotSequence == sequence ) {
1177  continue;
1178  }
1179 
1180  // if the entity is not in the snapshot PVS
1181  if ( !( snapshot->pvs[ent->entityNumber >> 5] & ( 1 << ( ent->entityNumber & 31 ) ) ) ) {
1182  if ( ent->PhysicsTeamInPVS( pvsHandle ) ) {
1183  if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < mapSpawnCount && !ent->spawnArgs.GetBool("net_dynamic", "0")) { //_D3XP
1184  // server says it's not in PVS, client says it's in PVS
1185  // if that happens on map entities, most likely something is wrong
1186  // I can see that moving pieces along several PVS could be a legit situation though
1187  // this is a band aid, which means something is not done right elsewhere
1188  common->DWarning( "client thinks map entity 0x%x (%s) is stale, sequence 0x%x", ent->entityNumber, ent->name.c_str(), sequence );
1189  } else {
1190  ent->FreeModelDef();
1191 #ifdef CTF
1192  // possible fix for left over lights on CTF flag
1193  ent->FreeLightDef();
1194 #endif
1195  ent->UpdateVisuals();
1196  ent->GetPhysics()->UnlinkClip();
1197  }
1198  }
1199  continue;
1200  }
1201 
1202  // add the entity to the snapshot list
1204  ent->snapshotSequence = sequence;
1205  ent->snapshotBits = 0;
1206 
1207  base = clientEntityStates[clientNum][ent->entityNumber];
1208  if ( !base ) {
1209  // entity has probably fl.networkSync set to false
1210  continue;
1211  }
1212 
1213  base->state.BeginReading();
1214 
1215  deltaMsg.Init( &base->state, NULL, (const idBitMsg *)NULL );
1216 
1217  spawnId = deltaMsg.ReadBits( 32 - GENTITYNUM_BITS );
1218  typeNum = deltaMsg.ReadBits( idClass::GetTypeNumBits() );
1219  entityDefNumber = deltaMsg.ReadBits( entityDefBits );
1220 
1221  typeInfo = idClass::GetType( typeNum );
1222 
1223  // if the entity is not the right type
1224  if ( !typeInfo || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber ) {
1225  // should never happen - it does though. with != entityDefNumber only?
1226  common->DWarning( "entity '%s' is not the right type %p 0x%d 0x%x 0x%x 0x%x", ent->GetName(), typeInfo, ent->GetType()->typeNum, typeNum, ent->entityDefNumber, entityDefNumber );
1227  continue;
1228  }
1229 
1230  // read the class specific data from the base state
1231  ent->ReadFromSnapshot( deltaMsg );
1232  }
1233 
1234  // free the PVS
1235  pvs.FreeCurrentPVS( pvsHandle );
1236 
1237  // read the game and player state from the snapshot
1238  base = clientEntityStates[clientNum][ENTITYNUM_NONE]; // ENTITYNUM_NONE is used for the game and player state
1239  if ( base ) {
1240  base->state.BeginReading();
1241  }
1242  newBase = entityStateAllocator.Alloc();
1243  newBase->entityNumber = ENTITYNUM_NONE;
1244  newBase->next = snapshot->firstEntityState;
1245  snapshot->firstEntityState = newBase;
1246  newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
1247  newBase->state.BeginWriting();
1248  deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
1249  if ( player->spectating && player->spectator != player->entityNumber && gameLocal.entities[ player->spectator ] && gameLocal.entities[ player->spectator ]->IsType( idPlayer::Type ) ) {
1250  static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->ReadPlayerStateFromSnapshot( deltaMsg );
1251  weap = static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->weapon.GetEntity();
1252  if ( weap && ( weap->GetRenderEntity()->bounds[0] == weap->GetRenderEntity()->bounds[1] ) ) {
1253  // update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view
1254  weap->GetAnimator()->GetBounds( gameLocal.time, weap->GetRenderEntity()->bounds );
1255  weap->UpdateVisuals();
1256  }
1257  } else {
1258  player->ReadPlayerStateFromSnapshot( deltaMsg );
1259  }
1260  ReadGameStateFromSnapshot( deltaMsg );
1261 
1262  // visualize the snapshot
1263  ClientShowSnapshot( clientNum );
1264 
1265  // process entity events
1267 }
1268 
1269 /*
1270 ================
1271 idGameLocal::ClientApplySnapshot
1272 ================
1273 */
1274 bool idGameLocal::ClientApplySnapshot( int clientNum, int sequence ) {
1275  return ApplySnapshot( clientNum, sequence );
1276 }
1277 
1278 /*
1279 ================
1280 idGameLocal::ClientProcessEntityNetworkEventQueue
1281 ================
1282 */
1284  idEntity *ent;
1285  entityNetEvent_t *event;
1286  idBitMsg eventMsg;
1287 
1288  while( eventQueue.Start() ) {
1289  event = eventQueue.Start();
1290 
1291  // only process forward, in order
1292  if ( event->time > time ) {
1293  break;
1294  }
1295 
1296  idEntityPtr< idEntity > entPtr;
1297 
1298  if( !entPtr.SetSpawnId( event->spawnId ) ) {
1299  if( !gameLocal.entities[ event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 ) ] ) {
1300  // if new entity exists in this position, silently ignore
1301  NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
1302  }
1303  } else {
1304  ent = entPtr.GetEntity();
1305  assert( ent );
1306 
1307  eventMsg.Init( event->paramsBuf, sizeof( event->paramsBuf ) );
1308  eventMsg.SetSize( event->paramsSize );
1309  eventMsg.BeginReading();
1310  if ( !ent->ClientReceiveEvent( event->event, event->time, eventMsg ) ) {
1311  NetworkEventWarning( event, "unknown event" );
1312  }
1313  }
1314 
1315  entityNetEvent_t* freedEvent = eventQueue.Dequeue();
1316  assert( freedEvent == event );
1317  eventQueue.Free( event );
1318  }
1319 }
1320 
1321 /*
1322 ================
1323 idGameLocal::ClientProcessReliableMessage
1324 ================
1325 */
1326 void idGameLocal::ClientProcessReliableMessage( int clientNum, const idBitMsg &msg ) {
1327  int id, line;
1328  idPlayer *p;
1329  idDict backupSI;
1330 
1331  InitLocalClient( clientNum );
1332 
1333  id = msg.ReadByte();
1334  switch( id ) {
1336  InitClientDeclRemap( clientNum );
1337  break;
1338  }
1340  int type, index;
1341  char name[MAX_STRING_CHARS];
1342 
1343  type = msg.ReadByte();
1344  index = msg.ReadLong();
1345  msg.ReadString( name, sizeof( name ) );
1346 
1347  const idDecl *decl = declManager->FindType( (declType_t)type, name, false );
1348  if ( decl != NULL ) {
1349  if ( index >= clientDeclRemap[clientNum][type].Num() ) {
1350  clientDeclRemap[clientNum][type].AssureSize( index + 1, -1 );
1351  }
1352  clientDeclRemap[clientNum][type][index] = decl->Index();
1353  }
1354  break;
1355  }
1357  int client = msg.ReadByte();
1358  int spawnId = msg.ReadLong();
1359  if ( !entities[ client ] ) {
1360  SpawnPlayer( client );
1361  entities[ client ]->FreeModelDef();
1362  }
1363  // fix up the spawnId to match what the server says
1364  // otherwise there is going to be a bogus delete/new of the client entity in the first ClientReadFromSnapshot
1365  spawnIds[ client ] = spawnId;
1366  break;
1367  }
1369  int spawnId = msg.ReadBits( 32 );
1370  idEntityPtr< idEntity > entPtr;
1371  if( !entPtr.SetSpawnId( spawnId ) ) {
1372  break;
1373  }
1374  delete entPtr.GetEntity();
1375  break;
1376  }
1378  case GAME_RELIABLE_MESSAGE_TCHAT: { // (client should never get a TCHAT though)
1379  char name[128];
1380  char text[128];
1381  msg.ReadString( name, sizeof( name ) );
1382  msg.ReadString( text, sizeof( text ) );
1383  mpGame.AddChatLine( "%s^0: %s\n", name, text );
1384  break;
1385  }
1387  snd_evt_t snd_evt = (snd_evt_t)msg.ReadByte();
1388  mpGame.PlayGlobalSound( -1, snd_evt );
1389  break;
1390  }
1393  if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
1394  const idSoundShader *shader = declManager->SoundByIndex( index );
1395  mpGame.PlayGlobalSound( -1, SND_COUNT, shader->GetName() );
1396  }
1397  break;
1398  }
1399  case GAME_RELIABLE_MESSAGE_DB: {
1401  int parm1, parm2;
1402  parm1 = msg.ReadByte( );
1403  parm2 = msg.ReadByte( );
1404  mpGame.PrintMessageEvent( -1, msg_evt, parm1, parm2 );
1405  break;
1406  }
1408  entityNetEvent_t *event;
1409 
1410  // allocate new event
1411  event = eventQueue.Alloc();
1413 
1414  event->spawnId = msg.ReadBits( 32 );
1415  event->event = msg.ReadByte();
1416  event->time = msg.ReadLong();
1417 
1418  event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
1419  if ( event->paramsSize ) {
1420  if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
1421  NetworkEventWarning( event, "invalid param size" );
1422  return;
1423  }
1424  msg.ReadByteAlign();
1425  msg.ReadData( event->paramsBuf, event->paramsSize );
1426  }
1427  break;
1428  }
1430  idDict info;
1431  msg.ReadDeltaDict( info, NULL );
1432  gameLocal.SetServerInfo( info );
1433  break;
1434  }
1436 #ifdef _D3XP
1437  int newServerInfo = msg.ReadBits(1);
1438  if(newServerInfo) {
1439  idDict info;
1440  msg.ReadDeltaDict( info, NULL );
1441  gameLocal.SetServerInfo( info );
1442  }
1443 #endif
1444  MapRestart();
1445  break;
1446  }
1448  line = msg.ReadByte( );
1449  p = static_cast< idPlayer * >( entities[ clientNum ] );
1450  if ( !p ) {
1451  break;
1452  }
1453  p->tourneyLine = line;
1454  break;
1455  }
1457  char voteString[ MAX_STRING_CHARS ];
1458  int clientNum = msg.ReadByte( );
1459  msg.ReadString( voteString, sizeof( voteString ) );
1460  mpGame.ClientStartVote( clientNum, voteString );
1461  break;
1462  }
1464  int result = msg.ReadByte( );
1465  int yesCount = msg.ReadByte( );
1466  int noCount = msg.ReadByte( );
1467  mpGame.ClientUpdateVote( (idMultiplayerGame::vote_result_t)result, yesCount, noCount );
1468  break;
1469  }
1471  int numPortals = msg.ReadLong();
1472  assert( numPortals == gameRenderWorld->NumPortals() );
1473  for ( int i = 0; i < numPortals; i++ ) {
1475  }
1476  break;
1477  }
1479  qhandle_t portal = msg.ReadLong();
1480  int blockingBits = msg.ReadBits( NUM_RENDER_PORTAL_BITS );
1481  assert( portal > 0 && portal <= gameRenderWorld->NumPortals() );
1482  gameRenderWorld->SetPortalState( portal, blockingBits );
1483  break;
1484  }
1487  break;
1488  }
1491  break;
1492  }
1493  default: {
1494  Error( "Unknown server->client reliable message: %d", id );
1495  break;
1496  }
1497  }
1498 }
1499 
1500 /*
1501 ================
1502 idGameLocal::ClientPrediction
1503 ================
1504 */
1505 gameReturn_t idGameLocal::ClientPrediction( int clientNum, const usercmd_t *clientCmds, bool lastPredictFrame ) {
1506  idEntity *ent;
1507  idPlayer *player;
1508  gameReturn_t ret;
1509 
1510  ret.sessionCommand[ 0 ] = '\0';
1511 
1512  player = static_cast<idPlayer *>( entities[clientNum] );
1513  if ( !player ) {
1514  return ret;
1515  }
1516 
1517  // check for local client lag
1519  player->isLagged = true;
1520  } else {
1521  player->isLagged = false;
1522  }
1523 
1524  InitLocalClient( clientNum );
1525 
1526  // update the game time
1527  framenum++;
1528  previousTime = time;
1529  time += msec;
1530 
1531  // update the real client time and the new frame flag
1532  if ( time > realClientTime ) {
1533  realClientTime = time;
1534  isNewFrame = true;
1535  } else {
1536  isNewFrame = false;
1537  }
1538 
1539 #ifdef _D3XP
1540  slow.Set( time, previousTime, msec, framenum, realClientTime );
1541  fast.Set( time, previousTime, msec, framenum, realClientTime );
1542 #endif
1543 
1544  // set the user commands for this frame
1545  memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
1546 
1547  // run prediction on all entities from the last snapshot
1548  for( ent = snapshotEntities.Next(); ent != NULL; ent = ent->snapshotNode.Next() ) {
1549  ent->thinkFlags |= TH_PHYSICS;
1550  ent->ClientPredictionThink();
1551  }
1552 
1553  // service any pending events
1555 
1556  // show any debug info for this frame
1557  if ( isNewFrame ) {
1558  RunDebugInfo();
1559  D_DrawDebugLines();
1560  }
1561 
1562  if ( sessionCommand.Length() ) {
1563  strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) );
1564  }
1565  return ret;
1566 }
1567 
1568 /*
1569 ===============
1570 idGameLocal::Tokenize
1571 ===============
1572 */
1573 void idGameLocal::Tokenize( idStrList &out, const char *in ) {
1574  char buf[ MAX_STRING_CHARS ];
1575  char *token, *next;
1576 
1577  idStr::Copynz( buf, in, MAX_STRING_CHARS );
1578  token = buf;
1579  next = strchr( token, ';' );
1580  while ( token ) {
1581  if ( next ) {
1582  *next = '\0';
1583  }
1584  idStr::ToLower( token );
1585  out.Append( token );
1586  if ( next ) {
1587  token = next + 1;
1588  next = strchr( token, ';' );
1589  } else {
1590  token = NULL;
1591  }
1592  }
1593 }
1594 
1595 /*
1596 ===============
1597 idGameLocal::DownloadRequest
1598 ===============
1599 */
1600 bool idGameLocal::DownloadRequest( const char *IP, const char *guid, const char *paks, char urls[ MAX_STRING_CHARS ] ) {
1601  if ( !cvarSystem->GetCVarInteger( "net_serverDownload" ) ) {
1602  return false;
1603  }
1604  if ( cvarSystem->GetCVarInteger( "net_serverDownload" ) == 1 ) {
1605  // 1: single URL redirect
1606  if ( !strlen( cvarSystem->GetCVarString( "si_serverURL" ) ) ) {
1607  common->Warning( "si_serverURL not set" );
1608  return false;
1609  }
1610  idStr::snPrintf( urls, MAX_STRING_CHARS, "1;%s", cvarSystem->GetCVarString( "si_serverURL" ) );
1611  return true;
1612  } else {
1613  // 2: table of pak URLs
1614  // first token is the game pak if request, empty if not requested by the client
1615  // there may be empty tokens for paks the server couldn't pinpoint - the order matters
1616  idStr reply = "2;";
1617  idStrList dlTable, pakList;
1618  int i, j;
1619 
1620  Tokenize( dlTable, cvarSystem->GetCVarString( "net_serverDlTable" ) );
1621  Tokenize( pakList, paks );
1622 
1623  for ( i = 0; i < pakList.Num(); i++ ) {
1624  if ( i > 0 ) {
1625  reply += ";";
1626  }
1627  if ( pakList[ i ][ 0 ] == '\0' ) {
1628  if ( i == 0 ) {
1629  // pak 0 will always miss when client doesn't ask for game bin
1630  common->DPrintf( "no game pak request\n" );
1631  } else {
1632  common->DPrintf( "no pak %d\n", i );
1633  }
1634  continue;
1635  }
1636  for ( j = 0; j < dlTable.Num(); j++ ) {
1637  if ( !fileSystem->FilenameCompare( pakList[ i ], dlTable[ j ] ) ) {
1638  break;
1639  }
1640  }
1641  if ( j == dlTable.Num() ) {
1642  common->Printf( "download for %s: pak not matched: %s\n", IP, pakList[ i ].c_str() );
1643  } else {
1644  idStr url = cvarSystem->GetCVarString( "net_serverDlBaseURL" );
1645  url.AppendPath( dlTable[ j ] );
1646  reply += url;
1647  common->DPrintf( "download for %s: %s\n", IP, url.c_str() );
1648  }
1649  }
1650 
1651  idStr::Copynz( urls, reply, MAX_STRING_CHARS );
1652  return true;
1653  }
1654  return false;
1655 }
1656 
1657 /*
1658 ===============
1659 idEventQueue::Alloc
1660 ===============
1661 */
1663  entityNetEvent_t* event = eventAllocator.Alloc();
1664  event->prev = NULL;
1665  event->next = NULL;
1666  return event;
1667 }
1668 
1669 /*
1670 ===============
1671 idEventQueue::Free
1672 ===============
1673 */
1675  // should only be called on an unlinked event!
1676  assert( !event->next && !event->prev );
1677  eventAllocator.Free( event );
1678 }
1679 
1680 /*
1681 ===============
1682 idEventQueue::Shutdown
1683 ===============
1684 */
1686  eventAllocator.Shutdown();
1687  this->Init();
1688 }
1689 
1690 /*
1691 ===============
1692 idEventQueue::Init
1693 ===============
1694 */
1695 void idEventQueue::Init( void ) {
1696  start = NULL;
1697  end = NULL;
1698 }
1699 
1700 /*
1701 ===============
1702 idEventQueue::Dequeue
1703 ===============
1704 */
1706  entityNetEvent_t* event = start;
1707  if ( !event ) {
1708  return NULL;
1709  }
1710 
1711  start = start->next;
1712 
1713  if ( !start ) {
1714  end = NULL;
1715  } else {
1716  start->prev = NULL;
1717  }
1718 
1719  event->next = NULL;
1720  event->prev = NULL;
1721 
1722  return event;
1723 }
1724 
1725 /*
1726 ===============
1727 idEventQueue::RemoveLast
1728 ===============
1729 */
1731  entityNetEvent_t *event = end;
1732  if ( !event ) {
1733  return NULL;
1734  }
1735 
1736  end = event->prev;
1737 
1738  if ( !end ) {
1739  start = NULL;
1740  } else {
1741  end->next = NULL;
1742  }
1743 
1744  event->next = NULL;
1745  event->prev = NULL;
1746 
1747  return event;
1748 }
1749 
1750 /*
1751 ===============
1752 idEventQueue::Enqueue
1753 ===============
1754 */
1756  if ( behaviour == OUTOFORDER_DROP ) {
1757  // go backwards through the queue and determine if there are
1758  // any out-of-order events
1759  while ( end && end->time > event->time ) {
1760  entityNetEvent_t *outOfOrder = RemoveLast();
1761  common->DPrintf( "WARNING: new event with id %d ( time %d ) caused removal of event with id %d ( time %d ), game time = %d.\n", event->event, event->time, outOfOrder->event, outOfOrder->time, gameLocal.time );
1762  Free( outOfOrder );
1763  }
1764  } else if ( behaviour == OUTOFORDER_SORT && end ) {
1765  // NOT TESTED -- sorting out of order packets hasn't been
1766  // tested yet... wasn't strictly necessary for
1767  // the patch fix.
1768  entityNetEvent_t *cur = end;
1769  // iterate until we find a time < the new event's
1770  while ( cur && cur->time > event->time ) {
1771  cur = cur->prev;
1772  }
1773  if ( !cur ) {
1774  // add to start
1775  event->next = start;
1776  event->prev = NULL;
1777  start = event;
1778  } else {
1779  // insert
1780  event->prev = cur;
1781  event->next = cur->next;
1782  cur->next = event;
1783  }
1784  return;
1785  }
1786 
1787  // add the new event
1788  event->next = NULL;
1789  event->prev = NULL;
1790 
1791  if ( end ) {
1792  end->next = event;
1793  event->prev = end;
1794  } else {
1795  start = event;
1796  }
1797  end = event;
1798 }
virtual void ServerWriteInitialReliableMessages(int clientNum)
void BeginWriting(void)
Definition: BitMsg.h:265
static int snPrintf(char *dest, int size, const char *fmt,...) id_attribute((format(printf
Definition: Str.cpp:1465
virtual bool UploadImage(const char *imageName, const byte *data, int width, int height)=0
bool GetBounds(int currentTime, idBounds &bounds)
void WriteByte(int c)
Definition: BitMsg.h:283
idVec4 colorGreen
Definition: Lib.cpp:118
void ClientReadWarmupTime(const idBitMsg &msg)
int GetInt(const char *key, const char *defaultString="0") const
Definition: Dict.h:252
int qhandle_t
Definition: Lib.h:81
void ProcessVoiceChat(int clientNum, bool team, int index)
assert(prefInfo.fullscreenBtn)
int Cmp(const char *text) const
Definition: Str.h:652
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
static idTypeInfo * GetType(int num)
Definition: Class.cpp:570
const int MAX_GLOBAL_SHADER_PARMS
Definition: RenderWorld.h:44
int snapshotBits
Definition: Entity.h:119
idNetworkSystem * networkSystem
idVec3 GetCenter(void) const
Definition: Bounds.h:211
void Tokenize(idStrList &out, const char *in)
void ToLower(void)
Definition: Str.h:817
int sequence
Definition: Game_local.h:126
idVec4 colorWhite
Definition: Lib.cpp:116
void Printf(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:699
virtual void ServerClientConnect(int clientNum, const char *guid)
struct idEntity::entityFlags_s fl
void FreeCurrentPVS(pvsHandle_t handle) const
Definition: Pvs.cpp:1127
#define MAX_GENTITIES
Definition: Game_local.h:83
type * GetEntity(void) const
Definition: Game_local.h:695
const int MAX_EVENT_PARAM_SIZE
Definition: Game_local.h:132
#define LAGO_HEIGHT
Definition: Game_local.h:43
float GetFloat(void) const
Definition: CVarSystem.h:144
void WriteGameStateToSnapshot(idBitMsgDelta &msg) const
int GetSpawnId(const idEntity *ent) const
bool isNewFrame
Definition: Game_local.h:333
void PlayGlobalSound(int to, snd_evt_t evt, const char *shader=NULL)
ID_INLINE T Max(T x, T y)
Definition: Lib.h:158
idRandom random
Definition: Game_local.h:291
int Length(void) const
Definition: Str.h:702
void void void void void Error(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:783
virtual void SpawnPlayer(int clientNum)
void AssureSize(int newSize)
Definition: List.h:445
bool IsType(const idTypeInfo &c) const
Definition: Class.h:337
void EnterGame(int clientNum)
idStr sessionCommand
Definition: Game_local.h:303
char * urls[]
Definition: multithread.c:19
int ClientRemapDecl(declType_t type, int index)
virtual int GetCVarInteger(const char *name) const =0
virtual void UnlinkClip(void)=0
void SaveWriteState(int &s, int &b) const
Definition: BitMsg.h:218
int spawnIds[MAX_GENTITIES]
Definition: Game_local.h:276
idRenderSystem * renderSystem
int ReadLong(void) const
Definition: BitMsg.h:375
int previousTime
Definition: Game_local.h:318
void WantKilled(int clientNum)
case const int
Definition: Callbacks.cpp:52
void ReadByteAlign(void) const
Definition: BitMsg.h:355
static int GetTypeNumBits(void)
Definition: Class.h:246
int pvs[ENTITY_PVS_SIZE]
Definition: Game_local.h:128
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
void WriteBits(int value, int numBits)
Definition: BitMsg.cpp:648
declType_t
Definition: DeclManager.h:65
void PrintMessageEvent(int to, msg_evt_t evt, int parm1=-1, int parm2=-1)
bool isClient
Definition: Game_local.h:327
const char * GetName(void) const
Definition: DeclManager.h:140
Definition: Game.h:57
void InitAsyncNetwork(void)
virtual bool ClientApplySnapshot(int clientNum, int sequence)
bool isServer
Definition: Game_local.h:326
void FreeSnapshotsOlderThanSequence(int clientNum, int sequence)
void MapRestart(void)
virtual void FreeModelDef(void)
Definition: Entity.cpp:1197
bool SetSpawnId(int id)
Definition: Game_local.h:676
pvsHandle_t MergeCurrentPVS(pvsHandle_t pvs1, pvsHandle_t pvs2) const
Definition: Pvs.cpp:1075
snd_evt_t
GLuint GLuint GLsizei GLenum type
Definition: glext.h:2845
void void ServerProcessEntityNetworkEventQueue(void)
void UpdateLagometer(int aheadOfServer, int dupeUsercmds)
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
int ReadDeltaLong(int oldValue) const
Definition: BitMsg.h:414
int ReadString(char *buffer, int bufferSize) const
Definition: BitMsg.cpp:424
int entityDefBits
Definition: Game_local.h:335
byte paramsBuf[MAX_EVENT_PARAM_SIZE]
Definition: Game_local.h:139
void WriteString(const char *s, int maxLength=-1, bool make7Bit=true)
Definition: BitMsg.cpp:174
idLinkList< idEntity > snapshotEntities
Definition: Game_local.h:331
void NetworkEventWarning(const entityNetEvent_t *event, const char *fmt,...) id_attribute((format(printf
void WriteFloat(float f)
Definition: BitMsg.h:558
void Set(const char *key, const char *value)
Definition: Dict.cpp:275
int ReadBits(int numBits) const
Definition: BitMsg.cpp:709
void ClientUpdateVote(vote_result_t result, int yesCount, int noCount)
idDict spawnArgs
Definition: Entity.h:122
static float Rint(float f)
Definition: Math.h:793
int i
Definition: process.py:33
virtual bool FilenameCompare(const char *s1, const char *s2) const =0
idCVar net_clientShowSnapshot("net_clientShowSnapshot","0", CVAR_GAME|CVAR_INTEGER,"", 0, 3, idCmdSystem::ArgCompletion_Integer< 0, 3 >)
void Free(entityNetEvent_t *event)
int ReadByte(void) const
Definition: BitMsg.h:363
GLuint GLuint num
Definition: glext.h:5390
Boolean result
bool HasChanged(void) const
Definition: BitMsg.h:534
virtual void ClientPredictionThink(void)
Definition: Entity.cpp:4746
int GetNumBitsRead(void) const
Definition: BitMsg.h:247
static const int MAX_PVS_AREAS
Definition: Entity.h:109
virtual int NumPortals(void) const =0
virtual void FreeLightDef(void)
Definition: Entity.cpp:1209
virtual int GetNumDeclTypes(void) const =0
idBlockAlloc< entityState_t, 256 > entityStateAllocator
Definition: Game_local.h:568
void Init(byte *data, int length)
Definition: BitMsg.h:155
int ReadData(void *data, int length) const
Definition: BitMsg.cpp:457
void InitLocalClient(int clientNum)
void WriteData(const void *data, int length)
Definition: BitMsg.cpp:210
struct entityNetEvent_s * prev
Definition: Game_local.h:141
idEntity * SpawnEntityType(const idTypeInfo &classdef, const idDict *args=NULL, bool bIsClientReadSnapshot=false)
#define LAGO_WIDTH
Definition: Game_local.h:42
void void void Warning(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:735
pvsHandle_t SetupCurrentPVS(const idVec3 &source, const pvsType_t type=PVS_NORMAL) const
Definition: Pvs.cpp:948
virtual void BufferCommandText(cmdExecution_t exec, const char *text)=0
int typeNum
Definition: Class.h:285
void ClientProcessEntityNetworkEventQueue(void)
virtual void SetPortalState(qhandle_t portal, int blockingBits)=0
int ServerRemapDecl(int clientNum, declType_t type, int index)
int RandomInt(void)
Definition: Random.h:70
bool ReadDeltaDict(idDict &dict, const idDict *base) const
Definition: BitMsg.cpp:558
void Init(const idBitMsg *base, idBitMsg *newBase, idBitMsg *delta)
Definition: BitMsg.h:518
void ClientReadStartState(const idBitMsg &msg)
int Index(void) const
Definition: DeclManager.h:165
idEventQueue eventQueue
Definition: Game_local.h:571
void SetSeed(int seed)
Definition: Random.h:62
byte stateBuf[MAX_ENTITY_STATE_SIZE]
Definition: Game_local.h:121
virtual void ServerClientBegin(int clientNum)
idPhysics * GetPhysics(void) const
Definition: Entity.cpp:2607
GLuint index
Definition: glext.h:3476
void D_DrawDebugLines(void)
Definition: SysCmds.cpp:1325
int snapshotSequence
Definition: Entity.h:118
bool SpawnEntityDef(const idDict &args, idEntity **ent=NULL, bool setDefaults=true)
void AddChatLine(const char *fmt,...) id_attribute((format(printf
idCVar net_clientSelfSmoothing("net_clientSelfSmoothing","0.6", CVAR_GAME|CVAR_FLOAT,"smooth self position if network causes prediction error.", 0.0f, 0.95f)
#define MAX_STRING_CHARS
Definition: Lib.h:95
virtual bool ServerApplySnapshot(int clientNum, int sequence)
void CastVote(int clientNum, bool vote)
bool IsPureReady(void) const
virtual renderEntity_t * GetRenderEntity(void)
Definition: Entity.cpp:1506
idCVar net_clientLagOMeter("net_clientLagOMeter","1", CVAR_GAME|CVAR_BOOL|CVAR_NOCHEAT|CVAR_ARCHIVE,"draw prediction graph")
void WriteLong(int c)
Definition: BitMsg.h:295
GLuint GLuint end
Definition: glext.h:2845
virtual void virtual void virtual void DWarning(const char *fmt,...) id_attribute((format(printf
#define MAX_CLIENTS
Definition: Game_local.h:81
idCommon * common
Definition: Common.cpp:206
int GetSize(void) const
Definition: BitMsg.h:187
bool GetBool(const char *key, const char *defaultString="0") const
Definition: Dict.h:256
Definition: Dict.h:65
#define NULL
Definition: Lib.h:88
virtual const char * GetCVarString(const char *name) const =0
void BeginReading(void) const
Definition: BitMsg.h:346
void LittleRevBytes(void *bp, int elsize, int elcount)
Definition: Lib.cpp:285
virtual void SetServerInfo(const idDict &serverInfo)
Definition: Game_local.cpp:899
void Clear(void)
Definition: Dict.cpp:201
entityNetEvent_t * Dequeue(void)
void ServerSendChatMessage(int to, const char *name, const char *text)
virtual const idDecl * FindType(declType_t type, const char *name, bool makeDefault=true)=0
int GetInteger(void) const
Definition: CVarSystem.h:143
int entityDefNumber
Definition: Entity.h:112
static void Copynz(char *dest, const char *src, int destsize)
Definition: Str.cpp:1376
virtual allowReply_t ServerAllowClient(int numClients, const char *IP, const char *guid, const char *password, char reason[MAX_STRING_CHARS])
virtual void DebugClearLines(int time)=0
float clientSmoothing
Definition: Game_local.h:334
virtual void WriteToSnapshot(idBitMsgDelta &msg) const
Definition: Entity.cpp:4878
int GetNumPVSAreas(void)
Definition: Entity.cpp:1363
void ServerSendDeclRemapToClient(int clientNum, declType_t type, int index)
void ShutdownAsyncNetwork(void)
virtual void ServerClientDisconnect(int clientNum)
void SetSize(int size)
Definition: BitMsg.h:191
virtual idAnimator * GetAnimator(void)
Definition: Entity.cpp:5213
struct snapshot_s * next
Definition: Game_local.h:129
struct entityNetEvent_s * next
Definition: Game_local.h:140
virtual void DrawText(const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align=1, const int lifetime=0, bool depthTest=false)=0
bool PhysicsTeamInPVS(pvsHandle_t pvsHandle)
Definition: Entity.cpp:1398
idLinkList< idEntity > spawnNode
Definition: Entity.h:114
idGameLocal gameLocal
Definition: Game_local.cpp:64
idLinkList< idEntity > snapshotNode
Definition: Entity.h:117
virtual void ServerSendReliableMessage(int clientNum, const idBitMsg &msg)
virtual void ClientProcessReliableMessage(int clientNum, const idBitMsg &msg)
virtual int ClientGetTimeSinceLastPacket(void)
virtual const idSoundShader * SoundByIndex(int index, bool forceParse=true)=0
virtual void Printf(const char *fmt,...) id_attribute((format(printf
entityNetEvent_t * RemoveLast(void)
void ServerWriteInitialReliableMessages(int clientNum)
void SetSelfSmooth(bool b)
Definition: Player.h:835
const int ENTITY_PVS_SIZE
Definition: Game_local.h:115
idBounds Expand(const float d) const
Definition: Bounds.h:317
static int BitsForInteger(int i)
Definition: Math.h:727
void AppendPath(const char *text)
Definition: Str.cpp:830
idLinkList< idEntity > spawnedEntities
Definition: Game_local.h:281
void ClientStartVote(int clientNum, const char *voteString)
idCVar net_clientSmoothing("net_clientSmoothing","0.8", CVAR_GAME|CVAR_FLOAT,"smooth other clients angles and position.", 0.0f, 0.95f)
entityNetEvent_t * Alloc()
virtual const char * GetDeclNameFromType(declType_t type) const =0
int numClients
Definition: Game_local.h:271
idDeclManager * declManager
entityState_t * clientEntityStates[MAX_CLIENTS][MAX_GENTITIES]
Definition: Game_local.h:565
void WriteToSnapshot(idBitMsgDelta &msg) const
void WritePlayerStateToSnapshot(idBitMsgDelta &msg) const
void ClientShowSnapshot(int clientNum) const
byte lagometer[LAGO_IMG_HEIGHT][LAGO_IMG_WIDTH][4]
Definition: Game_local.h:588
entityNetEvent_t * end
Definition: Game_local.h:213
idCVar net_clientShowSnapshotRadius("net_clientShowSnapshotRadius","128", CVAR_GAME|CVAR_FLOAT,"")
idEntity * entities[MAX_GENTITIES]
Definition: Game_local.h:275
idDict userInfo[MAX_CLIENTS]
Definition: Game_local.h:272
virtual bool ClientReceiveEvent(int event, int time, const idBitMsg &msg)
Definition: Entity.cpp:4988
virtual void ClientReadSnapshot(int clientNum, int sequence, const int gameFrame, const int gameTime, const int dupeUsercmds, const int aheadOfServer, const idBitMsg &msg)
virtual int GetNumDecls(declType_t type)=0
bool isLagged
Definition: Player.h:361
int localClientNum
Definition: Game_local.h:330
struct entityState_s * next
Definition: Game_local.h:122
#define LAGO_IMG_HEIGHT
Definition: Game_local.h:41
int Append(const type &obj)
Definition: List.h:646
idDict serverInfo
Definition: Game_local.h:270
void SetInt(const char *key, int val)
Definition: Dict.h:192
float globalShaderParms[MAX_GLOBAL_SHADER_PARMS]
Definition: Game_local.h:289
Definition: Matrix.h:333
static int static int vsnPrintf(char *dest, int size, const char *fmt, va_list argptr)
Definition: Str.cpp:1502
void UpdateVisuals(void)
Definition: Entity.cpp:1310
GLuint id
Definition: glext.h:3103
bool GetBool(void) const
Definition: CVarSystem.h:142
idBitMsg state
Definition: Game_local.h:120
virtual gameReturn_t ClientPrediction(int clientNum, const usercmd_t *clientCmds, bool lastPredictFrame)
virtual const idDecl * DeclByIndex(declType_t type, int index, bool forceParse=true)=0
bool ApplySnapshot(int clientNum, int sequence)
int tourneyLine
Definition: Player.h:349
int mapSpawnCount
Definition: Game_local.h:540
void ProcessChatMessage(int clientNum, bool team, const char *name, const char *text, const char *sound)
tuple f
Definition: idal.py:89
GLuint in
Definition: glext.h:5388
void InitClientDeclRemap(int clientNum)
idList< int > clientDeclRemap[MAX_CLIENTS][DECL_MAX_TYPES]
Definition: Game_local.h:563
idAngles viewAngles
Definition: Player.h:258
void Append(const char a)
Definition: Str.h:729
int Num(void) const
Definition: List.h:265
idMat3 ToMat3(void) const
Definition: Angles.cpp:199
unsigned char byte
Definition: Lib.h:75
bool IntersectsBounds(const idBounds &a) const
Definition: Bounds.h:361
virtual const idBounds & GetAbsBounds(int id=-1) const =0
void RunDebugInfo(void)
const GLcharARB * name
Definition: glext.h:3629
void RestoreWriteState(int s, int b)
Definition: BitMsg.h:223
usercmd_t usercmds[MAX_CLIENTS]
Definition: Game_local.h:273
void ReadPlayerStateFromSnapshot(const idBitMsgDelta &msg)
virtual void ServerWriteSnapshot(int clientNum, int sequence, idBitMsg &msg, byte *clientInPVS, int numPVSClients)
byte * GetData(void)
Definition: BitMsg.h:167
Definition: Str.h:116
idBounds bounds
Definition: RenderWorld.h:95
GLsizei const GLcharARB const GLint * length
Definition: glext.h:3599
const char * GetName(void) const
Definition: Entity.cpp:875
static void ServiceEvents(void)
Definition: Event.cpp:493
idEventQueue savedEventQueue
Definition: Game_local.h:572
const char * c_str(void) const
Definition: Str.h:487
int spawnCount
Definition: Game_local.h:539
void DropWeapon(int clientNum)
const int MAX_GAME_MESSAGE_SIZE
Definition: Game_local.h:113
int GetSeed(void) const
Definition: Random.h:66
entityState_t * firstEntityState
Definition: Game_local.h:127
virtual void DebugClearPolygons(int time)=0
int clientPVS[MAX_CLIENTS][ENTITY_PVS_SIZE]
Definition: Game_local.h:566
void ReadGameStateFromSnapshot(const idBitMsgDelta &msg)
bool IsImplicit(void) const
Definition: DeclManager.h:149
void ReadFromSnapshot(const idBitMsgDelta &msg)
virtual bool GetCVarBool(const char *name) const =0
void SetBool(const bool value)
Definition: CVarSystem.h:147
virtual int GetPortalState(qhandle_t portal)=0
char sessionCommand[MAX_STRING_CHARS]
Definition: Game.h:46
entityNetEvent_t * Start(void)
Definition: Game_local.h:209
const int * GetPVSAreas(void)
Definition: Entity.cpp:1375
float ReadFloat(void) const
Definition: BitMsg.h:625
#define LAGO_IMG_WIDTH
Definition: Game_local.h:40
void WriteDeltaLong(int oldValue, int newValue)
Definition: BitMsg.h:332
idBlockAlloc< snapshot_t, 64 > snapshotAllocator
Definition: Game_local.h:569
snapshot_t * clientSnapshots[MAX_CLIENTS]
Definition: Game_local.h:567
const int NUM_RENDER_PORTAL_BITS
Definition: Game_local.h:116
int realClientTime
Definition: Game_local.h:332
virtual bool DownloadRequest(const char *IP, const char *guid, const char *paks, char urls[MAX_STRING_CHARS])
GLint j
Definition: qgl.h:264
virtual void DebugBounds(const idVec4 &color, const idBounds &bounds, const idVec3 &org=vec3_origin, const int lifetime=0)=0
idRenderWorld * gameRenderWorld
Definition: Game_local.cpp:55
virtual void ServerProcessReliableMessage(int clientNum, const idBitMsg &msg)
char * va(const char *fmt,...)
Definition: Str.cpp:1568
Definition: Pvs.h:57
void WriteBits(int value, int numBits)
Definition: BitMsg.cpp:110
#define GENTITYNUM_BITS
Definition: Game_local.h:82
void SaveEntityNetworkEvent(const idEntity *ent, int event, const idBitMsg *msg)
allowReply_t
Definition: Game.h:56
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
idMultiplayerGame mpGame
Definition: Game_local.h:305
int entityNumber
Definition: Entity.h:111
GLfloat GLfloat p
Definition: glext.h:4674
idStr name
Definition: Entity.h:121
entityNetEvent_t * start
Definition: Game_local.h:212
int thinkFlags
Definition: Entity.h:125
virtual bool ServerReceiveEvent(int event, int time, const idBitMsg &msg)
Definition: Entity.cpp:4973
idCVar password("password","", CVAR_GAME|CVAR_NOCHEAT,"client password used when connecting")
idCVar net_clientMaxPrediction("net_clientMaxPrediction","1000", CVAR_SYSTEM|CVAR_INTEGER|CVAR_NOCHEAT,"maximum number of milliseconds a client can predict ahead of server.")
int spectator
Definition: Player.h:334
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
ID_INLINE T Min(T x, T y)
Definition: Lib.h:159
bool spectating
Definition: Player.h:340
void ServerClientConnect(int clientNum)
int ReadBits(int numBits) const
Definition: BitMsg.cpp:362
virtual int BoundsInAreas(const idBounds &bounds, int *areas, int maxAreas) const =0
void ServerCallVote(int clientNum, const idBitMsg &msg)
virtual void ReadFromSnapshot(const idBitMsgDelta &msg)
Definition: Entity.cpp:4886
#define LAGO_IMAGE
Definition: Game_local.h:45
GLuint start
Definition: glext.h:2845
void DisconnectClient(int clientNum)
idPhysics * GetPlayerPhysics(void)
Definition: Player.h:815
int GetNumBitsWritten(void) const
Definition: BitMsg.h:210
void Enqueue(entityNetEvent_t *event, outOfOrderBehaviour_t oooBehaviour)
idBlockAlloc< entityNetEvent_t, 32 > eventAllocator
Definition: Game_local.h:214
#define ENTITYNUM_NONE
Definition: Game_local.h:84
void Clear(void)
Definition: List.h:184