debug多人对战游戏
一直以来,debug多人游戏都是依靠log。
刚好看到这个,有点意思: http://gafferongames.com/networking-for-game-programmers/debugging-multiplayer-games/
- Synchronous Debugging
- Crash Dumps and Info Stacks
- Journal Recording and Playback
Synchronous Debugging的主要思想就是当一个客户端没发数据包的时候,其它客户端只收不发,以和停在断点的那个客户端同步。缺点:如果一个客户端crash了, 其它客户端也都卡住啦
float timeSinceLastHeartbeat[MaxPlayers] = { 0.0f, ... }; while ( true ) { const float deltaTime = GetFrameTime(); bool waiting = true; while ( waiting ) { SendHeartbeatPacket(); while ( true ) { int fromPlayerId = -1; int packetSize = 0; unsigned char packet[1024]; if ( !RecievePacket( fromPlayerId, packet, packetSize ) ) break; if ( IsGamePacket() ) ProcessGamePacket( fromPlayerId, packet, packetSize ); else if ( IsHeartbeatPacket() ) timeSinceLastHeartbeat[fromPlayerId] = 0.0f; } waiting = false; for ( int i = 0; i < MaxPlayers; ++i ) { if ( timeSinceLastHeartbeat[i] > 0.1f && !IsLocalPlayer(i) ) { waiting = true; break; } } } SendGamePacket(); GameUpdate( deltaTime ); for ( int i = 0; i < MaxPlayers; ++i ) timeSinceLastHeartbeat[i] += deltaTime; }
Crash Dump可以做到输出可读的callstack,infostack就是:
const char MaxInfoLength = 256; const char MaxInfoDepth = 64; static int g_infoStackDepth = 0; static char g_infoStack[MaxInfoDepth][MaxInfoLength + 1]; void push_info_stack( const char * string ) { assert( g_infoStackDepth < MaxInfoDepth ); strncpy( &g_infoStack[g_infoStackDepth][0], string, MaxInfoLength ); g_infoStack[g_infoStackDepth][MaxInfoLength] = ''; g_infoStackDepth++; } void pop_info_stack() { assert( g_infoStackDepth > 0 ); g_infoStack[g_infoStackDepth][0] = ''; g_infoStackDepth--; } class InfoPushPopHelper { InfoPushPopHelper( const char * string ) { push_info_stack( string ); } ~InfoPushPopHelper() { pop_info_stack(); } }; #define INFO( format, ... ) \ char buffer[MaxInfoLength+1]; \ snprintf( buffer, MaxInfoLength+1, format, __VA_ARGS__ ); \ InfoPushPopHelper infoPushPop( buffer );
void processWad( const char wadFilename[] ) { INFO( "processing wad: %s", wadFilename ); WadLoader loader( wadFilename ); while ( true ) { WadResource * resource = loader.GetNextResource(); if ( !resource ) break; switch ( resource->GetType() ) { case WadResource::Image: processImage( resource ); break; case WadResource::Mesh: processMesh( resource ); break; // etc... } } } void processImage( WadResource * resource ) { INFO( "processing image: %s", resource->GetFileName() ); // etc... }
Journal Recording and Playback就是更大的工程了:记住一切输入,直接能重放bug重现过程。
Typically, you record things like frame times, player inputs, random number seeds and return codes from APIs – you can even record the set of sent and received packets. Yes, using this technique you can actually
make a recording of a client or server in a 32 player game, then play it back in the debugger, with no networking required. Now that’s debugging!
Take a look at this typical main loop for a multiplayer game:
while ( true ) { SendPackets( socket ); while ( true ) { int packetSize = 0; unsigned char packet[1024]; if ( !socket.RecievePacket( packet, packetSize ) ) break; assert( packetSize > 0 ); Game::ProcessPacket( packet, packetSize ); } float frameTime = Timer::getFrameTime(); Game::Update( frameTime ); }
Now lets add some journaling functions. Note that these functions
operate differently in recording and playback mode. When recording the
values you pass in are written to the journal and flushed to the disk.
In playback the data from the journal is read and replaces the value of
the variable passed in.
void journal_int( int & value ); void journal_bool( bool & value ); void journal_float( float & value ); void journal_bytes( unsigned char * data, int & size ); bool journal_playback(); bool journal_recording();
Here is how to use these functions to journal the game loop:
while ( true ) { SendPackets( socket ); while ( true ) { int packetSize = 0; unsigned char packet[1024]; bool result = false; if ( !journal_playback() ) result = socket.RecievePacket( packet, packetSize ); journal_bool( result ); if ( !result ) break; journal_bytes( packet, packetSize ); assert( packetSize > 0 ); Game::ProcessPacket( packet, packetSize ); } float frameTime = Timer::getFrameTime(); journal_float( frameTime ); Game::Update( frameTime ); }
浙公网安备 33010602011771号