#include "gameserver_db.h"
#include "vg/vg_m.h"
#include "vg/vg_msg.h"
+#include "gameserver_replay.h"
static u64 const k_steamid_max = 0xffffffffffffffff;
/*
* Send message to all clients if they are authenticated
*/
-static void gameserver_send_to_all( int ignore,
- const void *pData, u32 cbData,
- int nSendFlags )
+static void gameserver_send_to_all( int ignore, const void *pData, u32 cbData, int nSendFlags )
{
- for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+ for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
struct gameserver_client *client = &gameserver.clients[i];
netmsg_region *region = alloca( sizeof(netmsg_region) + NETWORK_REGION_MAX );
region->inetmsg_id = k_inetmsg_region;
- for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+ for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
struct gameserver_client *client = &gameserver.clients[i];
*/
static void handle_new_connection( HSteamNetConnection conn )
{
- SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
- hSteamNetworkingSockets, conn, -1 );
+ SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, -1 );
int index = -1;
- for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
- if( !gameserver.clients[i].active ){
+ for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
+ {
+ if( !gameserver.clients[i].active )
+ {
index = i;
break;
}
}
- if( index == -1 ){
+ if( index == -1 )
+ {
vg_error( "Server full\n" );
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, conn,
- 4500,
- NULL, 1 );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, conn, 4500, NULL, 1 );
return;
}
struct gameserver_client *client = &gameserver.clients[index];
- EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection(
- hSteamNetworkingSockets, conn );
+ EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection( hSteamNetworkingSockets, conn );
if( accept_status == k_EResultOK )
{
client->active = 1;
client->connection = conn;
- SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
- hSteamNetworkingSockets, conn, gameserver.client_group );
-
- SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
- hSteamNetworkingSockets, conn, index );
+ SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
+ SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
if( gameserver.loopback_test )
{
else
{
vg_warn( "Error accepting connection (id: %u)\n", conn );
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, conn,
- k_ESteamNetConnectionEnd_Misc_InternalError,
- NULL, 1 );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, conn,
+ k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
}
}
-static void on_auth_status( CallbackMsg_t *msg ){
+static void on_auth_status( CallbackMsg_t *msg )
+{
SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam;
- vg_info( " Authentication availibility: %s\n",
- string_ESteamNetworkingAvailability(info->m_eAvail) );
+ vg_info( " Authentication availibility: %s\n", string_ESteamNetworkingAvailability(info->m_eAvail) );
vg_info( " %s\n", info->m_debugMsg );
}
return -1;
}
else
- id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
- hSteamNetworkingSockets, hconn );
+ id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData( hSteamNetworkingSockets, hconn );
if( (id < 0) || (id >= NETWORK_MAX_PLAYERS) )
return -1;
handle_new_connection( info->m_hConn );
}
- if( (info->m_info.m_eState ==
- k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
- (info->m_info.m_eState ==
- k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ||
- (info->m_info.m_eState ==
- k_ESteamNetworkingConnectionState_Dead) ||
- (info->m_info.m_eState ==
- k_ESteamNetworkingConnectionState_None) )
+ if( (info->m_info.m_eState == k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
+ (info->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ||
+ (info->m_info.m_eState == k_ESteamNetworkingConnectionState_Dead) ||
+ (info->m_info.m_eState == k_ESteamNetworkingConnectionState_None) )
{
vg_info( "End reason: %d\n", info->m_info.m_eEndReason );
}
else
{
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
}
}
}
if( client_id == -1 )
{
vg_warn( "Recieved version from unkown connection (%u)\n", msg->m_conn );
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, msg->m_conn,
- k_ESteamNetConnectionEnd_Misc_InternalError,
- NULL, 1 );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, msg->m_conn,
+ k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
return;
}
if( client->version )
{
- vg_warn( "Already have version for this client (%d conn: %u)",
- client_id, msg->m_conn );
+ vg_warn( "Already have version for this client (%d conn: %u)", client_id, msg->m_conn );
return;
}
}
int client_id = gameserver_conid( msg->m_conn );
- if( client_id == -1 ) {
- vg_warn( "Recieved auth ticket from unkown connection (%u)\n",
- msg->m_conn );
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, msg->m_conn,
- k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
+ if( client_id == -1 )
+ {
+ vg_warn( "Recieved auth ticket from unkown connection (%u)\n", msg->m_conn );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, msg->m_conn,
+ k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
return;
}
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", client_id, msg->m_conn );
+ if( client->steamid )
+ {
+ vg_warn( "Already authorized this user but another app ticket was sent again (%d conn: %u)\n",
+ client_id, msg->m_conn );
return;
}
- if( client->version == 0 ){
+ if( client->version == 0 )
+ {
vg_error( "Client has not sent their version yet (%u)\n", msg->m_conn );
remove_client( client_id );
return;
vg_low( "Attempting to verify user\n" );
- if( msg->m_cbSize < sizeof(netmsg_auth) ){
+ if( msg->m_cbSize < sizeof(netmsg_auth) )
+ {
vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn );
remove_client( client_id );
return;
netmsg_auth *auth = msg->m_pData;
- if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length ||
- auth->ticket_length > 1024 ){
- vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
- auth->ticket_length );
+ if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length || auth->ticket_length > 1024 )
+ {
+ vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n", auth->ticket_length );
remove_client( client_id );
return;
}
u8 decrypted[1024];
u32 ticket_len = 1024;
- int success = SteamEncryptedAppTicket_BDecryptTicket(
- auth->ticket, auth->ticket_length, decrypted,
- &ticket_len, gameserver.app_symmetric_key,
- k_nSteamEncryptedAppTicketSymmetricKeyLen );
+ int success = SteamEncryptedAppTicket_BDecryptTicket( auth->ticket, auth->ticket_length, decrypted,
+ &ticket_len, gameserver.app_symmetric_key,
+ k_nSteamEncryptedAppTicketSymmetricKeyLen );
- if( !success ){
+ if( !success )
+ {
vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn );
vg_error( " ticket length: %u\n", auth->ticket_length );
remove_client( client_id );
return;
}
- if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )){
+ if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len ))
+ {
RTime32 ctime = time(NULL),
- tickettime = SteamEncryptedAppTicket_GetTicketIssueTime(
- decrypted, ticket_len ),
+ tickettime = SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len ),
expiretime = tickettime + 24*3*60*60;
- if( ctime > expiretime ){
+ if( ctime > expiretime )
+ {
vg_error( "Ticket expired (client %u)\n", msg->m_conn );
remove_client( client_id );
return;
CSteamID steamid;
SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid );
- vg_success( "User is authenticated! steamid %lu (%u)\n",
- steamid.m_unAll64Bits, msg->m_conn );
-
+ vg_success( "User is authenticated! steamid %lu (%u)\n", steamid.m_unAll64Bits, msg->m_conn );
client->steamid = steamid.m_unAll64Bits;
gameserver_player_join( client_id );
}
* -----------------------------------------------------------------------------
*/
-static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size ){
- if( msg->m_cbSize < size ) {
+static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size )
+{
+ if( msg->m_cbSize < size )
+ {
vg_error( "Invalid packet size (must be at least %u)\n", size );
return 0;
}
- else{
+ else
return 1;
- }
}
-struct db_set_username_thread_data {
+struct db_set_username_thread_data
+{
u64 steamid;
char username[ NETWORK_USERNAME_MAX ];
};
-static void gameserver_update_db_username( db_request *db_req ){
+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 )
db_updateuser( inf->steamid, inf->username, admin );
}
-static int gameserver_item_eq( struct gameserver_item *ia,
- struct gameserver_item *ib ){
+static int gameserver_item_eq( struct gameserver_item *ia, struct gameserver_item *ib )
+{
if( ia->hash == ib->hash )
if( !strcmp(ia->uid,ib->uid) )
return 1;
* Match addons between two player IDs. if clear is set, then the flags between
* those two IDs will all be set to 0.
*/
-static void gameserver_update_knowledge_table( int client0, int client1,
- int clear ){
+static void gameserver_update_knowledge_table( int client0, int client1, int clear )
+{
u32 idx = network_pair_index( client0, client1 );
struct gameserver_client *c0 = &gameserver.clients[client0],
* table of other players. if clear is set, all references to client will be set
* to 0.
*/
-static void gameserver_update_all_knowledge( int client, int clear ){
- for( int i=0; i<NETWORK_MAX_PLAYERS; i ++ ){
+static void gameserver_update_all_knowledge( int client, int clear )
+{
+ for( int i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+ {
if( i == client )
continue;
}
}
-static void gameserver_propogate_player_frame( int client_id,
- netmsg_playerframe *frame,
- u32 size ){
+static void gameserver_propogate_player_frame( int client_id, netmsg_playerframe *frame, u32 size )
+{
u32 basic_size = sizeof(netmsg_playerframe) + ((24*3)/8);
netmsg_playerframe *full = alloca(size),
*basic= alloca(basic_size);
struct gameserver_client *c0 = &gameserver.clients[client_id];
c0->instance = frame->flags & NETMSG_PLAYERFRAME_INSTANCE_ID;
- for( int i=0; i<vg_list_size(gameserver.clients); i++ )
+ for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
if( i == client_id )
continue;
}
if( send_full )
- {
- gameserver_send_to_client( i, full, size,
- k_nSteamNetworkingSend_Unreliable );
- }
+ gameserver_send_to_client( i, full, size, k_nSteamNetworkingSend_Unreliable );
else
- {
- gameserver_send_to_client( i, basic, basic_size,
- k_nSteamNetworkingSend_Unreliable );
- }
+ gameserver_send_to_client( i, basic, basic_size, k_nSteamNetworkingSend_Unreliable );
}
}
netmsg_playerusername *src = msg->m_pData;
- u32 name_len = network_msgstring( src->name, msg->m_cbSize,
- sizeof(netmsg_playerusername),
- client->username,
+ u32 name_len = network_msgstring( src->name, msg->m_cbSize, sizeof(netmsg_playerusername), client->username,
NETWORK_USERNAME_MAX );
/* update other users about this change */
- netmsg_playerusername *prop = alloca(sizeof(netmsg_playerusername)+
- NETWORK_USERNAME_MAX );
+ netmsg_playerusername *prop = alloca(sizeof(netmsg_playerusername)+NETWORK_USERNAME_MAX );
prop->inetmsg_id = k_inetmsg_playerusername;
prop->index = client_id;
- u32 chs = vg_strncpy( client->username, prop->name, NETWORK_USERNAME_MAX,
- k_strncpy_always_add_null );
+ u32 chs = vg_strncpy( client->username, prop->name, NETWORK_USERNAME_MAX, k_strncpy_always_add_null );
vg_info( "client #%d changed name to: %s\n", client_id, prop->name );
u32 propsize = sizeof(netmsg_playerusername) + chs + 1;
- gameserver_send_to_all( client_id, prop, propsize,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_all( client_id, prop, propsize, k_nSteamNetworkingSend_Reliable );
/* update database about this */
- db_request *call = db_alloc_request(
- sizeof(struct db_set_username_thread_data) );
+ 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 );
+ vg_strncpy( client->username, inf->username, sizeof(inf->username), k_strncpy_always_add_null );
call->handler = gameserver_update_db_username;
db_send_request( call );
}
else if( tmp->inetmsg_id == k_inetmsg_playerframe )
{
- gameserver_propogate_player_frame( client_id,
- msg->m_pData, msg->m_cbSize );
+ gameserver_propogate_player_frame( client_id, msg->m_pData, msg->m_cbSize );
+ _gs_replay_save_frame( client_id, msg->m_pData, msg->m_cbSize );
}
else if( tmp->inetmsg_id == k_inetmsg_playeritem )
{
/* record */
if( item->type_index >= k_netmsg_playeritem_max )
{
- vg_warn( "Client #%d invalid equip type %u\n",
- client_id, (u32)item->type_index );
+ vg_warn( "Client #%d invalid equip type %u\n", client_id, (u32)item->type_index );
return;
}
char *dest = client->items[ item->type_index ].uid;
- network_msgstring( item->uid, msg->m_cbSize, sizeof(netmsg_playeritem),
- dest, ADDON_UID_MAX );
+ network_msgstring( item->uid, msg->m_cbSize, sizeof(netmsg_playeritem), dest, ADDON_UID_MAX );
vg_info( "Client #%d equiped: [%s] %s\n",
client_id,
netmsg_playeritem *prop = alloca(msg->m_cbSize);
memcpy( prop, msg->m_pData, msg->m_cbSize );
prop->client = client_id;
- gameserver_send_to_all( client_id, prop, msg->m_cbSize,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_all( client_id, prop, msg->m_cbSize, k_nSteamNetworkingSend_Reliable );
}
else if( tmp->inetmsg_id == k_inetmsg_chat )
{
prop->inetmsg_id = k_inetmsg_chat;
prop->client = client_id;
- u32 l = network_msgstring( chat->msg, msg->m_cbSize, sizeof(netmsg_chat),
- prop->msg, NETWORK_MAX_CHAT );
+ u32 l = network_msgstring( chat->msg, msg->m_cbSize, sizeof(netmsg_chat), prop->msg, NETWORK_MAX_CHAT );
vg_info( "[%d]: %s\n", client_id, prop->msg );
- gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1,
- k_nSteamNetworkingSend_Reliable );
+ /* WARNING FIXME WARNING FIXME ------------------------------------------- */
+ if( !strcmp( prop->msg, "save" ) )
+ {
+ _gs_write_replay_to_disk( client_id, 1.0*60.0, "/tmp/server-replay.replay" );
+ }
+ else if( !strcmp( prop->msg, "play" ) )
+ {
+ _gs_test_replay( "/tmp/server-replay.replay" );
+ }
+
+ gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, k_nSteamNetworkingSend_Reliable );
}
else if( tmp->inetmsg_id == k_inetmsg_region )
{
client->region, NETWORK_REGION_MAX );
client->region_flags = region->flags;
- l = vg_strncpy( client->region, prop->loc, NETWORK_REGION_MAX,
- k_strncpy_always_add_null );
+ l = vg_strncpy( client->region, prop->loc, NETWORK_REGION_MAX, k_strncpy_always_add_null );
- gameserver_send_to_all( client_id, prop, sizeof(netmsg_region)+l+1,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_all( client_id, prop, sizeof(netmsg_region)+l+1, k_nSteamNetworkingSend_Reliable );
vg_info( "client %d moved to region: %s\n", client_id, client->region );
}
else
{
- vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
- tmp->inetmsg_id );
+ vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
}
}
static void gameserver_request_respond( enum request_status status,
netmsg_request *res, vg_msg *body,
- SteamNetworkingMessage_t *msg ){
+ SteamNetworkingMessage_t *msg )
+{
int client_id = gameserver_conid( msg->m_conn );
u32 len = 0;
- if( body ){
+ if( body )
+ {
len = body->cur.co;
vg_low( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
vg_msg_print( body, len );
const char *endpoint = vg_msg_getkvstr( &data, "endpoint" );
- if( !endpoint ){
- gameserver_request_respond( k_request_status_invalid_endpoint,
- res, NULL, msg );
+ if( !endpoint )
+ {
+ gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
return;
}
else
gameserver_cat_table( &body, mod, route, week, "rows" );
- if( body.error != k_vg_msg_error_OK ){
- gameserver_request_respond( k_request_status_out_of_memory,
- res, NULL, msg );
+ if( body.error != k_vg_msg_error_OK )
+ {
+ gameserver_request_respond( k_request_status_out_of_memory, res, NULL, msg );
return;
}
gameserver_request_respond( k_request_status_ok, res, &body, msg );
}
- else if( !strcmp( endpoint, "setlap" ) ){
- if( client->steamid == k_steamid_max ){
- gameserver_request_respond( k_request_status_unauthorized,
- res, NULL, msg );
+ else if( !strcmp( endpoint, "setlap" ) )
+ {
+ if( client->steamid == k_steamid_max )
+ {
+ gameserver_request_respond( k_request_status_unauthorized, res, NULL, msg );
return;
}
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 );
+ !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 );
+ if( centiseconds < 5*100 )
+ {
+ gameserver_request_respond( k_request_status_client_error, res, NULL, msg );
return;
}
db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
gameserver_request_respond( k_request_status_ok, res, NULL, msg );
}
- else{
- gameserver_request_respond( k_request_status_invalid_endpoint,
- res, NULL, msg );
+ else
+ {
+ gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
}
}
return;
}
- db_request *call = db_alloc_request(
- sizeof(struct user_request_thread_data) );
+ 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;
}
else
{
- vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
- tmp->inetmsg_id );
+ vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
release_message( msg );
}
}
static void process_network_message( SteamNetworkingMessage_t *msg )
{
- if( msg->m_cbSize < sizeof(netmsg_blank) ){
- vg_warn( "Discarding message (too small: %d)\n",
- msg->m_cbSize );
+ if( msg->m_cbSize < sizeof(netmsg_blank) )
+ {
+ vg_warn( "Discarding message (too small: %d)\n", msg->m_cbSize );
return;
}
{
len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
hSteamNetworkingSockets,
- gameserver.client_group, messages, vg_list_size(messages) );
+ gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
if( len <= 0 )
return;
if( gameserver.loopback_test )
{
- HSteamNetConnection conid = msg->m_conn;
- msg->m_conn = 0;
- msg->m_nUserData ++;
- process_network_message( msg );
- msg->m_conn = conid;
+ 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 );
}
}
-static u64 seconds_to_server_ticks( double s ){
+u64 seconds_to_server_ticks( f64 s )
+{
return s / 0.01;
}
-int main( int argc, char *argv[] ){
+int main( int argc, char *argv[] )
+{
+ vg_log_init();
+
signal( SIGINT, inthandler );
signal( SIGQUIT, inthandler );
signal( SIGPIPE, SIG_IGN );
char *arg;
while( vg_argp( argc, argv ) )
{
- if( vg_long_opt( "noauth" ) )
+ if( vg_long_opt( "noauth", "Disable server authentication" ) )
gameserver.auth_mode = eServerModeNoAuthentication;
- if( vg_long_opt( "loopback" ) )
+ 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 );
/* steamworks init
* --------------------------------------------------------------- */
steamworks_ensure_txt( "2103940" );
- if( gameserver.auth_mode == eServerModeAuthentication ){
- if( !vg_load_steam_symetric_key( "application_key",
- gameserver.app_symmetric_key )){
+ if( gameserver.auth_mode == eServerModeAuthentication )
+ {
+ if( !vg_load_steam_symetric_key( "application_key", gameserver.app_symmetric_key ))
return 0;
- }
}
- else{
+ 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;
}
SteamAPI_ManualDispatch_Init();
HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
- hSteamNetworkingSockets =
- SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
+ hSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
- steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
- on_connect_status );
+ steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_connect_status );
vg_success( "Steamworks API running\n" );
steamworks_event_loop( hsteampipe );
SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
localAddr.m_port = NETWORK_PORT;
- listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
- hSteamNetworkingSockets, &localAddr, 0, NULL );
- gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
- hSteamNetworkingSockets );
-
- u64 server_ticks = 8000,
- last_record_save = 8000,
- last_scoreboard_gen = 0;
+ 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 );
- while( !sig_stop ){
+ while( !sig_stop )
+ {
steamworks_event_loop( hsteampipe );
poll_connections();
+ _gs_replay_server_tick();
usleep(10000);
- server_ticks ++;
+ gameserver.ticks ++;
if( db_killed() )
break;
}
- SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
- gameserver.client_group );
- SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
- hSteamNetworkingSockets, listener );
+ SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
+ SteamAPI_ISteamNetworkingSockets_CloseListenSocket( hSteamNetworkingSockets, listener );
vg_info( "Shutting down\n..." );
SteamGameServer_Shutdown();
return 0;
}
+
+#include "gameserver_replay.c"
--- /dev/null
+#include "gameserver.h"
+#include "gameserver_replay.h"
+
+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" );
+
+ for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+ {
+ gs_replay *replay = &_gs_replay.replays[ i ];
+ if( replay->frame_count )
+ 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 )
+ {
+ vg_error( "Replay frame count is 0, nothing to save..\n" );
+ return;
+ }
+
+ /* FIXME: Should we do this on another 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;
+ while(1)
+ {
+ gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+ if( frame->tick_recorded < min_ticks )
+ break;
+
+ if( frame->previous_queue_item == VG_MEM_QUEUE_INVALID )
+ break;
+
+ frame_id = frame->previous_queue_item;
+ }
+
+ u32 frame_count = 0;
+ while(1)
+ {
+ gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+ 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;
+ }
+
+ fclose( fp );
+ vg_success( "Written %u frames to disk.\n", frame_count );
+}
+
+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 ];
+
+ u32 total_size = sizeof( gs_replay_frame ) + frame_size;
+
+ u32 dest_item_id;
+ gs_replay_frame *dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+ if( !dest_frame )
+ {
+ /* 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 );
+
+ for( u32 i=0; i<64; i ++ )
+ {
+ gs_replay_frame *free_frame = vg_queue_tail_data( ring );
+ if( !free_frame )
+ break;
+
+ /* blocked by valid frame */
+ if( (free_frame->tick_recorded >= min_ticks) && (free_frame->network_frame.client!=255) )
+ 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 );
+ }
+
+ dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+ if( !dest_frame )
+ {
+ /* policy 3: grow (double size) */
+ u32 cur_size = _gs_replay.ring_buffer.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;
+
+ 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->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 ++;
+}