server edit 1
authorhgn <hgodden00@gmail.com>
Sun, 30 Mar 2025 06:49:19 +0000 (07:49 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 30 Mar 2025 06:49:19 +0000 (07:49 +0100)
15 files changed:
build.c
src/gameserver.c
src/gameserver.h
src/gameserver_database.c [new file with mode: 0644]
src/gameserver_database.h [new file with mode: 0644]
src/gameserver_db.h
src/gameserver_replay.c
src/gameserver_replay.h
src/gameserver_transfer.c [new file with mode: 0644]
src/gameserver_transfer.h [new file with mode: 0644]
src/network.c
src/network_compression.h
src/network_msg.h
src/player_remote.c
src/queuetest.c [new file with mode: 0644]

diff --git a/build.c b/build.c
index 95766b48423948eea9204d2f3026af4dbecfb9b1..46e3becc4906daf02b45f7fab0fe739f67d145af 100644 (file)
--- a/build.c
+++ b/build.c
@@ -407,6 +407,24 @@ void s_testing_server(void)
    compile_server( &test_proj, &vg_test_env );
 }
 
+void s_queuetest_build(void)
+{
+   vg_info( "running script: s_queuetest_build(void)\n" );
+
+   struct vg_project test_proj;
+   vg_project_init( &test_proj, "bin", "queuetest", &vg_test_env, 0 );
+
+   vg_str sources = {0};
+   vg_strcat( &sources, "src/queuetest.c vg/vg_tool.c \\\n " );
+
+   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 " );
+   vg_compiler_run( &test_proj, &vg_test_env, &conf, sources.buffer, "qtest", k_obj_type_exe );
+}
+
 int main( int argc, char *argv[] )
 {
    vg_log_init();
@@ -425,6 +443,9 @@ int main( int argc, char *argv[] )
       if( vg_long_opt( "testing-server", NULL ) )
          s_testing_server();
 
+      if( vg_long_opt( "queuetest", NULL ) )
+         s_queuetest_build();
+
       if( vg_long_opt( "tools", NULL ) )
          s_compile_tools();
 
index 9f4614f8363cc87f6ecdee18e5d50e45b78ee1c6..e55d21b693f3f399a8ed0d8f4583812cf96ba5b2 100644 (file)
@@ -13,43 +13,38 @@ volatile sig_atomic_t sig_stop;
 #include "gameserver.h" 
 #include "vg/vg_opt.h"
 #include "network_common.h"
-#include "gameserver_db.h"
+#include "gameserver_database.h"
 #include "vg/vg_m.h"
 #include "vg/vg_msg.h"
 #include "gameserver_replay.h"
+#include "gameserver_transfer.h"
 
 static u64 const k_steamid_max = 0xffffffffffffffff;
 
+ISteamNetworkingSockets *hSteamNetworkingSockets;
+ISteamNetworkingUtils *hSteamNetworkingUtils;
+
 static void inthandler( int signum ) {
    sig_stop = 1;
 }
 
 static void release_message( SteamNetworkingMessage_t *msg )
 {
-   msg->m_nUserData --;
-
-   if( msg->m_nUserData == 0 )
-      SteamAPI_SteamNetworkingMessage_t_Release( msg );
+   SteamAPI_SteamNetworkingMessage_t_Release( msg );
 }
 
 /*
  * Send message to single client, with authentication checking
  */
-static void gameserver_send_to_client( i32 client_id,
-                                       const void *pData, u32 cbData,
-                                       int nSendFlags )
+static void gameserver_send_to_client( i32 client_id, const void *pData, u32 cbData, int nSendFlags )
 {
    struct gameserver_client *client = &gameserver.clients[ client_id ];
 
-   if( gameserver.loopback_test && !client->connection ) 
-      return;
-
    if( !client->steamid )
       return;
 
-   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
-         hSteamNetworkingSockets, client->connection,
-         pData, cbData, nSendFlags, NULL );
+   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, client->connection,
+                                                             pData, cbData, nSendFlags, NULL );
 }
 
 /*
@@ -70,9 +65,6 @@ static void gameserver_send_version_to_client( int index )
 {
    struct gameserver_client *client = &gameserver.clients[index];
 
-   if( gameserver.loopback_test && !client->connection ) 
-      return;
-
    netmsg_version version;
    version.inetmsg_id = k_inetmsg_version;
    version.version = NETWORK_SKATERIFT_VERSION;
@@ -88,25 +80,25 @@ static void gameserver_send_version_to_client( int index )
 static void gameserver_player_join( int index )
 {
    struct gameserver_client *joiner = &gameserver.clients[index];
-   
+
+   SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes( hSteamNetworkingSockets, joiner->connection, 2,
+                                                              (int[]){ 0,0 }, (u16[]){ 10, 1 } );
+
    netmsg_playerjoin join = { .inetmsg_id = k_inetmsg_playerjoin,
                               .index = index,
                               .steamid = joiner->steamid };
 
-   gameserver_send_to_all( index, &join, sizeof(join),
-                           k_nSteamNetworkingSend_Reliable );
+   gameserver_send_to_all( index, &join, sizeof(join), k_nSteamNetworkingSend_Reliable );
 
    /* 
     * update the joining user about current connections and our version
     */
    gameserver_send_version_to_client( index );
 
-   netmsg_playerusername *username = 
-      alloca( sizeof(netmsg_playerusername) + NETWORK_USERNAME_MAX );
+   netmsg_playerusername *username = alloca( sizeof(netmsg_playerusername) + NETWORK_USERNAME_MAX );
    username->inetmsg_id = k_inetmsg_playerusername;
 
-   netmsg_playeritem *item = 
-      alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
+   netmsg_playeritem *item = alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
    item->inetmsg_id = k_inetmsg_playeritem;
 
    netmsg_region *region = alloca( sizeof(netmsg_region) + NETWORK_REGION_MAX );
@@ -123,40 +115,30 @@ static void gameserver_player_join( int index )
       netmsg_playerjoin init = { .inetmsg_id = k_inetmsg_playerjoin,
                                  .index = i,
                                  .steamid = client->steamid };
-      gameserver_send_to_client( index, &init, sizeof(init),
-                                 k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_client( index, &init, sizeof(init), k_nSteamNetworkingSend_Reliable );
 
       /* username */
       username->index = i;
-      u32 chs = vg_strncpy( client->username, username->name, 
-                            NETWORK_USERNAME_MAX,
-                            k_strncpy_always_add_null );
+      u32 chs = vg_strncpy( client->username, username->name, NETWORK_USERNAME_MAX, k_strncpy_always_add_null );
       u32 size = sizeof(netmsg_playerusername) + chs + 1;
-      gameserver_send_to_client( index, username, size,
-                                 k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_client( index, username, size, k_nSteamNetworkingSend_Reliable );
 
       /* items */
       for( int j=0; j<k_netmsg_playeritem_max; j++ )
       {
-         chs = vg_strncpy( client->items[j].uid, item->uid, ADDON_UID_MAX, 
-                           k_strncpy_always_add_null );
+         chs = vg_strncpy( client->items[j].uid, item->uid, ADDON_UID_MAX, k_strncpy_always_add_null );
          item->type_index = j;
          item->client = i;
          size = sizeof(netmsg_playeritem) + chs + 1;
-         gameserver_send_to_client( index, item, size,
-                                    k_nSteamNetworkingSend_Reliable );
+         gameserver_send_to_client( index, item, size, k_nSteamNetworkingSend_Reliable );
       }
 
       /* region */
-      
       region->client = i;
       region->flags = client->region_flags;
-      u32 l = vg_strncpy( client->region, region->loc, NETWORK_REGION_MAX, 
-                          k_strncpy_always_add_null );
+      u32 l = vg_strncpy( client->region, region->loc, NETWORK_REGION_MAX, k_strncpy_always_add_null );
       size = sizeof(netmsg_region) + l + 1;
-
-      gameserver_send_to_client( index, region, size,
-                                 k_nSteamNetworkingSend_Reliable );
+      gameserver_send_to_client( index, region, size, k_nSteamNetworkingSend_Reliable );
    }
 }
 
@@ -236,14 +218,6 @@ static void handle_new_connection( HSteamNetConnection conn )
 
       SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
       SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
-
-      if( gameserver.loopback_test )
-      {
-         vg_warn( "[DEV] Creating loopback client\n" );
-         struct gameserver_client *loopback = &gameserver.clients[1];
-         loopback->active = 1;
-         loopback->connection = 0;
-      }
    }
    else
    {
@@ -269,12 +243,7 @@ static i32 gameserver_conid( HSteamNetConnection hconn )
    i64 id;
 
    if( hconn == 0 )
-   {
-      if( gameserver.loopback_test )
-         return 1;
-      else
-         return -1;
-   }
+      return -1;
    else
       id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData( hSteamNetworkingSockets, hconn );
 
@@ -313,12 +282,6 @@ static void on_connect_status( CallbackMsg_t *msg )
       {
          gameserver_player_leave( client_id );
          remove_client( client_id );
-
-         if( gameserver.loopback_test )
-         {
-            gameserver_player_leave( 1 );
-            remove_client( 1 );
-         }
       }
       else 
       {
@@ -364,13 +327,6 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
    {
       client->steamid = k_steamid_max;
       gameserver_player_join( client_id );
-
-      if( gameserver.loopback_test )
-      {
-         struct gameserver_client *loopback = &gameserver.clients[1];
-         loopback->steamid = k_steamid_max;
-         gameserver_player_join( 1 );
-      }
    }
 }
 
@@ -568,7 +524,6 @@ static void gameserver_propogate_player_frame( int client_id, netmsg_playerframe
    basic->sound_effects = 0;
 
    struct gameserver_client *c0 = &gameserver.clients[client_id];
-   c0->instance = frame->flags & NETMSG_PLAYERFRAME_INSTANCE_ID;
 
    for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
@@ -576,17 +531,12 @@ static void gameserver_propogate_player_frame( int client_id, netmsg_playerframe
          continue;
 
       struct gameserver_client *ci = &gameserver.clients[i];
-
       int send_full = 0;
 
-      if( c0->instance == ci->instance )
-      {
-         u32 k_index = network_pair_index( client_id, i );
-         u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
-         
-         if( (k_mask & (CLIENT_KNOWLEDGE_SAME_WORLD0<<c0->instance)) )
-            send_full = 1;
-      }
+      u32 k_index = network_pair_index( client_id, i );
+      u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
+      if( (k_mask & CLIENT_KNOWLEDGE_SAME_WORLD0) )
+         send_full = 1;
 
       if( send_full )
          gameserver_send_to_client( i, full, size, k_nSteamNetworkingSend_Unreliable );
@@ -627,6 +577,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       gameserver_send_to_all( client_id, prop, propsize, k_nSteamNetworkingSend_Reliable );
 
       /* update database about this */
+#if 0
       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;
@@ -634,6 +585,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       vg_strncpy( client->username, inf->username, sizeof(inf->username), k_strncpy_always_add_null );
       call->handler = gameserver_update_db_username;
       db_send_request( call );
+#endif
    }
    else if( tmp->inetmsg_id == k_inetmsg_playerframe )
    {
@@ -686,9 +638,10 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       {
          _gs_write_replay_to_disk( client_id, 1.0*60.0, "/tmp/server-replay.replay" );
       }
-      else if( !strcmp( prop->msg, "play" ) )
+      else if( !strcmp( prop->msg, "transfer" ) )
       {
-         _gs_test_replay( "/tmp/server-replay.replay" );
+         _gs_start_transfer( client_id, "/tmp/server-replay.replay" );
+         //_gs_test_replay( client_id, "/tmp/server-replay.replay" );
       }
 
       gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, k_nSteamNetworkingSend_Reliable );
@@ -733,12 +686,6 @@ static void gameserver_request_respond( enum request_status status,
 
    res->status = status;
 
-   if( gameserver.loopback_test && !msg->m_conn )
-   {
-      release_message( msg );
-      return;
-   }
-
    SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
          hSteamNetworkingSockets, msg->m_conn,
          res, sizeof(netmsg_request) + len,
@@ -924,24 +871,22 @@ static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
 
    if( tmp->inetmsg_id == k_inetmsg_request )
    {
-      if( gameserver.loopback_test && (client_id == 1) )
-      {
-         release_message( msg );
-         return;
-      }
-
       if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
       {
          release_message( msg );
          return;
       }
 
+      /* DISABLED: REASON, CODE WASN'T THREAD SAFE. */
+      release_message( msg );
+#if 0
       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;
       db_send_request( call );
+#endif
    }
    else 
    {
@@ -969,16 +914,14 @@ static void process_network_message( SteamNetworkingMessage_t *msg )
    {
       gameserver_rx_300_400( msg );
    }
-   else{
+   else
+   {
       if( tmp->inetmsg_id == k_inetmsg_auth )
          gameserver_rx_auth( msg );
-      else if( tmp->inetmsg_id == k_inetmsg_version ){
+      else if( tmp->inetmsg_id == k_inetmsg_version )
          gameserver_rx_version( msg );
-      }
-      else {
-         vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
-                  tmp->inetmsg_id );
-      }
+      else 
+         vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
       release_message( msg );
    }
 }
@@ -1000,21 +943,6 @@ static void poll_connections(void)
       for( int i=0; i<len; i++ )
       {
          SteamNetworkingMessage_t *msg = messages[i];
-         msg->m_nUserData = 1;
-
-         if( gameserver.loopback_test )
-         {
-            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 );
       }
    }
@@ -1039,16 +967,16 @@ int main( int argc, char *argv[] )
       if( vg_long_opt( "noauth", "Disable server authentication"  ) )
          gameserver.auth_mode = eServerModeNoAuthentication;
 
-      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 );
    vg_alloc_quota();
-   db_init();
+   if( !db_init() )
+   {
+      return 0;
+   }
 
    /* steamworks init 
     * --------------------------------------------------------------- */
@@ -1073,6 +1001,7 @@ int main( int argc, char *argv[] )
    SteamAPI_ManualDispatch_Init();
    HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
    hSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
+   hSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI();
 
    steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
    steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_connect_status );
@@ -1097,12 +1026,12 @@ int main( int argc, char *argv[] )
       steamworks_event_loop( hsteampipe );
       poll_connections();
       _gs_replay_server_tick();
+      _gs_transfer_tick();
 
       usleep(10000);
       gameserver.ticks ++;
 
-      if( db_killed() )
-         break;
+      /* consume tasks here */
    }
    
    SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
@@ -1110,10 +1039,10 @@ int main( int argc, char *argv[] )
    
    vg_info( "Shutting down\n..." );
    SteamGameServer_Shutdown();
-   db_kill();
    db_free();
-
    return 0;
 }
 
 #include "gameserver_replay.c"
+#include "gameserver_transfer.c"
+#include "gameserver_database.c"
index 0b2620f895899b0419845ba53206fcd3a8cac456..ad5b95c4190c7be66bed0e9f6276f3676354ec18 100644 (file)
@@ -18,16 +18,15 @@ struct {
    HSteamNetPollGroup client_group;
    EServerMode auth_mode;
 
-   struct gameserver_client {
-      int active;
+   struct gameserver_client 
+   {
+      bool active, authenticated;
       u32 version;
-      int authenticated;
       HSteamNetConnection connection;
       char username[ NETWORK_USERNAME_MAX ];
 
-      u8 instance;
-
-      struct gameserver_item {
+      struct gameserver_item 
+      {
          char uid[ADDON_UID_MAX];
          u32  hash;
       }
@@ -42,14 +41,13 @@ struct {
 
    u8 client_knowledge_mask[ (NETWORK_MAX_PLAYERS*(NETWORK_MAX_PLAYERS-1))/2 ];
    u8 app_symmetric_key[ k_nSteamEncryptedAppTicketSymmetricKeyLen ];
-
-   bool loopback_test;
    u64 ticks;
 }
 static gameserver = {
    .auth_mode = eServerModeAuthentication
 };
 
-static ISteamNetworkingSockets *hSteamNetworkingSockets = NULL;
+extern ISteamNetworkingSockets *hSteamNetworkingSockets;
+extern ISteamNetworkingUtils *hSteamNetworkingUtils;
 
 u64 seconds_to_server_ticks( f64 s );
diff --git a/src/gameserver_database.c b/src/gameserver_database.c
new file mode 100644 (file)
index 0000000..ba4ca02
--- /dev/null
@@ -0,0 +1,330 @@
+#include "gameserver_database.h" 
+
+struct _gs_db _gs_db;
+
+/*
+ * Log the error code (or carry on if its OK).
+ */
+void log_sqlite3( int code )
+{
+   if( code == SQLITE_OK ) 
+      return;
+
+   vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
+}
+
+sqlite3_stmt *db_stmt( const char *code )
+{
+#ifdef DB_LOG_SQL_STATEMENTS
+   vg_low( code );
+#endif
+
+   sqlite3_stmt *stmt;
+   int fc = sqlite3_prepare_v2( _gs_db.db, code, -1, &stmt, NULL );
+
+   if( fc != SQLITE_OK )
+   {
+      log_sqlite3( fc );
+      sqlite3_finalize( stmt );
+      return NULL;
+   }
+
+   return stmt;
+}
+
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+{
+   return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+}
+
+/*
+ * Allowed characters in sqlite table names. We use "" as delimiters.
+ */
+static int db_verify_charset( const char *str, int mincount )
+{
+   for( int i=0; ; i++ )
+   {
+      char c = str[i];
+      if( c == '\0' )
+      {
+         if( i < mincount ) return 0;
+         else return 1;
+      }
+
+      if( !((c==' ')||(c=='!')||(c>='#'&&c<='~')) ) 
+         return 0;
+   }
+
+   return 0;
+}
+
+bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] )
+{
+   if( !db_verify_charset( mod_uid, 13 ) || !db_verify_charset( run_uid, 1 ) ) 
+      return 0;
+
+   vg_str a;
+   vg_strnull( &a, table_name, DB_TABLE_UID_MAX );
+   vg_strcat( &a, mod_uid );
+   vg_strcat( &a, ":" );
+   vg_strcat( &a, run_uid );
+
+   if( week ){
+      vg_strcat( &a, "#" );
+      vg_strcati32( &a, week );
+   }
+
+   return vg_strgood( &a );
+}
+
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid )
+{
+   char buf[ 512 ];
+   vg_str q;
+   vg_strnull( &q, buf, 512 );
+   vg_strcat( &q, "SELECT time FROM \"" );
+   vg_strcat( &q, table );
+   vg_strcat( &q, "\" WHERE steamid = ?;" );
+   if( !vg_strgood(&q) ) 
+      return 0;
+
+   sqlite3_stmt *stmt = db_stmt( q.buffer );
+   if( stmt )
+   {
+      sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+      int fc = sqlite3_step( stmt );
+
+      i32 result = 0;
+
+      if( fc == SQLITE_ROW )
+         result = sqlite3_column_int( stmt, 0 );
+      else if( fc != SQLITE_DONE )
+         log_sqlite3(fc);
+
+      sqlite3_finalize( stmt );
+      return result;
+   }
+   else 
+      return 0;
+}
+
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster )
+{
+   /* auto create table 
+    * ------------------------------------------*/
+   char buf[ 512 ];
+   vg_str q;
+   vg_strnull( &q, buf, 512 );
+   vg_strcat( &q, "CREATE TABLE IF NOT EXISTS \n \"" );
+   vg_strcat( &q, table );
+   vg_strcat( &q, "\"\n (steamid BIGINT UNIQUE, time INT);" );
+   if( !vg_strgood(&q) ) 
+      return 0;
+
+   vg_str str;
+   sqlite3_stmt *create_table = db_stmt( q.buffer );
+
+   if( create_table )
+   {
+      db_sqlite3_bind_sz( create_table, 1, table );
+
+      int fc = sqlite3_step( create_table );
+      sqlite3_finalize( create_table );
+      if( fc != SQLITE_DONE )
+         return 0;
+   }
+   else 
+      return 0;
+
+   if( only_if_faster )
+   {
+      i32 current = db_readusertime( table, steamid );
+      if( (current != 0) && (score > current) )
+         return 1;
+   }
+
+   /* insert score 
+    * -------------------------------------------------*/
+   vg_strnull( &q, buf, 512 );
+   vg_strcat( &q, "REPLACE INTO \"" );
+   vg_strcat( &q, table );
+   vg_strcat( &q, "\"(steamid,time)\n VALUES (?,?);" );
+   if( !vg_strgood(&q) ) 
+      return 0;
+
+   sqlite3_stmt *stmt = db_stmt( q.buffer );
+
+   if( stmt )
+   {
+      sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+      sqlite3_bind_int( stmt, 2, score );
+
+      int fc = sqlite3_step( stmt );
+      sqlite3_finalize( stmt );
+      if( fc != SQLITE_DONE )
+         return 0;
+      else 
+         return 1;
+   }
+   else 
+      return 0;
+}
+
+bool db_updateuser( u64 steamid, const char *username, int admin )
+{
+   sqlite3_stmt *stmt = db_stmt( "INSERT OR REPLACE INTO users (steamid, name, type) VALUES (?,?,?);" );
+
+   if( stmt )
+   {
+      sqlite3_bind_int64( stmt, 1, *((i64*)(&steamid)) ); 
+      db_sqlite3_bind_sz( stmt, 2, username );
+      sqlite3_bind_int( stmt, 3, admin );
+
+      int fc = sqlite3_step( stmt );
+      sqlite3_finalize(stmt);
+
+      if( fc == SQLITE_DONE )
+      {
+         vg_success( "Inserted %lu (%s), type: %d\n", steamid, username, admin );
+         return 1;
+      }
+      else
+      {
+         log_sqlite3( fc );
+         return 0;
+      }
+   }
+   else 
+      return 0;
+}
+
+bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type )
+{
+   sqlite3_stmt *stmt = db_stmt( "SELECT * FROM users WHERE steamid = ?;" );
+   if( !stmt ) return 0;
+
+   sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+   int fc = sqlite3_step( stmt );
+
+   if( fc != SQLITE_ROW )
+   {
+      log_sqlite3( fc );
+      sqlite3_finalize( stmt );
+      return 0;
+   }
+
+   if( out_username )
+   {
+      const char *name = (const char *)sqlite3_column_text( stmt, 1 );
+      vg_strncpy( name, out_username, username_max, k_strncpy_allow_cutoff );
+   }
+   
+   if( out_type )
+      *out_type = sqlite3_column_int( stmt, 2 );
+   
+   sqlite3_finalize( stmt );
+   return 1;
+}
+
+#if 0
+static void _db_thread_end(void){
+   pthread_mutex_lock( &_gs_db.mux );
+   _gs_db.kill = 1;
+   pthread_mutex_unlock( &_gs_db.mux );
+   sqlite3_close( _gs_db.db );
+}
+#endif
+
+static void *database_worker_thread(void *_)
+{
+   int rc = sqlite3_open( "highscores.db", &_gs_db.db );
+
+   if( rc )
+   {
+      vg_error( "database failure: %s\n", sqlite3_errmsg(_gs_db.db) );
+      sqlite3_close( _gs_db.db );
+      return NULL;
+   }
+
+   sqlite3_stmt *stmt = db_stmt( "CREATE TABLE IF NOT EXISTS \n"
+                                 " users(steamid BIGINT UNIQUE, name VARCHAR(128), type INT);" );
+   if( stmt )
+   {
+      int fc = sqlite3_step( stmt );
+      sqlite3_finalize(stmt);
+
+      if( fc == SQLITE_DONE )
+      {
+         vg_success( "Created users table\n" );
+         db_updateuser( 76561198072130043, "harry", 2 );
+      }
+      else
+      {
+         log_sqlite3( fc );
+         sqlite3_close( _gs_db.db );
+         return NULL;
+      }
+   }
+   else 
+   {
+      sqlite3_close( _gs_db.db );
+      return NULL;
+   }
+
+   /*
+    * Request processing loop 
+    */
+   while(1)
+   {
+      pthread_mutex_lock( &_gs_db.mux );
+
+#if 0
+      u32 processed = 0;
+
+      for( u32 i=0; i<16; i ++ )
+      {
+         db_request *req = vg_queue_tail_data( &_gs_db.queue );
+         if( !req )
+            break;
+         pthread_mutex_unlock( &_gs_db.mux );
+         
+         req->handler( req );
+         processed ++;
+
+         pthread_mutex_lock( &_gs_db.mux );
+         vg_queue_pop( &_gs_db.queue );
+      }
+
+      pthread_mutex_unlock( &_gs_db.mux );
+
+      if( processed )
+         vg_low( "Processed %u _gs_db.requests.\n", processed );
+#endif
+
+      pthread_mutex_unlock( &_gs_db.mux );
+      usleep(50000);
+   }
+
+   vg_low( "Database thread terminates.\n" );
+   return NULL;
+}
+
+bool db_init(void)
+{
+   _gs_db.queue.buffer = (u8 *)vg_linear_alloc( vg_mem.rtmemory, DB_REQUEST_BUFFER_SIZE ),
+   _gs_db.queue.size = DB_REQUEST_BUFFER_SIZE;
+
+   if( pthread_mutex_init( &_gs_db.mux, NULL ) )
+      return 0;
+
+   if( pthread_create( &_gs_db.thread, NULL, _gs_db.worker_thread, NULL ) )
+      return 0;
+
+   return 1;
+}
+
+void db_free(void)
+{
+   pthread_join( _gs_db.thread, NULL );
+   pthread_mutex_destroy( &_gs_db.mux );
+}
diff --git a/src/gameserver_database.h b/src/gameserver_database.h
new file mode 100644 (file)
index 0000000..d222aea
--- /dev/null
@@ -0,0 +1,104 @@
+#pragma once
+
+#include "vg/vg_log.h"
+#include "vg/vg_mem_queue.h"
+#include "network_common.h"
+#include "dep/sqlite3/sqlite3.h"
+#include <pthread.h>
+#include <unistd.h>
+
+#define DB_COURSE_UID_MAX 32
+#define DB_TABLE_UID_MAX (ADDON_UID_MAX+DB_COURSE_UID_MAX+32)
+#define DB_LOG_SQL_STATEMENTS
+#define DB_REQUEST_BUFFER_SIZE (1024*2)
+
+typedef struct db_request db_request;
+struct db_request 
+{
+   void (*handler)( db_request *req );
+   u32 size,_;
+   u8 data[];
+};
+
+struct _gs_db
+{
+   sqlite3 *db;
+   pthread_t thread;
+   pthread_mutex_t mux;
+
+   vg_queue queue;
+   int kill;
+}
+extern _gs_db;
+
+/*
+ * Perpare statement and auto throw away if fails. Returns NULL on failure.
+ */
+sqlite3_stmt *db_stmt( const char *code );
+void log_sqlite3( int code );
+
+/*
+ * bind zero terminated string
+ */
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz );
+
+/*
+ * Find table name from mod UID and course UID, plus the week number
+ */
+bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] );
+
+/*
+ * Read value from highscore table. If not found or error, returns 0
+ */
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid );
+
+/*
+ * Write to highscore table
+ */
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster );
+
+/*
+ * Set username and type
+ */
+bool db_updateuser( u64 steamid, const char *username, int admin );
+
+/*
+ * Get user info 
+ */
+bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type );
+
+/*
+ * Create database connection and users table
+ */
+bool db_init(void);
+void db_free(void);
+
+static db_request *db_alloc_request( u32 size ) { return NULL; }
+static void db_send_request( db_request *request ) {}
+
+#if 0
+static db_request *db_alloc_request( u32 size )
+{
+   u32 total = sizeof(db_request) + size;
+
+   pthread_mutex_lock( &database.mux );
+
+   u32 queue_offset;
+   db_request *req = vg_queue_alloc( &database.queue, total, NULL );
+   if( req )
+   {
+      req->size = size;
+      return req;
+   }
+   else 
+   {
+      pthread_mutex_unlock( &database.mux );
+      return NULL;
+   }
+}
+
+static void db_send_request( db_request *request )
+{
+   pthread_mutex_unlock( &database.mux );
+}
+#endif
index f6abfd371d1b66974a12d62d65a6b0354100eb3c..d0069bb911b91c44b5bc2e3b81641261046bcdac 100644 (file)
@@ -1,5 +1,4 @@
-#ifndef GAMESERVER_DB_H
-#define GAMESERVER_DB_H
+#pragma once
 
 #include "vg/vg_log.h"
 #include "vg/vg_mem_queue.h"
 
 #define DB_COURSE_UID_MAX 32
 #define DB_TABLE_UID_MAX (ADDON_UID_MAX+DB_COURSE_UID_MAX+32)
-//#define DB_CRASH_ON_SQLITE_ERROR
 #define DB_LOG_SQL_STATEMENTS
 #define DB_REQUEST_BUFFER_SIZE (1024*2)
 
 typedef struct db_request db_request;
-struct db_request {
+struct db_request 
+{
    void (*handler)( db_request *req );
    u32 size,_;
    u8 data[];
 };
 
-struct {
+struct _gs_db
+{
    sqlite3 *db;
    pthread_t thread;
    pthread_mutex_t mux;
@@ -29,355 +29,50 @@ struct {
    vg_queue queue;
    int kill;
 }
-static database;
-
-/*
- * Log the error code (or carry on if its OK).
- */
-static void log_sqlite3( int code )
-{
-   if( code == SQLITE_OK ) return;
-   //vg_print_backtrace();
-   vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
-
-#ifdef DB_CRASH_ON_SQLITE_ERROR 
-   int crash = *((int*)2);
-#endif
-}
+extern _gs_db;
 
 /*
  * Perpare statement and auto throw away if fails. Returns NULL on failure.
  */
-static sqlite3_stmt *db_stmt( const char *code ){
-#ifdef DB_LOG_SQL_STATEMENTS
-   vg_low( code );
-#endif
-
-   sqlite3_stmt *stmt;
-   int fc = sqlite3_prepare_v2( database.db, code, -1, &stmt, NULL );
-
-   if( fc != SQLITE_OK ){
-      log_sqlite3( fc );
-      sqlite3_finalize( stmt );
-      return NULL;
-   }
-
-   return stmt;
-}
+sqlite3_stmt *db_stmt( const char *code );
 
 /*
  * bind zero terminated string
  */
-static int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz ){
-   return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
-}
-
-/*
- * Allowed characters in sqlite table names. We use "" as delimiters.
- */
-static int db_verify_charset( const char *str, int mincount ){
-   for( int i=0; ; i++ ){
-      char c = str[i];
-      if( c == '\0' ){
-         if( i < mincount ) return 0;
-         else return 1;
-      }
-
-      if( !((c==' ')||(c=='!')||(c>='#'&&c<='~')) ) return 0;
-   }
-
-   return 0;
-}
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz );
 
 /*
  * Find table name from mod UID and course UID, plus the week number
  */
-static int db_get_highscore_table_name( const char *mod_uid,
-                                        const char *run_uid,
-                                        u32 week,
-                                        char table_name[DB_TABLE_UID_MAX] ){
-   if( !db_verify_charset( mod_uid, 13 ) ||
-       !db_verify_charset( run_uid, 1 ) ) return 0;
-
-   vg_str a;
-   vg_strnull( &a, table_name, DB_TABLE_UID_MAX );
-   vg_strcat( &a, mod_uid );
-   vg_strcat( &a, ":" );
-   vg_strcat( &a, run_uid );
-
-   if( week ){
-      vg_strcat( &a, "#" );
-      vg_strcati32( &a, week );
-   }
-
-   return vg_strgood( &a );
-}
+bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] );
 
 /*
  * Read value from highscore table. If not found or error, returns 0
  */
-static i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ){
-   char buf[ 512 ];
-   vg_str q;
-   vg_strnull( &q, buf, 512 );
-   vg_strcat( &q, "SELECT time FROM \"" );
-   vg_strcat( &q, table );
-   vg_strcat( &q, "\" WHERE steamid = ?;" );
-   if( !vg_strgood(&q) ) return 0;
-
-   sqlite3_stmt *stmt = db_stmt( q.buffer );
-   if( stmt ){
-      sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
-      int fc = sqlite3_step( stmt );
-
-      i32 result = 0;
-
-      if( fc == SQLITE_ROW )
-         result = sqlite3_column_int( stmt, 0 );
-      else if( fc != SQLITE_DONE )
-         log_sqlite3(fc);
-
-      sqlite3_finalize( stmt );
-      return result;
-   }
-   else return 0;
-}
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid );
 
 /*
  * Write to highscore table
  */
-static int db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, 
-                             i32 score, int only_if_faster ){
-   /* auto create table 
-    * ------------------------------------------*/
-   char buf[ 512 ];
-   vg_str q;
-   vg_strnull( &q, buf, 512 );
-   vg_strcat( &q, "CREATE TABLE IF NOT EXISTS \n \"" );
-   vg_strcat( &q, table );
-   vg_strcat( &q, "\"\n (steamid BIGINT UNIQUE, time INT);" );
-   if( !vg_strgood(&q) ) return 0;
-
-   vg_str str;
-   sqlite3_stmt *create_table = db_stmt( q.buffer );
-
-   if( create_table ){
-      db_sqlite3_bind_sz( create_table, 1, table );
-
-      int fc = sqlite3_step( create_table );
-      sqlite3_finalize( create_table );
-      if( fc != SQLITE_DONE )
-         return 0;
-   }
-   else return 0;
-
-   if( only_if_faster ){
-      i32 current = db_readusertime( table, steamid );
-      if( (current != 0) && (score > current) )
-         return 1;
-   }
-
-   /* insert score 
-    * -------------------------------------------------*/
-   vg_strnull( &q, buf, 512 );
-   vg_strcat( &q, "REPLACE INTO \"" );
-   vg_strcat( &q, table );
-   vg_strcat( &q, "\"(steamid,time)\n VALUES (?,?);" );
-   if( !vg_strgood(&q) ) return 0;
-
-   sqlite3_stmt *stmt = db_stmt( q.buffer );
-
-   if( stmt ){
-      sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
-      sqlite3_bind_int( stmt, 2, score );
-
-      int fc = sqlite3_step( stmt );
-      sqlite3_finalize( stmt );
-      if( fc != SQLITE_DONE )
-         return 0;
-      else 
-         return 1;
-   }
-   else return 0;
-}
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster );
 
 /*
  * Set username and type
  */
-static int db_updateuser( u64 steamid, const char *username, int admin ){
-   sqlite3_stmt *stmt = db_stmt(
-         "INSERT OR REPLACE INTO users (steamid, name, type) "
-         "VALUES (?,?,?);" );
-
-   if( stmt ){
-      sqlite3_bind_int64( stmt, 1, *((i64*)(&steamid)) ); 
-      db_sqlite3_bind_sz( stmt, 2, username );
-      sqlite3_bind_int( stmt, 3, admin );
-
-      int fc = sqlite3_step( stmt );
-      sqlite3_finalize(stmt);
-
-      if( fc == SQLITE_DONE ){
-         vg_success( "Inserted %lu (%s), type: %d\n",
-                     steamid, username, admin );
-         return 1;
-      }
-      else{
-         log_sqlite3( fc );
-         return 0;
-      }
-   }
-   else return 0;
-}
+bool db_updateuser( u64 steamid, const char *username, int admin );
 
 /*
  * Get user info 
  */
-static int db_getuserinfo( u64 steamid, char *out_username, u32 username_max, 
-                           i32 *out_type ){
-   sqlite3_stmt *stmt = db_stmt( "SELECT * FROM users WHERE steamid = ?;" );
-   if( !stmt ) return 0;
-
-   sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
-   int fc = sqlite3_step( stmt );
-
-   if( fc != SQLITE_ROW ){
-      log_sqlite3( fc );
-      sqlite3_finalize( stmt );
-      return 0;
-   }
-
-   if( out_username ){
-      const char *name = (const char *)sqlite3_column_text( stmt, 1 );
-      vg_strncpy( name, out_username, username_max, k_strncpy_allow_cutoff );
-   }
-   
-   if( out_type )
-      *out_type = sqlite3_column_int( stmt, 2 );
-   
-   sqlite3_finalize( stmt );
-   return 1;
-}
-
-static void _db_thread_end(void){
-   pthread_mutex_lock( &database.mux );
-   database.kill = 1;
-   pthread_mutex_unlock( &database.mux );
-   sqlite3_close( database.db );
-}
-
-static void *db_loop(void *_){
-   int rc = sqlite3_open( "highscores.db", &database.db );
-
-   if( rc ){
-      vg_error( "database failure: %s\n", sqlite3_errmsg(database.db) );
-      _db_thread_end();
-      return NULL;
-   }
-
-   sqlite3_stmt *stmt = db_stmt(
-         "CREATE TABLE IF NOT EXISTS \n"
-         " users(steamid BIGINT UNIQUE, name VARCHAR(128), type INT);" );
-
-   if( stmt ){
-      int fc = sqlite3_step( stmt );
-      sqlite3_finalize(stmt);
-
-      if( fc == SQLITE_DONE ){
-         vg_success( "Created users table\n" );
-         db_updateuser( 76561198072130043, "harry", 2 );
-      }
-      else{
-         log_sqlite3( fc );
-         _db_thread_end();
-         return NULL;
-      }
-   }
-   else {
-      _db_thread_end();
-      return NULL;
-   }
-
-   /*
-    * Request processing loop 
-    */
-   while(1)
-   {
-      pthread_mutex_lock( &database.mux );
-
-      if( database.kill )
-      {
-         pthread_mutex_unlock( &database.mux );
-         _db_thread_end();
-         break;
-      }
-
-      u32 processed = 0;
-
-      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 ++;
-
-         pthread_mutex_lock( &database.mux );
-         vg_queue_pop( &database.queue );
-      }
-
-      pthread_mutex_unlock( &database.mux );
-
-      if( processed )
-         vg_low( "Processed %u database requests.\n", processed );
-
-      usleep(50000);
-   }
-
-   vg_low( "Database thread terminates.\n" );
-   return NULL;
-}
+bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type );
 
 /*
  * 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 ),
-   database.queue.size = DB_REQUEST_BUFFER_SIZE;
-
-   if( pthread_mutex_init( &database.mux, NULL ) )
-      return 0;
-
-   if( pthread_create( &database.thread, NULL, db_loop, NULL ) )
-      return 0;
-
-   return 1;
-}
-
-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)
-{
-   pthread_mutex_lock( &database.mux );
-   database.kill = 1;
-   pthread_mutex_unlock( &database.mux );
-   pthread_join( database.thread, NULL );
-}
-
-static void db_free(void)
-{
-   pthread_mutex_destroy( &database.mux );
-}
+bool db_init(void);
+void db_free(void);
 
+#if 0
 static db_request *db_alloc_request( u32 size )
 {
    u32 total = sizeof(db_request) + size;
@@ -402,5 +97,4 @@ static void db_send_request( db_request *request )
 {
    pthread_mutex_unlock( &database.mux );
 }
-
-#endif /* GAMESERVER_DB_H */
+#endif
index e3eea44ef18174b61753a20b21fc0b530ed9820f..5f29c7f61b9e5ffd6c2b923bad6c1aacee96a28e 100644 (file)
@@ -19,51 +19,11 @@ void _gs_replay_server_tick(void)
             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 )
@@ -72,7 +32,7 @@ void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *pa
       return;
    }
    
-   /* FIXME: Should we do this on another thread */
+   /* FIXME: Should we do this on another thread, or write to a buffer then send that for a save thread */
    FILE *fp = fopen( path, "w" );
    if( !fp )
    {
@@ -93,10 +53,21 @@ void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *pa
       frame_id = frame->previous_queue_item;
    }
 
+   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   netmsg_playerjoin join = { .inetmsg_id = k_inetmsg_playerjoin,
+                              .index = 255,
+                              .steamid = client->steamid };
+   u16 join_size = sizeof(netmsg_playerjoin);
+   fwrite( &join_size, sizeof(join_size), 1, fp );
+   fwrite( &join, sizeof(join), 1, fp );
+
    u32 frame_count = 0;
    while(1)
    {
       gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+      netmsg_playerframe *playerframe = &frame->network_frame;
+      playerframe->client = 255;
+
       fwrite( &frame->frame_size, sizeof(frame->frame_size), 1, fp );
       fwrite( &frame->network_frame, frame->frame_size, 1, fp );
       frame_count ++;
@@ -106,6 +77,12 @@ void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *pa
          break;
    }
 
+   netmsg_playerleave leave = { .inetmsg_id = k_inetmsg_playerleave,
+                                .index = 255 };
+   u16 leave_size = sizeof(netmsg_playerleave);
+   fwrite( &leave_size, sizeof(leave_size), 1, fp );
+   fwrite( &leave, sizeof(leave), 1, fp );
+
    fclose( fp );
    vg_success( "Written %u frames to disk.\n", frame_count );
 }
index e11059e6dcdab0b7016e08d21a7f3c0f6f1e60c7..69d3f4c2d20bc85bdc5df3c77d1f4e13ae0e9ed3 100644 (file)
@@ -3,6 +3,9 @@
 
 typedef struct gs_replay_frame gs_replay_frame;
 typedef struct gs_replay gs_replay;
+#if 0
+typedef struct gs_replay_playback gs_replay_playback;
+#endif
 
 struct gs_replay_frame
 {
@@ -19,21 +22,19 @@ struct _gs_replay
    struct gs_replay
    {
       u32 frame_count,
-          head_item;
+          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 );
+void _gs_test_replay( u8 client_id, const char *path );
+void _gs_transfer_tick(void);
diff --git a/src/gameserver_transfer.c b/src/gameserver_transfer.c
new file mode 100644 (file)
index 0000000..f42874c
--- /dev/null
@@ -0,0 +1,74 @@
+#include "gameserver_transfer.h"
+
+struct _gs_transfer _gs_transfer;
+
+static void _gs_packet_free( SteamNetworkingMessage_t *pMsg )
+{
+   gs_transfer *transfer = &_gs_transfer.transfers[ pMsg->m_nUserData ];
+   transfer->packet_reference_count --;
+}
+
+void _gs_transfer_tick(void)
+{
+   for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+   {
+      struct gameserver_client *client = &gameserver.clients[i];
+
+      if( client->active )
+      {
+         gs_transfer *transfer = &_gs_transfer.transfers[i];
+         if( transfer->state == k_transfer_state_none )
+            continue;
+
+         if( transfer->state == k_transfer_state_start )
+         {
+            vg_low( "Start transfer\n" );
+            transfer->state = k_transfer_state_transfer;
+         }
+         else if( transfer->state == k_transfer_state_transfer )
+         {
+            u32 size = GS_TRANSFER_BYTES_PER_TICK;
+            if( transfer->offset + size >= transfer->len )
+            {
+               size = transfer->len - transfer->offset;
+               transfer->state = k_transfer_state_all_written;
+            }
+
+            SteamNetworkingMessage_t *msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, 0 );
+            msg->m_conn = client->connection;
+            msg->m_nUserData = i;
+            msg->m_pData = transfer->file_buffer + transfer->offset;
+            msg->m_cbSize = size;
+            msg->m_pfnFreeData = _gs_packet_free;
+            msg->m_idxLane = 1;
+            transfer->packet_reference_count ++;
+            transfer->offset += size;
+            SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+         }
+         else if( transfer->state == k_transfer_state_all_written )
+         {
+            if( transfer->packet_reference_count == 0 )
+            {
+               vg_success( "Transfer to client %u completed.\n", (u32)i );
+               free( transfer->file_buffer );
+               transfer->file_buffer = NULL;
+               transfer->state = k_transfer_state_none;
+            }
+         }
+      }
+   }
+}
+
+void _gs_start_transfer( u8 client_id, const char *path )
+{
+   gs_transfer *transfer = &_gs_transfer.transfers[ client_id ];
+   if( transfer->state != k_transfer_state_none )
+   {
+      vg_error( "Tried to start transfer while one already active (%u)\n", (u32)client_id );
+      return;
+   }
+
+   transfer->file_buffer = vg_file_read( NULL, path, &transfer->len );
+   transfer->offset = 0;
+   transfer->state = k_transfer_state_start;
+}
diff --git a/src/gameserver_transfer.h b/src/gameserver_transfer.h
new file mode 100644 (file)
index 0000000..7f4f18b
--- /dev/null
@@ -0,0 +1,29 @@
+#pragma once
+#include "gameserver.h"
+
+#define GS_TRANSFER_BYTES_PER_TICK   256
+
+typedef struct gs_transfer gs_transfer;
+
+struct _gs_transfer
+{
+   struct gs_transfer
+   {
+      enum transfer_state
+      {
+         k_transfer_state_none,
+         k_transfer_state_start,
+         k_transfer_state_transfer,
+         k_transfer_state_all_written
+      }
+      state;
+
+      void *file_buffer;
+      u32 len, offset;
+      u32 packet_reference_count;
+   }
+   transfers[ NETWORK_MAX_PLAYERS ];
+}
+extern _gs_transfer;
+
+void _gs_start_transfer( u8 client_id, const char *path );
index cc71717314bfc5b81e622bef0252984e972db8ca..a2335a10d44c7e97784d9ae80f4a596c0f7b42c3 100644 (file)
@@ -541,7 +541,8 @@ static void poll_remote_connection(void){
    SteamNetworkingMessage_t *messages[32];
    int len;
 
-   for( int i=0; i<10; i++ ){
+   for( int i=0; i<10; i++ )
+   {
       len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
             hSteamNetworkingSockets, network_client.remote, 
             messages, VG_ARRAY_LEN(messages));
@@ -549,9 +550,16 @@ static void poll_remote_connection(void){
       if( len <= 0 )
          return;
 
-      for( int i=0; i<len; i++ ){
+      for( int i=0; i<len; i++ )
+      {
          SteamNetworkingMessage_t *msg = messages[i];
 
+         if( msg->m_idxLane == 1 )
+         {
+            vg_low( "Ignoring... %d bytes\n", msg->m_cbSize );
+            continue;
+         }
+
          if( msg->m_cbSize < sizeof(netmsg_blank) ){
             vg_warn( "Discarding message (too small: %d)\n", msg->m_cbSize );
             continue;
index f83f0f589dbd123377765d095cc04b9e31d9936f..8af4eac5b99d580a5ff39ef862aee37875667ba8 100644 (file)
@@ -34,16 +34,16 @@ static void bitpack_bytes( bitpack_ctx *ctx, u32 bytes, void *data ){
    ctx->bytes += bytes;
 }
 
-static u32 bitpack_qf32( bitpack_ctx *ctx, u32 bits, 
-                         f32 min, f32 max, f32 *v ){
-   u32 mask = (0x1 << bits) - 1;
-
-   if( ctx->mode == k_bitpack_compress ){
+static u32 bitpack_qf32( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, f32 *v )
+{
+   if( ctx->mode == k_bitpack_compress )
+   {
       u32 a = vg_quantf( *v, bits, min, max );
       bitpack_bytes( ctx, bits/8, &a );
       return a;
    }
-   else {
+   else 
+   {
       u32 a = 0;
       bitpack_bytes( ctx, bits/8, &a );
       *v = vg_dequantf( a, bits, min, max );
@@ -51,28 +51,30 @@ static u32 bitpack_qf32( bitpack_ctx *ctx, u32 bits,
    }
 }
 
-static void bitpack_qv2f( bitpack_ctx *ctx, u32 bits,
-                          f32 min, f32 max, v2f v ){
+static void bitpack_qv2f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v2f v )
+{
    for( u32 i=0; i<2; i ++ )
       bitpack_qf32( ctx, bits, min, max, v+i );
 }
 
-static void bitpack_qv3f( bitpack_ctx *ctx, u32 bits,
-                          f32 min, f32 max, v3f v ){
+static void bitpack_qv3f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v3f v )
+{
    for( u32 i=0; i<3; i ++ )
       bitpack_qf32( ctx, bits, min, max, v+i );
 }
 
-static void bitpack_qv4f( bitpack_ctx *ctx, u32 bits,
-                          f32 min, f32 max, v4f v ){
+static void bitpack_qv4f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v4f v )
+{
    for( u32 i=0; i<4; i ++ )
       bitpack_qf32( ctx, bits, min, max, v+i );
 }
 
-static void bitpack_qquat( bitpack_ctx *ctx, v4f quat ){
+static void bitpack_qquat( bitpack_ctx *ctx, v4f quat )
+{
    const f32 k_domain = 0.70710678118f;
 
-   if( ctx->mode == k_bitpack_compress ){
+   if( ctx->mode == k_bitpack_compress )
+   {
       v4f qabs;
       for( u32 i=0; i<4; i++ )
          qabs[i] = fabsf(quat[i]);
index 8b2112d1d164911a19a20bb5c0591d87539018a4..0ad47a32b70dc0efe17f8b364a620c7ab45c83a6 100644 (file)
@@ -42,8 +42,10 @@ struct netmsg_version{
 
 #define NETMSG_BOUNDARY_BIT 0x8000
 #define NETMSG_GATE_BOUNDARY_BIT 0x4000
+#define NETMSG_CAMERA_BOUNDARY_BIT 0x2000
+
 #define NETMSG_BOUNDARY_MASK (NETMSG_BOUNDARY_BIT|NETMSG_GATE_BOUNDARY_BIT)
-#define NETMSG_PLAYERFRAME_INSTANCE_ID 0x3
+#define NETMSG_PLAYERFRAME_REMOVED0 0x3
 #define NETMSG_PLAYERFRAME_HAVE_GLIDER 0x4
 #define NETMSG_PLAYERFRAME_GLIDER_ORPHAN 0x8
 
@@ -52,13 +54,10 @@ enum{ k_inetmsg_playerframe = 200 };
 struct netmsg_playerframe
 {
    u16 inetmsg_id;
-   u16 boundary_hash; /* used for animating correctly through gates, teleport..
-                         msb is a flip flop for teleporting
-                         second msb is flip flop for gate */
+   u16 boundary_hash;
    u8 client, subsystem, 
       flags, sound_effects;
    f64 timestamp;
-
    u8 animdata[];
 };
 
index fabafb90ba2dc77e32136142748b7b4b57b4957a..12db20a3c4f44a6e24fa885b8f305a1613db4026 100644 (file)
@@ -310,11 +310,13 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg )
 void remote_player_send_playerframe(void)
 {
    u8 sysid = localplayer.subsystem;
-   if( sysid >= k_player_subsystem_max ) return;
+   if( sysid >= k_player_subsystem_max ) 
+      return;
 
    struct player_subsystem_interface *sys = player_subsystems[sysid];
 
-   if( sys->animator_size ){
+   if( sys->animator_size )
+   {
       u32 max_buf_size = sys->animator_size + sizeof(localplayer.sfx_buffer),
           base_size = sizeof(struct netmsg_playerframe),
           max_packet = base_size + max_buf_size;
@@ -332,11 +334,16 @@ void remote_player_send_playerframe(void)
          .bytes = 0
       };
 
+      frame->timestamp = vg.time_real;
+      frame->boundary_hash = localplayer.boundary_hash;
+
+      /* camera 
+       * -------------------------------- */
+      bitpack_qv2f( &ctx, 8, 0.0f, VG_TAUf, (v2f){ fmod( localplayer.angles[0], VG_TAUf ), localplayer.angles[1] } );
+
       /* animation 
        * -----------------------------------------------*/
 
-      frame->timestamp = vg.time_real;
-      frame->boundary_hash = localplayer.boundary_hash;
       if( sys->network_animator_exchange )
          sys->network_animator_exchange( &ctx, sys->animator_data );
       else
@@ -352,29 +359,22 @@ void remote_player_send_playerframe(void)
       /* glider
        * -------------------------------------------------------------*/
 
-      if( localplayer.have_glider || 
-            (localplayer.subsystem == k_player_subsystem_glide) ) {
+      if( localplayer.have_glider || (localplayer.subsystem == k_player_subsystem_glide) ) 
          frame->flags |= NETMSG_PLAYERFRAME_HAVE_GLIDER;
-      }
 
       if( localplayer.glider_orphan )
          frame->flags |= NETMSG_PLAYERFRAME_GLIDER_ORPHAN;
 
-      if( frame->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|
-                          NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){
-         player_glide_remote_animator_exchange( &ctx,    
-                                                &player_glide.remote_animator );
-      }
+      if( frame->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|NETMSG_PLAYERFRAME_GLIDER_ORPHAN) )
+         player_glide_remote_animator_exchange( &ctx, &player_glide.remote_animator );
 
       /* ------- */
 
       u32 wire_size = base_size + ctx.bytes;
       netplayers.up_bytes += wire_size;
-
-      SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
-            hSteamNetworkingSockets, network_client.remote, 
-            frame, wire_size,
-            k_nSteamNetworkingSend_Unreliable, NULL );
+      SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, network_client.remote, 
+                                                                frame, wire_size, k_nSteamNetworkingSend_Unreliable, 
+                                                                NULL );
    }
 }
 
diff --git a/src/queuetest.c b/src/queuetest.c
new file mode 100644 (file)
index 0000000..e3ce9fb
--- /dev/null
@@ -0,0 +1,160 @@
+#define _DEFAULT_SOURCE
+#include <signal.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+
+#include "vg/vg_platform.h"
+#include "vg/vg_async2.h"
+#include "vg/vg_log.h"
+#include "vg/vg_m.h"
+
+vg_async_queue t0_q, t1_q = { .upper_memory_limit = 1024*1024*100 };
+pthread_t worker_thread, other_thread;
+pthread_mutex_t print_mutex;
+vg_rand rand2,rand3;
+
+volatile sig_atomic_t sig_stop;
+static void inthandler( int signum ) 
+{
+   sig_stop = 1;
+}
+
+static void siesta( u32 amt )
+{
+   //usleep( amt & 0x3 );
+   //usleep(amt);
+}
+
+static void some_callback( vg_async_task *task )
+{
+   //u32 us = vg_randu32( &rand2 ) % 1000;
+   pthread_mutex_lock( &print_mutex );
+   //vg_low( "message %u\n", us );
+   pthread_mutex_unlock( &print_mutex );
+   siesta( 1000 );
+}
+
+static void *worker_thread_func( void *_ )
+{
+   static u32 slower = 0;
+
+   while(1)
+   {
+      siesta(10000);
+      usleep(1);
+
+      slower ++;
+
+      if( slower >= 10000 )
+      {
+         slower = 0;
+         pthread_mutex_lock( &t1_q.lock );
+         u32 bytes = vg_queue_usage( &t1_q.queue );
+
+         f32 cap   = t1_q.upper_memory_limit,
+             usage = bytes,
+             pc    = usage / cap;
+
+         char bar[ 21 ];
+         bar[20] = '\0';
+
+         for( u32 i=0; i<20; i ++ )
+         {
+            f32 v = (f32)i / 20.0f;
+            if( pc > v )
+               bar[i] = '#';
+            else
+               bar[i] = ' ';
+         }
+
+         pthread_mutex_lock( &print_mutex );
+         if( pc < 0.9f )
+            vg_info( "Usage: %08u, %03u %08u |%s|\n", bytes, t1_q.queue.allocation_count, t1_q.queue.head_offset, bar );
+         else
+            vg_warn( "Usage: %08u, %03u %08u |%s|\n", bytes, t1_q.queue.allocation_count, t1_q.queue.head_offset, bar );
+         pthread_mutex_unlock( &print_mutex );
+         pthread_mutex_unlock( &t1_q.lock );
+      }
+
+      if( vg_async_consume( &t1_q, 100 ) )
+         break;
+   }
+
+   return NULL;
+}
+
+static void *other_thread_func( void *_ )
+{
+   while( !sig_stop )
+   {
+      siesta(100000);
+      vg_async_task *task = vg_allocate_async_task( &t1_q, vg_randu32( &rand3 ) % (20*1024), 1 );
+      task->handler = some_callback;
+      vg_async_task_dispatch( &t1_q, task );
+      siesta(100 + (vg_randu32( &rand3 ) % 100) );
+   }
+
+   vg_info( "dies\n" );
+
+   return NULL;
+}
+
+int main( int argc, char *argv[] )
+{
+   vg_log_init();
+
+   signal( SIGINT, inthandler );
+   signal( SIGQUIT, inthandler );
+   signal( SIGPIPE, SIG_IGN );
+
+   vg_rand rand;
+   vg_rand_seed( &rand, 34 );
+   vg_rand_seed( &rand2, 38 );
+   vg_rand_seed( &rand3, 384 );
+
+   if( !vg_init_async_queue( &t0_q ) )
+      return 0;
+
+   if( !vg_init_async_queue( &t1_q ) )
+      return 0;
+
+   if( pthread_create( &worker_thread, NULL, worker_thread_func, NULL ) )
+      return 0;
+
+   if( pthread_create( &other_thread, NULL, other_thread_func, NULL ) )
+      return 0;
+
+   if( pthread_mutex_init( &print_mutex, NULL ) )
+      return 0;
+
+   vg_low( "Started\n" );
+
+   u32 tocker = 0;
+
+   while( !sig_stop )
+   {
+      vg_async_task *task = vg_allocate_async_task( &t1_q, vg_randu32( &rand ) % (70*1024), 1 );
+      task->handler = some_callback;
+      vg_async_task_dispatch( &t1_q, task );
+
+      siesta(1 + (vg_randu32( &rand ) % 10) + tocker );
+
+      i32 mv = vg_randu32(&rand) % 10;
+      mv -= 5;
+
+      if( tocker < 10 )
+         tocker = 10;
+      else
+         tocker += mv;
+   }
+
+   vg_async_queue_end( &t1_q, k_async_quit_when_empty );
+   pthread_join( worker_thread, NULL );
+   pthread_join( other_thread, NULL );
+
+   vg_low( "Stopted\n" );
+   return 1;
+}
+
+#include "vg/vg_async2.c"