From a7d144c7905105909cc4434e0ab43008bbb8f89f Mon Sep 17 00:00:00 2001 From: hgn Date: Sat, 28 Oct 2023 16:19:45 +0100 Subject: [PATCH] full request roundtrip --- gameserver.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++-- gameserver_db.h | 46 ++++++++++-- network.c | 144 +++++++++++++++++++++++++++++++++++++- network.h | 15 +++- network_msg.h | 21 ++++++ world_sfd.c | 15 +++- world_sfd.h | 1 + 7 files changed, 410 insertions(+), 14 deletions(-) diff --git a/gameserver.c b/gameserver.c index 3cbf877..b90a4a8 100644 --- a/gameserver.c +++ b/gameserver.c @@ -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; diff --git a/gameserver_db.h b/gameserver_db.h index d3d3429..9085248 100644 --- a/gameserver_db.h +++ b/gameserver_db.h @@ -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 ){ diff --git a/network.c b/network.c index 838653c..d60dc0c 100644 --- 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, diff --git a/network.h b/network.h index e6407eb..752c6c3 100644 --- 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 */ diff --git a/network_msg.h b/network_msg.h index 42976eb..83f8707 100644 --- a/network_msg.h +++ b/network_msg.h @@ -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 */ diff --git a/world_sfd.c b/world_sfd.c index a92a4b0..b40d57a 100644 --- a/world_sfd.c +++ b/world_sfd.c @@ -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 } } diff --git a/world_sfd.h b/world_sfd.h index 9049cc2..c83b0b2 100644 --- a/world_sfd.h +++ b/world_sfd.h @@ -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 ); -- 2.25.1