#include "vg/vg_m.h"
#include "vg/vg_msg.h"
#include "gameserver_replay.h"
-#include "gameserver_transfer.h"
+#include "gameserver_requests.h"
+
+struct _gameserver _gameserver =
+{
+ .auth_mode = eServerModeAuthentication,
+ .tasks =
+ {
+ .upper_memory_limit = 1024*1024*8
+ }
+};
static u64 const k_steamid_max = 0xffffffffffffffff;
ISteamNetworkingSockets *hSteamNetworkingSockets;
ISteamNetworkingUtils *hSteamNetworkingUtils;
-static void inthandler( int signum ) {
- sig_stop = 1;
-}
-
-static void release_message( SteamNetworkingMessage_t *msg )
+static void inthandler( int signum )
{
- SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ sig_stop = 3;
}
/*
*/
static void gameserver_send_to_client( i32 client_id, const void *pData, u32 cbData, int nSendFlags )
{
- struct gameserver_client *client = &gameserver.clients[ client_id ];
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
if( !client->steamid )
return;
{
for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
- struct gameserver_client *client = &gameserver.clients[i];
+ struct gameserver_client *client = &_gameserver.clients[i];
if( i != ignore )
gameserver_send_to_client( i, pData, cbData, nSendFlags );
static void gameserver_send_version_to_client( int index )
{
- struct gameserver_client *client = &gameserver.clients[index];
+ struct gameserver_client *client = &_gameserver.clients[index];
netmsg_version version;
version.inetmsg_id = k_inetmsg_version;
*/
static void gameserver_player_join( int index )
{
- struct gameserver_client *joiner = &gameserver.clients[index];
+ struct gameserver_client *joiner = &_gameserver.clients[index];
SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes( hSteamNetworkingSockets, joiner->connection, 2,
(int[]){ 0,0 }, (u16[]){ 10, 1 } );
for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
- struct gameserver_client *client = &gameserver.clients[i];
+ struct gameserver_client *client = &_gameserver.clients[i];
if( (i == index) || !client->steamid )
continue;
* Handle server update that player has left
*/
static void gameserver_player_leave( int index ){
- if( gameserver.auth_mode == eServerModeAuthentication ){
- if( !gameserver.clients[ index ].steamid )
+ if( _gameserver.auth_mode == eServerModeAuthentication ){
+ if( !_gameserver.clients[ index ].steamid )
return;
}
* Deletes client at index and disconnects the connection handle if it was
* set.
*/
-static void remove_client( int index ){
- struct gameserver_client *client = &gameserver.clients[index];
- if( client->connection ){
- SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
- hSteamNetworkingSockets, client->connection, -1 );
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, client->connection,
- k_ESteamNetConnectionEnd_Misc_InternalError,
- NULL, 1 );
+static void remove_client( int index )
+{
+ struct gameserver_client *client = &_gameserver.clients[index];
+ if( client->connection )
+ {
+ SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, client->connection, -1 );
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, client->connection,
+ k_ESteamNetConnectionEnd_Misc_InternalError, NULL, 1 );
}
memset( client, 0, sizeof(struct gameserver_client) );
gameserver_update_all_knowledge( index, 1 );
+ _gs_requests_client_disconnect( index );
}
/*
for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
- if( !gameserver.clients[i].active )
+ if( !_gameserver.clients[i].active )
{
index = i;
break;
return;
}
- struct gameserver_client *client = &gameserver.clients[index];
+ struct gameserver_client *client = &_gameserver.clients[index];
EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection( hSteamNetworkingSockets, conn );
if( accept_status == k_EResultOK )
{
vg_success( "Accepted client (id: %u, index: %d)\n", conn, index );
+ _gameserver.global_uid ++;
+ client->session_uid = _gameserver.global_uid;
+
client->active = 1;
client->connection = conn;
- SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
+ SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, _gameserver.client_group );
SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
}
else
return;
}
- struct gameserver_client *client = &gameserver.clients[ client_id ];
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
if( client->version )
{
/* this is the sign on point for non-auth servers,
* for auth servers it comes at the end of rx_auth
*/
- if( gameserver.auth_mode != eServerModeAuthentication )
+ if( _gameserver.auth_mode != eServerModeAuthentication )
{
client->steamid = k_steamid_max;
gameserver_player_join( client_id );
* to the client list first.
*/
static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
- if( gameserver.auth_mode != eServerModeAuthentication ){
+ if( _gameserver.auth_mode != eServerModeAuthentication ){
vg_warn( "Running server without authentication. "
"Connection %u tried to authenticate.\n", msg->m_conn );
return;
return;
}
- struct gameserver_client *client = &gameserver.clients[ client_id ];
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
if( client->steamid )
{
vg_warn( "Already authorized this user but another app ticket was sent again (%d conn: %u)\n",
u32 ticket_len = 1024;
int success = SteamEncryptedAppTicket_BDecryptTicket( auth->ticket, auth->ticket_length, decrypted,
- &ticket_len, gameserver.app_symmetric_key,
+ &ticket_len, _gameserver.app_symmetric_key,
k_nSteamEncryptedAppTicketSymmetricKeyLen );
if( !success )
return 1;
}
-struct db_set_username_thread_data
-{
- u64 steamid;
- char username[ NETWORK_USERNAME_MAX ];
-};
-
-static void gameserver_update_db_username( db_request *db_req )
-{
- struct db_set_username_thread_data *inf = (void *)db_req->data;
-
- if( inf->steamid == k_steamid_max )
- return;
-
- int admin = 0;
- if( inf->steamid == 76561198072130043 )
- admin = 2;
-
- db_updateuser( inf->steamid, inf->username, admin );
-}
-
static int gameserver_item_eq( struct gameserver_item *ia, struct gameserver_item *ib )
{
if( ia->hash == ib->hash )
{
u32 idx = network_pair_index( client0, client1 );
- struct gameserver_client *c0 = &gameserver.clients[client0],
- *c1 = &gameserver.clients[client1];
+ struct gameserver_client *c0 = &_gameserver.clients[client0],
+ *c1 = &_gameserver.clients[client1];
u8 flags = 0x00;
flags |= CLIENT_KNOWLEDGE_SAME_WORLD1;
}
- gameserver.client_knowledge_mask[idx] = flags;
+ _gameserver.client_knowledge_mask[idx] = flags;
}
/*
if( i == client )
continue;
- struct gameserver_client *ci = &gameserver.clients[i];
+ struct gameserver_client *ci = &_gameserver.clients[i];
if( ci->steamid )
gameserver_update_knowledge_table( client, i, clear );
basic->subsystem = 4; /* (.._basic_info: 24f*3 animator ) */
basic->sound_effects = 0;
- struct gameserver_client *c0 = &gameserver.clients[client_id];
+ struct gameserver_client *c0 = &_gameserver.clients[client_id];
for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
if( i == client_id )
continue;
- struct gameserver_client *ci = &gameserver.clients[i];
+ struct gameserver_client *ci = &_gameserver.clients[i];
int send_full = 0;
u32 k_index = network_pair_index( client_id, i );
- u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
+ u8 k_mask = _gameserver.client_knowledge_mask[ k_index ];
if( (k_mask & CLIENT_KNOWLEDGE_SAME_WORLD0) )
send_full = 1;
int client_id = gameserver_conid( msg->m_conn );
if( client_id == -1 ) return;
- struct gameserver_client *client = &gameserver.clients[ client_id ];
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
if( tmp->inetmsg_id == k_inetmsg_playerusername )
{
u32 propsize = sizeof(netmsg_playerusername) + chs + 1;
gameserver_send_to_all( client_id, prop, propsize, k_nSteamNetworkingSend_Reliable );
-
- /* update database about this */
-#if 0
- db_request *call = db_alloc_request( sizeof(struct db_set_username_thread_data) );
- /* FIXME: Call can be NULL, crash possible. */
- struct db_set_username_thread_data *inf = (void *)call->data;
- inf->steamid = client->steamid;
- vg_strncpy( client->username, inf->username, sizeof(inf->username), k_strncpy_always_add_null );
- call->handler = gameserver_update_db_username;
- db_send_request( call );
-#endif
+ db_action_set_username( client->steamid, client->username );
}
else if( tmp->inetmsg_id == k_inetmsg_playerframe )
{
}
else if( !strcmp( prop->msg, "transfer" ) )
{
- _gs_start_transfer( client_id, "/tmp/server-replay.replay" );
+ //_gs_start_transfer( client_id, "/tmp/server-replay.replay" );
//_gs_test_replay( client_id, "/tmp/server-replay.replay" );
}
}
}
-static void gameserver_request_respond( enum request_status status,
- netmsg_request *res, vg_msg *body,
+u32 gameserver_get_current_week(void)
+{
+ return time(NULL) / (7*24*60*60);
+}
+
+
+#if 0
+static void gameserver_request_respond( enum request_status status, netmsg_request *res, vg_msg *body,
SteamNetworkingMessage_t *msg )
{
int client_id = gameserver_conid( msg->m_conn );
res->status = status;
- SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
- hSteamNetworkingSockets, msg->m_conn,
- res, sizeof(netmsg_request) + len,
- k_nSteamNetworkingSend_Reliable, NULL );
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, msg->m_conn,
+ res, sizeof(netmsg_request) + len,
+ k_nSteamNetworkingSend_Reliable, NULL );
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
struct user_request_thread_data {
SteamNetworkingMessage_t *msg;
};
-static u32 gameserver_get_current_week(void){
- return time(NULL) / (7*24*60*60);
-}
-
static enum request_status gameserver_cat_table(
vg_msg *msg,
const char *mod, const char *route, u32 week, const char *alias )
int client_id = gameserver_conid( msg->m_conn );
if( client_id == -1 )
{
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
return;
}
- struct gameserver_client *client = &gameserver.clients[ client_id ];
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
netmsg_request *req = (netmsg_request *)msg->m_pData;
vg_msg data;
return;
}
- if( !strcmp( endpoint, "scoreboard" ) ){
+ if( !strcmp( endpoint, "scoreboard" ) )
+ {
const char *mod = vg_msg_getkvstr( &data, "mod" );
const char *route = vg_msg_getkvstr( &data, "route" );
u32 week;
vg_msg_getkvintg( &data, "week", k_vg_msg_u32, &week, NULL );
- if( week == NETWORK_LEADERBOARD_CURRENT_WEEK ){
- gameserver_cat_table( &body, mod, route,
- gameserver_get_current_week(), "rows_weekly" );
+ if( week == NETWORK_LEADERBOARD_CURRENT_WEEK )
+ {
+ gameserver_cat_table( &body, mod, route, gameserver_get_current_week(), "rows_weekly" );
}
- else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK ){
+ else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK )
+ {
gameserver_cat_table( &body, mod, route, 0, "rows" );
- gameserver_cat_table( &body, mod, route,
- gameserver_get_current_week(), "rows_weekly" );
+ gameserver_cat_table( &body, mod, route, gameserver_get_current_week(), "rows_weekly" );
}
else
gameserver_cat_table( &body, mod, route, week, "rows" );
gameserver_request_respond( k_request_status_invalid_endpoint, res, NULL, msg );
}
}
+#endif
static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg )
{
int client_id = gameserver_conid( msg->m_conn );
if( client_id == -1 )
{
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
return;
}
if( tmp->inetmsg_id == k_inetmsg_request )
{
- if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
- {
- release_message( msg );
- return;
- }
-
- /* DISABLED: REASON, CODE WASN'T THREAD SAFE. */
- release_message( msg );
-#if 0
- db_request *call = db_alloc_request( sizeof(struct user_request_thread_data) );
- /* FIXME: Call can be NULL, crash possible. */
- struct user_request_thread_data *inf = (void *)call->data;
- inf->msg = msg;
- call->handler = gameserver_process_user_request;
- db_send_request( call );
-#endif
+ _gs_handle_request_message( client_id, msg );
}
else
{
vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
}
if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) )
{
gameserver_rx_200_300( msg );
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) )
{
gameserver_rx_version( msg );
else
vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
- release_message( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
}
{
len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
hSteamNetworkingSockets,
- gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
+ _gameserver.client_group, messages, VG_ARRAY_LEN(messages) );
if( len <= 0 )
return;
int main( int argc, char *argv[] )
{
+ _gameserver.thread = pthread_self();
+
vg_log_init();
signal( SIGINT, inthandler );
while( vg_argp( argc, argv ) )
{
if( vg_long_opt( "noauth", "Disable server authentication" ) )
- gameserver.auth_mode = eServerModeNoAuthentication;
+ _gameserver.auth_mode = eServerModeNoAuthentication;
if( vg_long_opt( "replay-info", "Print replay info periodically" ) )
_gs_replay.print_info = 1;
}
+
+ if( !vg_init_async_queue( &_gameserver.tasks ) )
+ goto E0;
vg_set_mem_quota( 80*1024*1024 );
vg_alloc_quota();
if( !db_init() )
- {
- return 0;
- }
+ goto E0;
+
+ _gs_requests_init();
/* steamworks init
* --------------------------------------------------------------- */
steamworks_ensure_txt( "2103940" );
- if( gameserver.auth_mode == eServerModeAuthentication )
+ if( _gameserver.auth_mode == eServerModeAuthentication )
{
- if( !vg_load_steam_symetric_key( "application_key", gameserver.app_symmetric_key ))
- return 0;
+ if( !vg_load_steam_symetric_key( "application_key", _gameserver.app_symmetric_key ))
+ goto E1;
}
else
vg_warn( "Running without user authentication.\n" );
- if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1, gameserver.auth_mode, "1.0.0.0" ) )
+ if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1, _gameserver.auth_mode, "1.0.0.0" ) )
{
vg_error( "SteamGameServer_Init failed\n" );
- return 0;
+ goto E1;
}
void *hSteamGameServer = SteamAPI_SteamGameServer();
localAddr.m_port = NETWORK_PORT;
listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP( hSteamNetworkingSockets, &localAddr, 0, NULL );
- gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup( hSteamNetworkingSockets );
- gameserver.ticks = seconds_to_server_ticks( 30.0 * 60.0 );
+ _gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup( hSteamNetworkingSockets );
+ _gameserver.ticks = seconds_to_server_ticks( 30.0 * 60.0 );
- while( !sig_stop )
+ while(1)
{
steamworks_event_loop( hsteampipe );
poll_connections();
_gs_replay_server_tick();
- _gs_transfer_tick();
+ _gs_requests_tick();
usleep(10000);
- gameserver.ticks ++;
+ _gameserver.ticks ++;
- /* consume tasks here */
+ if( sig_stop == 3 )
+ {
+ vg_info( "Shutting down...\n" );
+ sig_stop = 1;
+ vg_async_queue_end( &_gs_db.tasks, k_async_quit_when_empty );
+ }
+
+ if( vg_async_consume( &_gameserver.tasks, 100 ) )
+ break;
}
+
+ vg_info( "Program ends\n" );
- SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
+ SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, _gameserver.client_group );
SteamAPI_ISteamNetworkingSockets_CloseListenSocket( hSteamNetworkingSockets, listener );
- vg_info( "Shutting down\n..." );
+
SteamGameServer_Shutdown();
- db_free();
- return 0;
+E1:db_free();
+E0:return 0;
}
#include "gameserver_replay.c"
-#include "gameserver_transfer.c"
+#include "gameserver_requests.c"
#include "gameserver_database.c"
+#include "vg/vg_async2.c"
+#include "vg/vg_mem_pool.c"
#include "vg/vg_steam_networking.h"
#include "vg/vg_steam_http.h"
#include "vg/vg_steam_auth.h"
+#include "vg/vg_async2.h"
#include "network_msg.h"
#include "network_common.h"
#include <sys/socket.h>
#define CLIENT_KNOWLEDGE_SAME_WORLD1 0x2
#define CLIENT_KNOWLEDGE_FRIENDS 0x4 /* unused */
-struct {
+#define THREAD_0 VG_ASSERT( pthread_equal( pthread_self(), _gameserver.thread ) );
+#define THREAD_1 VG_ASSERT( pthread_equal( pthread_self(), _gs_db.worker_thread ) );
+
+struct _gameserver
+{
HSteamNetPollGroup client_group;
EServerMode auth_mode;
u32 region_flags;
u64 steamid;
+ u64 session_uid;
}
clients[ NETWORK_MAX_PLAYERS ];
u8 client_knowledge_mask[ (NETWORK_MAX_PLAYERS*(NETWORK_MAX_PLAYERS-1))/2 ];
u8 app_symmetric_key[ k_nSteamEncryptedAppTicketSymmetricKeyLen ];
u64 ticks;
+ u64 global_uid;
+
+ vg_async_queue tasks;
+ pthread_t thread;
}
-static gameserver = {
- .auth_mode = eServerModeAuthentication
-};
+extern _gameserver;
extern ISteamNetworkingSockets *hSteamNetworkingSockets;
extern ISteamNetworkingUtils *hSteamNetworkingUtils;
u64 seconds_to_server_ticks( f64 s );
+u32 gameserver_get_current_week(void);
#include "gameserver_database.h"
+#include "vg/vg_async2.h"
-struct _gs_db _gs_db;
+struct _gs_db _gs_db =
+{
+ .tasks =
+ {
+ .upper_memory_limit = 1024*1024*40
+ }
+};
+
+/* database system
+ * ----------------------------------------------------------- */
/*
* Log the error code (or carry on if its OK).
*/
-void log_sqlite3( int code )
+static void log_sqlite3( int code )
{
+ THREAD_1;
if( code == SQLITE_OK )
return;
vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
}
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+{
+ THREAD_1;
+ return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+}
+
sqlite3_stmt *db_stmt( const char *code )
{
+ THREAD_1;
#ifdef DB_LOG_SQL_STATEMENTS
vg_low( code );
#endif
return stmt;
}
-int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+static void *database_worker_thread(void *_)
{
- return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+ int rc = sqlite3_open( "highscores.db", &_gs_db.db );
+
+ if( rc )
+ {
+ vg_error( "database failure: %s\n", sqlite3_errmsg(_gs_db.db) );
+ sqlite3_close( _gs_db.db );
+ return NULL;
+ }
+
+ sqlite3_stmt *stmt = db_stmt( "CREATE TABLE IF NOT EXISTS \n"
+ " users(steamid BIGINT UNIQUE, name VARCHAR(128), type INT);" );
+ if( stmt )
+ {
+ int fc = sqlite3_step( stmt );
+ sqlite3_finalize(stmt);
+
+ if( fc == SQLITE_DONE )
+ {
+ vg_success( "Created users table\n" );
+ //db_updateuser( 76561198072130043, "harry", 2 );
+ }
+ else
+ {
+ log_sqlite3( fc );
+ sqlite3_close( _gs_db.db );
+ return NULL;
+ }
+ }
+ else
+ {
+ sqlite3_close( _gs_db.db );
+ return NULL;
+ }
+
+ /*
+ * Request processing loop
+ */
+ while(1)
+ {
+ usleep(50000);
+
+ if( vg_async_consume( &_gs_db.tasks, 100 ) )
+ break;
+ }
+
+ sqlite3_close( _gs_db.db );
+ vg_low( "Database thread terminates.\n" );
+ vg_async_queue_end( &_gameserver.tasks, k_async_quit_immediate );
+ return NULL;
+}
+
+bool db_init(void)
+{
+ if( !vg_init_async_queue( &_gs_db.tasks ) )
+ return 0;
+
+ if( pthread_create( &_gs_db.worker_thread, NULL, database_worker_thread, NULL ) )
+ return 0;
+
+ return 1;
+}
+
+void db_free(void)
+{
+ pthread_join( _gs_db.worker_thread, NULL );
}
/*
return 0;
}
+/* database internals
+ * ----------------------------------------------------------- */
+
bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] )
{
if( !db_verify_charset( mod_uid, 13 ) || !db_verify_charset( run_uid, 1 ) )
vg_strcat( &a, ":" );
vg_strcat( &a, run_uid );
- if( week ){
+ if( week )
+ {
vg_strcat( &a, "#" );
vg_strcati32( &a, week );
}
i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid )
{
+ THREAD_1;
+
char buf[ 512 ];
vg_str q;
vg_strnull( &q, buf, 512 );
bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster )
{
+ THREAD_1;
+
/* auto create table
* ------------------------------------------*/
char buf[ 512 ];
bool db_updateuser( u64 steamid, const char *username, int admin )
{
+ THREAD_1;
+
sqlite3_stmt *stmt = db_stmt( "INSERT OR REPLACE INTO users (steamid, name, type) VALUES (?,?,?);" );
if( stmt )
bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type )
{
+ THREAD_1;
+
sqlite3_stmt *stmt = db_stmt( "SELECT * FROM users WHERE steamid = ?;" );
- if( !stmt ) return 0;
+ if( !stmt )
+ return 0;
sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
int fc = sqlite3_step( stmt );
return 1;
}
-#if 0
-static void _db_thread_end(void){
- pthread_mutex_lock( &_gs_db.mux );
- _gs_db.kill = 1;
- pthread_mutex_unlock( &_gs_db.mux );
- sqlite3_close( _gs_db.db );
-}
-#endif
+struct task_set_username
+{
+ u64 steamid;
+ char name[NETWORK_USERNAME_MAX];
+};
-static void *database_worker_thread(void *_)
+static void task_set_username( vg_async_task *task )
{
- int rc = sqlite3_open( "highscores.db", &_gs_db.db );
+ THREAD_1;
+ struct task_set_username *info = (void *)task->data;
+ if( info->steamid == k_steamid_max )
+ return;
- if( rc )
- {
- vg_error( "database failure: %s\n", sqlite3_errmsg(_gs_db.db) );
- sqlite3_close( _gs_db.db );
- return NULL;
- }
+ sqlite3_stmt *stmt = db_stmt( "INSERT OR REPLACE INTO users (steamid, name) VALUES (?,?);" );
- sqlite3_stmt *stmt = db_stmt( "CREATE TABLE IF NOT EXISTS \n"
- " users(steamid BIGINT UNIQUE, name VARCHAR(128), type INT);" );
if( stmt )
{
+ sqlite3_bind_int64( stmt, 1, *((i64*)(&info->steamid)) );
+ db_sqlite3_bind_sz( stmt, 2, info->name );
+
int fc = sqlite3_step( stmt );
sqlite3_finalize(stmt);
if( fc == SQLITE_DONE )
- {
- vg_success( "Created users table\n" );
- db_updateuser( 76561198072130043, "harry", 2 );
- }
+ vg_success( "Updated username for %lu (%s)\n", info->steamid, info->name );
else
- {
log_sqlite3( fc );
- sqlite3_close( _gs_db.db );
- return NULL;
- }
- }
- else
- {
- sqlite3_close( _gs_db.db );
- return NULL;
}
+}
- /*
- * Request processing loop
- */
- while(1)
- {
- pthread_mutex_lock( &_gs_db.mux );
+void db_action_set_username( u64 steamid, const char *username )
+{
+ THREAD_0;
+
+ vg_async_task *task = vg_allocate_async_task( &_gs_db.tasks, sizeof(struct task_set_username), 1 );
+ task->handler = task_set_username;
+ struct task_set_username *info = (void *)task->data;
+ info->steamid = steamid;
+ vg_strncpy( username, info->name, sizeof(info->name), k_strncpy_always_add_null );
+ vg_async_task_dispatch( &_gs_db.tasks, task );
+}
-#if 0
- u32 processed = 0;
- for( u32 i=0; i<16; i ++ )
- {
- db_request *req = vg_queue_tail_data( &_gs_db.queue );
- if( !req )
- break;
- pthread_mutex_unlock( &_gs_db.mux );
-
- req->handler( req );
- processed ++;
-
- pthread_mutex_lock( &_gs_db.mux );
- vg_queue_pop( &_gs_db.queue );
- }
- pthread_mutex_unlock( &_gs_db.mux );
- if( processed )
- vg_low( "Processed %u _gs_db.requests.\n", processed );
-#endif
- pthread_mutex_unlock( &_gs_db.mux );
- usleep(50000);
- }
+enum request_status gameserver_cat_table(
+ vg_msg *msg,
+ const char *mod, const char *route, u32 week, const char *alias )
+{
+ char table_name[ DB_TABLE_UID_MAX ];
+ if( !db_get_highscore_table_name( mod, route, week, table_name ) )
+ return k_request_status_out_of_memory;
- vg_low( "Database thread terminates.\n" );
- return NULL;
-}
+ char buf[512];
+ vg_str q;
+ vg_strnull( &q, buf, 512 );
+ vg_strcat( &q, "SELECT * FROM \"" );
+ vg_strcat( &q, table_name );
+ vg_strcat( &q, "\" ORDER BY time ASC LIMIT 10;" );
+ if( !vg_strgood(&q) )
+ return k_request_status_out_of_memory;
-bool db_init(void)
-{
- _gs_db.queue.buffer = (u8 *)vg_linear_alloc( vg_mem.rtmemory, DB_REQUEST_BUFFER_SIZE ),
- _gs_db.queue.size = DB_REQUEST_BUFFER_SIZE;
+ sqlite3_stmt *stmt = db_stmt( q.buffer );
+ if( !stmt )
+ return k_request_status_database_error;
- if( pthread_mutex_init( &_gs_db.mux, NULL ) )
- return 0;
+ vg_msg_frame( msg, alias );
+ for( u32 i=0; i<10; i ++ ){
+ int fc = sqlite3_step( stmt );
- if( pthread_create( &_gs_db.thread, NULL, _gs_db.worker_thread, NULL ) )
- return 0;
+ if( fc == SQLITE_ROW ){
+ i32 time = sqlite3_column_int( stmt, 1 );
+ i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
+ u64 steamid = *((u64 *)&steamid_i64);
- return 1;
-}
+ if( steamid == k_steamid_max )
+ continue;
-void db_free(void)
-{
- pthread_join( _gs_db.thread, NULL );
- pthread_mutex_destroy( &_gs_db.mux );
+ vg_msg_frame( msg, "" );
+ vg_msg_wkvnum( msg, "time", k_vg_msg_u32, 1, &time );
+ vg_msg_wkvnum( msg, "steamid", k_vg_msg_u64, 1, &steamid );
+
+ char username[32];
+ if( db_getuserinfo( steamid, username, sizeof(username), NULL ) )
+ vg_msg_wkvstr( msg, "username", username );
+ vg_msg_end_frame( msg );
+ }
+ else if( fc == SQLITE_DONE ){
+ break;
+ }
+ else {
+ log_sqlite3( fc );
+ break;
+ }
+ }
+
+ sqlite3_finalize( stmt );
+ vg_msg_end_frame( msg );
+ return k_request_status_ok;
}
+
+
#include "vg/vg_log.h"
#include "vg/vg_mem_queue.h"
+#include "vg/vg_async2.h"
+#include "vg/vg_msg.h"
#include "network_common.h"
#include "dep/sqlite3/sqlite3.h"
#include <pthread.h>
#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
struct _gs_db
{
sqlite3 *db;
- pthread_t thread;
- pthread_mutex_t mux;
-
- vg_queue queue;
- int kill;
+ pthread_t worker_thread;
+ vg_async_queue tasks;
}
extern _gs_db;
+void db_action_set_username( u64 steamid, const char *username );
+
+#if 0
/*
* Perpare statement and auto throw away if fails. Returns NULL on failure.
*/
* Get user info
*/
bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type );
+#endif
/*
* 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
+enum request_status gameserver_cat_table(
+ vg_msg *msg,
+ const char *mod, const char *route, u32 week, const char *alias );
void _gs_replay_server_tick(void)
{
- _gs_replay.print_ticker ++;
- if( _gs_replay.print_ticker > seconds_to_server_ticks(5) )
- {
- _gs_replay.print_ticker = 0;
+}
- vg_info( "------------ Replay info -----------\n" );
+struct serialized_replay_header
+{
+ u32 total_frames,
+ head_offset;
+};
- for( u32 i=0; 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 );
- }
+struct serialized_replay
+{
+ char path[ 1024 ];
+ struct serialized_replay_header header;
+ u32 buffer_size;
+ u8 buffer[];
+};
+
+static void task_write_replay( vg_async_task *task )
+{
+ THREAD_1;
+ struct serialized_replay *info = (void *)task->data;
+
+ FILE *fp = fopen( info->path, "w" );
+ if( !fp )
+ {
+ vg_error( "Failed to open '%s' for writing\n", fp );
+ return;
}
+
+ fwrite( &info->header, sizeof(info->header), 1, fp );
+ fwrite( &info->buffer, info->buffer_size, 1, fp );
+ fclose( fp );
+
+ vg_success( "Saved replay: %s\n", info->path );
}
void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *path )
{
- u64 min_ticks = gameserver.ticks - seconds_to_server_ticks( server_duration );
+ u64 min_ticks = _gameserver.ticks - seconds_to_server_ticks( server_duration );
gs_replay *replay = &_gs_replay.replays[ client_id ];
- if( replay->frame_count == 0 )
+ if( replay->ring_buffer.allocation_count == 0 )
{
vg_error( "Replay frame count is 0, nothing to save..\n" );
return;
}
/* FIXME: Should we do this on another thread, or write to a buffer then send that for a save thread */
- FILE *fp = fopen( path, "w" );
- if( !fp )
- {
- vg_error( "Failed to open '%s' for writing\n", fp );
- return;
- }
- u32 frame_id = replay->head_item;
+ u32 frame_id = replay->ring_buffer.head_offset;
+ u32 buffer_size = 0, total_frames = 0;
while(1)
{
- gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
+ gs_replay_frame *frame = vg_queue_data( &replay->ring_buffer, frame_id );
+ buffer_size += vg_queue_item_size( &replay->ring_buffer, frame_id );
+ total_frames ++;
+
if( frame->tick_recorded < min_ticks )
break;
- if( frame->previous_queue_item == VG_MEM_QUEUE_INVALID )
+ if( !vg_queue_previous( &replay->ring_buffer, frame_id, &frame_id ) )
break;
-
- frame_id = frame->previous_queue_item;
}
- struct gameserver_client *client = &gameserver.clients[ client_id ];
- netmsg_playerjoin join = { .inetmsg_id = k_inetmsg_playerjoin,
- .index = 255,
- .steamid = client->steamid };
- u16 join_size = sizeof(netmsg_playerjoin);
- fwrite( &join_size, sizeof(join_size), 1, fp );
- fwrite( &join, sizeof(join), 1, fp );
+ u32 task_size = sizeof( struct serialized_replay ) + buffer_size;
+ vg_async_task *task = vg_allocate_async_task( &_gs_db.tasks, task_size, 1 );
+ struct serialized_replay *replay_inf = (void *)task->data;
+ vg_queue_memcpy( &replay->ring_buffer, replay_inf->buffer, frame_id, buffer_size );
+ task->handler = task_write_replay;
+ replay_inf->buffer_size = buffer_size;
+ replay_inf->header.total_frames = total_frames;
+ replay_inf->header.head_offset = buffer_size - vg_queue_item_size( &replay->ring_buffer, replay->ring_buffer.head_offset );
+ vg_strncpy( path, replay_inf->path, sizeof(replay_inf->path), k_strncpy_always_add_null );
- u32 frame_count = 0;
- while(1)
- {
- gs_replay_frame *frame = vg_queue_data( &_gs_replay.ring_buffer, frame_id );
- netmsg_playerframe *playerframe = &frame->network_frame;
- playerframe->client = 255;
-
- fwrite( &frame->frame_size, sizeof(frame->frame_size), 1, fp );
- fwrite( &frame->network_frame, frame->frame_size, 1, fp );
- frame_count ++;
-
- frame_id = frame->next_queue_item;
- if( frame_id == VG_MEM_QUEUE_INVALID )
- break;
- }
-
- netmsg_playerleave leave = { .inetmsg_id = k_inetmsg_playerleave,
- .index = 255 };
- u16 leave_size = sizeof(netmsg_playerleave);
- fwrite( &leave_size, sizeof(leave_size), 1, fp );
- fwrite( &leave, sizeof(leave), 1, fp );
+ vg_queue_item *first = (void*)replay_inf->buffer;
+ first->prev_size = 0;
- fclose( fp );
- vg_success( "Written %u frames to disk.\n", frame_count );
+ vg_async_task_dispatch( &_gs_db.tasks, task );
}
void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_size )
{
- vg_queue *ring = &_gs_replay.ring_buffer;
gs_replay *dest_replay = &_gs_replay.replays[ client_id ];
+ vg_queue *ring = &dest_replay->ring_buffer;
u32 total_size = sizeof( gs_replay_frame ) + frame_size;
/* policy 1: free if 4 minutes passed in server ticks
* 2: player left the server
*/
- u64 min_ticks = gameserver.ticks - seconds_to_server_ticks( 4.0 * 60.0 );
+ u64 min_ticks = _gameserver.ticks - seconds_to_server_ticks( 4.0 * 60.0 );
for( u32 i=0; i<64; i ++ )
{
break;
/* blocked by valid frame */
- if( (free_frame->tick_recorded >= min_ticks) && (free_frame->network_frame.client!=255) )
+ if( free_frame->tick_recorded >= min_ticks )
break;
- /* clean refs */
- if( free_frame->next_queue_item != VG_MEM_QUEUE_INVALID )
- {
- gs_replay_frame *next_frame = vg_queue_data( ring, free_frame->next_queue_item );
- next_frame->previous_queue_item = VG_MEM_QUEUE_INVALID;
- }
- if( free_frame->network_frame.client != 255 )
- {
- gs_replay *free_replay = &_gs_replay.replays[ free_frame->network_frame.client ];
- free_replay->frame_count --;
- if( free_replay->frame_count == 0 )
- free_replay->head_item = VG_MEM_QUEUE_INVALID;
- }
-
vg_queue_pop( ring );
}
if( !dest_frame )
{
/* policy 3: grow (double size) */
- u32 cur_size = _gs_replay.ring_buffer.size;
+ u32 cur_size = ring->size;
if( cur_size < 1024 )
cur_size = 1024;
u32 new_size = cur_size * 2;
- vg_low( "growing replay buffer (%u bytes)\n", new_size );
- vg_queue new_q = { .buffer = malloc( new_size ), .size = new_size };
- vg_queue_copy_upgrade( ring, &new_q );
- free( ring->buffer );
- *ring = new_q;
+ if( new_size > REPLAY_SIZE_LIMIT )
+ new_size = REPLAY_SIZE_LIMIT;
- dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
- VG_ASSERT( dest_frame );
+ if( new_size != ring->size )
+ {
+ vg_low( "growing replay buffer (%u bytes)\n", new_size );
+
+ vg_queue new_q = { .buffer = malloc( new_size ), .size = new_size };
+ vg_queue_copy_upgrade( ring, &new_q );
+
+ free( ring->buffer );
+ *ring = new_q;
+
+ dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id );
+ VG_ASSERT( dest_frame );
+ }
}
}
memcpy( &dest_frame->network_frame, frame, frame_size );
dest_frame->network_frame.client = client_id;
- dest_frame->tick_recorded = gameserver.ticks;
+ dest_frame->tick_recorded = _gameserver.ticks;
dest_frame->frame_size = frame_size;
dest_frame->_ = 0;
- dest_frame->next_queue_item = VG_MEM_QUEUE_INVALID;
-
- if( dest_replay->frame_count )
- {
- dest_frame->previous_queue_item = dest_replay->head_item;
- gs_replay_frame *prev_frame = vg_queue_data( ring, dest_replay->head_item );
- prev_frame->next_queue_item = dest_item_id;
- }
- else
- {
- dest_frame->previous_queue_item = VG_MEM_QUEUE_INVALID;
- }
-
- dest_replay->head_item = dest_item_id;
- dest_replay->frame_count ++;
}
#pragma once
#include "network_msg.h"
+#define REPLAY_SIZE_LIMIT 1024*1024*2
+
typedef struct gs_replay_frame gs_replay_frame;
typedef struct gs_replay gs_replay;
-#if 0
-typedef struct gs_replay_playback gs_replay_playback;
-#endif
struct gs_replay_frame
{
u64 tick_recorded;
- u32 previous_queue_item;
- u32 next_queue_item;
u16 frame_size;
u16 _;
struct netmsg_playerframe network_frame;
{
struct gs_replay
{
- u32 frame_count,
- head_item;
+ vg_queue ring_buffer;
}
replays[ NETWORK_MAX_PLAYERS ];
- struct vg_queue ring_buffer;
-
bool print_info;
u64 print_ticker;
}
--- /dev/null
+#include "gameserver_requests.h"
+#include "gameserver_database.h"
+#include "vg/vg_mem_pool.h"
+#include <stddef.h>
+
+struct _gs_requests _gs_requests;
+
+static void log_request_status( gs_request *req )
+{
+ THREAD_0;
+
+ const char *associated_username = "none";
+ struct gameserver_client *client = &_gameserver.clients[ req->client_id ];
+ if( (client->active) && (client->session_uid == req->user_uid) )
+ associated_username = client->username;
+
+ u16 pool_id = vg_pool_id( &_gs_requests.request_pool, req );
+
+ const char *request_state_str = (const char *[])
+ {
+ [k_request_state_none] = "None",
+ [k_request_state_server_processing] = "Processing",
+ [k_request_state_server_error] = "Internal Error",
+ [k_request_state_transfer_start] = "Start transfer",
+ [k_request_state_transfer] = "Transferring",
+ [k_request_state_finished] = "Finished",
+ [k_request_state_max ] = NULL
+ }
+ [ req->state ];
+
+ const char *colour = (const char *[])
+ {
+ KRED, KGRN, KYEL, KBLU,
+ KMAG, KCYN
+ }[ pool_id % 6 ];
+
+ vg_low( "req[%s%s##%hu" KWHT "] State: %s, Status: %u\n",
+ colour, associated_username, pool_id, request_state_str, (u32)req->status );
+}
+
+void _gs_requests_init(void)
+{
+ u32 total_requests = GS_MAX_REQUESTS*NETWORK_MAX_PLAYERS;
+ u32 alloc_size = sizeof(gs_request)*total_requests;
+
+ _gs_requests.request_buffer = malloc( alloc_size );
+ memset( _gs_requests.request_buffer, 0, alloc_size );
+
+ vg_pool *pool = &_gs_requests.request_pool;
+ pool->buffer = _gs_requests.request_buffer;
+ pool->count = total_requests;
+ pool->stride = sizeof( gs_request );
+ pool->offset = offsetof( gs_request, poolnode );
+ vg_pool_init( pool );
+
+ _gs_requests.transfer_stream_buffer = malloc( GS_TRANSFER_MAX_SIZE*NETWORK_MAX_PLAYERS );
+}
+
+static void gs_request_release( gs_request *req )
+{
+ THREAD_0;
+
+ if( req->message )
+ {
+ SteamAPI_SteamNetworkingMessage_t_Release( req->message );
+ req->message = NULL;
+ }
+}
+
+void _gs_requests_client_disconnect( u32 client_id )
+{
+ THREAD_0;
+
+ gs_request_client *rc = &_gs_requests.clients[ client_id ];
+
+ while( rc->current_request )
+ {
+ gs_request *req = vg_pool_item( &_gs_requests.request_pool, rc->current_request );
+
+ if( vg_pool_unwatch( &_gs_requests.request_pool, rc->current_request ) )
+ gs_request_release( req );
+
+ rc->current_request = req->waiting_request;
+ rc->active_request_count --;
+ }
+}
+
+struct task_request_run_info
+{
+ u16 pool_id;
+};
+static void task_request_run( vg_async_task *task );
+
+void _gs_requests_tick(void)
+{
+ THREAD_0;
+
+ for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+ {
+ struct gameserver_client *client = &_gameserver.clients[i];
+
+ if( !client->active )
+ continue;
+
+ gs_request_client *rc = &_gs_requests.clients[i];
+ if( rc->current_request == 0 )
+ continue;
+
+ gs_request *req = vg_pool_item( &_gs_requests.request_pool, rc->current_request );
+
+ if( req->state == k_request_state_none )
+ {
+ req->data_buffer = _gs_requests.transfer_stream_buffer + (i*GS_TRANSFER_MAX_SIZE);
+ req->data_buffer_send_size = 0;
+ req->send_offset = 0;
+ vg_pool_watch( &_gs_requests.request_pool, rc->current_request );
+
+ vg_async_task *run_task = vg_allocate_async_task( &_gs_db.tasks, sizeof(struct task_request_run_info), 1 );
+ run_task->handler = task_request_run;
+ struct task_request_run_info *info = (void *)run_task->data;
+ info->pool_id = rc->current_request;
+ vg_async_task_dispatch( &_gs_db.tasks, run_task );
+
+ req->state = k_request_state_server_processing;
+ log_request_status( req );
+ }
+ else if( req->state == k_request_state_server_processing )
+ {
+ /* we do nothing here, we're waiting for the other thread to complete it's work. */
+ }
+ else if( req->state == k_request_state_server_error )
+ {
+ u32 size = sizeof(netmsg_request);
+ SteamNetworkingMessage_t *msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+ msg->m_conn = client->connection;
+ msg->m_idxLane = 1;
+ netmsg_request *res = msg->m_pData;
+ res->inetmsg_id = k_inetmsg_response;
+ res->id = req->client_request_id;
+ res->status = req->status;
+ SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+ req->state = k_request_state_finished;
+ log_request_status( req );
+ }
+ else if( req->state == k_request_state_transfer_start )
+ {
+ u32 size = sizeof(netmsg_request) + sizeof(struct netmsg_transfer_header);
+ SteamNetworkingMessage_t *msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+ msg->m_conn = client->connection;
+ msg->m_idxLane = 1;
+
+ struct netmsg_request *res = msg->m_pData;
+ res->inetmsg_id = k_inetmsg_response;
+ res->id = req->client_request_id;
+ res->status = k_request_status_transfer_header;
+
+ struct netmsg_transfer_header *header = (void *)res->buffer;
+ header->data_size = req->data_buffer_send_size;
+ header->chunks = (req->data_buffer_send_size+(GS_TRANSFER_BYTES_PER_TICK-1)) / GS_TRANSFER_BYTES_PER_TICK;
+
+ SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+ req->state = k_request_state_transfer;
+ log_request_status( req );
+ }
+ else if( req->state == k_request_state_transfer )
+ {
+ u32 size = GS_TRANSFER_BYTES_PER_TICK;
+ if( req->send_offset + size >= req->data_buffer_send_size )
+ {
+ size = req->data_buffer_send_size - req->send_offset;
+ req->state = k_request_state_finished;
+ log_request_status( req );
+ }
+
+ u32 message_size = sizeof(netmsg_request) + size;
+ SteamNetworkingMessage_t *msg =
+ SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, message_size );
+ msg->m_conn = client->connection;
+ msg->m_idxLane = 1;
+
+ struct netmsg_request *res = msg->m_pData;
+ res->inetmsg_id = k_inetmsg_response;
+ res->id = req->client_request_id;
+ res->status = k_request_status_transfer_continue;
+ memcpy( res->buffer, req->data_buffer + req->send_offset, size );
+
+ SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &msg, NULL );
+ req->send_offset += size;
+ }
+ else if( req->state == k_request_state_finished )
+ {
+ if( vg_pool_unwatch( &_gs_requests.request_pool, rc->current_request ) )
+ gs_request_release( req );
+
+ rc->current_request = req->waiting_request;
+ rc->active_request_count --;
+
+ req->state = k_request_state_none;
+ log_request_status( req );
+ }
+ }
+}
+
+static void task_request_processing_complete( vg_async_task *task )
+{
+ THREAD_0;
+ struct task_request_run_info *info = (void *)task->data;
+ gs_request *req = vg_pool_item( &_gs_requests.request_pool, info->pool_id );
+
+ SteamAPI_SteamNetworkingMessage_t_Release( req->message );
+ req->message = NULL;
+
+ /* check if we're still pointing at the same user of the same session */
+ struct gameserver_client *client = &_gameserver.clients[ req->client_id ];
+ if( (client->active == 0) || (client->session_uid != req->user_uid) )
+ {
+ /* should be going from 1 -> 0 */
+ VG_ASSERT( vg_pool_unwatch( &_gs_requests.request_pool, info->pool_id ) );
+ gs_request_release( req );
+
+ vg_low( "Ignoring response because session uid%u != user uid%u. (ok)\n", client->session_uid, req->user_uid );
+ return;
+ }
+
+ /* OK or client error */
+ if( req->client_request_id )
+ {
+ if( req->status < k_request_status_ok )
+ req->state = k_request_state_server_error;
+ else
+ req->state = k_request_state_transfer_start;
+ }
+ else
+ {
+ /* request ID of 0 means the client doesn't care about getting the response back */
+ req->state = k_request_state_finished;
+ }
+ log_request_status( req );
+
+ /* should be going from 2 -> 1 */
+ VG_ASSERT( vg_pool_unwatch( &_gs_requests.request_pool, info->pool_id ) == 0 );
+}
+
+static void task_request_run( vg_async_task *task )
+{
+ THREAD_1;
+
+ struct task_request_run_info *info = (void *)task->data;
+ gs_request *req = vg_pool_item( &_gs_requests.request_pool, info->pool_id );
+
+ netmsg_request *client_packet = (netmsg_request *)req->message->m_pData;
+ vg_msg client_msg;
+ vg_msg_init( &client_msg, client_packet->buffer, req->message->m_cbSize - sizeof(netmsg_request) );
+
+ const char *endpoint = vg_msg_getkvstr( &client_msg, "endpoint" );
+ if( !endpoint )
+ req->status = k_request_status_invalid_endpoint;
+ else
+ {
+ /* create response packet */
+ bool no_content = 0;
+ vg_msg body_msg;
+ vg_msg_init( &body_msg, req->data_buffer, GS_TRANSFER_MAX_SIZE );
+
+ if( !strcmp( endpoint, "scoreboard" ) )
+ {
+ const char *mod = vg_msg_getkvstr( &client_msg, "mod" );
+ const char *route = vg_msg_getkvstr( &client_msg, "route" );
+ u32 week;
+ vg_msg_getkvintg( &client_msg, "week", k_vg_msg_u32, &week, NULL );
+
+ if( week == NETWORK_LEADERBOARD_CURRENT_WEEK )
+ {
+ gameserver_cat_table( &body_msg, mod, route, gameserver_get_current_week(), "rows_weekly" );
+ }
+ else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK )
+ {
+ gameserver_cat_table( &body_msg, mod, route, 0, "rows" );
+ gameserver_cat_table( &body_msg, mod, route, gameserver_get_current_week(), "rows_weekly" );
+ }
+ else
+ gameserver_cat_table( &body_msg, mod, route, week, "rows" );
+ }
+#if 0
+ else if( !strcmp( endpoint, "setlap" ) )
+ {
+ if( client->steamid == k_steamid_max )
+ {
+ gameserver_request_respond( k_request_status_unauthorized, res, NULL, msg );
+ return;
+ }
+
+ const char *mod = vg_msg_getkvstr( &data, "mod" );
+ const char *route = vg_msg_getkvstr( &data, "route" );
+
+ char weekly_table[ DB_TABLE_UID_MAX ],
+ alltime_table[ DB_TABLE_UID_MAX ];
+
+ u32 week = gameserver_get_current_week();
+
+ if( !db_get_highscore_table_name( mod, route, 0, alltime_table ) ||
+ !db_get_highscore_table_name( mod, route, week, weekly_table ) )
+ {
+ gameserver_request_respond( k_request_status_out_of_memory, res, NULL, msg );
+ return;
+ }
+
+ i32 centiseconds;
+ vg_msg_getkvintg( &data, "time", k_vg_msg_i32, ¢iseconds, NULL );
+ if( centiseconds < 5*100 )
+ {
+ gameserver_request_respond( k_request_status_client_error, res, NULL, msg );
+ return;
+ }
+
+ db_writeusertime( alltime_table, client->steamid, centiseconds, 1 );
+ db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
+ gameserver_request_respond( k_request_status_ok, res, NULL, msg );
+ }
+#endif
+ else
+ {
+ req->status = k_request_status_invalid_endpoint;
+ }
+
+ if( body_msg.error != k_vg_msg_error_OK )
+ {
+ req->status = k_request_status_out_of_memory;
+ }
+
+ if( req->status == k_request_status_ok )
+ req->data_buffer_send_size = body_msg.cur.co;
+ }
+
+ vg_async_task *return_task = vg_allocate_async_task( &_gameserver.tasks, sizeof(struct task_request_run_info), 1 );
+ memcpy( return_task->data, info, sizeof(struct task_request_run_info) );
+ return_task->handler = task_request_processing_complete;
+ vg_async_task_dispatch( &_gameserver.tasks, return_task );
+}
+
+void _gs_handle_request_message( u32 client_id, SteamNetworkingMessage_t *msg )
+{
+ THREAD_0;
+
+ if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
+ {
+ vg_error( "Client malformed request (no data?) %u\n", client_id );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ return;
+ }
+
+ struct gameserver_client *client = &_gameserver.clients[ client_id ];
+ gs_request_client *rc = &_gs_requests.clients[ client_id ];
+ netmsg_request *client_packet = (netmsg_request *)msg->m_pData;
+
+ enum request_status error_status = 0;
+
+ if( rc->active_request_count < GS_MAX_REQUESTS )
+ {
+ u16 new_id = vg_pool_lru( &_gs_requests.request_pool );
+ if( new_id )
+ {
+ rc->active_request_count ++;
+
+ vg_pool_watch( &_gs_requests.request_pool, new_id );
+ gs_request *req = vg_pool_item( &_gs_requests.request_pool, new_id );
+
+ req->state = k_request_state_none;
+ req->message = msg;
+ req->user_uid = client->session_uid;
+ req->client_id = client_id;
+ req->client_request_id = client_packet->id;
+ req->status = k_request_status_ok;
+ req->data_buffer = NULL;
+ req->data_buffer_send_size = 0;
+ req->send_offset = 0;
+
+ gs_request *last_request = vg_pool_item( &_gs_requests.request_pool, rc->current_request ),
+ *next = last_request;
+
+ while( next )
+ {
+ last_request = next;
+ next = vg_pool_item( &_gs_requests.request_pool, last_request->waiting_request );
+ }
+
+ if( last_request )
+ last_request->waiting_request = new_id;
+ else
+ rc->current_request = new_id;
+
+ log_request_status( req );
+ return;
+ }
+ else
+ error_status = k_request_status_server_error; /* this should be vanishingly rare */
+ }
+ else
+ error_status = k_request_status_too_many_requests;
+
+ vg_error( "req[%s##error] State: fail, Status: %u\n", client->username, (u32)error_status );
+
+ u32 size = sizeof(netmsg_request);
+ SteamNetworkingMessage_t *reply_msg = SteamAPI_ISteamNetworkingUtils_AllocateMessage( hSteamNetworkingUtils, size );
+ reply_msg->m_conn = client->connection;
+ reply_msg->m_idxLane = 1;
+ netmsg_request *res = reply_msg->m_pData;
+ res->inetmsg_id = k_inetmsg_response;
+ res->id = client_packet->id;
+ res->status = error_status;
+ SteamAPI_ISteamNetworkingSockets_SendMessages( hSteamNetworkingSockets, 1, &reply_msg, NULL );
+
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+}
--- /dev/null
+#pragma once
+#include "gameserver.h"
+#include "vg/vg_mem_pool.h"
+
+#define GS_TRANSFER_BYTES_PER_TICK 256
+#define GS_TRANSFER_MAX_SIZE 4*1024*1024
+#define GS_MAX_REQUESTS 8
+
+typedef struct gs_transfer gs_transfer;
+typedef struct gs_request_client gs_request_client;
+typedef struct gs_request gs_request;
+
+struct gs_request
+{
+ vg_pool_node poolnode;
+ u16 waiting_request;
+
+ enum request_state
+ {
+ k_request_state_none,
+ k_request_state_server_processing,
+ k_request_state_server_error,
+ k_request_state_transfer_start,
+ k_request_state_transfer,
+ k_request_state_finished,
+ k_request_state_max
+ }
+ state;
+
+ SteamNetworkingMessage_t *message;
+
+ u64 user_uid;
+ u32 client_id;
+ u8 client_request_id;
+
+ enum request_status status;
+ void *data_buffer;
+ u32 data_buffer_send_size, send_offset;
+};
+
+struct _gs_requests
+{
+ gs_request *request_buffer;
+ vg_pool request_pool;
+
+ void *transfer_stream_buffer;
+
+ struct gs_request_client
+ {
+ u16 current_request;
+ u32 active_request_count;
+ }
+ clients[ NETWORK_MAX_PLAYERS ];
+}
+extern _gs_requests;
+
+void _gs_requests_init(void);
+void _gs_requests_tick(void);
+void _gs_requests_client_disconnect( u32 client_id );
+void _gs_handle_request_message( u32 client_id, SteamNetworkingMessage_t *msg );
+++ /dev/null
-#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; i<NETWORK_MAX_PLAYERS; i ++ )
- {
- struct gameserver_client *client = &gameserver.clients[i];
-
- if( client->active )
- {
- 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;
-}
+++ /dev/null
-#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 );
}
}
-static void request_auth_ticket(void){
+static void request_auth_ticket(void)
+{
/*
* TODO Check for one thats cached on the disk and load it.
* This might be OK though because steam seems to cache the result
vg_steam_async_call *call = vg_alloc_async_steam_api_call();
call->userdata = NULL;
call->p_handler = on_auth_ticket_recieved;
- call->id =
- SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, NULL, 0 );
+ call->id = SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, NULL, 0 );
}
static void network_send_username(void){
}
static void network_send_request( netmsg_request *req, vg_msg *body,
- void (*callback)(
- netmsg_request *res, vg_msg *body,
- u64 userdata),
- u64 userdata ){
+ void (*callback)( void *data, u32 data_size, u64 userdata, enum request_status status ),
+ u64 userdata )
+{
u32 len = 0;
- if( body ){
+ if( body )
+ {
len = body->cur.co;
vg_info( "Request scoreboard. Info (%u):\n", body->cur.co );
vg_msg_print( body, len );
- if( body->error != k_vg_msg_error_OK ){
+ if( body->error != k_vg_msg_error_OK )
+ {
vg_error( "Body not OK\n" );
return;
}
}
- if( callback ){
+ if( callback )
+ {
req->id = vg_pool_lru( &network_client.request_pool );
- if( req->id ){
+ if( req->id )
+ {
vg_pool_watch( &network_client.request_pool, req->id );
- struct network_request *pn =
- vg_pool_item( &network_client.request_pool, req->id );
+ struct network_request *pn = vg_pool_item( &network_client.request_pool, req->id );
pn->callback = callback;
pn->sendtime = vg.time_real;
pn->userdata = userdata;
+ pn->status = k_request_status_sent;
}
- else{
+ else
+ {
vg_error( "Unable to send request. Pool is full.\n" );
return;
}
else
req->id = 0;
- SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
- hSteamNetworkingSockets, network_client.remote,
- req, sizeof(netmsg_request)+len,
- k_nSteamNetworkingSend_Reliable, NULL );
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, network_client.remote,
+ req, sizeof(netmsg_request)+len,
+ k_nSteamNetworkingSend_Reliable, NULL );
}
-static void network_scoreboard_callback( netmsg_request *res, vg_msg *body,
- u64 userdata ){
- world_instance *world = &_world.main;
+static void network_scoreboard_callback( void *data, u32 data_size, u64 userdata, enum request_status status )
+{
+ if( status == k_request_status_ok )
+ {
+ vg_msg msg;
+ vg_msg_init( &msg, data, data_size );
+ vg_msg_print( &msg, msg.max );
- world_routes_recv_scoreboard( world, body, userdata, res->status );
- if( userdata == world_sfd.active_route_board )
- world_sfd_compile_active_scores();
+ world_routes_recv_scoreboard( &_world.main, &msg, userdata, status );
+ if( userdata == world_sfd.active_route_board )
+ world_sfd_compile_active_scores();
+ }
+ else
+ {
+ world_routes_recv_scoreboard( &_world.main, NULL, userdata, status );
+ }
}
-
-
/* mod_uid: world mod uid,
* route_uid: run name (just a string)
* week:
* .
* 10+ specific week index
*/
-void network_request_scoreboard( const char *mod_uid,
- const char *route_uid,
- u32 week, u64 userdata ){
+void network_request_scoreboard( const char *mod_uid, const char *route_uid, u32 week, u64 userdata )
+{
if( !network_connected() )
return;
req->inetmsg_id = k_inetmsg_request;
vg_msg data;
- vg_msg_init( &data, req->q, 512 );
+ vg_msg_init( &data, req->buffer, 512 );
vg_msg_wkvstr( &data, "endpoint", "scoreboard" );
vg_msg_wkvstr( &data, "mod", mod_uid );
vg_msg_wkvstr( &data, "route", route_uid );
network_send_request( req, &data, network_scoreboard_callback, userdata );
}
-static void network_publish_callback( netmsg_request *res, vg_msg *body, u64 userdata )
+void network_publish_laptime( const char *mod_uid, const char *route_uid, f64 lap_time )
{
- if( res->status != k_request_status_ok )
- {
- vg_error( "Publish laptime, server error #%d\n", (i32)res->status );
- }
-}
-
-void network_publish_laptime( const char *mod_uid,
- const char *route_uid, f64 lap_time ){
if( !network_connected() )
return;
req->inetmsg_id = k_inetmsg_request;
vg_msg data;
- vg_msg_init( &data, req->q, 512 );
+ vg_msg_init( &data, req->buffer, 512 );
vg_msg_wkvstr( &data, "endpoint", "setlap" );
vg_msg_wkvstr( &data, "mod", mod_uid );
vg_msg_wkvstr( &data, "route", route_uid );
vg_msg_wkvnum( &data, "time", k_vg_msg_i32, 1, &time_centiseconds );
- network_send_request( req, &data, network_publish_callback, 0 );
+ network_send_request( req, &data, NULL, 0 );
}
-static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
+static void network_request_rx_300_400( SteamNetworkingMessage_t *msg )
+{
netmsg_blank *tmp = msg->m_pData;
- if( tmp->inetmsg_id == k_inetmsg_request ){
-
+ if( tmp->inetmsg_id == k_inetmsg_request )
+ {
+ /* Could the server ever request info from the client?? */
}
- else if( tmp->inetmsg_id == k_inetmsg_response ){
+ else if( tmp->inetmsg_id == k_inetmsg_response )
+ {
netmsg_request *res = (netmsg_request *)msg->m_pData;
- vg_msg *body = NULL;
+ if( (res->id == 0) || (res->id > NETWORK_MAX_REQUESTS) )
+ {
+ vg_error( "Response with invalid ID: %u.\n", res->id );
+ return;
+ }
- vg_msg data;
- if( res->status == k_request_status_ok ){
- vg_msg_init( &data, res->q, msg->m_cbSize - sizeof(netmsg_request) );
- vg_success( "Response to #%d:\n", (i32)res->id );
- vg_msg_print( &data, data.max );
- body = &data;
+ struct network_request *pn = vg_pool_item( &network_client.request_pool, res->id );
+ if( pn->status == k_request_status_none )
+ {
+ vg_error( "Response to inactive request (id %u)\n", res->id );
+ return;
}
- else {
- vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+
+ u32 byte_count = msg->m_cbSize - sizeof(netmsg_request);
+ if( res->status == k_request_status_ok )
+ {
+ if( pn->callback )
+ pn->callback( res->buffer, byte_count, pn->userdata, k_request_status_ok );
+
+ vg_pool_unwatch( &network_client.request_pool, res->id );
+ pn->status = k_request_status_none;
}
+ else if( res->status == k_request_status_transfer_header )
+ {
+ if( network_client.recieving_request_id )
+ {
+ vg_error( "Transfer for previous request (%u) interrupted by new one (%u)\n",
+ network_client.recieving_request_id, res->id );
+
+ struct network_request *prev = vg_pool_item( &network_client.request_pool, network_client.recieving_request_id );
+ vg_pool_unwatch( &network_client.request_pool, network_client.recieving_request_id );
+ prev->status = k_request_status_none;
+
+ network_client.recieving_request_id = 0;
+ network_client.recieve_offset = 0;
+ network_client.data_buffer_recieve_size = 0;
+ }
+
+ struct netmsg_transfer_header *header = (void *)res->buffer;
- if( res->id ){
- struct network_request *pn =
- vg_pool_item( &network_client.request_pool, res->id );
- pn->callback( res, body, pn->userdata );
+ if( header->data_size > 1024*1024*4 )
+ {
+ vg_error( "Transfer too large to handle! Size: %u bytes\n", header->data_size );
+ vg_pool_unwatch( &network_client.request_pool, res->id );
+ pn->status = k_request_status_none;
+ return;
+ }
+
+ network_client.recieving_request_id = res->id;
+ network_client.recieve_offset = 0;
+ network_client.data_buffer_recieve_size = header->data_size;
+
+ pn->status = k_request_status_receiving;
+ }
+ else if( res->status == k_request_status_transfer_continue )
+ {
+ if( network_client.recieving_request_id != res->id )
+ {
+ vg_error( "Transfer protocal fault, against requests %u and %u. Discarding.\n",
+ network_client.recieving_request_id, res->id );
+
+ struct network_request *prev = vg_pool_item( &network_client.request_pool, network_client.recieving_request_id );
+ vg_pool_unwatch( &network_client.request_pool, network_client.recieving_request_id );
+ vg_pool_unwatch( &network_client.request_pool, res->id );
+ prev->status = k_request_status_none;
+ pn->status = k_request_status_none;
+ network_client.recieving_request_id = 0;
+ network_client.recieve_offset = 0;
+ network_client.data_buffer_recieve_size = 0;
+ return;
+ }
+
+ bool end = 0;
+ if( network_client.recieve_offset + byte_count >= network_client.data_buffer_recieve_size )
+ {
+ end = 1;
+ byte_count = network_client.data_buffer_recieve_size - network_client.recieve_offset;
+ }
+
+ memcpy( network_client.data_buffer + network_client.recieve_offset, res->buffer, byte_count );
+ network_client.recieve_offset += byte_count;
+
+ if( end )
+ {
+ vg_success( "Transfer finished! (%u bytes)\n", network_client.recieve_offset );
+ if( pn->callback )
+ {
+ pn->callback( network_client.data_buffer, network_client.data_buffer_recieve_size,
+ pn->userdata, k_request_status_ok );
+ }
+
+ vg_pool_unwatch( &network_client.request_pool, res->id );
+ pn->status = k_request_status_none;
+
+ network_client.recieving_request_id = 0;
+ network_client.recieve_offset = 0;
+ network_client.data_buffer_recieve_size = 0;
+ }
+
+ // TODO: Timeout request & disconnecting from server
+ }
+ else
+ {
vg_pool_unwatch( &network_client.request_pool, res->id );
+ vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+ pn->status = k_request_status_none;
}
}
}
k_nSteamNetworkingSend_Reliable, NULL );
}
-static void network_disconnect(void){
- SteamAPI_ISteamNetworkingSockets_CloseConnection(
- hSteamNetworkingSockets, network_client.remote, 0, NULL, 0 );
+static void network_disconnect(void)
+{
+ SteamAPI_ISteamNetworkingSockets_CloseConnection( hSteamNetworkingSockets, network_client.remote, 0, NULL, 0 );
network_client.remote = 0;
network_client.state = k_ESteamNetworkingConnectionState_None;
- for( int i=0; i<VG_ARRAY_LEN(netplayers.list); i++ ){
+ for( int i=0; i<VG_ARRAY_LEN(netplayers.list); i++ )
+ {
netplayers.list[i].active = 0;
}
+
+ /* return the infinity stones */
+ network_client.data_buffer_recieve_size = 0;
+ network_client.recieve_offset = 0;
+ network_client.recieving_request_id = 0;
+ for( u32 i=0; i<NETWORK_MAX_REQUESTS; i ++ )
+ {
+ struct network_request *request = &network_client.request_buffer[ i ];
+
+ if( request->status != k_request_status_none )
+ {
+ vg_warn( "Clipping request #%u. Timeout of some kind\n", i );
+ request->status = k_request_status_none;
+ vg_pool_unwatch( &network_client.request_pool, i+1 );
+ }
+ }
}
void network_status_string( vg_str *str, u32 *colour )
{
SteamNetworkingMessage_t *msg = messages[i];
- if( msg->m_idxLane == 1 )
- {
- vg_low( "Ignoring... %d bytes\n", msg->m_cbSize );
- continue;
- }
-
if( msg->m_cbSize < sizeof(netmsg_blank) ){
vg_warn( "Discarding message (too small: %d)\n", msg->m_cbSize );
continue;
netmsg_blank *tmp = msg->m_pData;
- if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
+ if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) )
+ {
player_remote_rx_200_300( msg );
}
- else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
+ else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) )
+ {
network_request_rx_300_400( msg );
}
- else {
- if( tmp->inetmsg_id == k_inetmsg_version ){
+ else
+ {
+ if( tmp->inetmsg_id == k_inetmsg_version )
+ {
netmsg_version *version = msg->m_pData;
- if( version->version != NETWORK_SKATERIFT_VERSION ){
+ if( version->version != NETWORK_SKATERIFT_VERSION )
+ {
network_disconnect();
/* we dont want to connect to this server ever */
network_client.retries = 999;
network_client.last_attempt = 999999999.9;
vg_error( "version mismatch with server\n" );
}
- else {
+ else
+ {
network_client.remote_version = version->version;
network_sign_on_complete();
}
if( steam_ready )
{
u32 alloc_size = sizeof(struct network_request)*NETWORK_MAX_REQUESTS;
- network_client.request_buffer =
- vg_linear_alloc( vg_mem.rtmemory, alloc_size );
+ network_client.request_buffer = vg_linear_alloc( vg_mem.rtmemory, alloc_size );
memset( network_client.request_buffer, 0, alloc_size );
+ network_client.data_buffer = vg_linear_alloc( vg_mem.rtmemory, 4*1024*1024 );
vg_pool *pool = &network_client.request_pool;
pool->buffer = network_client.request_buffer;
pool->offset = offsetof( struct network_request, poolnode );
vg_pool_init( pool );
- steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
- on_server_connect_status );
- steam_register_callback( k_iPersonaStateChange,
- on_persona_state_change );
+ steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_server_connect_status );
+ steam_register_callback( k_iPersonaStateChange, on_persona_state_change );
request_auth_ticket();
vg_console_reg_cmd( "say", cmd_network_send_message, NULL );
i32 auto_connect;
- struct network_request {
+ struct network_request
+ {
vg_pool_node poolnode;
- void (*callback)( netmsg_request *res, vg_msg *body, u64 userdata );
+ void (*callback)( void *data, u32 data_size, u64 userdata, enum request_status status );
f64 sendtime;
u64 userdata;
+
+ enum status
+ {
+ k_request_status_none,
+ k_request_status_sent,
+ k_request_status_receiving,
+ }
+ status;
}
*request_buffer;
vg_pool request_pool;
+ void *data_buffer;
+ u32 data_buffer_recieve_size;
+ u32 recieve_offset;
+ u8 recieving_request_id;
+
SteamNetworkingIPAddr ip;
char host_port[8], host_adress[256];
bool ip_resolved;
int packet_minsize( SteamNetworkingMessage_t *msg, u32 size );
void network_send_item( enum netmsg_playeritem_type type );
-void network_request_scoreboard( const char *mod_uid,
- const char *route_uid,
- u32 week, u64 userdata );
-void network_publish_laptime( const char *mod_uid,
- const char *route_uid, f64 lap_time );
+void network_request_scoreboard( const char *mod_uid, const char *route_uid, u32 week, u64 userdata );
+void network_publish_laptime( const char *mod_uid, const char *route_uid, f64 lap_time );
void chat_send_message( const char *message );
void render_server_status_gui(void);
void network_status_string( vg_str *str, u32 *colour );
struct netmsg_request {
u16 inetmsg_id;
u8 id, status;
- u8 q[];
+ u8 buffer[];
+};
+
+struct netmsg_transfer_header
+{
+ u32 data_size, chunks;
};
enum request_status {
k_request_status_client_error = 0,
k_request_status_invalid_endpoint = 1,
k_request_status_unauthorized = 2,
+ k_request_status_too_many_requests = 3,
k_request_status_server_error = 100,
k_request_status_out_of_memory = 101,
k_request_status_database_error = 102,
- k_request_status_ok = 200,
- k_request_status_not_found = 201
+ k_request_status_ok = 200, /* buffer contains response */
+ k_request_status_not_found = 201,
+ k_request_status_transfer_header = 202,
+ k_request_status_transfer_continue = 203
};
#pragma pack(pop)
world_routes_clear( world );
}
-void world_routes_recv_scoreboard( world_instance *world,
- vg_msg *body, u32 route_id,
- enum request_status status )
+void world_routes_recv_scoreboard( world_instance *world, vg_msg *body, u32 route_id, enum request_status status )
{
- if( route_id >= af_arrcount( &world->ent_route ) ){
+ if( route_id >= af_arrcount( &world->ent_route ) )
+ {
vg_error( "Scoreboard route_id out of range (%u)\n", route_id );
return;
}
struct leaderboard_cache *board = &world->leaderboard_cache[ route_id ];
board->status = status;
- if( body == NULL ){
+ if( body == NULL )
+ {
board->data_len = 0;
return;
}
- if( body->max > NETWORK_REQUEST_MAX ){
- vg_error( "Scoreboard leaderboard too big (%u>%u)\n", body->max,
- NETWORK_REQUEST_MAX );
+ if( body->max > NETWORK_REQUEST_MAX )
+ {
+ vg_error( "Scoreboard leaderboard too big (%u>%u)\n", body->max, NETWORK_REQUEST_MAX );
return;
}
}
}
-void world_sfd_compile_scores( struct leaderboard_cache *board,
- const char *title )
+void world_sfd_compile_scores( struct leaderboard_cache *board, const char *title )
{
for( u32 i=0; i<13; i++ )
sfd_clear(i);