full request roundtrip
authorhgn <hgodden00@gmail.com>
Sat, 28 Oct 2023 15:19:45 +0000 (16:19 +0100)
committerhgn <hgodden00@gmail.com>
Sat, 28 Oct 2023 15:19:45 +0000 (16:19 +0100)
gameserver.c
gameserver_db.h
network.c
network.h
network_msg.h
world_sfd.c
world_sfd.h

index 3cbf87791fc0167a1c5d91c7a0f44c6fbb35040d..b90a4a8a34905c337709985477ebcab5913fbb2a 100644 (file)
@@ -16,6 +16,7 @@ volatile sig_atomic_t sig_stop;
 #include "network_common.h"
 #include "gameserver_db.h"
 #include "vg/vg_m.h"
+#include "vg/vg_msg.h"
 
 static void inthandler( int signum ) {
    sig_stop = 1;
@@ -393,6 +394,175 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg ){
       gameserver_send_to_all( client_id, prop, msg->m_cbSize, 
                               k_nSteamNetworkingSend_Reliable );
    }
+   else {
+      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
+               tmp->inetmsg_id );
+   }
+}
+
+static void gameserver_request_respond( enum request_status status,
+                                        netmsg_request *res, vg_msg *body,
+                                        SteamNetworkingMessage_t *msg ){
+   int client_id = gameserver_client_index( msg->m_conn );
+   u32 len = 0;
+   if( status == k_request_status_ok ){
+      len = body->len;
+
+      vg_success( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
+      vg_msg_print( body );
+   }
+   else{
+      vg_warn( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
+   }
+
+   res->status = status;
+
+   SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
+         hSteamNetworkingSockets, msg->m_conn,
+         res, sizeof(netmsg_request) + len,
+         k_nSteamNetworkingSend_Reliable, NULL );
+   SteamAPI_SteamNetworkingMessage_t_Release( msg );
+}
+
+struct user_request_thread_data {
+   SteamNetworkingMessage_t *msg;
+};
+
+static void gameserver_process_user_request( db_request *db_req ){
+   struct user_request_thread_data *inf = (void *)db_req->data;
+   SteamNetworkingMessage_t *msg = inf->msg;
+
+   int client_id = gameserver_client_index( msg->m_conn );
+   if( client_id == -1 ){
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
+      return;
+   }
+
+   netmsg_request *req = (netmsg_request *)msg->m_pData;
+   vg_msg data = {0};
+   data.buf = req->q;
+   data.len = msg->m_cbSize - sizeof(netmsg_request);
+   data.max = data.len;
+
+   /* create response packet */
+   netmsg_request *res = alloca( sizeof(netmsg_request) + 512 );
+   res->inetmsg_id = k_inetmsg_response;
+   res->id = req->id;
+   vg_msg body = {0};
+   body.buf = res->q;
+   body.max = 512;
+
+   const char *endpoint = vg_msg_seekkvstr( &data, "endpoint", 0 );
+
+   if( !endpoint ){
+      gameserver_request_respond( k_request_status_invalid_endpoint,
+                                  res, NULL, msg );
+      return;
+   }
+
+   if( !strcmp( endpoint, "scoreboard" ) ){
+      const char *mod = vg_msg_seekkvstr( &data, "mod", 0 );
+      const char *route = vg_msg_seekkvstr( &data, "route", 0 );
+      u32 week = vg_msg_seekkvu32( &data, "week", 0 );
+      
+      char table_name[ DB_TABLE_UID_MAX ];
+      if( !db_get_highscore_table_name( mod, route, week, table_name ) ){
+         gameserver_request_respond( k_request_status_out_of_memory,
+                                     res, NULL, msg );
+         return;
+      }
+
+      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 DESC LIMIT 10;" );
+      if( !vg_strgood(&q) ) {
+         gameserver_request_respond( k_request_status_out_of_memory,
+                                     res, NULL, msg );
+         return;
+      }
+
+      sqlite3_stmt *stmt = db_stmt( q.buffer );
+
+      if( !stmt ){
+         gameserver_request_respond( k_request_status_database_error,
+                                     res, NULL, msg );
+         return;
+      }
+
+      vg_msg_frame( &body, "rows" );
+      for( u32 i=0; i<10; i ++ ){
+         int fc = sqlite3_step( stmt );
+
+         if( fc == SQLITE_ROW ){
+            i32 time = sqlite3_column_int( stmt, 1 );
+            i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
+            u64 steamid = *((u64 *)&steamid_i64);
+
+            vg_msg_frame( &body, "" );
+            vg_msg_wkvu32( &body, "time", time );
+            vg_msg_wkvu64( &body, "steamid", steamid );
+
+            char username[32];
+            if( db_getuserinfo( steamid, username, sizeof(username), NULL ) ){
+               vg_msg_wkvstr( &body, "username", username );
+            }
+
+            vg_msg_end_frame( &body );
+         }
+         else if( fc == SQLITE_DONE ){
+            break;
+         }
+         else {
+            log_sqlite3( fc );
+            break;
+         }
+      }
+
+      sqlite3_finalize( stmt );
+      vg_msg_end_frame( &body );
+
+      if( body.error != k_vg_msg_error_OK ){
+         gameserver_request_respond( k_request_status_out_of_memory,
+                                     res, NULL, msg );
+         return;
+      }
+
+      gameserver_request_respond( k_request_status_ok, res, &body, msg );
+   }
+   else{
+      gameserver_request_respond( k_request_status_invalid_endpoint,
+                                  res, NULL, msg );
+   }
+}
+
+static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg ){
+   netmsg_blank *tmp = msg->m_pData;
+
+   int client_id = gameserver_client_index( msg->m_conn );
+   if( client_id == -1 ){
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
+      return;
+   }
+
+   if( tmp->inetmsg_id == k_inetmsg_request ){
+      if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
+         return;
+
+      db_request *call = db_alloc_request( 
+                           sizeof(struct user_request_thread_data) );
+      struct user_request_thread_data *inf = (void *)call->data;
+      inf->msg = msg;
+      call->handler = gameserver_process_user_request;
+      db_send_request( call );
+   }
+   else {
+      vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
+               tmp->inetmsg_id );
+      SteamAPI_SteamNetworkingMessage_t_Release( msg );
+   }
 }
 
 static void poll_connections(void){
@@ -420,6 +590,10 @@ static void poll_connections(void){
 
          if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
             gameserver_rx_200_300( msg );
+            SteamAPI_SteamNetworkingMessage_t_Release( msg );
+         }
+         else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
+            gameserver_rx_300_400( msg );
          }
          else{
             if( tmp->inetmsg_id == k_inetmsg_auth )
@@ -428,9 +602,8 @@ static void poll_connections(void){
                vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
                         tmp->inetmsg_id );
             }
+            SteamAPI_SteamNetworkingMessage_t_Release( msg );
          }
-
-         SteamAPI_SteamNetworkingMessage_t_Release( msg );
       }
    }
 }
@@ -442,8 +615,8 @@ static u64 seconds_to_server_ticks( double s ){
 static void test_runner( db_request *req ){
    vg_warn( "RUNNER\n" );
    char table[DB_TABLE_UID_MAX];
-   if( db_get_highscore_table_name( "sr003-local-mp_mtzero", 
-                                    "megapark-yellow", 302, table ) ){
+   if( db_get_highscore_table_name( "sr002-local-mp_mtzero", 
+                                    "Coastal Run", 0, table ) ){
       if( db_writeusertime( table, 76561198072130043, 232, 1 ) ){
          vg_success( "Written time\n" );
          i32 v = db_readusertime( table, 76561198072130043 );
@@ -554,6 +727,7 @@ int main( int argc, char *argv[] ){
    
    vg_info( "Shutting down\n..." );
    SteamGameServer_Shutdown();
+   db_kill();
    db_free();
 
    return 0;
index d3d3429669c24ef3233b0c32c8c4808fad48c4c7..90852481e9ae1804b8523f78189c05cdd983bc51 100644 (file)
@@ -11,7 +11,7 @@
 
 #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_CRASH_ON_SQLITE_ERROR
 #define DB_LOG_SQL_STATEMENTS
 #define DB_REQUEST_BUFFER_SIZE (1024*2)
 
@@ -92,8 +92,8 @@ static int db_verify_charset( const char *str, int mincount ){
 /*
  * Find table name from mod UID and course UID, plus the week number
  */
-static int db_get_highscore_table_name( char mod_uid[ADDON_UID_MAX], 
-                                        char run_uid[DB_COURSE_UID_MAX],
+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 ) ||
@@ -126,9 +126,8 @@ static i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ){
    if( !vg_strgood(&q) ) return 0;
 
    sqlite3_stmt *stmt = db_stmt( q.buffer );
-   sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
-
    if( stmt ){
+      sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
       int fc = sqlite3_step( stmt );
 
       i32 result = 0;
@@ -231,12 +230,40 @@ static int db_updateuser( u64 steamid, const char *username, int admin ){
    else return 0;
 }
 
+/*
+ * 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 );
-   pthread_mutex_destroy( &database.mux );
 }
 
 static void *db_loop(void *_){
@@ -337,10 +364,15 @@ static int db_killed(void){
    return result;
 }
 
-static void db_free(void){
+static void db_kill(void){
    pthread_mutex_lock( &database.mux );
    database.kill = 1;
    pthread_mutex_unlock( &database.mux );
+   pthread_join( database.thread, NULL );
+}
+
+static void db_free(void){
+   pthread_mutex_destroy( &database.mux );
 }
 
 static db_request *db_alloc_request( u32 size ){
index 838653c33e8208b1174aa319e2a0b1977933bfe4..d60dc0cb38f429e32d102163e737649e2154e561 100644 (file)
--- a/network.c
+++ b/network.c
@@ -3,6 +3,7 @@
 #include "network_msg.h"
 #include "network_common.h"
 #include "player_remote.h"
+#include "world_sfd.h"
 
 static void scores_update(void);
 
@@ -147,6 +148,133 @@ static void network_send_username(void){
          k_nSteamNetworkingSend_Reliable, NULL );
 }
 
+static void network_send_request( netmsg_request *req, vg_msg *body,
+                                  void (*callback)( netmsg_request *res, 
+                                                    vg_msg *body )){
+   u32 len = 0;
+   if( body ){
+      len = body->len;
+      vg_info( "Request scoreboard. Info (%u):\n", body->len );
+      vg_msg_print( body );
+
+      if( body->error != k_vg_msg_error_OK ){
+         vg_error( "Body not OK\n" );
+         return;
+      }
+   }
+
+   if( callback ){
+      req->id = vg_pool_lru( &network_client.request_pool );
+      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 );
+         pn->callback = callback;
+         pn->sendtime = vg.time_real;
+      }
+      else{
+         vg_error( "Unable to send request. Pool is full.\n" );
+         return;
+      }
+   }
+   else
+      req->id = 0;
+
+   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 ){
+   for( u32 i=0; i<13; i++ )
+      sfd_encode( i, "" );
+
+   if( res->status != k_request_status_ok ){
+      char buf[32];
+      vg_str s;
+      vg_strnull( &s, buf, 32 );
+      vg_strcat( &s, "Error: " );
+      vg_strcati32( &s, res->status );
+
+      sfd_encode( 4, buf );
+      return;
+   }
+
+   /* TODO: frame pointers?? */
+
+   u32 l = 0;
+   vg_msg rows = *body;
+   if( vg_msg_seekframe( &rows, "rows", k_vg_msg_first ) ){
+      vg_msg entry = rows;
+
+      while( vg_msg_seekframe( &entry, NULL, k_vg_msg_next ) ){
+         const char *username = vg_msg_seekkvstr( &entry, "username", 
+                                                  k_vg_msg_first );
+         sfd_encode( l ++, username );
+         vg_msg_skip_frame( &entry );
+      }
+   }
+}
+
+/* mod_uid: world mod uid,
+ * route_uid: run name (just a string)
+ * week: 0 for all-time, n for week #
+ */
+static void network_request_scoreboard( const char *mod_uid, 
+                                        const char *route_uid,
+                                        u32 week ){
+   if( !network_client.remote )
+      return;
+
+   netmsg_request *req = alloca( sizeof(netmsg_request) + 512 );
+   req->inetmsg_id = k_inetmsg_request;
+   req->id = 0; /* TODO: pool allocatable */
+
+   vg_msg data = {0};
+   data.buf = req->q;
+   data.max = 512;
+
+   vg_msg_wkvstr( &data, "endpoint", "scoreboard" );
+   vg_msg_wkvstr( &data, "mod", mod_uid );
+   vg_msg_wkvstr( &data, "route", route_uid );
+   vg_msg_wkvu32( &data, "week", week );
+   network_send_request( req, &data, network_scoreboard_callback );
+}
+
+static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
+   netmsg_blank *tmp = msg->m_pData;
+
+   if( tmp->inetmsg_id == k_inetmsg_request ){
+      
+   }
+   else if( tmp->inetmsg_id == k_inetmsg_response ){
+      netmsg_request *res = (netmsg_request *)msg->m_pData;
+
+      vg_msg *body = NULL;
+      vg_msg data = {0};
+
+      if( res->status == k_request_status_ok ){
+         data.buf = res->q;
+         data.len = msg->m_cbSize - sizeof(netmsg_request);
+         data.max = data.len;
+         vg_success( "Response to #%d:\n", (i32)res->id );
+         vg_msg_print( &data );
+         body = &data;
+      }
+      else {
+         vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+      }
+
+      if( res->id ){
+         struct network_request *pn = 
+            vg_pool_item( &network_client.request_pool, res->id );
+         pn->callback( res, body );
+         vg_pool_unwatch( &network_client.request_pool, res->id );
+      }
+   }
+}
+
 static void network_send_item( enum netmsg_playeritem_type type ){
    if( !network_client.remote )
       return;
@@ -201,7 +329,6 @@ static void network_send_item( enum netmsg_playeritem_type type ){
          hSteamNetworkingSockets, network_client.remote, 
          item, sizeof(netmsg_playeritem)+chs+1,
          k_nSteamNetworkingSend_Reliable, NULL );
-
 }
 
 static void network_disconnect(void){
@@ -363,6 +490,9 @@ static void poll_remote_connection(void){
          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) ){
+            network_request_rx_300_400( msg );
+         }
 
          SteamAPI_SteamNetworkingMessage_t_Release( msg );
       }
@@ -413,6 +543,18 @@ static void network_init(void){
    vg_console_reg_var( "network_info", &network_client.network_info,
                        k_var_dtype_i32, VG_VAR_PERSISTENT );
    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 );
+      memset( network_client.request_buffer, 0, alloc_size );
+
+      vg_pool *pool = &network_client.request_pool;
+      pool->buffer = network_client.request_buffer;
+      pool->count = NETWORK_MAX_REQUESTS;
+      pool->stride = sizeof( struct network_request );
+      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, 
index e6407eb031d619ed2ebb839813efeba8af53f6c4..752c6c3c46534e7bf580b5a60ef2c3462e08323e 100644 (file)
--- a/network.h
+++ b/network.h
@@ -12,6 +12,8 @@
 #include "highscores.h"
 #include "addon_types.h"
 
+#define NETWORK_MAX_REQUESTS 8
+
 static int network_scores_updated = 0;
 
 /* 
@@ -51,13 +53,24 @@ struct {
    u32 retries;
 
    i32 network_info;
+
+   struct network_request {
+      vg_pool_node poolnode;
+      void (*callback)( netmsg_request *res, vg_msg *body );
+      f64 sendtime;
+   }
+   *request_buffer;
+   vg_pool request_pool;
 }
 static network_client = {
-   .state = k_ESteamNetworkingConnectionState_None,
    .auth_mode = eServerModeAuthentication,
+   .state = k_ESteamNetworkingConnectionState_None,
 };
 
 static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size );
 static void network_send_item( enum netmsg_playeritem_type type );
+static void network_request_scoreboard( const char *mod_uid, 
+                                        const char *route_uid,
+                                        u32 week );
 
 #endif /* NETWORK_H */
index 42976ebb5e6e699828821ce66bf051b9a9bf8cea..83f87075b585e4db9aea727725bce29ee037aefe 100644 (file)
@@ -147,5 +147,26 @@ struct netmsg_chat {
    char msg[];
 };
 
+/* requests 300 */
+typedef struct netmsg_request netmsg_request;
+enum{ k_inetmsg_request = 300, k_inetmsg_response = 301 };
+struct netmsg_request {
+   u16 inetmsg_id;
+   u8 id, status;
+   u8 q[];
+};
+
+enum request_status {
+  k_request_status_client_error = 0,
+  k_request_status_invalid_endpoint = 1,
+
+  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
+};
+
 #pragma pack(pop)
 #endif /* NETWORK_MSG_H */
index a92a4b090719c2fd457e33f4a9a4eef00fb55f66..b40d57a5896cf8ffad66360d4af238fc993b42da 100644 (file)
@@ -78,8 +78,20 @@ static void world_sfd_update( world_instance *world, v3f pos ){
       if( (world_sfd.active_route_board != closest) || network_scores_updated ){
          network_scores_updated = 0;
          world_sfd.active_route_board = closest;
-
          ent_route *route = mdl_arritm( &world->ent_route, closest );
+
+         addon_reg *world_reg = world_static.addon_hub;
+         if( world_static.active_instance )
+            world_reg = world_static.addon_client;
+
+         char mod_uid[ ADDON_UID_MAX ];
+         addon_alias_uid( &world_reg->alias, mod_uid );
+
+         network_request_scoreboard( 
+               mod_uid, 
+               mdl_pstr( &world->meta, route->pstr_name ),
+               0 );
+#if 0
          u32 id = route->anon.official_track_id;
 
          if( id != 0xffffffff ){
@@ -93,6 +105,7 @@ static void world_sfd_update( world_instance *world, v3f pos ){
             sfd_encode( 0, mdl_pstr( &world->meta, route->pstr_name ) );
             sfd_encode( 1, "No data" );
          }
+#endif
       }
    }
 
index 9049cc20da2829143fa892e3f3131be975681e06..c83b0b2469a35d2931f8033faa0e8b457f9047d8 100644 (file)
@@ -21,6 +21,7 @@ struct world_sfd{
 static world_sfd;
 static void world_sfd_init(void);
 
+static void sfd_encode( u32 row, const char *str );
 static void sfd_render( world_instance *world, camera *cam, 
                            m4x3f transform );