returning to sensibilities
authorhgn <hgodden00@gmail.com>
Fri, 4 Apr 2025 07:11:06 +0000 (08:11 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 4 Apr 2025 07:11:06 +0000 (08:11 +0100)
15 files changed:
src/gameserver.c
src/gameserver.h
src/gameserver_database.c
src/gameserver_database.h
src/gameserver_replay.c
src/gameserver_replay.h
src/gameserver_requests.c [new file with mode: 0644]
src/gameserver_requests.h [new file with mode: 0644]
src/gameserver_transfer.c [deleted file]
src/gameserver_transfer.h [deleted file]
src/network.c
src/network.h
src/network_msg.h
src/world_routes.c
src/world_sfd.c

index e55d21b693f3f399a8ed0d8f4583812cf96ba5b2..008c8fcc9fba96790b4772cb8c1c8f1068f222d4 100644 (file)
@@ -17,20 +17,25 @@ volatile sig_atomic_t sig_stop;
 #include "vg/vg_m.h"
 #include "vg/vg_msg.h"
 #include "gameserver_replay.h"
-#include "gameserver_transfer.h"
+#include "gameserver_requests.h"
+
+struct _gameserver _gameserver = 
+{
+   .auth_mode = eServerModeAuthentication,
+   .tasks =
+   {
+      .upper_memory_limit = 1024*1024*8
+   }
+};
 
 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 )
+static void inthandler( int signum ) 
 {
-   SteamAPI_SteamNetworkingMessage_t_Release( msg );
+   sig_stop = 3;
 }
 
 /*
@@ -38,7 +43,7 @@ static void release_message( SteamNetworkingMessage_t *msg )
  */
 static void gameserver_send_to_client( i32 client_id, const void *pData, u32 cbData, int nSendFlags )
 {
-   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   struct gameserver_client *client = &_gameserver.clients[ client_id ];
 
    if( !client->steamid )
       return;
@@ -54,7 +59,7 @@ static void gameserver_send_to_all( int ignore, const void *pData, u32 cbData, i
 {
    for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
-      struct gameserver_client *client = &gameserver.clients[i];
+      struct gameserver_client *client = &_gameserver.clients[i];
 
       if( i != ignore )
          gameserver_send_to_client( i, pData, cbData, nSendFlags );
@@ -63,7 +68,7 @@ static void gameserver_send_to_all( int ignore, const void *pData, u32 cbData, i
 
 static void gameserver_send_version_to_client( int index )
 {
-   struct gameserver_client *client = &gameserver.clients[index];
+   struct gameserver_client *client = &_gameserver.clients[index];
 
    netmsg_version version;
    version.inetmsg_id = k_inetmsg_version;
@@ -79,7 +84,7 @@ static void gameserver_send_version_to_client( int index )
  */
 static void gameserver_player_join( int index )
 {
-   struct gameserver_client *joiner = &gameserver.clients[index];
+   struct gameserver_client *joiner = &_gameserver.clients[index];
 
    SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes( hSteamNetworkingSockets, joiner->connection, 2,
                                                               (int[]){ 0,0 }, (u16[]){ 10, 1 } );
@@ -106,7 +111,7 @@ static void gameserver_player_join( int index )
 
    for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
-      struct gameserver_client *client = &gameserver.clients[i];
+      struct gameserver_client *client = &_gameserver.clients[i];
 
       if( (i == index) || !client->steamid )
          continue;
@@ -146,8 +151,8 @@ static void gameserver_player_join( int index )
  * Handle server update that player has left
  */
 static void gameserver_player_leave( int index ){
-   if( gameserver.auth_mode == eServerModeAuthentication ){
-      if( !gameserver.clients[ index ].steamid )
+   if( _gameserver.auth_mode == eServerModeAuthentication ){
+      if( !_gameserver.clients[ index ].steamid )
          return;
    }
 
@@ -166,18 +171,18 @@ static void gameserver_update_all_knowledge( int client, int clear );
  * Deletes client at index and disconnects the connection handle if it was 
  * set.
  */
-static void remove_client( int index ){
-   struct gameserver_client *client = &gameserver.clients[index];
-   if( client->connection ){
-      SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
-            hSteamNetworkingSockets, client->connection, -1 );
-      SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-            hSteamNetworkingSockets, client->connection,
-            k_ESteamNetConnectionEnd_Misc_InternalError,
-            NULL, 1 );
+static void remove_client( int index )
+{
+   struct gameserver_client *client = &_gameserver.clients[index];
+   if( client->connection )
+   {
+      SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, client->connection, -1 );
+      SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, client->connection,
+                                                        k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
    }
    memset( client, 0, sizeof(struct gameserver_client) );
    gameserver_update_all_knowledge( index, 1 );
+   _gs_requests_client_disconnect( index );
 }
 
 /*
@@ -192,7 +197,7 @@ static void handle_new_connection( HSteamNetConnection conn )
 
    for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
-      if( !gameserver.clients[i].active )
+      if( !_gameserver.clients[i].active )
       {
          index = i;
          break;
@@ -206,17 +211,20 @@ static void handle_new_connection( HSteamNetConnection conn )
       return;
    }
 
-   struct gameserver_client *client = &gameserver.clients[index];
+   struct gameserver_client *client = &_gameserver.clients[index];
    EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection( hSteamNetworkingSockets, conn );
 
    if( accept_status == k_EResultOK )
    {
       vg_success( "Accepted client (id: %u, index: %d)\n", conn, index );
 
+      _gameserver.global_uid ++;
+      client->session_uid = _gameserver.global_uid;
+
       client->active = 1;
       client->connection = conn;
 
-      SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
+      SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, _gameserver.client_group );
       SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
    }
    else
@@ -303,7 +311,7 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
       return;
    }
 
-   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   struct gameserver_client *client = &_gameserver.clients[ client_id ];
 
    if( client->version )
    {
@@ -323,7 +331,7 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
    /* this is the sign on point for non-auth servers,
     * for auth servers it comes at the end of rx_auth 
     */
-   if( gameserver.auth_mode != eServerModeAuthentication )
+   if( _gameserver.auth_mode != eServerModeAuthentication )
    {
       client->steamid = k_steamid_max;
       gameserver_player_join( client_id );
@@ -335,7 +343,7 @@ static void gameserver_rx_version( SteamNetworkingMessage_t *msg )
  * to the client list first.
  */
 static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
-   if( gameserver.auth_mode != eServerModeAuthentication ){
+   if( _gameserver.auth_mode != eServerModeAuthentication ){
       vg_warn( "Running server without authentication. "
                "Connection %u tried to authenticate.\n", msg->m_conn );
       return;
@@ -350,7 +358,7 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
       return;
    }
 
-   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   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", 
@@ -387,7 +395,7 @@ static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
    u32 ticket_len = 1024;
 
    int success = SteamEncryptedAppTicket_BDecryptTicket( auth->ticket, auth->ticket_length, decrypted,
-                                                         &ticket_len, gameserver.app_symmetric_key,
+                                                         &ticket_len, _gameserver.app_symmetric_key,
                                                          k_nSteamEncryptedAppTicketSymmetricKeyLen );
 
    if( !success )
@@ -435,26 +443,6 @@ static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size )
       return 1;
 }
 
-struct db_set_username_thread_data 
-{
-   u64 steamid;
-   char username[ NETWORK_USERNAME_MAX ];
-};
-
-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 )
-      return;
-
-   int admin = 0;
-   if( inf->steamid == 76561198072130043 )
-      admin = 2;
-
-   db_updateuser( inf->steamid, inf->username, admin );
-}
-
 static int gameserver_item_eq( struct gameserver_item *ia, struct gameserver_item *ib )
 {
    if( ia->hash == ib->hash )
@@ -472,8 +460,8 @@ static void gameserver_update_knowledge_table( int client0, int client1, int cle
 {
    u32 idx = network_pair_index( client0, client1 );
 
-   struct gameserver_client *c0 = &gameserver.clients[client0],
-                            *c1 = &gameserver.clients[client1];
+   struct gameserver_client *c0 = &_gameserver.clients[client0],
+                            *c1 = &_gameserver.clients[client1];
 
    u8 flags = 0x00;
 
@@ -487,7 +475,7 @@ static void gameserver_update_knowledge_table( int client0, int client1, int cle
          flags |= CLIENT_KNOWLEDGE_SAME_WORLD1;
    }
 
-   gameserver.client_knowledge_mask[idx] = flags;
+   _gameserver.client_knowledge_mask[idx] = flags;
 }
 
 /*
@@ -502,7 +490,7 @@ static void gameserver_update_all_knowledge( int client, int clear )
       if( i == client )
          continue;
 
-      struct gameserver_client *ci = &gameserver.clients[i];
+      struct gameserver_client *ci = &_gameserver.clients[i];
 
       if( ci->steamid )
          gameserver_update_knowledge_table( client, i, clear );
@@ -523,18 +511,18 @@ static void gameserver_propogate_player_frame( int client_id, netmsg_playerframe
    basic->subsystem = 4; /* (.._basic_info: 24f*3 animator ) */
    basic->sound_effects = 0;
 
-   struct gameserver_client *c0 = &gameserver.clients[client_id];
+   struct gameserver_client *c0 = &_gameserver.clients[client_id];
 
    for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
    {
       if( i == client_id )
          continue;
 
-      struct gameserver_client *ci = &gameserver.clients[i];
+      struct gameserver_client *ci = &_gameserver.clients[i];
       int send_full = 0;
 
       u32 k_index = network_pair_index( client_id, i );
-      u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
+      u8 k_mask = _gameserver.client_knowledge_mask[ k_index ];
       if( (k_mask & CLIENT_KNOWLEDGE_SAME_WORLD0) )
          send_full = 1;
 
@@ -552,7 +540,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
    int client_id = gameserver_conid( msg->m_conn );
    if( client_id == -1 ) return;
 
-   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   struct gameserver_client *client = &_gameserver.clients[ client_id ];
 
    if( tmp->inetmsg_id == k_inetmsg_playerusername )
    {
@@ -575,17 +563,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
 
       u32 propsize = sizeof(netmsg_playerusername) + chs + 1;
       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;
-      inf->steamid = client->steamid;
-      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
+      db_action_set_username( client->steamid, client->username );
    }
    else if( tmp->inetmsg_id == k_inetmsg_playerframe )
    {
@@ -640,7 +618,7 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
       }
       else if( !strcmp( prop->msg, "transfer" ) )
       {
-         _gs_start_transfer( client_id, "/tmp/server-replay.replay" );
+         //_gs_start_transfer( client_id, "/tmp/server-replay.replay" );
          //_gs_test_replay( client_id, "/tmp/server-replay.replay" );
       }
 
@@ -671,8 +649,14 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg )
    }
 }
 
-static void gameserver_request_respond( enum request_status status,
-                                        netmsg_request *res, vg_msg *body,
+u32 gameserver_get_current_week(void)
+{
+   return time(NULL) / (7*24*60*60);
+}
+
+
+#if 0
+static void gameserver_request_respond( enum request_status status, netmsg_request *res, vg_msg *body,
                                         SteamNetworkingMessage_t *msg )
 {
    int client_id = gameserver_conid( msg->m_conn );
@@ -686,22 +670,17 @@ static void gameserver_request_respond( enum request_status status,
 
    res->status = status;
 
-   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
-         hSteamNetworkingSockets, msg->m_conn,
-         res, sizeof(netmsg_request) + len,
-         k_nSteamNetworkingSend_Reliable, NULL );
+   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, msg->m_conn,
+                                                             res, sizeof(netmsg_request) + len,
+                                                             k_nSteamNetworkingSend_Reliable, NULL );
 
-   release_message( msg );
+   SteamAPI_SteamNetworkingMessage_t_Release( msg );
 }
 
 struct user_request_thread_data {
    SteamNetworkingMessage_t *msg;
 };
 
-static u32 gameserver_get_current_week(void){
-   return time(NULL) / (7*24*60*60);
-}
-
 static enum request_status gameserver_cat_table( 
       vg_msg *msg, 
       const char *mod, const char *route, u32 week, const char *alias )
@@ -766,11 +745,11 @@ static void gameserver_process_user_request( db_request *db_req )
    int client_id = gameserver_conid( msg->m_conn );
    if( client_id == -1 )
    {
-      release_message( msg );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
       return;
    }
 
-   struct gameserver_client *client = &gameserver.clients[ client_id ];
+   struct gameserver_client *client = &_gameserver.clients[ client_id ];
 
    netmsg_request *req = (netmsg_request *)msg->m_pData;
    vg_msg data;
@@ -791,20 +770,21 @@ static void gameserver_process_user_request( db_request *db_req )
       return;
    }
 
-   if( !strcmp( endpoint, "scoreboard" ) ){
+   if( !strcmp( endpoint, "scoreboard" ) )
+   {
       const char *mod = vg_msg_getkvstr( &data, "mod" );
       const char *route = vg_msg_getkvstr( &data, "route" );
       u32 week;
       vg_msg_getkvintg( &data, "week", k_vg_msg_u32, &week, NULL );
       
-      if( week == NETWORK_LEADERBOARD_CURRENT_WEEK ){
-         gameserver_cat_table( &body, mod, route, 
-                               gameserver_get_current_week(), "rows_weekly" );
+      if( week == NETWORK_LEADERBOARD_CURRENT_WEEK )
+      {
+         gameserver_cat_table( &body, mod, route, gameserver_get_current_week(), "rows_weekly" );
       }
-      else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK ){
+      else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK )
+      {
          gameserver_cat_table( &body, mod, route, 0, "rows" );
-         gameserver_cat_table( &body, mod, route, 
-                               gameserver_get_current_week(), "rows_weekly" );
+         gameserver_cat_table( &body, mod, route, gameserver_get_current_week(), "rows_weekly" );
       }
       else 
          gameserver_cat_table( &body, mod, route, week, "rows" );
@@ -857,6 +837,7 @@ static void gameserver_process_user_request( db_request *db_req )
       gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
    }
 }
+#endif
 
 static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
 {
@@ -865,33 +846,18 @@ static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
    int client_id = gameserver_conid( msg->m_conn );
    if( client_id == -1 )
    {
-      release_message( msg );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
       return;
    }
 
    if( tmp->inetmsg_id == k_inetmsg_request )
    {
-      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
+      _gs_handle_request_message( client_id, msg );
    }
    else 
    {
       vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
-      release_message( msg );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
    }
 }
 
@@ -908,7 +874,7 @@ static void process_network_message( SteamNetworkingMessage_t *msg )
    if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) )
    {
       gameserver_rx_200_300( msg );
-      release_message( msg );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
    }
    else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) )
    {
@@ -922,7 +888,7 @@ static void process_network_message( SteamNetworkingMessage_t *msg )
          gameserver_rx_version( msg );
       else 
          vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
-      release_message( msg );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
    }
 }
 
@@ -935,7 +901,7 @@ static void poll_connections(void)
    {
       len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
             hSteamNetworkingSockets,
-            gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
+            _gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
 
       if( len <= 0 )
          return;
@@ -955,6 +921,8 @@ u64 seconds_to_server_ticks( f64 s )
 
 int main( int argc, char *argv[] )
 {
+   _gameserver.thread = pthread_self();
+
    vg_log_init();
 
    signal( SIGINT, inthandler );
@@ -965,34 +933,37 @@ int main( int argc, char *argv[] )
    while( vg_argp( argc, argv ) )
    {
       if( vg_long_opt( "noauth", "Disable server authentication"  ) )
-         gameserver.auth_mode = eServerModeNoAuthentication;
+         _gameserver.auth_mode = eServerModeNoAuthentication;
 
       if( vg_long_opt( "replay-info", "Print replay info periodically" ) )
          _gs_replay.print_info = 1;
    }
+
+   if( !vg_init_async_queue( &_gameserver.tasks ) )
+      goto E0;
    
    vg_set_mem_quota( 80*1024*1024 );
    vg_alloc_quota();
    if( !db_init() )
-   {
-      return 0;
-   }
+      goto E0;
+
+   _gs_requests_init();
 
    /* steamworks init 
     * --------------------------------------------------------------- */
    steamworks_ensure_txt( "2103940" );
-   if( gameserver.auth_mode == eServerModeAuthentication )
+   if( _gameserver.auth_mode == eServerModeAuthentication )
    {
-      if( !vg_load_steam_symetric_key( "application_key", gameserver.app_symmetric_key ))
-         return 0;
+      if( !vg_load_steam_symetric_key( "application_key", _gameserver.app_symmetric_key ))
+         goto E1;
    }
    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;
+      goto E1;
    }
 
    void *hSteamGameServer = SteamAPI_SteamGameServer();
@@ -1018,31 +989,43 @@ int main( int argc, char *argv[] )
    localAddr.m_port = NETWORK_PORT;
 
    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 );
+   _gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup( hSteamNetworkingSockets );
+   _gameserver.ticks = seconds_to_server_ticks( 30.0 * 60.0 );
 
-   while( !sig_stop )
+   while(1)
    {
       steamworks_event_loop( hsteampipe );
       poll_connections();
       _gs_replay_server_tick();
-      _gs_transfer_tick();
+      _gs_requests_tick();
 
       usleep(10000);
-      gameserver.ticks ++;
+      _gameserver.ticks ++;
 
-      /* consume tasks here */
+      if( sig_stop == 3 )
+      {
+         vg_info( "Shutting down...\n" );
+         sig_stop = 1;
+         vg_async_queue_end( &_gs_db.tasks, k_async_quit_when_empty );
+      }
+
+      if( vg_async_consume( &_gameserver.tasks, 100 ) )
+         break;
    }
+
+   vg_info( "Program ends\n" );
    
-   SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
+   SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, _gameserver.client_group );
    SteamAPI_ISteamNetworkingSockets_CloseListenSocket( hSteamNetworkingSockets, listener );
    
-   vg_info( "Shutting down\n..." );
+
    SteamGameServer_Shutdown();
-   db_free();
-   return 0;
+E1:db_free();
+E0:return 0;
 }
 
 #include "gameserver_replay.c"
-#include "gameserver_transfer.c"
+#include "gameserver_requests.c"
 #include "gameserver_database.c"
+#include "vg/vg_async2.c"
+#include "vg/vg_mem_pool.c"
index ad5b95c4190c7be66bed0e9f6276f3676354ec18..d22331e7566ec2ec8b48afbfe5169c42da74e08d 100644 (file)
@@ -6,6 +6,7 @@
 #include "vg/vg_steam_networking.h"
 #include "vg/vg_steam_http.h"
 #include "vg/vg_steam_auth.h"
+#include "vg/vg_async2.h"
 #include "network_msg.h"
 #include "network_common.h"
 #include <sys/socket.h>
 #define CLIENT_KNOWLEDGE_SAME_WORLD1 0x2
 #define CLIENT_KNOWLEDGE_FRIENDS     0x4 /* unused */
 
-struct {
+#define THREAD_0 VG_ASSERT( pthread_equal( pthread_self(), _gameserver.thread ) );
+#define THREAD_1 VG_ASSERT( pthread_equal( pthread_self(), _gs_db.worker_thread ) );
+
+struct _gameserver
+{
    HSteamNetPollGroup client_group;
    EServerMode auth_mode;
 
@@ -36,18 +41,22 @@ struct {
       u32  region_flags;
 
       u64  steamid;
+      u64  session_uid;
    }
    clients[ NETWORK_MAX_PLAYERS ];
 
    u8 client_knowledge_mask[ (NETWORK_MAX_PLAYERS*(NETWORK_MAX_PLAYERS-1))/2 ];
    u8 app_symmetric_key[ k_nSteamEncryptedAppTicketSymmetricKeyLen ];
    u64 ticks;
+   u64 global_uid;
+
+   vg_async_queue tasks;
+   pthread_t thread;
 }
-static gameserver = {
-   .auth_mode = eServerModeAuthentication
-};
+extern _gameserver;
 
 extern ISteamNetworkingSockets *hSteamNetworkingSockets;
 extern ISteamNetworkingUtils *hSteamNetworkingUtils;
 
 u64 seconds_to_server_ticks( f64 s );
+u32 gameserver_get_current_week(void);
index ba4ca02c23ddf7ef74b2dfc7c810301beb1147c2..f7503cc9355caf0a096662baebbae6e28aab854c 100644 (file)
@@ -1,20 +1,38 @@
 #include "gameserver_database.h" 
+#include "vg/vg_async2.h"
 
-struct _gs_db _gs_db;
+struct _gs_db _gs_db = 
+{
+   .tasks = 
+   {
+      .upper_memory_limit = 1024*1024*40
+   }
+};
+
+/* database system 
+ * ----------------------------------------------------------- */
 
 /*
  * Log the error code (or carry on if its OK).
  */
-void log_sqlite3( int code )
+static void log_sqlite3( int code )
 {
+   THREAD_1;
    if( code == SQLITE_OK ) 
       return;
 
    vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
 }
 
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+{
+   THREAD_1;
+   return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+}
+
 sqlite3_stmt *db_stmt( const char *code )
 {
+   THREAD_1;
 #ifdef DB_LOG_SQL_STATEMENTS
    vg_low( code );
 #endif
@@ -32,9 +50,73 @@ sqlite3_stmt *db_stmt( const char *code )
    return stmt;
 }
 
-int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+static void *database_worker_thread(void *_)
 {
-   return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+   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)
+   {
+      usleep(50000);
+
+      if( vg_async_consume( &_gs_db.tasks, 100 ) )
+         break;
+   }
+
+   sqlite3_close( _gs_db.db );
+   vg_low( "Database thread terminates.\n" );
+   vg_async_queue_end( &_gameserver.tasks, k_async_quit_immediate );
+   return NULL;
+}
+
+bool db_init(void)
+{
+   if( !vg_init_async_queue( &_gs_db.tasks ) )
+      return 0;
+
+   if( pthread_create( &_gs_db.worker_thread, NULL, database_worker_thread, NULL ) )
+      return 0;
+
+   return 1;
+}
+
+void db_free(void)
+{
+   pthread_join( _gs_db.worker_thread, NULL );
 }
 
 /*
@@ -58,6 +140,9 @@ static int db_verify_charset( const char *str, int mincount )
    return 0;
 }
 
+/* database internals
+ * ----------------------------------------------------------- */
+
 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 ) ) 
@@ -69,7 +154,8 @@ bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32
    vg_strcat( &a, ":" );
    vg_strcat( &a, run_uid );
 
-   if( week ){
+   if( week )
+   {
       vg_strcat( &a, "#" );
       vg_strcati32( &a, week );
    }
@@ -79,6 +165,8 @@ bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32
 
 i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid )
 {
+   THREAD_1;
+
    char buf[ 512 ];
    vg_str q;
    vg_strnull( &q, buf, 512 );
@@ -110,6 +198,8 @@ i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid )
 
 bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster )
 {
+   THREAD_1;
+
    /* auto create table 
     * ------------------------------------------*/
    char buf[ 512 ];
@@ -172,6 +262,8 @@ bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, boo
 
 bool db_updateuser( u64 steamid, const char *username, int admin )
 {
+   THREAD_1;
+
    sqlite3_stmt *stmt = db_stmt( "INSERT OR REPLACE INTO users (steamid, name, type) VALUES (?,?,?);" );
 
    if( stmt )
@@ -200,8 +292,11 @@ bool db_updateuser( u64 steamid, const char *username, int admin )
 
 bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type )
 {
+   THREAD_1;
+
    sqlite3_stmt *stmt = db_stmt( "SELECT * FROM users WHERE steamid = ?;" );
-   if( !stmt ) return 0;
+   if( !stmt ) 
+      return 0;
 
    sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
    int fc = sqlite3_step( stmt );
@@ -226,105 +321,106 @@ bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out
    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
+struct task_set_username
+{
+   u64 steamid;
+   char name[NETWORK_USERNAME_MAX];
+};
 
-static void *database_worker_thread(void *_)
+static void task_set_username( vg_async_task *task )
 {
-   int rc = sqlite3_open( "highscores.db", &_gs_db.db );
+   THREAD_1;
+   struct task_set_username *info = (void *)task->data;
+   if( info->steamid == k_steamid_max )
+      return;
 
-   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( "INSERT OR REPLACE INTO users (steamid, name) VALUES (?,?);" );
 
-   sqlite3_stmt *stmt = db_stmt( "CREATE TABLE IF NOT EXISTS \n"
-                                 " users(steamid BIGINT UNIQUE, name VARCHAR(128), type INT);" );
    if( stmt )
    {
+      sqlite3_bind_int64( stmt, 1, *((i64*)(&info->steamid)) ); 
+      db_sqlite3_bind_sz( stmt, 2, info->name );
+
       int fc = sqlite3_step( stmt );
       sqlite3_finalize(stmt);
 
       if( fc == SQLITE_DONE )
-      {
-         vg_success( "Created users table\n" );
-         db_updateuser( 76561198072130043, "harry", 2 );
-      }
+         vg_success( "Updated username for %lu (%s)\n", info->steamid, info->name );
       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 );
+void db_action_set_username( u64 steamid, const char *username )
+{
+   THREAD_0;
+
+   vg_async_task *task = vg_allocate_async_task( &_gs_db.tasks, sizeof(struct task_set_username), 1 );
+   task->handler = task_set_username;
+   struct task_set_username *info = (void *)task->data;
+   info->steamid = steamid;
+   vg_strncpy( username, info->name, sizeof(info->name), k_strncpy_always_add_null );
+   vg_async_task_dispatch( &_gs_db.tasks, task );
+}
 
-#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);
-   }
+enum request_status gameserver_cat_table( 
+      vg_msg *msg, 
+      const char *mod, const char *route, u32 week, const char *alias )
+{
+   char table_name[ DB_TABLE_UID_MAX ];
+   if( !db_get_highscore_table_name( mod, route, week, table_name ) )
+      return k_request_status_out_of_memory;
 
-   vg_low( "Database thread terminates.\n" );
-   return NULL;
-}
+   char buf[512];
+   vg_str q;
+   vg_strnull( &q, buf, 512 );
+   vg_strcat( &q, "SELECT * FROM \"" );
+   vg_strcat( &q, table_name );
+   vg_strcat( &q, "\" ORDER BY time ASC LIMIT 10;" );
+   if( !vg_strgood(&q) )
+      return k_request_status_out_of_memory;
 
-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;
+   sqlite3_stmt *stmt = db_stmt( q.buffer );
+   if( !stmt )
+      return k_request_status_database_error;
 
-   if( pthread_mutex_init( &_gs_db.mux, NULL ) )
-      return 0;
+   vg_msg_frame( msg, alias );
+   for( u32 i=0; i<10; i ++ ){
+      int fc = sqlite3_step( stmt );
 
-   if( pthread_create( &_gs_db.thread, NULL, _gs_db.worker_thread, NULL ) )
-      return 0;
+      if( fc == SQLITE_ROW ){
+         i32 time = sqlite3_column_int( stmt, 1 );
+         i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
+         u64 steamid = *((u64 *)&steamid_i64);
 
-   return 1;
-}
+         if( steamid == k_steamid_max )
+            continue;
 
-void db_free(void)
-{
-   pthread_join( _gs_db.thread, NULL );
-   pthread_mutex_destroy( &_gs_db.mux );
+         vg_msg_frame( msg, "" );
+         vg_msg_wkvnum( msg, "time", k_vg_msg_u32, 1, &time );
+         vg_msg_wkvnum( msg, "steamid", k_vg_msg_u64, 1, &steamid );
+
+         char username[32];
+         if( db_getuserinfo( steamid, username, sizeof(username), NULL ) )
+            vg_msg_wkvstr( msg, "username", username );
+         vg_msg_end_frame( msg );
+      }
+      else if( fc == SQLITE_DONE ){
+         break;
+      }
+      else {
+         log_sqlite3( fc );
+         break;
+      }
+   }
+
+   sqlite3_finalize( stmt );
+   vg_msg_end_frame( msg );
+   return k_request_status_ok;
 }
+
+
index d222aea291adb7b37aaf28aa7d143fc7cb8df809..0b1a7036dcedc650e4c4d0f261873e8d6942aa4a 100644 (file)
@@ -2,6 +2,8 @@
 
 #include "vg/vg_log.h"
 #include "vg/vg_mem_queue.h"
+#include "vg/vg_async2.h"
+#include "vg/vg_msg.h"
 #include "network_common.h"
 #include "dep/sqlite3/sqlite3.h"
 #include <pthread.h>
@@ -10,7 +12,6 @@
 #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 
@@ -23,14 +24,14 @@ struct db_request
 struct _gs_db
 {
    sqlite3 *db;
-   pthread_t thread;
-   pthread_mutex_t mux;
-
-   vg_queue queue;
-   int kill;
+   pthread_t worker_thread;
+   vg_async_queue tasks;
 }
 extern _gs_db;
 
+void db_action_set_username( u64 steamid, const char *username );
+
+#if 0
 /*
  * Perpare statement and auto throw away if fails. Returns NULL on failure.
  */
@@ -66,6 +67,7 @@ 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 );
+#endif
 
 /*
  * Create database connection and users table
@@ -73,32 +75,6 @@ bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out
 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
+enum request_status gameserver_cat_table( 
+      vg_msg *msg, 
+      const char *mod, const char *route, u32 week, const char *alias );
index 5f29c7f61b9e5ffd6c2b923bad6c1aacee96a28e..2b1b2b86c85243cde0f94e7c18c0db23395f956e 100644 (file)
@@ -5,92 +5,89 @@ 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" );
+struct serialized_replay_header
+{
+   u32 total_frames,
+       head_offset;
+};
 
-      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 );
-      }
+struct serialized_replay
+{
+   char path[ 1024 ];
+   struct serialized_replay_header header;
+   u32 buffer_size;
+   u8 buffer[];
+};
+
+static void task_write_replay( vg_async_task *task )
+{
+   THREAD_1;
+   struct serialized_replay *info = (void *)task->data;
+
+   FILE *fp = fopen( info->path, "w" );
+   if( !fp )
+   {
+      vg_error( "Failed to open '%s' for writing\n", fp );
+      return;
    }
+
+   fwrite( &info->header, sizeof(info->header), 1, fp );
+   fwrite( &info->buffer, info->buffer_size, 1, fp );
+   fclose( fp );
+
+   vg_success( "Saved replay: %s\n", info->path );
 }
 
 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 );
+   u64 min_ticks = _gameserver.ticks - seconds_to_server_ticks( server_duration );
 
    gs_replay *replay = &_gs_replay.replays[ client_id ];
-   if( replay->frame_count == 0 )
+   if( replay->ring_buffer.allocation_count == 0 )
    {
       vg_error( "Replay frame count is 0, nothing to save..\n" );
       return;
    }
    
    /* 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 )
-   {
-      vg_error( "Failed to open '%s' for writing\n", fp );
-      return;
-   }
 
-   u32 frame_id = replay->head_item;
+   u32 frame_id = replay->ring_buffer.head_offset;
+   u32 buffer_size = 0, total_frames = 0;
    while(1)
    {
-      gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+      gs_replay_frame *frame = vg_queue_data( &replay->ring_buffer, frame_id );
+      buffer_size += vg_queue_item_size( &replay->ring_buffer, frame_id );
+      total_frames ++;
+
       if( frame->tick_recorded < min_ticks )
          break;
 
-      if( frame->previous_queue_item == VG_MEM_QUEUE_INVALID )
+      if( !vg_queue_previous( &replay->ring_buffer, frame_id, &frame_id ) )
          break;
-
-      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 task_size = sizeof( struct serialized_replay ) + buffer_size;
+   vg_async_task *task = vg_allocate_async_task( &_gs_db.tasks, task_size, 1 );
+   struct serialized_replay *replay_inf = (void *)task->data;
+   vg_queue_memcpy( &replay->ring_buffer, replay_inf->buffer, frame_id, buffer_size );
+   task->handler = task_write_replay;
+   replay_inf->buffer_size = buffer_size;
+   replay_inf->header.total_frames = total_frames;
+   replay_inf->header.head_offset = buffer_size - vg_queue_item_size( &replay->ring_buffer, replay->ring_buffer.head_offset );
+   vg_strncpy( path, replay_inf->path, sizeof(replay_inf->path), k_strncpy_always_add_null );
 
-   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 ++;
-
-      frame_id = frame->next_queue_item;
-      if( frame_id == VG_MEM_QUEUE_INVALID )
-         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 );
+   vg_queue_item *first = (void*)replay_inf->buffer;
+   first->prev_size = 0;
 
-   fclose( fp );
-   vg_success( "Written %u frames to disk.\n", frame_count );
+   vg_async_task_dispatch( &_gs_db.tasks, task );
 }
 
 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 ];
+   vg_queue *ring = &dest_replay->ring_buffer;
 
    u32 total_size = sizeof( gs_replay_frame ) + frame_size;
 
@@ -101,7 +98,7 @@ void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_s
       /* 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 );
+      u64 min_ticks = _gameserver.ticks - seconds_to_server_ticks( 4.0 * 60.0 );
 
       for( u32 i=0; i<64; i ++ )
       {
@@ -110,23 +107,9 @@ void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_s
             break;
 
          /* blocked by valid frame */
-         if( (free_frame->tick_recorded >= min_ticks) && (free_frame->network_frame.client!=255) )
+         if( free_frame->tick_recorded >= min_ticks )
             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 );
       }
 
@@ -134,40 +117,33 @@ void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_s
       if( !dest_frame )
       {
          /* policy 3: grow (double size) */
-         u32 cur_size = _gs_replay.ring_buffer.size;
+         u32 cur_size = ring->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;
+         if( new_size > REPLAY_SIZE_LIMIT )
+            new_size = REPLAY_SIZE_LIMIT;
 
-         dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
-         VG_ASSERT( dest_frame );
+         if( new_size != ring->size )
+         {
+            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->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 ++;
 }
index 69d3f4c2d20bc85bdc5df3c77d1f4e13ae0e9ed3..97b14e8472a8114981053bf928d04a2789b8fdef 100644 (file)
@@ -1,17 +1,14 @@
 #pragma once
 #include "network_msg.h"
 
+#define REPLAY_SIZE_LIMIT 1024*1024*2
+
 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
 {
    u64 tick_recorded;
-   u32 previous_queue_item;
-   u32 next_queue_item;
    u16 frame_size;
    u16 _;
    struct netmsg_playerframe network_frame;
@@ -21,13 +18,10 @@ struct _gs_replay
 {
    struct gs_replay
    {
-      u32 frame_count,
-          head_item;     
+      vg_queue ring_buffer;
    }
    replays[ NETWORK_MAX_PLAYERS ];
 
-   struct vg_queue ring_buffer;
-
    bool print_info;
    u64 print_ticker;
 }
diff --git a/src/gameserver_requests.c b/src/gameserver_requests.c
new file mode 100644 (file)
index 0000000..19abfd5
--- /dev/null
@@ -0,0 +1,414 @@
+#include "gameserver_requests.h"
+#include "gameserver_database.h"
+#include "vg/vg_mem_pool.h"
+#include <stddef.h>
+
+struct _gs_requests _gs_requests;
+
+static void log_request_status( gs_request *req )
+{
+   THREAD_0;
+
+   const char *associated_username = "none";
+   struct gameserver_client *client = &_gameserver.clients[ req->client_id ];
+   if( (client->active) && (client->session_uid == req->user_uid) )
+      associated_username = client->username;
+
+   u16 pool_id = vg_pool_id( &_gs_requests.request_pool, req );
+
+   const char *request_state_str = (const char *[])
+   {
+      [k_request_state_none] = "None",
+      [k_request_state_server_processing] = "Processing",
+      [k_request_state_server_error] = "Internal Error",
+      [k_request_state_transfer_start] = "Start transfer",
+      [k_request_state_transfer] = "Transferring",
+      [k_request_state_finished] = "Finished",
+      [k_request_state_max ] = NULL
+   }
+   [ req->state ];
+
+   const char *colour = (const char *[])
+   {
+      KRED, KGRN, KYEL, KBLU,
+      KMAG, KCYN
+   }[ pool_id % 6 ];
+
+   vg_low( "req[%s%s##%hu" KWHT "] State: %s, Status: %u\n", 
+            colour, associated_username, pool_id, request_state_str, (u32)req->status );
+}
+
+void _gs_requests_init(void)
+{
+   u32 total_requests = GS_MAX_REQUESTS*NETWORK_MAX_PLAYERS;
+   u32 alloc_size = sizeof(gs_request)*total_requests;
+
+   _gs_requests.request_buffer = malloc( alloc_size );
+   memset( _gs_requests.request_buffer, 0, alloc_size );
+
+   vg_pool *pool = &_gs_requests.request_pool;
+   pool->buffer = _gs_requests.request_buffer;
+   pool->count = total_requests;
+   pool->stride = sizeof( gs_request );
+   pool->offset = offsetof( gs_request, poolnode );
+   vg_pool_init( pool );
+
+   _gs_requests.transfer_stream_buffer = malloc( GS_TRANSFER_MAX_SIZE*NETWORK_MAX_PLAYERS );
+}
+
+static void gs_request_release( gs_request *req )
+{
+   THREAD_0;
+
+   if( req->message )
+   {
+      SteamAPI_SteamNetworkingMessage_t_Release( req->message );
+      req->message = NULL;
+   }
+}
+
+void _gs_requests_client_disconnect( u32 client_id )
+{
+   THREAD_0;
+
+   gs_request_client *rc = &_gs_requests.clients[ client_id ];
+
+   while( rc->current_request )
+   {
+      gs_request *req = vg_pool_item( &_gs_requests.request_pool, rc->current_request );
+
+      if( vg_pool_unwatch( &_gs_requests.request_pool, rc->current_request ) )
+         gs_request_release( req );
+
+      rc->current_request = req->waiting_request;
+      rc->active_request_count --;
+   }
+}
+
+struct task_request_run_info
+{
+   u16 pool_id;
+};
+static void task_request_run( vg_async_task *task );
+
+void _gs_requests_tick(void)
+{
+   THREAD_0;
+
+   for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+   {
+      struct gameserver_client *client = &_gameserver.clients[i];
+
+      if( !client->active )
+         continue;
+
+      gs_request_client *rc = &_gs_requests.clients[i];
+      if( rc->current_request == 0 )
+         continue;
+
+      gs_request *req = vg_pool_item( &_gs_requests.request_pool, rc->current_request );
+
+      if( req->state == k_request_state_none )
+      {
+         req->data_buffer = _gs_requests.transfer_stream_buffer + (i*GS_TRANSFER_MAX_SIZE);
+         req->data_buffer_send_size = 0;
+         req->send_offset = 0;
+         vg_pool_watch( &_gs_requests.request_pool, rc->current_request );
+
+         vg_async_task *run_task = vg_allocate_async_task( &_gs_db.tasks, sizeof(struct task_request_run_info), 1 );
+         run_task->handler = task_request_run;
+         struct task_request_run_info *info = (void *)run_task->data;
+         info->pool_id = rc->current_request;
+         vg_async_task_dispatch( &_gs_db.tasks, run_task );
+
+         req->state = k_request_state_server_processing;
+         log_request_status( req );
+      }
+      else if( req->state == k_request_state_server_processing )
+      {
+         /* we do nothing here, we're waiting for the other thread to complete it's work. */
+      }
+      else if( req->state == k_request_state_server_error )
+      {
+         u32 size = sizeof(netmsg_request);
+         SteamNetworkingMessage_t *msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+         msg->m_conn = client->connection;
+         msg->m_idxLane = 1;
+         netmsg_request *res = msg->m_pData;
+         res->inetmsg_id = k_inetmsg_response;
+         res->id = req->client_request_id;
+         res->status = req->status;
+         SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+         req->state = k_request_state_finished;
+         log_request_status( req );
+      }
+      else if( req->state == k_request_state_transfer_start )
+      {
+         u32 size = sizeof(netmsg_request) + sizeof(struct netmsg_transfer_header);
+         SteamNetworkingMessage_t *msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+         msg->m_conn = client->connection;
+         msg->m_idxLane = 1;
+
+         struct netmsg_request *res = msg->m_pData;
+         res->inetmsg_id = k_inetmsg_response;
+         res->id = req->client_request_id;
+         res->status = k_request_status_transfer_header;
+
+         struct netmsg_transfer_header *header = (void *)res->buffer;
+         header->data_size = req->data_buffer_send_size;
+         header->chunks = (req->data_buffer_send_size+(GS_TRANSFER_BYTES_PER_TICK-1)) / GS_TRANSFER_BYTES_PER_TICK;
+
+         SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+         req->state = k_request_state_transfer;
+         log_request_status( req );
+      }
+      else if( req->state == k_request_state_transfer )
+      {
+         u32 size = GS_TRANSFER_BYTES_PER_TICK;
+         if( req->send_offset + size >= req->data_buffer_send_size )
+         {
+            size = req->data_buffer_send_size - req->send_offset;
+            req->state = k_request_state_finished;
+            log_request_status( req );
+         }
+
+         u32 message_size = sizeof(netmsg_request) + size;
+         SteamNetworkingMessage_t *msg = 
+            SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, message_size );
+         msg->m_conn = client->connection;
+         msg->m_idxLane = 1;
+
+         struct netmsg_request *res = msg->m_pData;
+         res->inetmsg_id = k_inetmsg_response;
+         res->id = req->client_request_id;
+         res->status = k_request_status_transfer_continue;
+         memcpy( res->buffer, req->data_buffer + req->send_offset, size );
+
+         SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+         req->send_offset += size;
+      }
+      else if( req->state == k_request_state_finished )
+      {
+         if( vg_pool_unwatch( &_gs_requests.request_pool, rc->current_request ) )
+            gs_request_release( req );
+
+         rc->current_request = req->waiting_request;
+         rc->active_request_count --;
+
+         req->state = k_request_state_none;
+         log_request_status( req );
+      }
+   }
+}
+
+static void task_request_processing_complete( vg_async_task *task )
+{
+   THREAD_0;
+   struct task_request_run_info *info = (void *)task->data;
+   gs_request *req = vg_pool_item( &_gs_requests.request_pool, info->pool_id );
+
+   SteamAPI_SteamNetworkingMessage_t_Release( req->message );
+   req->message = NULL;
+
+   /* check if we're still pointing at the same user of the same session */
+   struct gameserver_client *client = &_gameserver.clients[ req->client_id ];
+   if( (client->active == 0) || (client->session_uid != req->user_uid) )
+   {
+      /* should be going from 1 -> 0 */
+      VG_ASSERT( vg_pool_unwatch( &_gs_requests.request_pool, info->pool_id ) );
+      gs_request_release( req );
+
+      vg_low( "Ignoring response because session uid%u != user uid%u. (ok)\n", client->session_uid, req->user_uid );
+      return;
+   }
+
+   /* OK or client error */
+   if( req->client_request_id )
+   {
+      if( req->status < k_request_status_ok )
+         req->state = k_request_state_server_error;
+      else
+         req->state = k_request_state_transfer_start;
+   }
+   else
+   { 
+      /* request ID of 0 means the client doesn't care about getting the response back */
+      req->state = k_request_state_finished;
+   }
+   log_request_status( req );
+
+   /* should be going from 2 -> 1 */
+   VG_ASSERT( vg_pool_unwatch( &_gs_requests.request_pool, info->pool_id ) == 0 );
+}
+
+static void task_request_run( vg_async_task *task )
+{
+   THREAD_1;
+
+   struct task_request_run_info *info = (void *)task->data;
+   gs_request *req = vg_pool_item( &_gs_requests.request_pool, info->pool_id );
+
+   netmsg_request *client_packet = (netmsg_request *)req->message->m_pData;
+   vg_msg client_msg;
+   vg_msg_init( &client_msg, client_packet->buffer, req->message->m_cbSize - sizeof(netmsg_request) );
+
+   const char *endpoint = vg_msg_getkvstr( &client_msg, "endpoint" );
+   if( !endpoint )
+      req->status = k_request_status_invalid_endpoint;
+   else
+   {
+      /* create response packet */
+      bool no_content = 0;
+      vg_msg body_msg;
+      vg_msg_init( &body_msg, req->data_buffer, GS_TRANSFER_MAX_SIZE );
+
+      if( !strcmp( endpoint, "scoreboard" ) )
+      {
+         const char *mod = vg_msg_getkvstr( &client_msg, "mod" );
+         const char *route = vg_msg_getkvstr( &client_msg, "route" );
+         u32 week;
+         vg_msg_getkvintg( &client_msg, "week", k_vg_msg_u32, &week, NULL );
+         
+         if( week == NETWORK_LEADERBOARD_CURRENT_WEEK )
+         {
+            gameserver_cat_table( &body_msg, mod, route, gameserver_get_current_week(), "rows_weekly" );
+         }
+         else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK )
+         {
+            gameserver_cat_table( &body_msg, mod, route, 0, "rows" );
+            gameserver_cat_table( &body_msg, mod, route, gameserver_get_current_week(), "rows_weekly" );
+         }
+         else 
+            gameserver_cat_table( &body_msg, mod, route, week, "rows" );
+      }
+#if 0
+      else if( !strcmp( endpoint, "setlap" ) )
+      {
+         if( client->steamid == k_steamid_max )
+         {
+            gameserver_request_respond( k_request_status_unauthorized, res, NULL, msg );
+            return;
+         }
+
+         const char *mod = vg_msg_getkvstr( &data, "mod" );
+         const char *route = vg_msg_getkvstr( &data, "route" );
+         
+         char weekly_table[ DB_TABLE_UID_MAX ],
+              alltime_table[ DB_TABLE_UID_MAX ];
+
+         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 );
+            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 );
+            return;
+         }
+
+         db_writeusertime( alltime_table, client->steamid, centiseconds, 1 );
+         db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
+         gameserver_request_respond( k_request_status_ok, res, NULL, msg );
+      }
+#endif
+      else
+      {
+         req->status = k_request_status_invalid_endpoint;
+      }
+      
+      if( body_msg.error != k_vg_msg_error_OK )
+      {
+         req->status = k_request_status_out_of_memory;
+      }
+
+      if( req->status == k_request_status_ok )
+         req->data_buffer_send_size = body_msg.cur.co;
+   }
+
+   vg_async_task *return_task = vg_allocate_async_task( &_gameserver.tasks, sizeof(struct task_request_run_info), 1 );
+   memcpy( return_task->data, info, sizeof(struct task_request_run_info) );
+   return_task->handler = task_request_processing_complete;
+   vg_async_task_dispatch( &_gameserver.tasks, return_task );
+}
+
+void _gs_handle_request_message( u32 client_id, SteamNetworkingMessage_t *msg )
+{
+   THREAD_0;
+
+   if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
+   {
+      vg_error( "Client malformed request (no data?) %u\n", client_id );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
+      return;
+   }
+
+   struct gameserver_client *client = &_gameserver.clients[ client_id ];
+   gs_request_client *rc = &_gs_requests.clients[ client_id ];
+   netmsg_request *client_packet = (netmsg_request *)msg->m_pData;
+
+   enum request_status error_status = 0;
+
+   if( rc->active_request_count < GS_MAX_REQUESTS )
+   {
+      u16 new_id = vg_pool_lru( &_gs_requests.request_pool );
+      if( new_id )
+      {
+         rc->active_request_count ++;
+
+         vg_pool_watch( &_gs_requests.request_pool, new_id );
+         gs_request *req = vg_pool_item( &_gs_requests.request_pool, new_id );
+
+         req->state = k_request_state_none;
+         req->message = msg;
+         req->user_uid = client->session_uid;
+         req->client_id = client_id;
+         req->client_request_id = client_packet->id;
+         req->status = k_request_status_ok;
+         req->data_buffer = NULL;
+         req->data_buffer_send_size = 0;
+         req->send_offset = 0;
+         
+         gs_request *last_request = vg_pool_item( &_gs_requests.request_pool, rc->current_request ),
+                    *next = last_request;
+
+         while( next )
+         {
+            last_request = next;
+            next = vg_pool_item( &_gs_requests.request_pool, last_request->waiting_request );
+         }
+
+         if( last_request )
+            last_request->waiting_request = new_id;
+         else
+            rc->current_request = new_id;
+
+         log_request_status( req );
+         return;
+      }
+      else
+         error_status = k_request_status_server_error; /* this should be vanishingly rare */
+   }
+   else
+      error_status = k_request_status_too_many_requests;
+
+   vg_error( "req[%s##error] State: fail, Status: %u\n", client->username, (u32)error_status );
+
+   u32 size = sizeof(netmsg_request);
+   SteamNetworkingMessage_t *reply_msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+   reply_msg->m_conn = client->connection;
+   reply_msg->m_idxLane = 1;
+   netmsg_request *res = reply_msg->m_pData;
+   res->inetmsg_id = k_inetmsg_response;
+   res->id = client_packet->id;
+   res->status = error_status;
+   SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &reply_msg, NULL );
+
+   SteamAPI_SteamNetworkingMessage_t_Release( msg );
+}
diff --git a/src/gameserver_requests.h b/src/gameserver_requests.h
new file mode 100644 (file)
index 0000000..bf23f9a
--- /dev/null
@@ -0,0 +1,60 @@
+#pragma once
+#include "gameserver.h"
+#include "vg/vg_mem_pool.h"
+
+#define GS_TRANSFER_BYTES_PER_TICK   256
+#define GS_TRANSFER_MAX_SIZE         4*1024*1024
+#define GS_MAX_REQUESTS              8
+
+typedef struct gs_transfer gs_transfer;
+typedef struct gs_request_client gs_request_client;
+typedef struct gs_request gs_request;
+
+struct gs_request
+{
+   vg_pool_node poolnode;
+   u16 waiting_request;
+
+   enum request_state
+   {
+      k_request_state_none,
+      k_request_state_server_processing,
+      k_request_state_server_error,
+      k_request_state_transfer_start,
+      k_request_state_transfer,
+      k_request_state_finished,
+      k_request_state_max
+   }
+   state;
+
+   SteamNetworkingMessage_t *message;
+
+   u64 user_uid;
+   u32 client_id;
+   u8  client_request_id;
+
+   enum request_status status;
+   void *data_buffer;
+   u32   data_buffer_send_size, send_offset;
+};
+
+struct _gs_requests
+{
+   gs_request *request_buffer;
+   vg_pool request_pool;
+
+   void *transfer_stream_buffer;
+
+   struct gs_request_client
+   {
+      u16 current_request;
+      u32 active_request_count;
+   }
+   clients[ NETWORK_MAX_PLAYERS ];
+}
+extern _gs_requests;
+
+void _gs_requests_init(void);
+void _gs_requests_tick(void);
+void _gs_requests_client_disconnect( u32 client_id );
+void _gs_handle_request_message( u32 client_id, SteamNetworkingMessage_t *msg );
diff --git a/src/gameserver_transfer.c b/src/gameserver_transfer.c
deleted file mode 100644 (file)
index f42874c..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#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
deleted file mode 100644 (file)
index 7f4f18b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#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 a2335a10d44c7e97784d9ae80f4a596c0f7b42c3..9acddffc3e04fadafaef0d9fd68085677cf837d4 100644 (file)
@@ -67,7 +67,8 @@ static void on_auth_ticket_recieved( void *result, void *context ){
    }
 }
 
-static void request_auth_ticket(void){
+static void request_auth_ticket(void)
+{
    /* 
     * TODO Check for one thats cached on the disk and load it.
     * This might be OK though because steam seems to cache the result 
@@ -78,8 +79,7 @@ static void request_auth_ticket(void){
    vg_steam_async_call *call = vg_alloc_async_steam_api_call();
    call->userdata = NULL;
    call->p_handler = on_auth_ticket_recieved;
-   call->id = 
-      SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, NULL, 0 );
+   call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, NULL, 0 );
 }
 
 static void network_send_username(void){
@@ -122,33 +122,37 @@ void network_send_region(void)
 }
 
 static void network_send_request( netmsg_request *req, vg_msg *body,
-                                  void (*callback)( 
-                                     netmsg_request *res, vg_msg *body, 
-                                     u64 userdata),
-                                  u64 userdata ){
+                                  void (*callback)( void *data, u32 data_size, u64 userdata, enum request_status status ),
+                                  u64 userdata )
+{
    u32 len = 0;
-   if( body ){
+   if( body )
+   {
       len = body->cur.co;
       vg_info( "Request scoreboard. Info (%u):\n", body->cur.co );
       vg_msg_print( body, len );
 
-      if( body->error != k_vg_msg_error_OK ){
+      if( body->error != k_vg_msg_error_OK )
+      {
          vg_error( "Body not OK\n" );
          return;
       }
    }
 
-   if( callback ){
+   if( callback )
+   {
       req->id = vg_pool_lru( &network_client.request_pool );
-      if( req->id ){
+      if( req->id )
+      {
          vg_pool_watch( &network_client.request_pool, req->id );
-         struct network_request *pn = 
-            vg_pool_item( &network_client.request_pool, req->id );
+         struct network_request *pn = vg_pool_item( &network_client.request_pool, req->id );
          pn->callback = callback;
          pn->sendtime = vg.time_real;
          pn->userdata = userdata;
+         pn->status = k_request_status_sent;
       }
-      else{
+      else
+      {
          vg_error( "Unable to send request. Pool is full.\n" );
          return;
       }
@@ -156,23 +160,29 @@ static void network_send_request( netmsg_request *req, vg_msg *body,
    else
       req->id = 0;
 
-   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
-         hSteamNetworkingSockets, network_client.remote, 
-         req, sizeof(netmsg_request)+len,
-         k_nSteamNetworkingSend_Reliable, NULL );
+   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, network_client.remote, 
+                                                             req, sizeof(netmsg_request)+len,
+                                                             k_nSteamNetworkingSend_Reliable, NULL );
 }
 
-static void network_scoreboard_callback( netmsg_request *res, vg_msg *body,
-                                         u64 userdata ){
-   world_instance *world = &_world.main;
+static void network_scoreboard_callback( void *data, u32 data_size, u64 userdata, enum request_status status )
+{
+   if( status == k_request_status_ok )
+   {
+      vg_msg msg;
+      vg_msg_init( &msg, data, data_size );
+      vg_msg_print( &msg, msg.max );
 
-   world_routes_recv_scoreboard( world, body, userdata, res->status );
-   if( userdata == world_sfd.active_route_board )
-      world_sfd_compile_active_scores();
+      world_routes_recv_scoreboard( &_world.main, &msg, userdata, status );
+      if( userdata == world_sfd.active_route_board )
+         world_sfd_compile_active_scores();
+   }
+   else
+   {
+      world_routes_recv_scoreboard( &_world.main, NULL, userdata, status );
+   }
 }
 
-
-
 /* mod_uid: world mod uid,
  * route_uid: run name (just a string)
  * week: 
@@ -182,9 +192,8 @@ static void network_scoreboard_callback( netmsg_request *res, vg_msg *body,
  *   .
  *   10+ specific week index
  */
-void network_request_scoreboard( const char *mod_uid, 
-                                 const char *route_uid,
-                                 u32 week, u64 userdata ){
+void network_request_scoreboard( const char *mod_uid, const char *route_uid, u32 week, u64 userdata )
+{
    if( !network_connected() ) 
       return;
 
@@ -192,7 +201,7 @@ void network_request_scoreboard( const char *mod_uid,
    req->inetmsg_id = k_inetmsg_request;
 
    vg_msg data;
-   vg_msg_init( &data, req->q, 512 );
+   vg_msg_init( &data, req->buffer, 512 );
    vg_msg_wkvstr( &data, "endpoint", "scoreboard" );
    vg_msg_wkvstr( &data, "mod", mod_uid );
    vg_msg_wkvstr( &data, "route", route_uid );
@@ -200,16 +209,8 @@ 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 )
+void network_publish_laptime( const char *mod_uid, const char *route_uid, f64 lap_time )
 {
-   if( res->status != k_request_status_ok )
-   {
-      vg_error( "Publish laptime, server error #%d\n", (i32)res->status );
-   }
-}
-
-void network_publish_laptime( const char *mod_uid, 
-                              const char *route_uid, f64 lap_time ){
    if( !network_connected() )
       return;
 
@@ -219,41 +220,132 @@ void network_publish_laptime( const char *mod_uid,
    req->inetmsg_id = k_inetmsg_request;
 
    vg_msg data;
-   vg_msg_init( &data, req->q, 512 );
+   vg_msg_init( &data, req->buffer, 512 );
    vg_msg_wkvstr( &data, "endpoint", "setlap" );
    vg_msg_wkvstr( &data, "mod", mod_uid );
    vg_msg_wkvstr( &data, "route", route_uid );
    vg_msg_wkvnum( &data, "time", k_vg_msg_i32, 1, &time_centiseconds );
-   network_send_request( req, &data, network_publish_callback, 0 );
+   network_send_request( req, &data, NULL, 0 );
 }
 
-static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
+static void network_request_rx_300_400( SteamNetworkingMessage_t *msg )
+{
    netmsg_blank *tmp = msg->m_pData;
 
-   if( tmp->inetmsg_id == k_inetmsg_request ){
-      
+   if( tmp->inetmsg_id == k_inetmsg_request )
+   {
+      /* Could the server ever request info from the client?? */
    }
-   else if( tmp->inetmsg_id == k_inetmsg_response ){
+   else if( tmp->inetmsg_id == k_inetmsg_response )
+   {
       netmsg_request *res = (netmsg_request *)msg->m_pData;
 
-      vg_msg *body = NULL;
+      if( (res->id == 0) || (res->id > NETWORK_MAX_REQUESTS) )
+      {
+         vg_error( "Response with invalid ID: %u.\n", res->id );
+         return;
+      }
 
-      vg_msg data;
-      if( res->status == k_request_status_ok ){
-         vg_msg_init( &data, res->q, msg->m_cbSize - sizeof(netmsg_request) );
-         vg_success( "Response to #%d:\n", (i32)res->id );
-         vg_msg_print( &data, data.max );
-         body = &data;
+      struct network_request *pn = vg_pool_item( &network_client.request_pool, res->id );
+      if( pn->status == k_request_status_none )
+      {
+         vg_error( "Response to inactive request (id %u)\n", res->id );
+         return;
       }
-      else {
-         vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+
+      u32 byte_count = msg->m_cbSize - sizeof(netmsg_request);
+      if( res->status == k_request_status_ok )
+      {
+         if( pn->callback )
+            pn->callback( res->buffer, byte_count, pn->userdata, k_request_status_ok );
+         
+         vg_pool_unwatch( &network_client.request_pool, res->id );
+         pn->status = k_request_status_none;
       }
+      else if( res->status == k_request_status_transfer_header )
+      {
+         if( network_client.recieving_request_id )
+         {
+            vg_error( "Transfer for previous request (%u) interrupted by new one (%u)\n", 
+                        network_client.recieving_request_id, res->id );
+
+            struct network_request *prev = vg_pool_item( &network_client.request_pool, network_client.recieving_request_id );
+            vg_pool_unwatch( &network_client.request_pool, network_client.recieving_request_id );
+            prev->status = k_request_status_none;
+
+            network_client.recieving_request_id = 0;
+            network_client.recieve_offset = 0;
+            network_client.data_buffer_recieve_size = 0;
+         }
+
+         struct netmsg_transfer_header *header = (void *)res->buffer;
 
-      if( res->id ){
-         struct network_request *pn = 
-            vg_pool_item( &network_client.request_pool, res->id );
-         pn->callback( res, body, pn->userdata );
+         if( header->data_size > 1024*1024*4 )
+         {
+            vg_error( "Transfer too large to handle! Size: %u bytes\n", header->data_size );
+            vg_pool_unwatch( &network_client.request_pool, res->id );
+            pn->status = k_request_status_none;
+            return;
+         }
+
+         network_client.recieving_request_id = res->id;
+         network_client.recieve_offset = 0;
+         network_client.data_buffer_recieve_size = header->data_size;
+
+         pn->status = k_request_status_receiving;
+      }
+      else if( res->status == k_request_status_transfer_continue )
+      {
+         if( network_client.recieving_request_id != res->id )
+         {
+            vg_error( "Transfer protocal fault, against requests %u and %u. Discarding.\n", 
+                        network_client.recieving_request_id, res->id );
+
+            struct network_request *prev = vg_pool_item( &network_client.request_pool, network_client.recieving_request_id );
+            vg_pool_unwatch( &network_client.request_pool, network_client.recieving_request_id );
+            vg_pool_unwatch( &network_client.request_pool, res->id );
+            prev->status = k_request_status_none;
+            pn->status = k_request_status_none;
+            network_client.recieving_request_id = 0;
+            network_client.recieve_offset = 0;
+            network_client.data_buffer_recieve_size = 0;
+            return;
+         }
+
+         bool end = 0;
+         if( network_client.recieve_offset + byte_count >= network_client.data_buffer_recieve_size )
+         {
+            end = 1;
+            byte_count = network_client.data_buffer_recieve_size - network_client.recieve_offset;
+         }
+
+         memcpy( network_client.data_buffer + network_client.recieve_offset, res->buffer, byte_count );
+         network_client.recieve_offset += byte_count;
+
+         if( end )
+         {
+            vg_success( "Transfer finished! (%u bytes)\n", network_client.recieve_offset );
+            if( pn->callback )
+            {
+               pn->callback( network_client.data_buffer, network_client.data_buffer_recieve_size, 
+                             pn->userdata, k_request_status_ok );
+            }
+
+            vg_pool_unwatch( &network_client.request_pool, res->id );
+            pn->status = k_request_status_none;
+
+            network_client.recieving_request_id = 0;
+            network_client.recieve_offset = 0;
+            network_client.data_buffer_recieve_size = 0;
+         }
+
+         // TODO: Timeout request & disconnecting from server 
+      }
+      else
+      {
          vg_pool_unwatch( &network_client.request_pool, res->id );
+         vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+         pn->status = k_request_status_none;
       }
    }
 }
@@ -311,15 +403,32 @@ void network_send_item( enum netmsg_playeritem_type type )
          k_nSteamNetworkingSend_Reliable, NULL );
 }
 
-static void network_disconnect(void){
-   SteamAPI_ISteamNetworkingSockets_CloseConnection( 
-         hSteamNetworkingSockets, network_client.remote, 0, NULL, 0 );
+static void network_disconnect(void)
+{
+   SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, network_client.remote, 0, NULL, 0 );
    network_client.remote = 0;
    network_client.state = k_ESteamNetworkingConnectionState_None;
 
-   for( int i=0; i<VG_ARRAY_LEN(netplayers.list); i++ ){
+   for( int i=0; i<VG_ARRAY_LEN(netplayers.list); i++ )
+   {
       netplayers.list[i].active = 0;
    }
+
+   /* return the infinity stones */
+   network_client.data_buffer_recieve_size = 0;
+   network_client.recieve_offset = 0;
+   network_client.recieving_request_id = 0;
+   for( u32 i=0; i<NETWORK_MAX_REQUESTS; i ++ )
+   {
+      struct network_request *request = &network_client.request_buffer[ i ];
+
+      if( request->status != k_request_status_none )
+      {
+         vg_warn( "Clipping request #%u. Timeout of some kind\n", i );
+         request->status = k_request_status_none;
+         vg_pool_unwatch( &network_client.request_pool, i+1 );
+      }
+   }
 }
 
 void network_status_string( vg_str *str, u32 *colour )
@@ -554,12 +663,6 @@ static void poll_remote_connection(void){
       {
          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;
@@ -567,23 +670,29 @@ static void poll_remote_connection(void){
 
          netmsg_blank *tmp = msg->m_pData;
 
-         if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
+         if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) )
+         {
             player_remote_rx_200_300( msg );
          }
-         else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
+         else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) )
+         {
             network_request_rx_300_400( msg );
          }
-         else {
-            if( tmp->inetmsg_id == k_inetmsg_version ){
+         else 
+         {
+            if( tmp->inetmsg_id == k_inetmsg_version )
+            {
                netmsg_version *version = msg->m_pData;
-               if( version->version != NETWORK_SKATERIFT_VERSION ){
+               if( version->version != NETWORK_SKATERIFT_VERSION )
+               {
                   network_disconnect();
                   /* we dont want to connect to this server ever */
                   network_client.retries = 999;
                   network_client.last_attempt = 999999999.9;
                   vg_error( "version mismatch with server\n" );
                }
-               else {
+               else 
+               {
                   network_client.remote_version = version->version;
                   network_sign_on_complete();
                }
@@ -773,9 +882,9 @@ void network_init(void)
    if( steam_ready )
    {
       u32 alloc_size = sizeof(struct network_request)*NETWORK_MAX_REQUESTS;
-      network_client.request_buffer = 
-         vg_linear_alloc( vg_mem.rtmemory, alloc_size );
+      network_client.request_buffer = vg_linear_alloc( vg_mem.rtmemory, alloc_size );
       memset( network_client.request_buffer, 0, alloc_size );
+      network_client.data_buffer = vg_linear_alloc( vg_mem.rtmemory, 4*1024*1024 );
 
       vg_pool *pool = &network_client.request_pool;
       pool->buffer = network_client.request_buffer;
@@ -784,10 +893,8 @@ void network_init(void)
       pool->offset = offsetof( struct network_request, poolnode );
       vg_pool_init( pool );
 
-      steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
-                               on_server_connect_status );
-      steam_register_callback( k_iPersonaStateChange, 
-                               on_persona_state_change );
+      steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_server_connect_status );
+      steam_register_callback( k_iPersonaStateChange, on_persona_state_change );
       request_auth_ticket();
 
       vg_console_reg_cmd( "say", cmd_network_send_message, NULL );
index 5152c361a6416f33070c22e1759c146cece2d157..f82e855b1fb62a636599b112c8622d698bf027b2 100644 (file)
@@ -54,15 +54,29 @@ struct network_client
 
    i32 auto_connect;
 
-   struct network_request {
+   struct network_request 
+   {
       vg_pool_node poolnode;
-      void (*callback)( netmsg_request *res, vg_msg *body, u64 userdata );
+      void (*callback)( void *data, u32 data_size, u64 userdata, enum request_status status );
       f64 sendtime;
       u64 userdata;
+      
+      enum status
+      {
+         k_request_status_none,
+         k_request_status_sent,
+         k_request_status_receiving,
+      }
+      status;
    }
    *request_buffer;
    vg_pool request_pool;
 
+   void *data_buffer;
+   u32 data_buffer_recieve_size;
+   u32 recieve_offset;
+   u8  recieving_request_id;
+
    SteamNetworkingIPAddr ip;
    char host_port[8], host_adress[256];
    bool ip_resolved;
@@ -79,11 +93,8 @@ extern network_client;
 
 int packet_minsize( SteamNetworkingMessage_t *msg, u32 size );
 void network_send_item( enum netmsg_playeritem_type type );
-void network_request_scoreboard( const char *mod_uid, 
-                                 const char *route_uid,
-                                 u32 week, u64 userdata );
-void network_publish_laptime( const char *mod_uid, 
-                              const char *route_uid, f64 lap_time );
+void network_request_scoreboard( const char *mod_uid, const char *route_uid, u32 week, u64 userdata );
+void network_publish_laptime( const char *mod_uid, const char *route_uid, f64 lap_time );
 void chat_send_message( const char *message );
 void render_server_status_gui(void);
 void network_status_string( vg_str *str, u32 *colour );
index 0ad47a32b70dc0efe17f8b364a620c7ab45c83a6..c156a2dec4a0cb13db352928d4576a15d8844128 100644 (file)
@@ -141,20 +141,28 @@ enum{ k_inetmsg_request = 300, k_inetmsg_response = 301 };
 struct netmsg_request {
    u16 inetmsg_id;
    u8 id, status;
-   u8 q[];
+   u8 buffer[];
+};
+
+struct netmsg_transfer_header 
+{
+   u32 data_size, chunks;
 };
 
 enum request_status {
   k_request_status_client_error = 0,
   k_request_status_invalid_endpoint = 1,
   k_request_status_unauthorized = 2,
+  k_request_status_too_many_requests = 3,
 
   k_request_status_server_error = 100,
   k_request_status_out_of_memory = 101,
   k_request_status_database_error = 102,
 
-  k_request_status_ok = 200,
-  k_request_status_not_found = 201
+  k_request_status_ok = 200,                    /* buffer contains response */
+  k_request_status_not_found = 201,
+  k_request_status_transfer_header = 202,
+  k_request_status_transfer_continue = 203
 };
 
 #pragma pack(pop)
index c6735bb49156d1dbea9ae7bd8a2820d5f08022cd..940ff4b661e5b836cd151d3627a39cbcce782ef9 100644 (file)
@@ -655,11 +655,10 @@ void world_gen_routes_ent_init( world_instance *world )
    world_routes_clear( world );
 }
 
-void world_routes_recv_scoreboard( world_instance *world, 
-                                   vg_msg *body, u32 route_id,
-                                   enum request_status status )
+void world_routes_recv_scoreboard( world_instance *world, vg_msg *body, u32 route_id, enum request_status status )
 {
-   if( route_id >= af_arrcount( &world->ent_route ) ){
+   if( route_id >= af_arrcount( &world->ent_route ) )
+   {
       vg_error( "Scoreboard route_id out of range (%u)\n", route_id );
       return;
    }
@@ -667,14 +666,15 @@ void world_routes_recv_scoreboard( world_instance *world,
    struct leaderboard_cache *board = &world->leaderboard_cache[ route_id ];
    board->status = status;
 
-   if( body == NULL ){
+   if( body == NULL )
+   {
       board->data_len = 0;
       return;
    }
 
-   if( body->max > NETWORK_REQUEST_MAX ){
-      vg_error( "Scoreboard leaderboard too big (%u>%u)\n", body->max,
-                NETWORK_REQUEST_MAX );
+   if( body->max > NETWORK_REQUEST_MAX )
+   {
+      vg_error( "Scoreboard leaderboard too big (%u>%u)\n", body->max, NETWORK_REQUEST_MAX );
       return;
    }
 
index 80cb56e7b6230b10952470d5b55b0360131484ba..d41037f4f84103fd7b17594c1ac4d20d7ececb8b 100644 (file)
@@ -79,8 +79,7 @@ void sfd_encode( v2i co, const char *str, enum world_sfd_align align )
    }
 }
 
-void world_sfd_compile_scores( struct leaderboard_cache *board,
-                               const char *title )
+void world_sfd_compile_scores( struct leaderboard_cache *board, const char *title )
 {
    for( u32 i=0; i<13; i++ )
       sfd_clear(i);