void BlobNetworkBypass_CustomDemoDataCallback( uint8 *pData, size_t iSize ) { // FIXME: need a version number! uint8 *pParse = pData; int iMaxIndex = LittleDWord( *(int *)pParse ); pParse += sizeof( int ); int iBitMax = (iMaxIndex / BITS_PER_INT) + 1; Assert( iSize == (sizeof( int ) + sizeof( float ) + sizeof( int ) + sizeof( int ) + (sizeof( int ) * iBitMax) + iMaxIndex*( sizeof( Vector ) + sizeof( float ) + sizeof( Vector ) )) ); g_pBlobNetworkBypass->fTimeDataUpdated = *(float *)pParse; pParse += sizeof( float ); g_pBlobNetworkBypass->iHighestIndexUsed = LittleDWord( *(int *)pParse ); pParse += sizeof( int ); g_pBlobNetworkBypass->iNumParticlesAllocated = LittleDWord( *(int *)pParse ); pParse += sizeof( int ); int *pIntParser = (int *)&g_pBlobNetworkBypass->bCurrentlyInUse; for( int i = 0; i != iBitMax; ++i ) { //read and convert the bitfield integers *pIntParser = LittleDWord( *(int *)pParse ); pParse += sizeof( int ); ++pIntParser; } //read positions memcpy( g_pBlobNetworkBypass->vParticlePositions, pParse, sizeof( Vector ) * iMaxIndex ); pParse += sizeof( Vector ) * iMaxIndex; //read radii memcpy( g_pBlobNetworkBypass->vParticleRadii, pParse, sizeof( float ) * iMaxIndex ); pParse += sizeof( float ) * iMaxIndex; //read closest surface direction memcpy( g_pBlobNetworkBypass->vParticleClosestSurfDir, pParse, sizeof( Vector ) * iMaxIndex ); pParse += sizeof( Vector ) * iMaxIndex; g_pBlobNetworkBypass->bDataUpdated = true; Assert( pParse == (pData + iSize) ); }
uint32 GenerateSoundEntryHash(char const *pSoundEntry) { // First we need to convert the sound entry to lowercase before we calculate the hash int nSoundEntryLength = strlen(pSoundEntry); char *pSoundEntryLowerCase = (char *)stackalloc(nSoundEntryLength + 1); for (int nIndex = 0; nIndex < nSoundEntryLength; nIndex++) pSoundEntryLowerCase[nIndex] = tolower(pSoundEntry[nIndex]); // Second we need to calculate the hash using the algorithm reconstructed from CS:GO const uint32 nMagicNumber = 0x5bd1e995; uint32 nSoundHash = SOUND_ENTRY_HASH_SEED ^ nSoundEntryLength; unsigned char *pData = (unsigned char *)pSoundEntryLowerCase; while (nSoundEntryLength >= 4) { uint32 nLittleDWord = LittleDWord(*(uint32 *)pData); nLittleDWord *= nMagicNumber; nLittleDWord ^= nLittleDWord >> 24; nLittleDWord *= nMagicNumber; nSoundHash *= nMagicNumber; nSoundHash ^= nLittleDWord; pData += 4; nSoundEntryLength -= 4; } switch (nSoundEntryLength) { case 3: nSoundHash ^= pData[2] << 16; case 2: nSoundHash ^= pData[1] << 8; case 1: nSoundHash ^= pData[0]; nSoundHash *= nMagicNumber; }; nSoundHash ^= nSoundHash >> 13; nSoundHash *= nMagicNumber; nSoundHash ^= nSoundHash >> 15; return nSoundHash; }
//----------------------------------------------------------------------------- // Purpose: // Input : cmd - // dt - // frame - //----------------------------------------------------------------------------- void CToolDemoFile::ReadCmdHeader( unsigned char& cmd, int& tick ) { Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE ); // Read the command int r = g_pFileSystem->Read ( &cmd, sizeof(byte), m_hDemoFile ); if ( r <=0 ) { Warning("Missing end tag in demo file.\n"); cmd = dem_stop; return; } Assert( cmd >= 1 && cmd <= dem_lastcmd ); // Read the timestamp g_pFileSystem->Read ( &tick, sizeof(int), m_hDemoFile ); tick = LittleDWord( tick ); }
//----------------------------------------------------------------------------- // Purpose: Receives incoming network data //----------------------------------------------------------------------------- void CSpaceWarServer::ReceiveNetworkData() { char *pchRecvBuf = NULL; uint32 cubMsgSize; CSteamID steamIDRemote; while ( SteamGameServerNetworking()->IsP2PPacketAvailable( &cubMsgSize ) ) { // free any previous receive buffer if ( pchRecvBuf ) free( pchRecvBuf ); // alloc a new receive buffer of the right size pchRecvBuf = (char *)malloc( cubMsgSize ); // see if there is any data waiting on the socket if ( !SteamGameServerNetworking()->ReadP2PPacket( pchRecvBuf, cubMsgSize, &cubMsgSize, &steamIDRemote ) ) break; if ( cubMsgSize < sizeof( DWORD ) ) { OutputDebugString( "Got garbage on server socket, too short\n" ); continue; } EMessage eMsg = (EMessage)LittleDWord( *(DWORD*)pchRecvBuf ); switch ( eMsg ) { case k_EMsgClientInitiateConnection: { // We always let clients do this without even checking for room on the server since we reserve that for // the authentication phase of the connection which comes next MsgServerSendInfo_t msg; msg.SetSteamIDServer( SteamGameServer()->GetSteamID().ConvertToUint64() ); #ifdef USE_GS_AUTH_API // You can only make use of VAC when using the Steam authentication system msg.SetSecure( SteamGameServer()->BSecure() ); #endif msg.SetServerName( m_sServerName.c_str() ); SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( MsgServerSendInfo_t ), k_EP2PSendReliable ); } break; case k_EMsgClientBeginAuthentication: { if ( cubMsgSize != sizeof( MsgClientBeginAuthentication_t ) ) { OutputDebugString( "Bad connection attempt msg\n" ); continue; } MsgClientBeginAuthentication_t *pMsg = (MsgClientBeginAuthentication_t*)pchRecvBuf; #ifdef USE_GS_AUTH_API OnClientBeginAuthentication( steamIDRemote, (void*)pMsg->GetTokenPtr(), pMsg->GetTokenLen() ); #else OnClientBeginAuthentication( steamIDRemote, 0 ); #endif } break; case k_EMsgClientSendLocalUpdate: { if ( cubMsgSize != sizeof( MsgClientSendLocalUpdate_t ) ) { OutputDebugString( "Bad client update msg\n" ); continue; } // Find the connection that should exist for this users address bool bFound = false; for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote ) { bFound = true; MsgClientSendLocalUpdate_t *pMsg = (MsgClientSendLocalUpdate_t*)pchRecvBuf; OnReceiveClientUpdateData( i, pMsg->AccessUpdateData() ); break; } } if ( !bFound ) OutputDebugString( "Got a client data update, but couldn't find a matching client\n" ); } break; case k_EMsgClientPing: { // send back a response MsgServerPingResponse_t msg; SteamGameServerNetworking()->SendP2PPacket( steamIDRemote, &msg, sizeof( msg ), k_EP2PSendUnreliable ); } break; case k_EMsgClientLeavingServer: { if ( cubMsgSize != sizeof( MsgClientLeavingServer_t ) ) { OutputDebugString( "Bad leaving server msg\n" ); continue; } // Find the connection that should exist for this users address bool bFound = false; for( uint32 i=0; i<MAX_PLAYERS_PER_SERVER; ++i ) { if ( m_rgClientData[i].m_SteamIDUser == steamIDRemote ) { bFound = true; RemovePlayerFromServer( i ); break; } // Also check for pending connections that may match if ( m_rgPendingClientData[i].m_SteamIDUser == steamIDRemote ) { #ifdef USE_GS_AUTH_API // Tell the GS the user is leaving the server SteamGameServer()->SendUserDisconnect( m_rgPendingClientData[i].m_SteamIDUser ); #endif // Clear our data on the user memset( &m_rgPendingClientData[i], 0 , sizeof( ClientConnectionData_t ) ); break; } } if ( !bFound ) OutputDebugString( "Got a client leaving server msg, but couldn't find a matching client\n" ); } default: char rgch[128]; sprintf_safe( rgch, "Invalid message %x\n", eMsg ); rgch[ sizeof(rgch) - 1 ] = 0; OutputDebugString( rgch ); } } if ( pchRecvBuf ) free( pchRecvBuf ); }
void CBlobParticleNetworkBypassAutoGame::PreRender( void ) { if( engine->IsRecordingDemo() && g_pBlobNetworkBypass->bDataUpdated ) { //record the update, TODO: compress the data by omitting the holes int iMaxIndex = MAX(g_pBlobNetworkBypass->iHighestIndexUsed, m_iOldHighestIndexUsed); int iBitMax = (iMaxIndex / BITS_PER_INT) + 1; size_t iDataSize = sizeof( int ) + sizeof( float ) + sizeof( int ) + sizeof( int ) + (sizeof( int ) * iBitMax) + iMaxIndex*( sizeof( Vector ) + sizeof( float ) + sizeof( Vector ) ); uint8 *pData = new uint8 [iDataSize]; uint8 *pWrite = pData; //let the receiver know how much of each array to expect *(int *)pWrite = LittleDWord( iMaxIndex ); pWrite += sizeof( int ); //write the update timestamp *(float *)pWrite = g_pBlobNetworkBypass->fTimeDataUpdated; pWrite += sizeof( float ); //record usage information, also helps us effectively compress the subsequent data by omitting the holes. *(int *)pWrite = LittleDWord( g_pBlobNetworkBypass->iHighestIndexUsed ); pWrite += sizeof( int ); *(int *)pWrite = LittleDWord( g_pBlobNetworkBypass->iNumParticlesAllocated ); pWrite += sizeof( int ); int *pIntParser = (int *)&g_pBlobNetworkBypass->bCurrentlyInUse; for( int i = 0; i != iBitMax; ++i ) { //convert and write the bitfield integers *(int *)pWrite = LittleDWord( *pIntParser ); pWrite += sizeof( int ); ++pIntParser; } //write positions memcpy( pWrite, g_pBlobNetworkBypass->vParticlePositions, sizeof( Vector ) * iMaxIndex ); pWrite += sizeof( Vector ) * iMaxIndex; //write radii memcpy( pWrite, g_pBlobNetworkBypass->vParticleRadii, sizeof( float ) * iMaxIndex ); pWrite += sizeof( float ) * iMaxIndex; //write closest surface direction memcpy( pWrite, g_pBlobNetworkBypass->vParticleClosestSurfDir, sizeof( Vector ) * iMaxIndex ); pWrite += sizeof( Vector ) * iMaxIndex; engine->RecordDemoCustomData( BlobNetworkBypass_CustomDemoDataCallback, pData, iDataSize ); Assert( pWrite == (pData + iDataSize) ); delete []pData; } //invalidate interpolation on freed indices, do a quick update for brand new indices { //operate on smaller chunks based on the assumption that LARGE portions of the end of the bitvecs are empty CBitVec<BITS_PER_INT> *pCurrentlyInUse = (CBitVec<BITS_PER_INT> *)&g_pBlobNetworkBypass->bCurrentlyInUse; CBitVec<BITS_PER_INT> *pOldInUse = (CBitVec<BITS_PER_INT> *)&m_bOldInUse; int iStop = (MAX(g_pBlobNetworkBypass->iHighestIndexUsed, m_iOldHighestIndexUsed) / BITS_PER_INT) + 1; int iBaseIndex = 0; //float fNewIndicesUpdateTime = g_pBlobNetworkBypass->bPositionsUpdated ? g_pBlobNetworkBypass->fTimeDataUpdated : gpGlobals->curtime; for( int i = 0; i != iStop; ++i ) { CBitVec<BITS_PER_INT> bInUseXOR; pCurrentlyInUse->Xor( *pOldInUse, &bInUseXOR ); //find bits that changed int j = 0; while( (j = bInUseXOR.FindNextSetBit( j )) != -1 ) { int iChangedUsageIndex = iBaseIndex + j; if( pOldInUse->IsBitSet( iChangedUsageIndex ) ) { //index no longer used g_BlobParticleInterpolation.vInterpolatedPositions[iChangedUsageIndex] = vec3_origin; s_PositionInterpolators[iChangedUsageIndex].ClearHistory(); g_BlobParticleInterpolation.vInterpolatedRadii[iChangedUsageIndex] = 1.0f; s_RadiusInterpolators[iChangedUsageIndex].ClearHistory(); g_BlobParticleInterpolation.vInterpolatedClosestSurfDir[iChangedUsageIndex] = vec3_origin; s_ClosestSurfDirInterpolators[iChangedUsageIndex].ClearHistory(); } else { //index just started being used. Assume we got an out of band update to the position g_BlobParticleInterpolation.vInterpolatedPositions[iChangedUsageIndex] = g_pBlobNetworkBypass->vParticlePositions[iChangedUsageIndex]; s_PositionInterpolators[iChangedUsageIndex].Reset( gpGlobals->curtime ); g_BlobParticleInterpolation.vInterpolatedRadii[iChangedUsageIndex] = g_pBlobNetworkBypass->vParticleRadii[iChangedUsageIndex]; s_RadiusInterpolators[iChangedUsageIndex].Reset( gpGlobals->curtime ); g_BlobParticleInterpolation.vInterpolatedClosestSurfDir[iChangedUsageIndex] = g_pBlobNetworkBypass->vParticleClosestSurfDir[iChangedUsageIndex]; s_ClosestSurfDirInterpolators[iChangedUsageIndex].Reset( gpGlobals->curtime ); //s_PositionInterpolators[iChangedUsageIndex].NoteChanged( gpGlobals->curtime, fNewIndicesUpdateTime, true ); } ++j; if( j == BITS_PER_INT ) break; } iBaseIndex += BITS_PER_INT; ++pCurrentlyInUse; ++pOldInUse; } memcpy( &m_bOldInUse, &g_pBlobNetworkBypass->bCurrentlyInUse, sizeof( m_bOldInUse ) ); m_iOldHighestIndexUsed = g_pBlobNetworkBypass->iHighestIndexUsed; } if( g_pBlobNetworkBypass->iHighestIndexUsed == 0 ) return; static ConVarRef cl_interpREF( "cl_interp" ); //now do the interpolation of positions still in use { float fInterpTime = gpGlobals->curtime - cl_interpREF.GetFloat(); CBitVec<BITS_PER_INT> *pIntParser = (CBitVec<BITS_PER_INT> *)&g_pBlobNetworkBypass->bCurrentlyInUse; int iStop = (g_pBlobNetworkBypass->iHighestIndexUsed / BITS_PER_INT) + 1; int iBaseIndex = 0; for( int i = 0; i != iStop; ++i ) { int j = 0; while( (j = pIntParser->FindNextSetBit( j )) != -1 ) { int iUpdateIndex = iBaseIndex + j; if( g_pBlobNetworkBypass->bDataUpdated ) { g_BlobParticleInterpolation.vInterpolatedPositions[iUpdateIndex] = g_pBlobNetworkBypass->vParticlePositions[iUpdateIndex]; s_PositionInterpolators[iUpdateIndex].NoteChanged( gpGlobals->curtime, g_pBlobNetworkBypass->fTimeDataUpdated, true ); g_BlobParticleInterpolation.vInterpolatedRadii[iUpdateIndex] = g_pBlobNetworkBypass->vParticleRadii[iUpdateIndex]; s_RadiusInterpolators[iUpdateIndex].NoteChanged( gpGlobals->curtime, g_pBlobNetworkBypass->fTimeDataUpdated, true ); g_BlobParticleInterpolation.vInterpolatedClosestSurfDir[iUpdateIndex] = g_pBlobNetworkBypass->vParticleClosestSurfDir[iUpdateIndex]; s_ClosestSurfDirInterpolators[iUpdateIndex].NoteChanged( gpGlobals->curtime, g_pBlobNetworkBypass->fTimeDataUpdated, true ); //s_PositionInterpolators[iUpdateIndex].AddToHead( gpGlobals->curtime, &g_pBlobNetworkBypass->vParticlePositions[iUpdateIndex], false ); } s_PositionInterpolators[iUpdateIndex].Interpolate( fInterpTime ); s_RadiusInterpolators[iUpdateIndex].Interpolate( fInterpTime ); s_ClosestSurfDirInterpolators[iUpdateIndex].Interpolate( fInterpTime ); ++j; if( j == BITS_PER_INT ) break; } iBaseIndex += BITS_PER_INT; ++pIntParser; } g_pBlobNetworkBypass->bDataUpdated = false; } }