From c37de3142399dffd8abbf4431558c5adccfff634 Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 4 Apr 2025 08:11:06 +0100 Subject: [PATCH] returning to sensibilities --- src/gameserver.c | 247 +++++++++++------------ src/gameserver.h | 17 +- src/gameserver_database.c | 260 ++++++++++++++++-------- src/gameserver_database.h | 46 +---- src/gameserver_replay.c | 166 +++++++-------- src/gameserver_replay.h | 12 +- src/gameserver_requests.c | 414 ++++++++++++++++++++++++++++++++++++++ src/gameserver_requests.h | 60 ++++++ src/gameserver_transfer.c | 74 ------- src/gameserver_transfer.h | 29 --- src/network.c | 267 ++++++++++++++++-------- src/network.h | 25 ++- src/network_msg.h | 14 +- src/world_routes.c | 16 +- src/world_sfd.c | 3 +- 15 files changed, 1090 insertions(+), 560 deletions(-) create mode 100644 src/gameserver_requests.c create mode 100644 src/gameserver_requests.h delete mode 100644 src/gameserver_transfer.c delete mode 100644 src/gameserver_transfer.h diff --git a/src/gameserver.c b/src/gameserver.c index e55d21b..008c8fc 100644 --- a/src/gameserver.c +++ b/src/gameserver.c @@ -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; iconnection, 2, (int[]){ 0,0 }, (u16[]){ 10, 1 } ); @@ -106,7 +111,7 @@ static void gameserver_player_join( int index ) for( int i=0; isteamid ) 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; isession_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; im_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" diff --git a/src/gameserver.h b/src/gameserver.h index ad5b95c..d22331e 100644 --- a/src/gameserver.h +++ b/src/gameserver.h @@ -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 @@ -14,7 +15,11 @@ #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); diff --git a/src/gameserver_database.c b/src/gameserver_database.c index ba4ca02..f7503cc 100644 --- a/src/gameserver_database.c +++ b/src/gameserver_database.c @@ -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; } + + diff --git a/src/gameserver_database.h b/src/gameserver_database.h index d222aea..0b1a703 100644 --- a/src/gameserver_database.h +++ b/src/gameserver_database.h @@ -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 @@ -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 ); diff --git a/src/gameserver_replay.c b/src/gameserver_replay.c index 5f29c7f..2b1b2b8 100644 --- a/src/gameserver_replay.c +++ b/src/gameserver_replay.c @@ -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; iframe_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 ++; } diff --git a/src/gameserver_replay.h b/src/gameserver_replay.h index 69d3f4c..97b14e8 100644 --- a/src/gameserver_replay.h +++ b/src/gameserver_replay.h @@ -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 index 0000000..19abfd5 --- /dev/null +++ b/src/gameserver_requests.c @@ -0,0 +1,414 @@ +#include "gameserver_requests.h" +#include "gameserver_database.h" +#include "vg/vg_mem_pool.h" +#include + +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; iactive ) + 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, ¢iseconds, 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 index 0000000..bf23f9a --- /dev/null +++ b/src/gameserver_requests.h @@ -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 index f42874c..0000000 --- a/src/gameserver_transfer.c +++ /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; iactive ) - { - 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 index 7f4f18b..0000000 --- a/src/gameserver_transfer.h +++ /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 ); diff --git a/src/network.c b/src/network.c index a2335a1..9acddff 100644 --- a/src/network.c +++ b/src/network.c @@ -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; istatus != 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 ); diff --git a/src/network.h b/src/network.h index 5152c36..f82e855 100644 --- a/src/network.h +++ b/src/network.h @@ -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 ); diff --git a/src/network_msg.h b/src/network_msg.h index 0ad47a3..c156a2d 100644 --- a/src/network_msg.h +++ b/src/network_msg.h @@ -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) diff --git a/src/world_routes.c b/src/world_routes.c index c6735bb..940ff4b 100644 --- a/src/world_routes.c +++ b/src/world_routes.c @@ -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; } diff --git a/src/world_sfd.c b/src/world_sfd.c index 80cb56e..d41037f 100644 --- a/src/world_sfd.c +++ b/src/world_sfd.c @@ -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); -- 2.25.1