/* ================ rvDebuggerClient::HandleInspectThreads Handle the message DBMSG_INSPECTTHREADS being sent from the server. This message is handled by adding the list of threads to a list for later lookup. ================ */ void rvDebuggerClient::HandleInspectThreads ( msg_t* msg ) { int count; ClearThreads ( ); // Loop over the number of threads in the message for ( count = (short)MSG_ReadShort ( msg ) ; count > 0; count -- ) { rvDebuggerThread* entry = new rvDebuggerThread; char temp[1024]; // Thread name MSG_ReadString ( msg, temp, 1024 ); entry->mName = temp; // Thread ID entry->mID = MSG_ReadLong ( msg ); // Thread state entry->mCurrent = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mDoneProcessing = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mWaiting = MSG_ReadBits ( msg, 1 ) ? true : false; entry->mDying = MSG_ReadBits ( msg, 1 ) ? true : false; // Add thread to list mThreads.Append ( entry ); } }
float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { float newV; *(int *)&newV = MSG_ReadBits( msg, 32 ); return newV; } return oldV; }
float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { if ( MSG_ReadBits( msg, 1 ) ) { byteAlias_t fi; fi.i = MSG_ReadBits( msg, 32 ); return fi.f; } return oldV; }
void demoCutParseRMG(msg_t *msg, clientConnection_t *clcCut, clientActive_t *clCut) { int i; clcCut->rmgHeightMapSize = (unsigned short)MSG_ReadShort(msg); if (clcCut->rmgHeightMapSize == 0) { return; } z_stream zdata; int flatDataSize; unsigned char heightmap1[15000]; // height map if (MSG_ReadBits(msg, 1)) { memset(&zdata, 0, sizeof(z_stream)); inflateInit(&zdata/*, Z_SYNC_FLUSH*/); MSG_ReadData (msg, heightmap1, clcCut->rmgHeightMapSize); zdata.next_in = heightmap1; zdata.avail_in = clcCut->rmgHeightMapSize; zdata.next_out = (unsigned char*)clcCut->rmgHeightMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate(&zdata,Z_SYNC_FLUSH); clcCut->rmgHeightMapSize = zdata.total_out; inflateEnd(&zdata); } else { MSG_ReadData(msg, (unsigned char*)clcCut->rmgHeightMap, clcCut->rmgHeightMapSize); } // Flatten map flatDataSize = MSG_ReadShort(msg); if (MSG_ReadBits(msg, 1)) { // Read the flatten map memset(&zdata, 0, sizeof(z_stream)); inflateInit(&zdata/*, Z_SYNC_FLUSH*/); MSG_ReadData ( msg, heightmap1, flatDataSize); zdata.next_in = heightmap1; zdata.avail_in = clcCut->rmgHeightMapSize; zdata.next_out = (unsigned char*)clcCut->rmgFlattenMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate(&zdata, Z_SYNC_FLUSH); inflateEnd(&zdata); } else { MSG_ReadData(msg, (unsigned char*)clcCut->rmgFlattenMap, flatDataSize); } // Seed clcCut->rmgSeed = MSG_ReadLong(msg); // Automap symbols clcCut->rmgAutomapSymbolCount = (unsigned short)MSG_ReadShort(msg); for(i = 0; i < clcCut->rmgAutomapSymbolCount; i++) { clcCut->rmgAutomapSymbols[i].mType = (int)MSG_ReadByte(msg); clcCut->rmgAutomapSymbols[i].mSide = (int)MSG_ReadByte(msg); clcCut->rmgAutomapSymbols[i].mOrigin[0] = (float)MSG_ReadLong(msg); clcCut->rmgAutomapSymbols[i].mOrigin[1] = (float)MSG_ReadLong(msg); } }
int MSG_ReadLong( msg_t *msg ) { int c; if ( msg->readcount+4 > msg->cursize ) { c = -1; } else { c = MSG_ReadBits( msg, 32 ); } return c; }
static int MSG_ReadSShort( msg_t *msg ) { int c; if ( msg->readcount+2 > msg->cursize ) { c = -1; } else { c = MSG_ReadBits( msg, -16 ); } return c; }
// returns -1 if no more characters are available int MSG_ReadByte( msg_t *msg ) { int c; if ( msg->readcount+1 > msg->cursize ) { c = -1; } else { c = (unsigned char)MSG_ReadBits( msg, 8 ); } return c; }
qboolean demoCutParseGamestate(msg_t *msg, clientConnection_t *clcCut, clientActive_t *clCut) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; clcCut->connectPacketCount = 0; Com_Memset(clCut, 0, sizeof(*clCut)); clcCut->serverCommandSequence = MSG_ReadLong(msg); clCut->gameState.dataCount = 1; while (1) { cmd = MSG_ReadByte(msg); if (cmd == svc_EOF) { break; } if (cmd == svc_configstring) { int len, start; start = msg->readcount; i = MSG_ReadShort(msg); if (i < 0 || i >= MAX_CONFIGSTRINGS) { Com_Printf("configstring > MAX_CONFIGSTRINGS"); return qfalse; } s = MSG_ReadBigString(msg); len = strlen(s); if (len + 1 + clCut->gameState.dataCount > MAX_GAMESTATE_CHARS) { Com_Printf("MAX_GAMESTATE_CHARS exceeded"); return qfalse; } // append it to the gameState string buffer clCut->gameState.stringOffsets[i] = clCut->gameState.dataCount; Com_Memcpy(clCut->gameState.stringData + clCut->gameState.dataCount, s, len + 1); clCut->gameState.dataCount += len + 1; } else if (cmd == svc_baseline) { newnum = MSG_ReadBits(msg, GENTITYNUM_BITS); if (newnum < 0 || newnum >= MAX_GENTITIES) { Com_Printf("Baseline number out of range: %i", newnum); return qfalse; } Com_Memset(&nullstate, 0, sizeof(nullstate)); es = &clCut->entityBaselines[newnum]; MSG_ReadDeltaEntity(msg, &nullstate, es, newnum); } else { Com_Printf("demoCutParseGameState: bad command byte"); return qfalse; } } clcCut->clientNum = MSG_ReadLong(msg); clcCut->checksumFeed = MSG_ReadLong(msg); // RMG stuff demoCutParseRMG(msg, clcCut, clCut); return qtrue; }
void demoCutParsePacketEntities(msg_t *msg, clSnapshot_t *oldSnap, clSnapshot_t *newSnap, clientActive_t *clCut) { /* The beast that is entity parsing */ int newnum; entityState_t *oldstate, *newstate; int oldindex = 0; int oldnum; newSnap->parseEntitiesNum = clCut->parseEntitiesNum; newSnap->numEntities = 0; newnum = MSG_ReadBits(msg, GENTITYNUM_BITS); while (1) { // read the entity index number if (oldSnap && oldindex < oldSnap->numEntities) { oldstate = &clCut->parseEntities[(oldSnap->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } else { oldstate = 0; oldnum = 99999; } newstate = &clCut->parseEntities[clCut->parseEntitiesNum]; if (!oldstate && (newnum == (MAX_GENTITIES-1))) { break; } else if (oldnum < newnum) { *newstate = *oldstate; oldindex++; } else if (oldnum == newnum) { oldindex++; MSG_ReadDeltaEntity(msg, oldstate, newstate, newnum); newnum = MSG_ReadBits(msg, GENTITYNUM_BITS); } else if (oldnum > newnum) { MSG_ReadDeltaEntity(msg, &clCut->entityBaselines[newnum], newstate, newnum); newnum = MSG_ReadBits(msg, GENTITYNUM_BITS); } if (newstate->number == MAX_GENTITIES-1) continue; clCut->parseEntitiesNum++; clCut->parseEntitiesNum &= (MAX_PARSE_ENTITIES-1); newSnap->numEntities++; } }
/* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline( msg_t *msg ) { sharedEntityState_t *es; int newnum; if ( !cgvm ) { Com_Error( ERR_DROP, "Received unexpected baseline" ); } if ( !cl.entityBaselines.pointer ) { Com_Error( ERR_DROP, "cgame needs to call trap_SetNetFields" ); } newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } es = DA_ElementPointer( cl.entityBaselines, newnum ); MSG_ReadDeltaEntity( msg, NULL, es, newnum ); }
void MSG_ReadField (msg_t *msg, int *toF, const netField_t *field, int print) { int trunc; if ( field->bits == -1) { // a -1 in the bits field means it's a float that's always between -1 and 1 int temp = MSG_ReadBits( msg, -16); *(float *)toF = (float)temp / 32767; } else if ( field->bits == 0 ) { // float if ( MSG_ReadBits( msg, 1 ) == 0 ) { *(float *)toF = 0.0f; } else { if ( MSG_ReadBits( msg, 1 ) == 0 ) { // integral float trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); // bias to allow equal parts positive and negative trunc -= FLOAT_INT_BIAS; *(float *)toF = trunc; if ( print ) { Com_Printf( "%s:%i ", field->name, trunc ); } } else { // full floating point value *toF = MSG_ReadBits( msg, 32 ); if ( print ) { Com_Printf( "%s:%f ", field->name, *(float *)toF ); } } } } else { if ( MSG_ReadBits( msg, 1 ) == 0 ) { *toF = 0; } else { // integer *toF = MSG_ReadBits( msg, field->bits ); if ( print ) { Com_Printf( "%s:%i ", field->name, *toF ); } } } }
/* ================ rvDebuggerServer::HandleAddBreakpoint Handle the DBMSG_ADDBREAKPOINT message being sent by the debugger client. This message is handled by adding a new breakpoint to the breakpoint list with the data supplied in the message. ================ */ void rvDebuggerServer::HandleAddBreakpoint ( msg_t* msg ) { bool onceOnly = false; long lineNumber; long id; char filename[MAX_PATH]; // Read the breakpoint info onceOnly = MSG_ReadBits ( msg, 1 ) ? true : false; lineNumber = MSG_ReadLong ( msg ); id = MSG_ReadLong ( msg ); MSG_ReadString ( msg, filename, MAX_PATH ); // Since breakpoints are used by both threads we need to // protect them with a crit section EnterCriticalSection ( &mCriticalSection ); mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id ) ); LeaveCriticalSection ( &mCriticalSection ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; char oldGame[MAX_QPATH]; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // save old gamedir Cvar_VariableStringBuffer("fs_game", oldGame, sizeof(oldGame)); // parse useful values out of CS_SERVERINFO CL_ParseServerInfo(); // parse serverId and other cvars CL_SystemInfoChanged(); // stop recording now so the demo won't have an unnecessary level load at the end. if(cl_autoRecordDemo->integer && clc.demorecording) CL_StopRecord_f(); // reinitialize the filesystem if the game directory has changed if(!cl_oldGameSet && (Cvar_Flags("fs_game") & CVAR_MODIFIED)) { cl_oldGameSet = qtrue; Q_strncpyz(cl_oldGame, oldGame, sizeof(cl_oldGame)); } FS_ConditionalRestart(clc.checksumFeed, qfalse); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
static demoPlay_t *demoPlayOpen( const char* fileName ) { demoPlay_t *play; fileHandle_t fileHandle; int fileSize, filePos; int i; msg_t msg; fileSize = FS_FOpenFileRead( fileName, &fileHandle, qtrue ); if (fileHandle<=0) { Com_Printf("Failed to open demo file %s \n", fileName ); return 0; } filePos = strlen( demoHeader ); i = FS_Read( &demoBuffer, filePos, fileHandle ); if ( i != filePos || Q_strncmp( demoBuffer, demoHeader, filePos )) { Com_Printf("demo file %s is wrong version\n", fileName ); FS_FCloseFile( fileHandle ); return 0; } play = Z_Malloc( sizeof( demoPlay_t )); Q_strncpyz( play->fileName, fileName, sizeof( play->fileName )); play->fileSize = fileSize; play->frame = &play->storageFrame[0]; play->nextFrame = &play->storageFrame[1]; for (i=0;i<DEMO_PLAY_CMDS;i++) play->commandStart[i] = i; play->commandFree = DEMO_PLAY_CMDS; for ( ; filePos<fileSize; ) { int blockSize, isFull, serverTime; FS_Read( &blockSize, 4, fileHandle ); blockSize = LittleLong( blockSize ); if (blockSize > sizeof(demoBuffer)) { Com_Printf( "Block too large to be read in.\n"); goto errorreturn; } if ( blockSize + filePos > fileSize) { Com_Printf( "Block would read past the end of the file.\n"); goto errorreturn; } FS_Read( demoBuffer, blockSize, fileHandle ); MSG_Init( &msg, demoBuffer, sizeof(demoBuffer) ); MSG_BeginReading( &msg ); msg.cursize = blockSize; isFull = MSG_ReadBits( &msg, 1 ); serverTime = MSG_ReadLong( &msg ); if (!play->startTime) play->startTime = serverTime; if (isFull) { if (play->fileIndexCount < DEMO_MAX_INDEX) { play->fileIndex[play->fileIndexCount].pos = filePos; play->fileIndex[play->fileIndexCount].frame = play->totalFrames; play->fileIndex[play->fileIndexCount].time = serverTime; play->fileIndexCount++; } } play->endTime = serverTime; filePos += 4 + blockSize; play->totalFrames++; } play->fileHandle = fileHandle; demoPlaySetIndex( play, 0 ); play->clientNum = -1; for( i=0;i<MAX_CLIENTS;i++) if (play->frame->clientData[i]) { play->clientNum = i; break; } return play; errorreturn: Z_Free( play ); FS_FCloseFile( fileHandle ); return 0; }
// Do very shallow parse of the demo (could be extended) just to get times and snapshot count static void CL_ParseDemo(void) { int tstart = 0; int demofile = 0; // Reset our demo data memset(&di, 0, sizeof(di)); // Parse start di.gameStartTime = -1; di.gameEndTime = -1; FS_Seek(clc.demofile, 0, FS_SEEK_SET); tstart = Sys_Milliseconds(); while (qtrue) { int r; msg_t buf; msg_t *msg; byte bufData[MAX_MSGLEN]; int s; int cmd; di.demoPos = FS_FTell(clc.demofile); // get the sequence number r = FS_Read(&s, 4, clc.demofile); if (r != 4) { CL_DemoCompleted(); return; } clc.serverMessageSequence = LittleLong(s); // init the message MSG_Init(&buf, bufData, sizeof(bufData)); // get the length r = FS_Read(&buf.cursize, 4, clc.demofile); if (r != 4) { break; } buf.cursize = LittleLong(buf.cursize); if (buf.cursize == -1) { break; } if (buf.cursize > buf.maxsize) { Com_FuncPrinf("demoMsglen > MAX_MSGLEN"); break; } r = FS_Read(buf.data, buf.cursize, clc.demofile); if (r != buf.cursize) { Com_FuncPrinf("Demo file was truncated.\n"); break; } clc.lastPacketTime = cls.realtime; buf.readcount = 0; // parse msg = &buf; MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong(msg); if (clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS) { clc.reliableAcknowledge = clc.reliableSequence; } // parse the message while (qtrue) { if (msg->readcount > msg->cursize) { Com_FuncDrop("read past end of server message"); return; } cmd = MSG_ReadByte(msg); if (cmd == svc_EOF) { break; } // other commands switch (cmd) { default: Com_FuncDrop("Illegible server message %d", cmd); return; case svc_nop: break; case svc_serverCommand: MSG_ReadLong(msg); MSG_ReadString(msg); break; case svc_gamestate: clc.serverCommandSequence = MSG_ReadLong(msg); cl.gameState.dataCount = 1; while (qtrue) { int cmd2 = MSG_ReadByte(msg); if (cmd2 == svc_EOF) { break; } if (cmd2 == svc_configstring) { MSG_ReadShort(msg); MSG_ReadBigString(msg); } else if (cmd2 == svc_baseline) { entityState_t s1, s2; memset(&s1, 0, sizeof(s1)); memset(&s2, 0, sizeof(s2)); MSG_ReadBits(msg, GENTITYNUM_BITS); MSG_ReadDeltaEntity(msg, &s1, &s2, 0); } else { Com_FuncDrop("bad command byte"); return; } } MSG_ReadLong(msg); MSG_ReadLong(msg); break; case svc_snapshot: CL_ParseDemoSnapShotSimple(msg); break; case svc_download: MSG_ReadShort(msg); break; } } if (!cl.snap.valid) { Com_FuncPrinf("!cl.snap.valid\n"); continue; } if (cl.snap.serverTime < cl.oldFrameServerTime) { // ignore snapshots that don't have entities if (cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE) { continue; } cls.state = CA_ACTIVE; // set the timedelta so we are exactly on this first frame cl.serverTimeDelta = cl.snap.serverTime - cls.realtime; cl.oldServerTime = cl.snap.serverTime; clc.timeDemoBaseTime = cl.snap.serverTime; } cl.oldFrameServerTime = cl.snap.serverTime; if (cl.newSnapshots) { CL_AdjustTimeDelta(); } di.lastServerTime = cl.snap.serverTime; if (di.firstServerTime == 0) { di.firstServerTime = cl.snap.serverTime; Com_FuncPrinf("firstServerTime %d\n", di.firstServerTime); } di.snapsInDemo++; } Com_FuncPrinf("Snaps in demo: %i\n", di.snapsInDemo); Com_FuncPrinf("last serverTime %d total %f minutes\n", cl.snap.serverTime, (cl.snap.serverTime - di.firstServerTime) / 1000.0 / 60.0); Com_FuncPrinf("parse time %f seconds\n", (float)(Sys_Milliseconds() - tstart) / 1000.0); FS_Seek(clc.demofile, 0, FS_SEEK_SET); clc.demoplaying = qfalse; demofile = clc.demofile; CL_ClearState(); Com_Memset(&clc, 0, sizeof(clc)); Com_ClearDownload(); clc.demofile = demofile; cls.state = CA_DISCONNECTED; cl_connectedToPureServer = qfalse; dpi.firstTime = di.firstServerTime; dpi.lastTime = di.lastServerTime; }
static void demoFrameUnpack( msg_t *msg, demoFrame_t *oldFrame, demoFrame_t *newFrame ) { int last; qboolean isDelta = MSG_ReadBits( msg, 1 ) ? qfalse : qtrue; if (!isDelta) oldFrame = 0; newFrame->serverTime = MSG_ReadLong( msg ); /* Read config strings */ newFrame->string.data[0] = 0; newFrame->string.used = 1; last = 0; /* Extract config strings */ while ( 1 ) { int i, num = MSG_ReadShort( msg ); if (!isDelta ) { for (i = last;i<num;i++) newFrame->string.offsets[i] = 0; } else { for (i = last;i<num;i++) demoFrameAddString( &newFrame->string, i, oldFrame->string.data + oldFrame->string.offsets[i] ); } if (num < MAX_CONFIGSTRINGS) { demoFrameAddString( &newFrame->string, num, MSG_ReadBigString( msg ) ); } else { break; } last = num + 1; } /* Extract player states */ Com_Memset( newFrame->clientData, 0, sizeof( newFrame->clientData )); last = MSG_ReadByte( msg ); while (last < MAX_CLIENTS) { playerState_t *oldPlayer, *newPlayer; newFrame->clientData[last] = 1; oldPlayer = isDelta && oldFrame->clientData[last] ? &oldFrame->clients[last] : &demoNullPlayerState; newPlayer = &newFrame->clients[last]; MSG_ReadDeltaPlayerstate( msg, oldPlayer, newPlayer ); last = MSG_ReadByte( msg ); } /* Extract entity states */ last = 0; while ( 1 ) { int i, num = MSG_ReadBits( msg, GENTITYNUM_BITS ); entityState_t *oldEntity, *newEntity; if ( isDelta ) { for (i = last;i<num;i++) if (oldFrame->entities[i].number == i) newFrame->entities[i] = oldFrame->entities[i]; else newFrame->entities[i].number = MAX_GENTITIES - 1; } else { for (i = last;i<num;i++) newFrame->entities[i].number = MAX_GENTITIES - 1; } if (num < MAX_GENTITIES - 1) { if (isDelta) { oldEntity = &oldFrame->entities[num]; if (oldEntity->number != num) oldEntity = &demoNullEntityState; } else { oldEntity = &demoNullEntityState; } newEntity = &newFrame->entities[i]; MSG_ReadDeltaEntity( msg, oldEntity, newEntity, num ); } else break; last = num + 1; } /* Read the area mask */ newFrame->areaUsed = MSG_ReadByte( msg ); MSG_ReadData( msg, newFrame->areamask, newFrame->areaUsed ); /* Read the command string data */ newFrame->commandUsed = MSG_ReadLong( msg ); MSG_ReadData( msg, newFrame->commandData, newFrame->commandUsed ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); #ifdef _DONETPROFILE_ int startBytes,endBytes; startBytes=msg->readcount; #endif // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len, start; start = msg->readcount; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); if (cl_shownet->integer >= 2) { Com_Printf("%3i: %d: %s\n", start, i, s); } /* if (i == CS_SERVERINFO) { //get the special value here char *f = strstr(s, "g_debugMelee"); if (f) { while (*f && *f != '\\') { //find the \ after it f++; } if (*f == '\\') { //got it int i = 0; f++; while (*f && *f != '\\' && i < 128) { hiddenCvarVal[i] = *f; i++; f++; } hiddenCvarVal[i] = 0; //resume here s = f; } } } */ len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); CL_ParseRMG ( msg ); //rwwRMG - get info for it from the server #ifdef _DONETPROFILE_ endBytes=msg->readcount; // ClReadProf().AddField("svc_gamestate",endBytes-startBytes); #endif // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed if( FS_ConditionalRestart( clc.checksumFeed ) ) { // don't set to true because we yet have to start downloading // enabling this can cause double loading of a map when connecting to // a server which has a different game directory set //clc.downloadRestart = qtrue; } // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
int MSG_ReadDelta( msg_t *msg, int oldV, int bits ) { if ( MSG_ReadBits( msg, 1 ) ) { return MSG_ReadBits( msg, bits ); } return oldV; }
void CL_ParseRMG ( msg_t* msg ) { clc.rmgHeightMapSize = (unsigned short)MSG_ReadShort ( msg ); if ( !clc.rmgHeightMapSize ) { return; } z_stream zdata; int size; unsigned char heightmap1[15000]; if ( MSG_ReadBits ( msg, 1 ) ) { // Read the heightmap memset(&zdata, 0, sizeof(z_stream)); inflateInit ( &zdata/*, Z_SYNC_FLUSH*/ ); MSG_ReadData ( msg, heightmap1, clc.rmgHeightMapSize ); zdata.next_in = heightmap1; zdata.avail_in = clc.rmgHeightMapSize; zdata.next_out = (unsigned char*)clc.rmgHeightMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate (&zdata,Z_SYNC_FLUSH ); clc.rmgHeightMapSize = zdata.total_out; inflateEnd(&zdata); } else { MSG_ReadData ( msg, (unsigned char*)clc.rmgHeightMap, clc.rmgHeightMapSize ); } size = (unsigned short)MSG_ReadShort ( msg ); if ( MSG_ReadBits ( msg, 1 ) ) { // Read the flatten map memset(&zdata, 0, sizeof(z_stream)); inflateInit ( &zdata/*, Z_SYNC_FLUSH*/ ); MSG_ReadData ( msg, heightmap1, size ); zdata.next_in = heightmap1; zdata.avail_in = clc.rmgHeightMapSize; zdata.next_out = (unsigned char*)clc.rmgFlattenMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate (&zdata, Z_SYNC_FLUSH); inflateEnd(&zdata); } else { MSG_ReadData ( msg, (unsigned char*)clc.rmgFlattenMap, size ); } // Read the seed clc.rmgSeed = MSG_ReadLong ( msg ); CL_ParseAutomapSymbols ( msg ); }
void demoConvert( const char *oldName, const char *newBaseName, qboolean smoothen ) { fileHandle_t oldHandle = 0; fileHandle_t newHandle = 0; int temp; int oldSize; int msgSequence; msg_t oldMsg; byte oldData[ MAX_MSGLEN ]; int oldTime, nextTime, fullTime; int clientNum; demoFrame_t *workFrame; int parseEntitiesNum = 0; demoConvert_t *convert; char bigConfigString[BIG_INFO_STRING]; int bigConfigNumber; const char *s; clSnapshot_t *oldSnap = 0; clSnapshot_t *newSnap; int levelCount = 0; char newName[MAX_OSPATH]; oldSize = FS_FOpenFileRead( oldName, &oldHandle, qtrue ); if (!oldHandle) { Com_Printf("Failed to open %s for conversion.", oldName); return; } /* Alloc some memory */ convert = Z_Malloc( sizeof( demoConvert_t) ); /* Initialize the first workframe's strings */ while (oldSize > 0) { MSG_Init( &oldMsg, oldData, sizeof( oldData ) ); /* Read the sequence number */ if (FS_Read( &convert->messageNum, 4, oldHandle) != 4) goto conversionerror; convert->messageNum = LittleLong( convert->messageNum ); oldSize -= 4; /* Read the message size */ if (FS_Read( &oldMsg.cursize,4, oldHandle) != 4) goto conversionerror; oldSize -= 4; oldMsg.cursize = LittleLong( oldMsg.cursize ); /* Negative size signals end of demo */ if (oldMsg.cursize < 0) break; if ( oldMsg.cursize > oldMsg.maxsize ) goto conversionerror; /* Read the actual message */ if (FS_Read( oldMsg.data, oldMsg.cursize, oldHandle ) != oldMsg.cursize) goto conversionerror; oldSize -= oldMsg.cursize; // init the bitstream MSG_BeginReading( &oldMsg ); // Skip the reliable sequence acknowledge number MSG_ReadLong( &oldMsg ); // // parse the message // while ( 1 ) { byte cmd; if ( oldMsg.readcount > oldMsg.cursize ) { Com_Printf ("Demo conversion, read past end of server message.\n"); goto conversionerror; } cmd = MSG_ReadByte( &oldMsg ); if ( cmd == svc_EOF) { break; } workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: temp = MSG_ReadLong( &oldMsg ); s = MSG_ReadString( &oldMsg ); if (temp<=msgSequence) break; // Com_Printf( " server command %s\n", s ); msgSequence = temp; Cmd_TokenizeString( s ); if ( !Q_stricmp( Cmd_Argv(0), "bcs0" ) ) { bigConfigNumber = atoi( Cmd_Argv(1) ); Q_strncpyz( bigConfigString, Cmd_Argv(2), sizeof( bigConfigString )); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs1" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs2" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); demoFrameAddString( &workFrame->string, bigConfigNumber, bigConfigString ); break; } if ( !Q_stricmp( Cmd_Argv(0), "cs" ) ) { int num = atoi( Cmd_Argv(1) ); s = Cmd_ArgsFrom( 2 ); demoFrameAddString( &workFrame->string, num, Cmd_ArgsFrom( 2 ) ); break; } if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { int len = strlen( s ) + 1; char *dst; if (workFrame->commandUsed + len + 1 > sizeof( workFrame->commandData)) { Com_Printf("Overflowed state command data.\n"); goto conversionerror; } dst = workFrame->commandData + workFrame->commandUsed; *dst = clientNum; Com_Memcpy( dst+1, s, len ); workFrame->commandUsed += len + 1; } break; case svc_gamestate: if (newHandle) { FS_FCloseFile( newHandle ); newHandle = 0; } if (levelCount) { Com_sprintf( newName, sizeof( newName ), "%s.%d.mme", newBaseName, levelCount ); } else { Com_sprintf( newName, sizeof( newName ), "%s.mme", newBaseName ); } fullTime = -1; clientNum = -1; oldTime = -1; Com_Memset( convert, 0, sizeof( *convert )); convert->frames[0].string.used = 1; levelCount++; newHandle = FS_FOpenFileWrite( newName ); if (!newHandle) { Com_Printf("Failed to open %s for target conversion target.\n", newName); goto conversionerror; return; } else { FS_Write ( demoHeader, strlen( demoHeader ), newHandle ); } Com_sprintf( newName, sizeof( newName ), "%s.txt", newBaseName ); workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; msgSequence = MSG_ReadLong( &oldMsg ); while( 1 ) { cmd = MSG_ReadByte( &oldMsg ); if (cmd == svc_EOF) break; if ( cmd == svc_configstring) { int num; const char *s; num = MSG_ReadShort( &oldMsg ); s = MSG_ReadBigString( &oldMsg ); demoFrameAddString( &workFrame->string, num, s ); } else if ( cmd == svc_baseline ) { int num = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); if ( num < 0 || num >= MAX_GENTITIES ) { Com_Printf( "Baseline number out of range: %i.\n", num ); goto conversionerror; } MSG_ReadDeltaEntity( &oldMsg, &demoNullEntityState, &convert->entityBaselines[num], num ); } else { Com_Printf( "Unknown block while converting demo gamestate.\n" ); goto conversionerror; } } clientNum = MSG_ReadLong( &oldMsg ); /* Skip the checksum feed */ MSG_ReadLong( &oldMsg ); break; case svc_snapshot: nextTime = MSG_ReadLong( &oldMsg ); /* Delta number, not needed */ newSnap = &convert->snapshots[convert->messageNum & PACKET_MASK]; Com_Memset (newSnap, 0, sizeof(*newSnap)); newSnap->deltaNum = MSG_ReadByte( &oldMsg ); newSnap->messageNum = convert->messageNum; if (!newSnap->deltaNum) { newSnap->deltaNum = -1; newSnap->valid = qtrue; // uncompressed frame oldSnap = NULL; } else { newSnap->deltaNum = newSnap->messageNum - newSnap->deltaNum; oldSnap = &convert->snapshots[newSnap->deltaNum & PACKET_MASK]; if (!oldSnap->valid) { Com_Printf( "Delta snapshot without base.\n" ); goto conversionerror; } else if (oldSnap->messageNum != newSnap->deltaNum) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_Printf ("Delta frame too old.\n"); } else if ( parseEntitiesNum - oldSnap->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap->valid = qtrue; // valid delta parse } } /* Snapflags, not needed */ newSnap->snapFlags = MSG_ReadByte( &oldMsg ); // read areamask workFrame->areaUsed = MSG_ReadByte( &oldMsg ); MSG_ReadData( &oldMsg, workFrame->areamask, workFrame->areaUsed ); if (clientNum <0 || clientNum >= MAX_CLIENTS) { Com_Printf("Got snapshot with invalid client.\n"); goto conversionerror; } MSG_ReadDeltaPlayerstate( &oldMsg, oldSnap ? &oldSnap->ps : &demoNullPlayerState, &newSnap->ps ); /* Read the individual entities */ newSnap->parseEntitiesNum = parseEntitiesNum; newSnap->numEntities = 0; Com_Memset( workFrame->entityData, 0, sizeof( workFrame->entityData )); /* The beast that is entity parsing */ { int newnum; entityState_t *oldstate, *newstate; int oldindex = 0; int oldnum; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); while ( 1 ) { // read the entity index number if (oldSnap && oldindex < oldSnap->numEntities) { oldstate = &convert->parseEntities[(oldSnap->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } else { oldstate = 0; oldnum = 99999; } newstate = &convert->parseEntities[parseEntitiesNum]; if ( !oldstate && (newnum == (MAX_GENTITIES-1))) { break; } else if ( oldnum < newnum ) { *newstate = *oldstate; oldindex++; } else if (oldnum == newnum) { oldindex++; MSG_ReadDeltaEntity( &oldMsg, oldstate, newstate, newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } else if (oldnum > newnum) { MSG_ReadDeltaEntity( &oldMsg, &convert->entityBaselines[newnum], newstate , newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } if (newstate->number == MAX_GENTITIES-1) continue; parseEntitiesNum++; parseEntitiesNum &= (MAX_PARSE_ENTITIES-1); newSnap->numEntities++; }} /* Stop processing this further since it's an invalid snap due to lack of delta data */ if (!newSnap->valid) break; /* Skipped snapshots will be set invalid in the circular buffer */ if ( newSnap->messageNum - convert->lastMessageNum >= PACKET_BACKUP ) { convert->lastMessageNum = newSnap->messageNum - ( PACKET_BACKUP - 1 ); } for ( ; convert->lastMessageNum < newSnap->messageNum ; convert->lastMessageNum++ ) { convert->snapshots[convert->lastMessageNum & PACKET_MASK].valid = qfalse; } convert->lastMessageNum = newSnap->messageNum + 1; /* compress the frame into the new format */ if (nextTime > oldTime) { demoFrame_t *cleanFrame; int writeIndex; for (temp = 0;temp<newSnap->numEntities;temp++) { int p = (newSnap->parseEntitiesNum+temp) & (MAX_PARSE_ENTITIES-1); entityState_t *newState = &convert->parseEntities[p]; workFrame->entities[newState->number] = *newState; } workFrame->clientData[clientNum] = 1; workFrame->clients[clientNum] = newSnap->ps; workFrame->serverTime = nextTime; /* Which frame from the cache to save */ writeIndex = convert->frameIndex - (DEMOCONVERTFRAMES/2); if (writeIndex >= 0) { const demoFrame_t *newFrame; msg_t writeMsg; // init the message MSG_Init( &writeMsg, demoBuffer, sizeof (demoBuffer)); MSG_Clear( &writeMsg ); MSG_Bitstream( &writeMsg ); newFrame = &convert->frames[ writeIndex % DEMOCONVERTFRAMES]; if ( smoothen ) demoFrameInterpolate( convert->frames, DEMOCONVERTFRAMES, writeIndex ); if ( nextTime > fullTime || writeIndex <= 0 ) { /* Plan the next time for a full write */ fullTime = nextTime + 2000; demoFramePack( &writeMsg, newFrame, 0 ); } else { const demoFrame_t *oldFrame = &convert->frames[ ( writeIndex -1 ) % DEMOCONVERTFRAMES]; demoFramePack( &writeMsg, newFrame, oldFrame ); } /* Write away the new data in the msg queue */ temp = LittleLong( writeMsg.cursize ); FS_Write (&temp, 4, newHandle ); FS_Write ( writeMsg.data , writeMsg.cursize, newHandle ); } /* Clean up the upcoming frame for all new changes */ convert->frameIndex++; cleanFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES]; cleanFrame->serverTime = 0; for (temp = 0;temp<MAX_GENTITIES;temp++) cleanFrame->entities[temp].number = MAX_GENTITIES-1; Com_Memset( cleanFrame->clientData, 0, sizeof ( cleanFrame->clientData )); Com_Memcpy( cleanFrame->string.data, workFrame->string.data, workFrame->string.used ); Com_Memcpy( cleanFrame->string.offsets, workFrame->string.offsets, sizeof( workFrame->string.offsets )); cleanFrame->string.used = workFrame->string.used; cleanFrame->commandUsed = 0; /* keep track of this last frame's time */ oldTime = nextTime; } break; case svc_download: // read block number temp = MSG_ReadShort ( &oldMsg ); if (!temp) //0 block, read file size MSG_ReadLong( &oldMsg ); // read block size temp = MSG_ReadShort ( &oldMsg ); // read the data block for ( ;temp>0;temp--) MSG_ReadByte( &oldMsg ); break; } } } conversionerror: FS_FCloseFile( oldHandle ); FS_FCloseFile( newHandle ); Z_Free( convert ); return; }
/* ===================== CL_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip( msg_t *msg ) { static short decoded[ 4096 ]; // !!! FIXME: don't hardcode. const int sender = MSG_ReadShort( msg ); const int generation = MSG_ReadByte( msg ); const int sequence = MSG_ReadLong( msg ); const int frames = MSG_ReadByte( msg ); const int packetsize = MSG_ReadShort( msg ); const int flags = MSG_ReadBits( msg, VOIP_FLAGCNT ); char encoded[ 1024 ]; int seqdiff = sequence - clc.voipIncomingSequence[ sender ]; int written = 0; int i; Com_DPrintf( "VoIP: %d-byte packet from client %d\n", packetsize, sender ); if ( sender < 0 ) { return; // short/invalid packet, bail. } else if ( generation < 0 ) { return; // short/invalid packet, bail. } else if ( sequence < 0 ) { return; // short/invalid packet, bail. } else if ( frames < 0 ) { return; // short/invalid packet, bail. } else if ( packetsize < 0 ) { return; // short/invalid packet, bail. } if ( packetsize > sizeof( encoded ) ) // overlarge packet? { int bytesleft = packetsize; while ( bytesleft ) { int br = bytesleft; if ( br > sizeof( encoded ) ) { br = sizeof( encoded ); } MSG_ReadData( msg, encoded, br ); bytesleft -= br; } return; // overlarge packet, bail. } if ( !clc.speexInitialized ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // can't handle VoIP without libspeex! } else if ( sender >= MAX_CLIENTS ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // bogus sender. } else if ( CL_ShouldIgnoreVoipSender( sender ) ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf( "VoIP: packet accepted!\n" ); // This is a new "generation" ... a new recording started, reset the bits. if ( generation != clc.voipIncomingGeneration[ sender ] ) { Com_DPrintf( "VoIP: new generation %d!\n", generation ); speex_bits_reset( &clc.speexDecoderBits[ sender ] ); clc.voipIncomingGeneration[ sender ] = generation; seqdiff = 0; } else if ( seqdiff < 0 ) // we're ahead of the sequence?! { // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf( "VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[ sender ] ); // reset the bits just in case. speex_bits_reset( &clc.speexDecoderBits[ sender ] ); seqdiff = 0; } else if ( seqdiff > 100 ) // more than 2 seconds of audio dropped? { // just start over. Com_DPrintf( "VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender ); speex_bits_reset( &clc.speexDecoderBits[ sender ] ); seqdiff = 0; } if ( seqdiff != 0 ) { Com_DPrintf( "VoIP: Dropped %d frames from client #%d\n", seqdiff, sender ); // tell speex that we're missing frames... for ( i = 0; i < seqdiff; i++ ) { assert( ( written + clc.speexFrameSize ) * 2 < sizeof( decoded ) ); speex_decode_int( clc.speexDecoder[ sender ], NULL, decoded + written ); written += clc.speexFrameSize; } } for ( i = 0; i < frames; i++ ) { const int len = MSG_ReadByte( msg ); if ( len < 0 ) { Com_DPrintf( "VoIP: Short packet!\n" ); break; } MSG_ReadData( msg, encoded, len ); // shouldn't happen, but just in case... if ( ( written + clc.speexFrameSize ) * 2 > sizeof( decoded ) ) { Com_DPrintf( "VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i ); CL_PlayVoip( sender, written, ( const byte * ) decoded, flags ); written = 0; } speex_bits_read_from( &clc.speexDecoderBits[ sender ], encoded, len ); speex_decode_int( clc.speexDecoder[ sender ], &clc.speexDecoderBits[ sender ], decoded + written ); #if 0 static FILE *encio = NULL; if ( encio == NULL ) { encio = fopen( "voip-incoming-encoded.bin", "wb" ); } if ( encio != NULL ) { fwrite( encoded, len, 1, encio ); fflush( encio ); } static FILE *decio = NULL; if ( decio == NULL ) { decio = fopen( "voip-incoming-decoded.bin", "wb" ); } if ( decio != NULL ) { fwrite( decoded + written, clc.speexFrameSize * 2, 1, decio ); fflush( decio ); } #endif written += clc.speexFrameSize; } Com_DPrintf( "VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i ); if ( written > 0 ) { CL_PlayVoip( sender, written, ( const byte * ) decoded, flags ); } clc.voipIncomingSequence[ sender ] = sequence + frames; }
/* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe ) { int newnum; entityState_t *oldstate; int oldindex, oldnum; newframe->parseEntitiesNum = cl.parseEntitiesNum; newframe->numEntities = 0; // delta from the entities present in oldframe oldindex = 0; oldstate = NULL; if ( !oldframe ) { oldnum = 99999; } else { if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } while ( 1 ) { // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum == ( MAX_GENTITIES - 1 ) ) { break; } if ( msg->readcount > msg->cursize ) { Com_Error( ERR_DROP, "CL_ParsePacketEntities: end of message" ); } while ( oldnum < newnum ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } if ( oldnum == newnum ) { // delta from previous state if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: delta: %i\n", msg->readcount, newnum ); } CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } continue; } if ( oldnum > newnum ) { // delta from baseline if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: baseline: %i\n", msg->readcount, newnum ); } CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[ newnum ], qfalse ); continue; } } // any remaining entities in the old frame are copied over while ( oldnum != 99999 ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 ) ]; oldnum = oldstate->number; } } if ( cl_shownuments->integer ) { Com_Printf( "Entities in packet: %i\n", newframe->numEntities ); } }
/* ===================== CL_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) { static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code const int sender = MSG_ReadShort(msg); const int generation = MSG_ReadByte(msg); const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT); unsigned char encoded[4000]; int numSamples; int seqdiff; int written = 0; int i; Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); if (sender < 0) return; // short/invalid packet, bail. else if (generation < 0) return; // short/invalid packet, bail. else if (sequence < 0) return; // short/invalid packet, bail. else if (frames < 0) return; // short/invalid packet, bail. else if (packetsize < 0) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } MSG_ReadData(msg, encoded, packetsize); if (ignoreData) { return; // just ignore legacy speex voip data } else if (!clc.voipCodecInitialized) { return; // can't handle VoIP without libopus! } else if (sender >= MAX_CLIENTS) { return; // bogus sender. } else if (CL_ShouldIgnoreVoipSender(sender)) { return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf("VoIP: packet accepted!\n"); seqdiff = sequence - clc.voipIncomingSequence[sender]; // This is a new "generation" ... a new recording started, reset the bits. if (generation != clc.voipIncomingGeneration[sender]) { Com_DPrintf("VoIP: new generation %d!\n", generation); opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); clc.voipIncomingGeneration[sender] = generation; seqdiff = 0; } else if (seqdiff < 0) { // we're ahead of the sequence?! // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[sender]); // reset the decoder just in case. opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; } else if (seqdiff * VOIP_MAX_PACKET_SAMPLES*2 >= sizeof (decoded)) { // dropped more than we can handle? // just start over. Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender); opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; } if (seqdiff != 0) { Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", seqdiff, sender); // tell opus that we're missing frames... for (i = 0; i < seqdiff; i++) { assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded)); numSamples = opus_decode(clc.opusDecoder[sender], NULL, 0, decoded + written, VOIP_MAX_PACKET_SAMPLES, 0); if ( numSamples <= 0 ) { Com_DPrintf("VoIP: Error decoding frame %d from client #%d\n", i, sender); continue; } written += numSamples; } } numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, ARRAY_LEN(decoded) - written, 0); if ( numSamples <= 0 ) { Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender); numSamples = 0; } #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } #endif written += numSamples; Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, frames); if(written > 0) CL_PlayVoip(sender, written, (const byte *) decoded, flags); clc.voipIncomingSequence[sender] = sequence + frames; }
uint32 EXT_FUNC MSG_ReadBits_api(int numbits) { return MSG_ReadBits(numbits); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); UI_UpdateConnectionString( "" ); // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd <= 0 ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: CS# %d %s (%d)\n",msg->readcount, i,s,len); } } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed #if 0 if ( fs_game->modified ) { } #endif // let the client game init and load data cls.state = CA_LOADING; CL_StartHunkUsers(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset( &nullstate, 0, sizeof( nullstate ) ); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong( msg ); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed FS_ConditionalRestart( clc.checksumFeed ); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } const char* str = MSG_ReadBigString( msg ); std::string s = str; cl.gameState[i] = str; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset( &nullstate, 0, sizeof( nullstate ) ); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong( msg ); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
uint32 ff2 = ((uint32)1 << 9) - 1; uint32 ff3 = 0xFFFFFFFF; uint32 t1, t2, t3; SZ_Clear(buf); MSG_StartBitWriting(buf); MSG_WriteBits(ff1, 31); MSG_WriteBits(ff2, 9); MSG_EndBitWriting(buf); MSG_BeginReading(); MSG_StartBitReading(buf); t1 = MSG_ReadBits(31); t2 = MSG_ReadBits(9); MSG_EndBitReading(buf); UINT32_EQUALS("31/9 Read failed (31)", ff1, t1); UINT32_EQUALS("31/9 Read failed (9)", ff2, t2); SZ_Clear(buf); MSG_StartBitWriting(buf); MSG_WriteBits(ff2, 9); MSG_WriteBits(ff1, 31); MSG_EndBitWriting(buf); MSG_BeginReading();
/* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, const clSnapshot_t *oldSnapshot, clSnapshot_t *newSnapshot ) { // The entity packet contains the delta between the two snapshots, with data only // for entities that were created, changed or removed. Entities entries are in // order of increasing entity number, as are entities in a snapshot. Using this we // have an efficient algorithm to create the new snapshot, that goes over the old // snapshot once from the beginning to the end. // If we don't have an old snapshot or it is empty, we'll recreate all entities // from the baseline entities as setting oldEntityNum to MAX_GENTITIES will force // us to only do step (3) below. unsigned int oldEntityNum = MAX_GENTITIES; if (oldSnapshot && oldSnapshot->entities.size() > 0){ oldEntityNum = oldSnapshot->entities[0].number; } // Likewise when we don't have an old snapshot, oldEntities just has to be an empty // vector so that we skip step (4) std::vector<entityState_t> dummyEntities; auto& oldEntities = oldSnapshot? oldSnapshot->entities : dummyEntities; auto& newEntities = newSnapshot->entities; unsigned int numEntities = MSG_ReadShort(msg); newEntities.reserve(numEntities); unsigned oldIndex = 0; while (true) { unsigned int newEntityNum = MSG_ReadBits(msg, GENTITYNUM_BITS); if (msg->readcount > msg->cursize) { Sys::Drop("CL_ParsePacketEntities: Unexpected end of message"); } if (newEntityNum == MAX_GENTITIES - 1) { break; } // (1) all entities that weren't specified between the previous newEntityNum and // the current one are unchanged and just copied over. while (oldEntityNum < newEntityNum) { newEntities.push_back(oldEntities[oldIndex]); oldIndex ++; if (oldIndex >= oldEntities.size()) { oldEntityNum = MAX_GENTITIES; } else { oldEntityNum = oldEntities[oldIndex].number; } } // (2) there is an entry for an entity in the old snapshot, apply the delta if (oldEntityNum == newEntityNum) { CL_DeltaEntity(msg, newSnapshot, newEntityNum, oldEntities[oldIndex]); oldIndex ++; if (oldIndex >= oldEntities.size()) { oldEntityNum = MAX_GENTITIES; } else { oldEntityNum = oldEntities[oldIndex].number; } } else { // (3) the entry isn't in the old snapshot, so the entity will be specified // from the baseline ASSERT_GT(oldEntityNum, newEntityNum); CL_DeltaEntity(msg, newSnapshot, newEntityNum, cl.entityBaselines[newEntityNum]); } } // (4) All remaining entities in the oldSnapshot are unchanged and copied over while (oldIndex < oldEntities.size()) { newEntities.push_back(oldEntities[oldIndex]); oldIndex ++; } ASSERT_EQ(numEntities, newEntities.size()); }