From 0945c6c301e38138b6ac54a02d5b3aea602de526 Mon Sep 17 00:00:00 2001 From: hgn Date: Tue, 9 Aug 2022 17:09:26 +0100 Subject: [PATCH] begin network integration --- build.sh | 4 + main.c | 43 +++++++--- network.h | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ server.c | 48 ++++++----- steam.h | 61 ++++++++++++++ 5 files changed, 357 insertions(+), 33 deletions(-) create mode 100644 steam.h diff --git a/build.sh b/build.sh index 55c61df..a4683b7 100755 --- a/build.sh +++ b/build.sh @@ -143,6 +143,7 @@ vg_command(){ # Dependencies cp vg/dep/steam/steamclient.so bin/linux_server/ + cp vg/dep/steam/libsteam_api.so bin/linux_server/ _compiler=$_linux_compiler _options=$_linux_options @@ -204,6 +205,9 @@ vg_command(){ test) run_game ;; + testserver) + run_server + ;; testnet) delay_run_game & run_server diff --git a/main.c b/main.c index a747e6f..bfac7fd 100644 --- a/main.c +++ b/main.c @@ -20,6 +20,14 @@ static int lightedit = 0; static int sv_scene = 0; /* Components */ +#define SR_NETWORKED + +/* uncomment this to run the game without any graphics being drawn */ +#define SR_NETWORK_TEST + +#include "steam.h" +#include "network.h" + #include "road.h" #include "scene.h" #include "ik.h" @@ -93,6 +101,8 @@ static int playermodel( int argc, char const *argv[] ) void vg_start(void) { + steam_init(); + vg_convar_push( (struct vg_convar){ .name = "fc", .data = &freecam, @@ -173,6 +183,8 @@ void vg_start(void) reset_player( 1, (const char *[]){ "start" } ); rb_init( &player.rb ); + + network_init(); } else { @@ -182,13 +194,19 @@ void vg_start(void) void vg_free(void) { + network_end(); vg_tex2d_free( texture_list, vg_list_size(texture_list) ); + /* TODO: THE REST OF THE GOD DAMN FREEING STUFF */ + steam_end(); } void vg_update(void) { + steam_update(); + if( sv_scene == 0 ) { + network_update(); player_update(); world_update(); //traffic_visualize( world.traffic, world.traffic_count ); @@ -309,6 +327,7 @@ void vg_render(void) glClearColor( 0.11f, 0.35f, 0.37f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); +#ifndef SR_NETWORK_TEST draw_origin_axis(); if( sv_scene == 0 ) @@ -319,6 +338,7 @@ void vg_render(void) { physics_test_render(); } +#endif } static void run_light_widget( struct light_widget *lw ) @@ -339,27 +359,26 @@ static void run_light_widget( struct light_widget *lw ) } } -void vg_ui(void) +static void run_debug_info(void) { - char buf[20]; - -#if 0 - snprintf( buf, 20, "%.2fm/s", v3_length( player.v ) ); + char buf[40]; + + snprintf( buf, 40, "%.2fm/s", v3_length( player.rb.v ) ); gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left ); - snprintf( buf, 20, "%.2f %.2f %.2f m/s", + snprintf( buf, 40, "%.2f %.2f %.2f m/s", player.a[0], player.a[1], player.a[2] ); gui_text( (ui_px [2]){ 0, 20 }, buf, 1, k_text_align_left ); - snprintf( buf, 20, "pos %.2f %.2f %.2f", - player.co[0], player.co[1], player.co[2] ); + snprintf( buf, 40, "pos %.2f %.2f %.2f", + player.rb.co[0], player.rb.co[1], player.rb.co[2] ); gui_text( (ui_px [2]){ 0, 40 }, buf, 1, k_text_align_left ); if( vg_gamepad_ready ) { for( int i=0; i<6; i++ ) { - snprintf( buf, 20, "%.2f", vg_gamepad.axes[i] ); + snprintf( buf, 40, "%.2f", vg_gamepad.axes[i] ); gui_text( (ui_px [2]){ 0, (i+3)*20 }, buf, 1, k_text_align_left ); } } @@ -368,8 +387,10 @@ void vg_ui(void) gui_text( (ui_px [2]){ 0, 60 }, "Gamepad not ready", 1, k_text_align_left ); } -#endif - +} + +void vg_ui(void) +{ if( lightedit ) { ui_global_ctx.cursor[0] = 10; diff --git a/network.h b/network.h index f97cfbc..1644982 100644 --- a/network.h +++ b/network.h @@ -2,7 +2,241 @@ #define NETWORK_H #include "vg/vg_stdint.h" +#include "steam.h" +#include "network_msg.h" +/* + * Interface + */ +/* Call it at start; Connects us to the gameserver */ +static void network_init(void); +/* Run this from main loop */ +static void network_update(void); + +/* Call it at shutdown */ +static void network_end(void); + +/* + * Can buffer up a bunch of these by calling many times, they will be + * sent at the next connection + */ +static void network_submit_highscore( u32 trackid, u16 points, u16 time ); + + +/* + * Game endpoints are provided with the same names to allow running without a + * network connection. + */ +#ifdef SR_NETWORKED + +/* + * Runtime connection stuff + */ +static u8 steam_app_ticket[ 1024 ]; +static u32 steam_app_ticket_length; + +static HSteamNetConnection cremote; +static ESteamNetworkingConnectionState cremote_state = + k_ESteamNetworkingConnectionState_None; + +/* + * Implementation + */ + +static void scores_update(void); + +static void on_auth_ticket_recieved( void *result, void *context ) +{ + EncryptedAppTicketResponse_t *response = result; + + if( response->m_eResult == k_EResultOK ) + { + vg_info( " New app ticket ready\n" ); + } + else + { + vg_warn( " Could not request new encrypted app ticket (%u)\n", + response->m_eResult ); + } + + if( SteamAPI_ISteamUser_GetEncryptedAppTicket( hSteamUser, + steam_app_ticket, + vg_list_size(steam_app_ticket), + &steam_app_ticket_length )) + { + vg_success( " Loaded app ticket (%u bytes)\n", steam_app_ticket_length ); + } + else + { + vg_error( " No ticket availible\n" ); + steam_app_ticket_length = 0; + } +} + +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 + */ + + vg_info( "Requesting new authorization ticket\n" ); + steam_async *call = steam_new_async(); + call->data = NULL; + call->p_handler = on_auth_ticket_recieved; + call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, + NULL, 0 ); +} + +static void server_connect(void) +{ + /* Connect to server if not connected */ + + SteamNetworkingIPAddr remoteAddr; + +#define USE_LOCALHOST +#ifdef USE_LOCALHOST + SteamAPI_SteamNetworkingIPAddr_SetIPv6LocalHost( &remoteAddr, 27402 ); +#else + const char *server_lon1 = "46.101.34.155:27402"; + SteamAPI_SteamNetworkingIPAddr_ParseString( &remoteAddr, server_lon1 ); +#endif + + char buf[256]; + SteamAPI_SteamNetworkingIPAddr_ToString( &remoteAddr, buf, 256, 1 ); + vg_info( "connect to: %s\n", buf ); + + cremote = SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress( + hSteamNetworkingSockets, &remoteAddr, 0, NULL ); +} + +static void scores_update(void) +{ + vg_log( "scores_update()\n" ); + + if( cremote_state == k_ESteamNetworkingConnectionState_Connected ) + { + /* + * request updated scores + */ + netmsg_scores_request req; + req.inetmsg_id = k_inetmsg_scores_request; + + SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( + hSteamNetworkingSockets, cremote, &req, + sizeof(netmsg_scores_request), + k_nSteamNetworkingSend_Reliable, NULL ); + } + else + { + /* + * if we are not connected, make a connection to the server and then in + * the future this function will be called again when it is connected + */ + server_connect(); + } +} + +static void poll_connection(void) +{ + SteamNetworkingMessage_t *messages[32]; + int len; + + while(1) + { + len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection( + hSteamNetworkingSockets, cremote, messages, vg_list_size(messages)); + + if( len <= 0 ) + return; + + for( int i=0; im_cbSize < sizeof(netmsg_blank) ) + { + vg_warn( "Discarding message (too small: %d)\n", + msg->m_cbSize ); + continue; + } + + netmsg_blank *tmp = msg->m_pData; + if( tmp->inetmsg_id == k_inetmsg_scores_info ) + { + netmsg_scores_info *info = msg->m_pData; + vg_log( "Recieved %u score records\n", info->record_count ); + + SteamAPI_ISteamNetworkingSockets_CloseConnection( + hSteamNetworkingSockets, cremote, 0, NULL, 1 ); + cremote_state = k_ESteamNetworkingConnectionState_None; + } + + SteamAPI_SteamNetworkingMessage_t_Release( msg ); + } + } +} + +static u64 in_server_ticks( double seconds ) +{ + return (u64)(seconds / 0.1); +} + +static void on_server_connect_status( CallbackMsg_t *msg ) +{ + SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam; + vg_info( " Connection status changed for %lu\n", info->m_hConn ); + vg_info( " %s -> %s\n", + string_ESteamNetworkingConnectionState(info->m_info.m_eState), + string_ESteamNetworkingConnectionState(info->m_eOldState) ); + + if( info->m_hConn == cremote ) + { + cremote_state = info->m_info.m_eState; + if( info->m_info.m_eState == + k_ESteamNetworkingConnectionState_Connected ) + { + vg_success(" Connected to remote server\n"); + scores_update(); + } + } + else + { + vg_warn( " Recieved signal from unknown connection\n" ); + } +} + +static void network_init(void) +{ + if( steam_ready ) + { + steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, + on_server_connect_status ); + request_auth_ticket(); + } +} + +static void network_update(void) +{ + if( steam_ready ) + { + static double last_update = -9000.0; + poll_connection(); + + if( vg_time > (last_update + 60.0) ) + { + last_update = vg_time; + scores_update(); + } + } +} + +static void network_end(void) +{ + /* TODO: Fire off any buffered highscores that need to be setn */ +} + +#endif #endif /* NETWORK_H */ diff --git a/server.c b/server.c index be19dcb..7d8e0e3 100644 --- a/server.c +++ b/server.c @@ -3,7 +3,7 @@ /* * This server application requires steamclient.so to be present in the * executable directory. This is not provided by vg system, it must be - * downloaded via steamcmd. It will likely be somewhere in /usr/.steam/ ... + * downloaded via steamcmd. It will likely be somewhere in ~/.steam/ ... */ #define _DEFAULT_SOURCE @@ -69,28 +69,26 @@ static void new_client_connecting( HSteamNetConnection client ) } } -static void handle_steam_callback( CallbackMsg_t *msg ) +static void on_auth_status( CallbackMsg_t *msg ) { - if( msg->m_iCallback == k_iSteamNetConnectionStatusChangedCallBack ) - { - SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam; - vg_info( " Connection status changed for %lu\n", info->m_hConn ); + SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam; + vg_info( " Authentication availibility: %s\n", + string_ESteamNetworkingAvailability(info->m_eAvail) ); + vg_info( " %s\n", info->m_debugMsg ); +} - vg_info( " %s -> %s\n", - string_ESteamNetworkingConnectionState(info->m_info.m_eState), - string_ESteamNetworkingConnectionState(info->m_eOldState) ); +static void on_connect_status( CallbackMsg_t *msg ) +{ + SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam; + vg_info( " Connection status changed for %lu\n", info->m_hConn ); - if( info->m_info.m_eState==k_ESteamNetworkingConnectionState_Connecting ) - { - new_client_connecting( info->m_hConn ); - } - } - else if( msg->m_iCallback == k_iSteamNetAuthenticationStatus ) + vg_info( " %s -> %s\n", + string_ESteamNetworkingConnectionState(info->m_info.m_eState), + string_ESteamNetworkingConnectionState(info->m_eOldState) ); + + if( info->m_info.m_eState==k_ESteamNetworkingConnectionState_Connecting ) { - SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam; - vg_info( " Authentication availibility: %s\n", - string_ESteamNetworkingAvailability(info->m_eAvail) ); - vg_info( " %s\n", info->m_debugMsg ); + new_client_connecting( info->m_hConn ); } } @@ -147,7 +145,6 @@ static void poll_connections(void) int main( int argc, char *argv[] ) { steamworks_ensure_txt( "2103940" ); - signal( SIGINT, inthandler ); if( !vg_load_steam_symetric_key( "application_key", steam_symetric_key ) ) @@ -174,8 +171,12 @@ int main( int argc, char *argv[] ) * Server code */ + steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status ); + steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, + on_connect_status ); + vg_success( "Steamworks API running\n" ); - steamworks_event_loop( hsteampipe, handle_steam_callback ); + steamworks_event_loop( hsteampipe ); /* * Create a listener @@ -202,12 +203,15 @@ int main( int argc, char *argv[] ) SteamAPI_ISteamHTTP_SendHTTPRequest( hSteamHTTP, test_req, &call1->id ); #endif + u64 server_ticks = 8000; + while( !sig_stop ) { - steamworks_event_loop( hsteampipe, handle_steam_callback ); + steamworks_event_loop( hsteampipe ); poll_connections(); usleep(100000); + server_ticks ++; } SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, diff --git a/steam.h b/steam.h new file mode 100644 index 0000000..57af429 --- /dev/null +++ b/steam.h @@ -0,0 +1,61 @@ +#ifndef STEAM_H +#define STEAM_H + +#include "vg/vg_steam.h" +#include "vg/vg_steam_networking.h" +#include "vg/vg_steam_auth.h" +#include "vg/vg_steam_http.h" + +/* + * We only want to use steamworks if building for the networked version, + * theres not much point otherwise. We mainly want steamworks for setting + * achievements etc.. so that includes our own server too. + * + * This file also wraps the functions and interfaces that we want to use to + * make them a bit easier to read, since they are the flat API they have very + * long names. in non-networked builds they will return default errors or do + * nothing. + */ + +static int steam_ready = 0; +static void *hSteamNetworkingSockets, + *hSteamUser; + +static HSteamPipe hSteamClientPipe; + +static void steam_init(void) +{ +#ifdef SR_NETWORKED + if( !SteamAPI_Init() ) + { + vg_error( "Steamworks failed to initialize\n" ); + return; + } + steam_ready = 1; + + SteamAPI_ManualDispatch_Init(); + vg_success( "Steamworks API running\n" ); + + /* Connect interfaces */ + hSteamClientPipe = SteamAPI_GetHSteamPipe(); + hSteamNetworkingSockets = SteamAPI_SteamNetworkingSockets_SteamAPI(); + hSteamUser = SteamAPI_SteamUser(); +#endif +} + +static void steam_update(void) +{ + if( steam_ready ) + steamworks_event_loop( hSteamClientPipe ); +} + +static void steam_end(void) +{ + if( steam_ready ) + { + vg_info( "Shutting down\n..." ); + SteamAPI_Shutdown(); + } +} + +#endif /* STEAM_H */ -- 2.25.1