# 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
test)
run_game
;;
+ testserver)
+ run_server
+ ;;
testnet)
delay_run_game &
run_server
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"
void vg_start(void)
{
+ steam_init();
+
vg_convar_push( (struct vg_convar){
.name = "fc",
.data = &freecam,
reset_player( 1, (const char *[]){ "start" } );
rb_init( &player.rb );
+
+ network_init();
}
else
{
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 );
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 )
{
physics_test_render();
}
+#endif
}
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 );
}
}
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;
#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; i<len; i++ )
+ {
+ SteamNetworkingMessage_t *msg = messages[i];
+
+ if( msg->m_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 */
/*
* 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
}
}
-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 );
}
}
int main( int argc, char *argv[] )
{
steamworks_ensure_txt( "2103940" );
-
signal( SIGINT, inthandler );
if( !vg_load_steam_symetric_key( "application_key", steam_symetric_key ) )
* 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
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,
--- /dev/null
+#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 */