server building; demo server replays...
authorhgn <hgodden00@gmail.com>
Wed, 26 Mar 2025 04:03:10 +0000 (04:03 +0000)
committerhgn <hgodden00@gmail.com>
Wed, 26 Mar 2025 04:03:10 +0000 (04:03 +0000)
build.c
content_skaterift/maps/mp_line1/main.mdl
src/gameserver.c
src/gameserver.h
src/gameserver_db.h
src/gameserver_replay.c [new file with mode: 0644]
src/gameserver_replay.h [new file with mode: 0644]
src/network.c
src/network_common.h
src/network_msg.h

diff --git a/build.c b/build.c
index c35905b9a7d4288216391e4e3deb4f91003d8d3b..95766b48423948eea9204d2f3026af4dbecfb9b1 100644 (file)
--- a/build.c
+++ b/build.c
@@ -274,11 +274,12 @@ void compile_server( struct vg_project *proj, struct vg_compiler_env *env )
    struct compile_result sqlite = build_sqlite_for_env( env );
 
    vg_str sources = {0};
-   vg_strcat( &sources, "gameserver.c vg/vg_tool.c vg/vg_steam.c \\\n " );
+   vg_strcat( &sources, "src/gameserver.c vg/vg_tool.c vg/vg_steam.c \\\n " );
    vg_strcat( &sources, sqlite.path.buffer );
 
    struct vg_compiler_conf conf = {0};
    vg_strcat( &conf.include, "-Isrc -I./dep " );
+   vg_strcat( &conf.include, "-I. -I./vg -I./vg/dep " );
    vg_strcat( &conf.library, "-L./vg/dep/steam " );
    vg_strcat( &conf.link, "-ldl -lpthread -lm "
                           "-lsdkencryptedappticket -lsteam_api " );
index e8d212ba3d739da9b42a1b4f89aef3fa650433db..019bc06db9abad6afe528cc542015158655edd90 100644 (file)
Binary files a/content_skaterift/maps/mp_line1/main.mdl and b/content_skaterift/maps/mp_line1/main.mdl differ
index e315fd0e69b7c096cbadd1d8b6d6abca6df12345..9f4614f8363cc87f6ecdee18e5d50e45b78ee1c6 100644 (file)
@@ -16,6 +16,7 @@ volatile sig_atomic_t sig_stop;
 #include "gameserver_db.h"
 #include "vg/vg_m.h"
 #include "vg/vg_msg.h"
+#include "gameserver_replay.h"
 
 static u64 const k_steamid_max = 0xffffffffffffffff;
 
@@ -54,11 +55,9 @@ static void gameserver_send_to_client( i32 client_id,
 /*
  * Send message to all clients if they are authenticated
  */
-static void gameserver_send_to_all( int ignore, 
-                                    const void *pData, u32 cbData, 
-                                    int nSendFlags )
+static void gameserver_send_to_all( int ignore, const void *pData, u32 cbData, int nSendFlags )
 {
-   for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+   for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
       struct gameserver_client *client = &gameserver.clients[i];
 
@@ -113,7 +112,7 @@ static void gameserver_player_join( int index )
    netmsg_region *region = alloca( sizeof(netmsg_region) + NETWORK_REGION_MAX );
    region->inetmsg_id = k_inetmsg_region;
 
-   for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+   for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
       struct gameserver_client *client = &gameserver.clients[i];
 
@@ -205,30 +204,28 @@ static void remove_client( int index ){
  */
 static void handle_new_connection( HSteamNetConnection conn )
 {
-   SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
-         hSteamNetworkingSockets, conn, -1 );
+   SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, -1 );
 
    int index = -1;
 
-   for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
-      if( !gameserver.clients[i].active ){
+   for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
+   {
+      if( !gameserver.clients[i].active )
+      {
          index = i;
          break;
       }
    }
 
-   if( index == -1 ){
+   if( index == -1 )
+   {
       vg_error( "Server full\n" );
-      SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-            hSteamNetworkingSockets, conn, 
-            4500,
-            NULL, 1 );
+      SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, conn, 4500, NULL, 1 );
       return;
    }
 
    struct gameserver_client *client = &gameserver.clients[index];
-   EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection(
-            hSteamNetworkingSockets, conn );
+   EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection( hSteamNetworkingSockets, conn );
 
    if( accept_status == k_EResultOK )
    {
@@ -237,11 +234,8 @@ static void handle_new_connection( HSteamNetConnection conn )
       client->active = 1;
       client->connection = conn;
 
-      SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
-            hSteamNetworkingSockets, conn, gameserver.client_group );
-      
-      SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
-            hSteamNetworkingSockets, conn, index );
+      SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
+      SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
 
       if( gameserver.loopback_test )
       {
@@ -254,17 +248,15 @@ static void handle_new_connection( HSteamNetConnection conn )
    else
    {
       vg_warn( "Error accepting connection (id: %u)\n", conn );
-      SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-            hSteamNetworkingSockets, conn, 
-            k_ESteamNetConnectionEnd_Misc_InternalError,
-            NULL, 1 );
+      SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, conn, 
+                                                        k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
    }
 }
 
-static void on_auth_status( CallbackMsg_t *msg ){
+static void on_auth_status( CallbackMsg_t *msg )
+{
    SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam;
-   vg_info( "  Authentication availibility: %s\n", 
-         string_ESteamNetworkingAvailability(info->m_eAvail) );
+   vg_info( "  Authentication availibility: %s\n", string_ESteamNetworkingAvailability(info->m_eAvail) );
    vg_info( "  %s\n", info->m_debugMsg );
 }
 
@@ -284,8 +276,7 @@ static i32 gameserver_conid( HSteamNetConnection hconn )
          return -1;
    }
    else
-      id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
-               hSteamNetworkingSockets, hconn );
+      id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData( hSteamNetworkingSockets, hconn );
 
    if( (id < 0) || (id >= NETWORK_MAX_PLAYERS) )
       return -1;
@@ -310,14 +301,10 @@ static void on_connect_status( CallbackMsg_t *msg )
       handle_new_connection( info->m_hConn );
    }
 
-   if( (info->m_info.m_eState == 
-            k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
-       (info->m_info.m_eState == 
-        k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ||
-       (info->m_info.m_eState == 
-        k_ESteamNetworkingConnectionState_Dead) ||
-       (info->m_info.m_eState ==
-        k_ESteamNetworkingConnectionState_None) )
+   if( (info->m_info.m_eState == k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
+       (info->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ||
+       (info->m_info.m_eState == k_ESteamNetworkingConnectionState_Dead) ||
+       (info->m_info.m_eState == k_ESteamNetworkingConnectionState_None) )
    {
       vg_info( "End reason: %d\n", info->m_info.m_eEndReason );
 
@@ -335,8 +322,7 @@ static void on_connect_status( CallbackMsg_t *msg )
       }
       else 
       {
-         SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-               hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
+         SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
       }
    }
 }
@@ -349,10 +335,8 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
    if( client_id == -1 ) 
    {
       vg_warn( "Recieved version from unkown connection (%u)\n", msg->m_conn );
-      SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-            hSteamNetworkingSockets, msg->m_conn,
-            k_ESteamNetConnectionEnd_Misc_InternalError,
-            NULL, 1 );
+      SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, msg->m_conn,
+                                                        k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
       return;
    }
 
@@ -360,8 +344,7 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
 
    if( client->version )
    {
-      vg_warn( "Already have version for this client (%d conn: %u)", 
-               client_id, msg->m_conn );
+      vg_warn( "Already have version for this client (%d conn: %u)", client_id, msg->m_conn );
       return;
    }
 
@@ -403,23 +386,24 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
    }
 
    int client_id = gameserver_conid( msg->m_conn );
-   if( client_id == -1 ) {
-      vg_warn( "Recieved auth ticket from unkown connection (%u)\n", 
-               msg->m_conn );
-      SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-            hSteamNetworkingSockets, msg->m_conn,
-            k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
+   if( client_id == -1 ) 
+   {
+      vg_warn( "Recieved auth ticket from unkown connection (%u)\n", msg->m_conn );
+      SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, msg->m_conn,
+                                                        k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
       return;
    }
 
    struct gameserver_client *client = &gameserver.clients[ client_id ];
-   if( client->steamid ){
-      vg_warn( "Already authorized this user but another app ticket was sent"
-               " again (%d conn: %u)\n", client_id, msg->m_conn );
+   if( client->steamid )
+   {
+      vg_warn( "Already authorized this user but another app ticket was sent again (%d conn: %u)\n", 
+               client_id, msg->m_conn );
       return;
    }
 
-   if( client->version == 0 ){
+   if( client->version == 0 )
+   {
       vg_error( "Client has not sent their version yet (%u)\n", msg->m_conn );
       remove_client( client_id );
       return;
@@ -427,7 +411,8 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
 
    vg_low( "Attempting to verify user\n" );
 
-   if( msg->m_cbSize < sizeof(netmsg_auth) ){
+   if( msg->m_cbSize < sizeof(netmsg_auth) )
+   {
       vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn );
       remove_client( client_id );
       return;
@@ -435,10 +420,9 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
 
    netmsg_auth *auth = msg->m_pData;
 
-   if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length ||
-       auth->ticket_length > 1024 ){
-      vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
-                  auth->ticket_length );
+   if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length || auth->ticket_length > 1024 )
+   {
+      vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n", auth->ticket_length );
       remove_client( client_id );
       return;
    }
@@ -446,25 +430,26 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
    u8 decrypted[1024];
    u32 ticket_len = 1024;
 
-   int success = SteamEncryptedAppTicket_BDecryptTicket(
-         auth->ticket, auth->ticket_length, decrypted,
-         &ticket_len, gameserver.app_symmetric_key,
-         k_nSteamEncryptedAppTicketSymmetricKeyLen );
+   int success = SteamEncryptedAppTicket_BDecryptTicket( auth->ticket, auth->ticket_length, decrypted,
+                                                         &ticket_len, gameserver.app_symmetric_key,
+                                                         k_nSteamEncryptedAppTicketSymmetricKeyLen );
 
-   if( !success ){
+   if( !success )
+   {
       vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn );
       vg_error( "  ticket length: %u\n", auth->ticket_length );
       remove_client( client_id );
       return;
    }
 
-   if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )){
+   if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len ))
+   {
       RTime32 ctime = time(NULL),
-              tickettime = SteamEncryptedAppTicket_GetTicketIssueTime(
-                    decrypted, ticket_len ),
+              tickettime = SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len ),
               expiretime = tickettime + 24*3*60*60;
       
-      if( ctime > expiretime ){
+      if( ctime > expiretime )
+      {
          vg_error( "Ticket expired (client %u)\n", msg->m_conn );
          remove_client( client_id );
          return;
@@ -473,9 +458,7 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
 
    CSteamID steamid;
    SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid );
-   vg_success( "User is authenticated! steamid %lu (%u)\n", 
-         steamid.m_unAll64Bits, msg->m_conn );
-
+   vg_success( "User is authenticated! steamid %lu (%u)\n", steamid.m_unAll64Bits, msg->m_conn );
    client->steamid = steamid.m_unAll64Bits;
    gameserver_player_join( client_id );
 }
@@ -485,22 +468,25 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
  * -----------------------------------------------------------------------------
  */
 
-static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size ){
-   if( msg->m_cbSize < size ) {
+static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size )
+{
+   if( msg->m_cbSize < size ) 
+   {
       vg_error( "Invalid packet size (must be at least %u)\n", size );
       return 0;
    }
-   else{
+   else
       return 1;
-   }
 }
 
-struct db_set_username_thread_data {
+struct db_set_username_thread_data 
+{
    u64 steamid;
    char username[ NETWORK_USERNAME_MAX ];
 };
 
-static void gameserver_update_db_username( db_request *db_req ){
+static void gameserver_update_db_username( db_request *db_req )
+{
    struct db_set_username_thread_data *inf = (void *)db_req->data;
 
    if( inf->steamid == k_steamid_max )
@@ -513,8 +499,8 @@ static void gameserver_update_db_username( db_request *db_req ){
    db_updateuser( inf->steamid, inf->username, admin );
 }
 
-static int gameserver_item_eq( struct gameserver_item *ia, 
-                               struct gameserver_item *ib ){
+static int gameserver_item_eq( struct gameserver_item *ia, struct gameserver_item *ib )
+{
    if( ia->hash == ib->hash )
       if( !strcmp(ia->uid,ib->uid) )
          return 1;
@@ -526,8 +512,8 @@ static int gameserver_item_eq( struct gameserver_item *ia,
  * Match addons between two player IDs. if clear is set, then the flags between
  * those two IDs will all be set to 0.
  */
-static void gameserver_update_knowledge_table( int client0, int client1, 
-                                               int clear ){
+static void gameserver_update_knowledge_table( int client0, int client1, int clear )
+{
    u32 idx = network_pair_index( client0, client1 );
 
    struct gameserver_client *c0 = &gameserver.clients[client0],
@@ -553,8 +539,10 @@ static void gameserver_update_knowledge_table( int client0, int client1,
  * table of other players. if clear is set, all references to client will be set
  * to 0.
  */
-static void gameserver_update_all_knowledge( int client, int clear ){
-   for( int i=0; i<NETWORK_MAX_PLAYERS; i ++ ){
+static void gameserver_update_all_knowledge( int client, int clear )
+{
+   for( int i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+   {
       if( i == client )
          continue;
 
@@ -565,9 +553,8 @@ static void gameserver_update_all_knowledge( int client, int clear ){
    }
 }
 
-static void gameserver_propogate_player_frame( int client_id, 
-                                               netmsg_playerframe *frame, 
-                                               u32 size ){
+static void gameserver_propogate_player_frame( int client_id, netmsg_playerframe *frame, u32 size )
+{
    u32 basic_size = sizeof(netmsg_playerframe) + ((24*3)/8);
    netmsg_playerframe *full = alloca(size),
                       *basic= alloca(basic_size);
@@ -583,7 +570,7 @@ static void gameserver_propogate_player_frame( int client_id,
    struct gameserver_client *c0 = &gameserver.clients[client_id];
    c0->instance = frame->flags & NETMSG_PLAYERFRAME_INSTANCE_ID;
 
-   for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+   for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
       if( i == client_id )
          continue;
@@ -602,15 +589,9 @@ static void gameserver_propogate_player_frame( int client_id,
       }
 
       if( send_full )
-      {
-         gameserver_send_to_client( i, full, size, 
-                                    k_nSteamNetworkingSend_Unreliable );
-      }
+         gameserver_send_to_client( i, full, size, k_nSteamNetworkingSend_Unreliable );
       else 
-      {
-         gameserver_send_to_client( i, basic, basic_size, 
-                                    k_nSteamNetworkingSend_Unreliable );
-      }
+         gameserver_send_to_client( i, basic, basic_size, k_nSteamNetworkingSend_Unreliable );
    }
 }
 
@@ -630,40 +611,34 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       
       netmsg_playerusername *src = msg->m_pData;
 
-      u32 name_len = network_msgstring( src->name, msg->m_cbSize, 
-                                        sizeof(netmsg_playerusername),
-                                        client->username, 
+      u32 name_len = network_msgstring( src->name, msg->m_cbSize, sizeof(netmsg_playerusername), client->username, 
                                         NETWORK_USERNAME_MAX );
 
       /* update other users about this change */
-      netmsg_playerusername *prop = alloca(sizeof(netmsg_playerusername)+
-                                             NETWORK_USERNAME_MAX );
+      netmsg_playerusername *prop = alloca(sizeof(netmsg_playerusername)+NETWORK_USERNAME_MAX );
                                            
       prop->inetmsg_id = k_inetmsg_playerusername;
       prop->index = client_id;
-      u32 chs = vg_strncpy( client->username, prop->name, NETWORK_USERNAME_MAX,
-                            k_strncpy_always_add_null );
+      u32 chs = vg_strncpy( client->username, prop->name, NETWORK_USERNAME_MAX, k_strncpy_always_add_null );
 
       vg_info( "client #%d changed name to: %s\n", client_id, prop->name );
 
       u32 propsize = sizeof(netmsg_playerusername) + chs + 1;
-      gameserver_send_to_all( client_id, prop, propsize,
-                              k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_all( client_id, prop, propsize, k_nSteamNetworkingSend_Reliable );
 
       /* update database about this */
-      db_request *call = db_alloc_request( 
-                           sizeof(struct db_set_username_thread_data) );
+      db_request *call = db_alloc_request( sizeof(struct db_set_username_thread_data) );
+      /* FIXME: Call can be NULL, crash possible. */
       struct db_set_username_thread_data *inf = (void *)call->data;
       inf->steamid = client->steamid;
-      vg_strncpy( client->username, inf->username, 
-                  sizeof(inf->username), k_strncpy_always_add_null );
+      vg_strncpy( client->username, inf->username, sizeof(inf->username), k_strncpy_always_add_null );
       call->handler = gameserver_update_db_username;
       db_send_request( call );
    }
    else if( tmp->inetmsg_id == k_inetmsg_playerframe )
    {
-      gameserver_propogate_player_frame( client_id, 
-                                         msg->m_pData, msg->m_cbSize );
+      gameserver_propogate_player_frame( client_id, msg->m_pData, msg->m_cbSize );
+      _gs_replay_save_frame( client_id, msg->m_pData, msg->m_cbSize );
    }
    else if( tmp->inetmsg_id == k_inetmsg_playeritem )
    {
@@ -672,15 +647,13 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       /* record */
       if( item->type_index >= k_netmsg_playeritem_max )
       {
-         vg_warn( "Client #%d invalid equip type %u\n", 
-                  client_id, (u32)item->type_index );
+         vg_warn( "Client #%d invalid equip type %u\n", client_id, (u32)item->type_index );
          return;
       }
       
       char *dest = client->items[ item->type_index ].uid;
 
-      network_msgstring( item->uid, msg->m_cbSize, sizeof(netmsg_playeritem),
-                         dest, ADDON_UID_MAX );
+      network_msgstring( item->uid, msg->m_cbSize, sizeof(netmsg_playeritem), dest, ADDON_UID_MAX );
 
       vg_info( "Client #%d equiped: [%s] %s\n", 
                client_id, 
@@ -696,8 +669,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       netmsg_playeritem *prop = alloca(msg->m_cbSize);
       memcpy( prop, msg->m_pData, msg->m_cbSize );
       prop->client = client_id;
-      gameserver_send_to_all( client_id, prop, msg->m_cbSize, 
-                              k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_all( client_id, prop, msg->m_cbSize, k_nSteamNetworkingSend_Reliable );
    }
    else if( tmp->inetmsg_id == k_inetmsg_chat )
    {
@@ -706,12 +678,20 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       prop->inetmsg_id = k_inetmsg_chat;
       prop->client = client_id;
 
-      u32 l = network_msgstring( chat->msg, msg->m_cbSize, sizeof(netmsg_chat),
-                                 prop->msg, NETWORK_MAX_CHAT );
+      u32 l = network_msgstring( chat->msg, msg->m_cbSize, sizeof(netmsg_chat), prop->msg, NETWORK_MAX_CHAT );
       vg_info( "[%d]: %s\n", client_id, prop->msg );
 
-      gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, 
-                              k_nSteamNetworkingSend_Reliable );
+      /* WARNING FIXME WARNING FIXME ------------------------------------------- */
+      if( !strcmp( prop->msg, "save" ) )
+      {
+         _gs_write_replay_to_disk( client_id, 1.0*60.0, "/tmp/server-replay.replay" );
+      }
+      else if( !strcmp( prop->msg, "play" ) )
+      {
+         _gs_test_replay( "/tmp/server-replay.replay" );
+      }
+
+      gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, k_nSteamNetworkingSend_Reliable );
    }
    else if( tmp->inetmsg_id == k_inetmsg_region )
    {
@@ -727,26 +707,25 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
             client->region, NETWORK_REGION_MAX );
       client->region_flags = region->flags;
 
-      l = vg_strncpy( client->region, prop->loc, NETWORK_REGION_MAX, 
-                      k_strncpy_always_add_null );
+      l = vg_strncpy( client->region, prop->loc, NETWORK_REGION_MAX, k_strncpy_always_add_null );
 
-      gameserver_send_to_all( client_id, prop, sizeof(netmsg_region)+l+1, 
-                              k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_all( client_id, prop, sizeof(netmsg_region)+l+1, k_nSteamNetworkingSend_Reliable );
       vg_info( "client %d moved to region: %s\n", client_id, client->region );
    }
    else 
    {
-      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
-               tmp->inetmsg_id );
+      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
    }
 }
 
 static void gameserver_request_respond( enum request_status status,
                                         netmsg_request *res, vg_msg *body,
-                                        SteamNetworkingMessage_t *msg ){
+                                        SteamNetworkingMessage_t *msg )
+{
    int client_id = gameserver_conid( msg->m_conn );
    u32 len = 0;
-   if( body ){
+   if( body )
+   {
       len = body->cur.co;
       vg_low( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
       vg_msg_print( body, len );
@@ -859,9 +838,9 @@ static void gameserver_process_user_request( db_request *db_req )
 
    const char *endpoint = vg_msg_getkvstr( &data, "endpoint" );
 
-   if( !endpoint ){
-      gameserver_request_respond( k_request_status_invalid_endpoint,
-                                  res, NULL, msg );
+   if( !endpoint )
+   {
+      gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
       return;
    }
 
@@ -883,18 +862,19 @@ static void gameserver_process_user_request( db_request *db_req )
       else 
          gameserver_cat_table( &body, mod, route, week, "rows" );
 
-      if( body.error != k_vg_msg_error_OK ){
-         gameserver_request_respond( k_request_status_out_of_memory,
-                                     res, NULL, msg );
+      if( body.error != k_vg_msg_error_OK )
+      {
+         gameserver_request_respond( k_request_status_out_of_memory, res, NULL, msg );
          return;
       }
 
       gameserver_request_respond( k_request_status_ok, res, &body, msg );
    }
-   else if( !strcmp( endpoint, "setlap" ) ){
-      if( client->steamid == k_steamid_max ){
-         gameserver_request_respond( k_request_status_unauthorized,
-                                     res, NULL, msg );
+   else if( !strcmp( endpoint, "setlap" ) )
+   {
+      if( client->steamid == k_steamid_max )
+      {
+         gameserver_request_respond( k_request_status_unauthorized, res, NULL, msg );
          return;
       }
 
@@ -907,17 +887,17 @@ static void gameserver_process_user_request( db_request *db_req )
       u32 week = gameserver_get_current_week();
 
       if( !db_get_highscore_table_name( mod, route, 0, alltime_table ) ||
-          !db_get_highscore_table_name( mod, route, week, weekly_table ) ){
-         gameserver_request_respond( k_request_status_out_of_memory,
-                                     res, NULL, msg );
+          !db_get_highscore_table_name( mod, route, week, weekly_table ) )
+      {
+         gameserver_request_respond( k_request_status_out_of_memory, res, NULL, msg );
          return;
       }
 
       i32 centiseconds;
       vg_msg_getkvintg( &data, "time", k_vg_msg_i32, &centiseconds, NULL );
-      if( centiseconds < 5*100 ){
-         gameserver_request_respond( k_request_status_client_error,
-                                     res, NULL, msg );
+      if( centiseconds < 5*100 )
+      {
+         gameserver_request_respond( k_request_status_client_error, res, NULL, msg );
          return;
       }
 
@@ -925,9 +905,9 @@ static void gameserver_process_user_request( db_request *db_req )
       db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
       gameserver_request_respond( k_request_status_ok, res, NULL, msg );
    }
-   else{
-      gameserver_request_respond( k_request_status_invalid_endpoint,
-                                  res, NULL, msg );
+   else
+   {
+      gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
    }
 }
 
@@ -956,8 +936,8 @@ static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
          return;
       }
 
-      db_request *call = db_alloc_request( 
-                           sizeof(struct user_request_thread_data) );
+      db_request *call = db_alloc_request( sizeof(struct user_request_thread_data) );
+      /* FIXME: Call can be NULL, crash possible. */
       struct user_request_thread_data *inf = (void *)call->data;
       inf->msg = msg;
       call->handler = gameserver_process_user_request;
@@ -965,17 +945,16 @@ static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
    }
    else 
    {
-      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
-               tmp->inetmsg_id );
+      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
       release_message( msg );
    }
 }
 
 static void process_network_message( SteamNetworkingMessage_t *msg )
 {
-   if( msg->m_cbSize < sizeof(netmsg_blank) ){
-      vg_warn( "Discarding message (too small: %d)\n", 
-            msg->m_cbSize );
+   if( msg->m_cbSize < sizeof(netmsg_blank) )
+   {
+      vg_warn( "Discarding message (too small: %d)\n",  msg->m_cbSize );
       return;
    }
 
@@ -1013,7 +992,7 @@ static void poll_connections(void)
    {
       len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
             hSteamNetworkingSockets,
-            gameserver.client_group, messages, vg_list_size(messages) );
+            gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
 
       if( len <= 0 )
          return;
@@ -1025,11 +1004,15 @@ static void poll_connections(void)
 
          if( gameserver.loopback_test )
          {
-            HSteamNetConnection conid = msg->m_conn;
-            msg->m_conn = 0;
-            msg->m_nUserData ++;
-            process_network_message( msg );
-            msg->m_conn = conid;
+            netmsg_blank *tmp = msg->m_pData;
+            if( tmp->inetmsg_id != k_inetmsg_playerframe )
+            {
+               HSteamNetConnection conid = msg->m_conn;
+               msg->m_conn = 0;
+               msg->m_nUserData ++;
+               process_network_message( msg );
+               msg->m_conn = conid;
+            }
          }
 
          process_network_message( msg );
@@ -1037,11 +1020,15 @@ static void poll_connections(void)
    }
 }
 
-static u64 seconds_to_server_ticks( double s ){
+u64 seconds_to_server_ticks( f64 s )
+{
    return s / 0.01;
 }
 
-int main( int argc, char *argv[] ){
+int main( int argc, char *argv[] )
+{
+   vg_log_init();
+
    signal( SIGINT, inthandler );
    signal( SIGQUIT, inthandler );
    signal( SIGPIPE, SIG_IGN );
@@ -1049,11 +1036,14 @@ int main( int argc, char *argv[] ){
    char *arg;
    while( vg_argp( argc, argv ) )
    {
-      if( vg_long_opt( "noauth" ) )
+      if( vg_long_opt( "noauth", "Disable server authentication"  ) )
          gameserver.auth_mode = eServerModeNoAuthentication;
 
-      if( vg_long_opt( "loopback" ) )
+      if( vg_long_opt( "loopback", "Development flag" ) )
          gameserver.loopback_test = 1;
+
+      if( vg_long_opt( "replay-info", "Print replay info periodically" ) )
+         _gs_replay.print_info = 1;
    }
    
    vg_set_mem_quota( 80*1024*1024 );
@@ -1063,18 +1053,16 @@ int main( int argc, char *argv[] ){
    /* steamworks init 
     * --------------------------------------------------------------- */
    steamworks_ensure_txt( "2103940" );
-   if( gameserver.auth_mode == eServerModeAuthentication ){
-      if( !vg_load_steam_symetric_key( "application_key", 
-                                       gameserver.app_symmetric_key )){
+   if( gameserver.auth_mode == eServerModeAuthentication )
+   {
+      if( !vg_load_steam_symetric_key( "application_key", gameserver.app_symmetric_key ))
          return 0;
-      }
    }
-   else{
+   else
       vg_warn( "Running without user authentication.\n" );
-   }
 
-   if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1, 
-                              gameserver.auth_mode, "1.0.0.0" ) ){
+   if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1, gameserver.auth_mode, "1.0.0.0" ) )
+   {
       vg_error( "SteamGameServer_Init failed\n" );
       return 0;
    }
@@ -1084,12 +1072,10 @@ int main( int argc, char *argv[] ){
 
    SteamAPI_ManualDispatch_Init();
    HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
-   hSteamNetworkingSockets = 
-      SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
+   hSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
 
    steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
-   steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
-                             on_connect_status );
+   steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_connect_status );
 
    vg_success( "Steamworks API running\n" );
    steamworks_event_loop( hsteampipe );
@@ -1102,30 +1088,25 @@ int main( int argc, char *argv[] ){
    SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
    localAddr.m_port = NETWORK_PORT;
 
-   listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
-                  hSteamNetworkingSockets, &localAddr, 0, NULL );
-   gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
-         hSteamNetworkingSockets );
-
-   u64 server_ticks = 8000,
-       last_record_save = 8000,
-       last_scoreboard_gen = 0;
+   listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( hSteamNetworkingSockets, &localAddr, 0, NULL );
+   gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup( hSteamNetworkingSockets );
+   gameserver.ticks = seconds_to_server_ticks( 30.0 * 60.0 );
 
-   while( !sig_stop ){
+   while( !sig_stop )
+   {
       steamworks_event_loop( hsteampipe );
       poll_connections();
+      _gs_replay_server_tick();
 
       usleep(10000);
-      server_ticks ++;
+      gameserver.ticks ++;
 
       if( db_killed() )
          break;
    }
    
-   SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
-         gameserver.client_group );
-   SteamAPI_ISteamNetworkingSockets_CloseListenSocket( 
-         hSteamNetworkingSockets, listener );
+   SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
+   SteamAPI_ISteamNetworkingSockets_CloseListenSocket( hSteamNetworkingSockets, listener );
    
    vg_info( "Shutting down\n..." );
    SteamGameServer_Shutdown();
@@ -1134,3 +1115,5 @@ int main( int argc, char *argv[] ){
 
    return 0;
 }
+
+#include "gameserver_replay.c"
index a03b1a0ac9c300609c7a6c7dc505fbb62d02c42b..0b2620f895899b0419845ba53206fcd3a8cac456 100644 (file)
@@ -44,9 +44,12 @@ struct {
    u8 app_symmetric_key[ k_nSteamEncryptedAppTicketSymmetricKeyLen ];
 
    bool loopback_test;
+   u64 ticks;
 }
 static gameserver = {
    .auth_mode = eServerModeAuthentication
 };
 
 static ISteamNetworkingSockets *hSteamNetworkingSockets = NULL;
+
+u64 seconds_to_server_ticks( f64 s );
index fe39931af843ae30b3715b6192367193dedc489a..f6abfd371d1b66974a12d62d65a6b0354100eb3c 100644 (file)
@@ -34,9 +34,10 @@ static database;
 /*
  * Log the error code (or carry on if its OK).
  */
-static void log_sqlite3( int code ){
+static void log_sqlite3( int code )
+{
    if( code == SQLITE_OK ) return;
-   vg_print_backtrace();
+   //vg_print_backtrace();
    vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
 
 #ifdef DB_CRASH_ON_SQLITE_ERROR 
@@ -300,10 +301,12 @@ static void *db_loop(void *_){
    /*
     * Request processing loop 
     */
-   while(1){
+   while(1)
+   {
       pthread_mutex_lock( &database.mux );
 
-      if( database.kill ){
+      if( database.kill )
+      {
          pthread_mutex_unlock( &database.mux );
          _db_thread_end();
          break;
@@ -311,16 +314,12 @@ static void *db_loop(void *_){
 
       u32 processed = 0;
 
-      for( u32 i=0; i<16; i ++ ){
-         db_request *req = NULL;
-         if( database.queue.tail ){
-            req = (db_request *)database.queue.tail->data;
-            pthread_mutex_unlock( &database.mux );
-         }
-         else{
-            pthread_mutex_unlock( &database.mux );
+      for( u32 i=0; i<16; i ++ )
+      {
+         db_request *req = vg_queue_tail_data( &database.queue );
+         if( !req )
             break;
-         }
+         pthread_mutex_unlock( &database.mux );
          
          req->handler( req );
          processed ++;
@@ -329,6 +328,8 @@ static void *db_loop(void *_){
          vg_queue_pop( &database.queue );
       }
 
+      pthread_mutex_unlock( &database.mux );
+
       if( processed )
          vg_low( "Processed %u database requests.\n", processed );
 
@@ -342,9 +343,9 @@ static void *db_loop(void *_){
 /*
  * Create database connection and users table
  */
-static int db_init(void){
-   database.queue.buffer = 
-      (u8 *)vg_linear_alloc( vg_mem.rtmemory, DB_REQUEST_BUFFER_SIZE ),
+static int db_init(void)
+{
+   database.queue.buffer = (u8 *)vg_linear_alloc( vg_mem.rtmemory, DB_REQUEST_BUFFER_SIZE ),
    database.queue.size = DB_REQUEST_BUFFER_SIZE;
 
    if( pthread_mutex_init( &database.mux, NULL ) )
@@ -356,42 +357,49 @@ static int db_init(void){
    return 1;
 }
 
-static int db_killed(void){
+static int db_killed(void)
+{
    pthread_mutex_lock( &database.mux );
    int result = database.kill;
    pthread_mutex_unlock( &database.mux );
    return result;
 }
 
-static void db_kill(void){
+static void db_kill(void)
+{
    pthread_mutex_lock( &database.mux );
    database.kill = 1;
    pthread_mutex_unlock( &database.mux );
    pthread_join( database.thread, NULL );
 }
 
-static void db_free(void){
+static void db_free(void)
+{
    pthread_mutex_destroy( &database.mux );
 }
 
-static db_request *db_alloc_request( u32 size ){
+static db_request *db_alloc_request( u32 size )
+{
    u32 total = sizeof(db_request) + size;
 
    pthread_mutex_lock( &database.mux );
-   vg_queue_frame *frame = vg_queue_alloc( &database.queue, total );
 
-   if( frame ){
-      db_request *req = (db_request *)frame->data;
+   u32 queue_offset;
+   db_request *req = vg_queue_alloc( &database.queue, total, NULL );
+   if( req )
+   {
       req->size = size;
       return req;
    }
-   else {
+   else 
+   {
       pthread_mutex_unlock( &database.mux );
       return NULL;
    }
 }
 
-static void db_send_request( db_request *request ){
+static void db_send_request( db_request *request )
+{
    pthread_mutex_unlock( &database.mux );
 }
 
diff --git a/src/gameserver_replay.c b/src/gameserver_replay.c
new file mode 100644 (file)
index 0000000..e3eea44
--- /dev/null
@@ -0,0 +1,196 @@
+#include "gameserver.h"
+#include "gameserver_replay.h"
+
+struct _gs_replay _gs_replay;
+
+void _gs_replay_server_tick(void)
+{
+   _gs_replay.print_ticker ++;
+   if( _gs_replay.print_ticker > seconds_to_server_ticks(5) )
+   {
+      _gs_replay.print_ticker = 0;
+
+      vg_info( "------------ Replay info -----------\n" );
+
+      for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+      {
+         gs_replay *replay = &_gs_replay.replays[ i ];
+         if( replay->frame_count )
+            vg_info( "Player %u: %u frames\n", i, replay->frame_count );
+      }
+   }
+
+   static u64 divider = 0;
+   divider ++;
+
+   if( divider >= 10 )
+   {
+      if( _gs_replay.playback_buffer )
+      {
+         u16 frame_size = *(u16 *)(_gs_replay.playback_buffer + _gs_replay.playback_buffer_offset);
+         _gs_replay.playback_buffer_offset += 2;
+
+         struct netmsg_playerframe *frame = _gs_replay.playback_buffer + _gs_replay.playback_buffer_offset;
+         frame->client = 1;
+
+         gameserver_send_to_client( 0, frame, frame_size, k_nSteamNetworkingSend_Unreliable );
+         _gs_replay.playback_buffer_offset += frame_size;
+
+         if( _gs_replay.playback_buffer_offset >= _gs_replay.playback_buffer_len )
+         {
+            vg_success( "End replay playback\n" );
+            _gs_replay.playback_buffer = NULL;
+            _gs_replay.playback_buffer_len = 0;
+            _gs_replay.playback_buffer_offset = 0;
+         }
+      }
+
+      divider = 0;
+   }
+}
+
+void _gs_test_replay( const char *path )
+{
+   free( _gs_replay.playback_buffer );
+   _gs_replay.playback_buffer = vg_file_read( NULL, path, &_gs_replay.playback_buffer_len );
+   _gs_replay.playback_buffer_offset = 0;
+   vg_info( "Test replay\n" );
+}
+
+void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *path )
+{
+   u64 min_ticks = gameserver.ticks - seconds_to_server_ticks( server_duration );
+   
+   /* FIXME--- this is to ignore the loopback */
+   if( client_id == 1 )
+      return;
+
+   gs_replay *replay = &_gs_replay.replays[ client_id ];
+   if( replay->frame_count == 0 )
+   {
+      vg_error( "Replay frame count is 0, nothing to save..\n" );
+      return;
+   }
+   
+   /* FIXME: Should we do this on another thread */
+   FILE *fp = fopen( path, "w" );
+   if( !fp )
+   {
+      vg_error( "Failed to open '%s' for writing\n", fp );
+      return;
+   }
+
+   u32 frame_id = replay->head_item;
+   while(1)
+   {
+      gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+      if( frame->tick_recorded < min_ticks )
+         break;
+
+      if( frame->previous_queue_item == VG_MEM_QUEUE_INVALID )
+         break;
+
+      frame_id = frame->previous_queue_item;
+   }
+
+   u32 frame_count = 0;
+   while(1)
+   {
+      gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+      fwrite( &frame->frame_size, sizeof(frame->frame_size), 1, fp );
+      fwrite( &frame->network_frame, frame->frame_size, 1, fp );
+      frame_count ++;
+
+      frame_id = frame->next_queue_item;
+      if( frame_id == VG_MEM_QUEUE_INVALID )
+         break;
+   }
+
+   fclose( fp );
+   vg_success( "Written %u frames to disk.\n", frame_count );
+}
+
+void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_size )
+{
+   vg_queue *ring = &_gs_replay.ring_buffer;
+   gs_replay *dest_replay = &_gs_replay.replays[ client_id ];
+
+   u32 total_size = sizeof( gs_replay_frame ) + frame_size;
+
+   u32 dest_item_id;
+   gs_replay_frame *dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+   if( !dest_frame )
+   {
+      /* policy 1: free if 4 minutes passed in server ticks
+       *        2: player left the server 
+       */
+      u64 min_ticks = gameserver.ticks - seconds_to_server_ticks( 4.0 * 60.0 );
+
+      for( u32 i=0; i<64; i ++ )
+      {
+         gs_replay_frame *free_frame = vg_queue_tail_data( ring );
+         if( !free_frame ) 
+            break;
+
+         /* blocked by valid frame */
+         if( (free_frame->tick_recorded >= min_ticks) && (free_frame->network_frame.client!=255) )
+            break;
+
+         /* clean refs */
+         if( free_frame->next_queue_item != VG_MEM_QUEUE_INVALID )
+         {
+            gs_replay_frame *next_frame = vg_queue_data( ring, free_frame->next_queue_item );
+            next_frame->previous_queue_item = VG_MEM_QUEUE_INVALID;
+         }
+         if( free_frame->network_frame.client != 255 )
+         {
+            gs_replay *free_replay = &_gs_replay.replays[ free_frame->network_frame.client ];
+            free_replay->frame_count --;
+            if( free_replay->frame_count == 0 )
+               free_replay->head_item = VG_MEM_QUEUE_INVALID;
+         }
+
+         vg_queue_pop( ring );
+      }
+
+      dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+      if( !dest_frame )
+      {
+         /* policy 3: grow (double size) */
+         u32 cur_size = _gs_replay.ring_buffer.size;
+         if( cur_size < 1024 )
+            cur_size = 1024;
+         u32 new_size = cur_size * 2;
+         vg_low( "growing replay buffer (%u bytes)\n", new_size );
+
+         vg_queue new_q = { .buffer = malloc( new_size ), .size = new_size };
+         vg_queue_copy_upgrade( ring, &new_q );
+         free( ring->buffer );
+         *ring = new_q;
+
+         dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+         VG_ASSERT( dest_frame );
+      }
+   }
+
+   memcpy( &dest_frame->network_frame, frame, frame_size );
+   dest_frame->network_frame.client = client_id;
+   dest_frame->tick_recorded = gameserver.ticks;
+   dest_frame->frame_size = frame_size;
+   dest_frame->_ = 0;
+   dest_frame->next_queue_item = VG_MEM_QUEUE_INVALID;
+
+   if( dest_replay->frame_count )
+   {
+      dest_frame->previous_queue_item = dest_replay->head_item;
+      gs_replay_frame *prev_frame = vg_queue_data( ring, dest_replay->head_item );
+      prev_frame->next_queue_item = dest_item_id;
+   }
+   else
+   {
+      dest_frame->previous_queue_item = VG_MEM_QUEUE_INVALID;
+   }
+
+   dest_replay->head_item = dest_item_id;
+   dest_replay->frame_count ++;
+}
diff --git a/src/gameserver_replay.h b/src/gameserver_replay.h
new file mode 100644 (file)
index 0000000..e11059e
--- /dev/null
@@ -0,0 +1,39 @@
+#pragma once
+#include "network_msg.h"
+
+typedef struct gs_replay_frame gs_replay_frame;
+typedef struct gs_replay gs_replay;
+
+struct gs_replay_frame
+{
+   u64 tick_recorded;
+   u32 previous_queue_item;
+   u32 next_queue_item;
+   u16 frame_size;
+   u16 _;
+   struct netmsg_playerframe network_frame;
+};
+
+struct _gs_replay
+{
+   struct gs_replay
+   {
+      u32 frame_count,
+          head_item;
+   }
+   replays[ NETWORK_MAX_PLAYERS ];
+   struct vg_queue ring_buffer;
+
+   bool print_info;
+   u64 print_ticker;
+
+   u32 playback_buffer_len,
+       playback_buffer_offset;
+   void *playback_buffer;
+}
+extern _gs_replay;
+
+void _gs_replay_server_tick(void);
+void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_size );
+void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *path );
+void _gs_test_replay( const char *path );
index 06e4d57d8ceb482385fb1b779bf17ccb84b78155..cc71717314bfc5b81e622bef0252984e972db8ca 100644 (file)
@@ -200,9 +200,10 @@ void network_request_scoreboard( const char *mod_uid,
    network_send_request( req, &data, network_scoreboard_callback, userdata );
 }
 
-static void network_publish_callback( netmsg_request *res, vg_msg *body,
-                                      u64 userdata ){
-   if( res->status != k_request_status_ok ){
+static void network_publish_callback( netmsg_request *res, vg_msg *body, u64 userdata )
+{
+   if( res->status != k_request_status_ok )
+   {
       vg_error( "Publish laptime, server error #%d\n", (i32)res->status );
    }
 }
index ca46e4784ad0584ccc390836efd40e06cefac79a..b2ecd562bf4216a1edcae58e360880539ad70554 100644 (file)
@@ -27,13 +27,12 @@ static u32 network_msgstring( const char *src,
    return vg_strncpy( src, buf, string_len, k_strncpy_always_add_null );
 }
 
-static u32 network_pair_index( u32 _a, u32 _b ){
+static u32 network_pair_index( u32 _a, u32 _b )
+{
    const u32 N = NETWORK_MAX_PLAYERS;
 
    if( !((_a != _b) && (_a<N) && (_b<N) ) )
-   {
       vg_fatal_error( "Programming error\n" );
-   }
 
    u32 a = VG_MIN( _a, _b ),
        b = VG_MAX( _a, _b );
index 4dfeb5e44a3cc8a65a0cbe97d159c43caaa1e09d..8b2112d1d164911a19a20bb5c0591d87539018a4 100644 (file)
@@ -49,15 +49,15 @@ struct netmsg_version{
 
 typedef struct netmsg_playerframe netmsg_playerframe;
 enum{ k_inetmsg_playerframe = 200 };
-struct netmsg_playerframe{
+struct netmsg_playerframe
+{
    u16 inetmsg_id;
-   f64 timestamp;
-
-   u8 client, subsystem, 
-      flags, sound_effects;
    u16 boundary_hash; /* used for animating correctly through gates, teleport..
                          msb is a flip flop for teleporting
                          second msb is flip flop for gate */
+   u8 client, subsystem, 
+      flags, sound_effects;
+   f64 timestamp;
 
    u8 animdata[];
 };
@@ -118,6 +118,24 @@ struct netmsg_region {
    char loc[];
 };
 
+#define NET_USER_PROFILE_FLAG_YEAR1_GOLD     0x1
+#define NET_USER_PROFILE_FLAG_YEAR1_SILVER   0x2
+#define NET_USER_PROFILE_FLAG_WORKSHOP_BADGE 0x4
+#define NET_USER_PROFILE_FLAG_SRCC_WINNER    0x8
+typedef struct netmsg_playerprofile netmsg_playerprofile;
+enum{ k_inetmsg_playerprofile = 207 };
+struct netmsg_playerprofile{
+   u16 inetmsg_id;
+   u32 profile_flags;
+   u16 top10_records_weekly,
+       top10_records_alltime;
+   u8  country_id;
+};
+
+struct netmsg_start_run{
+   
+};
+
 /* requests 300 */
 typedef struct netmsg_request netmsg_request;
 enum{ k_inetmsg_request = 300, k_inetmsg_response = 301 };