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