#include "network_common.h"
#include "gameserver_db.h"
#include "vg/vg_m.h"
+#include "vg/vg_msg.h"
static void inthandler( int signum ) {
sig_stop = 1;
gameserver_send_to_all( client_id, prop, msg->m_cbSize,
k_nSteamNetworkingSend_Reliable );
}
+ else {
+ 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 ){
+ int client_id = gameserver_client_index( msg->m_conn );
+ u32 len = 0;
+ if( status == k_request_status_ok ){
+ len = body->len;
+
+ vg_success( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
+ vg_msg_print( body );
+ }
+ else{
+ vg_warn( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
+ }
+
+ res->status = status;
+
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
+ hSteamNetworkingSockets, msg->m_conn,
+ res, sizeof(netmsg_request) + len,
+ k_nSteamNetworkingSend_Reliable, NULL );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+}
+
+struct user_request_thread_data {
+ SteamNetworkingMessage_t *msg;
+};
+
+static void gameserver_process_user_request( db_request *db_req ){
+ struct user_request_thread_data *inf = (void *)db_req->data;
+ SteamNetworkingMessage_t *msg = inf->msg;
+
+ int client_id = gameserver_client_index( msg->m_conn );
+ if( client_id == -1 ){
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ return;
+ }
+
+ netmsg_request *req = (netmsg_request *)msg->m_pData;
+ vg_msg data = {0};
+ data.buf = req->q;
+ data.len = msg->m_cbSize - sizeof(netmsg_request);
+ data.max = data.len;
+
+ /* create response packet */
+ netmsg_request *res = alloca( sizeof(netmsg_request) + 512 );
+ res->inetmsg_id = k_inetmsg_response;
+ res->id = req->id;
+ vg_msg body = {0};
+ body.buf = res->q;
+ body.max = 512;
+
+ const char *endpoint = vg_msg_seekkvstr( &data, "endpoint", 0 );
+
+ if( !endpoint ){
+ gameserver_request_respond( k_request_status_invalid_endpoint,
+ res, NULL, msg );
+ return;
+ }
+
+ if( !strcmp( endpoint, "scoreboard" ) ){
+ const char *mod = vg_msg_seekkvstr( &data, "mod", 0 );
+ const char *route = vg_msg_seekkvstr( &data, "route", 0 );
+ u32 week = vg_msg_seekkvu32( &data, "week", 0 );
+
+ char table_name[ DB_TABLE_UID_MAX ];
+ if( !db_get_highscore_table_name( mod, route, week, table_name ) ){
+ gameserver_request_respond( k_request_status_out_of_memory,
+ res, NULL, msg );
+ return;
+ }
+
+ 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 DESC LIMIT 10;" );
+ if( !vg_strgood(&q) ) {
+ gameserver_request_respond( k_request_status_out_of_memory,
+ res, NULL, msg );
+ return;
+ }
+
+ sqlite3_stmt *stmt = db_stmt( q.buffer );
+
+ if( !stmt ){
+ gameserver_request_respond( k_request_status_database_error,
+ res, NULL, msg );
+ return;
+ }
+
+ vg_msg_frame( &body, "rows" );
+ for( u32 i=0; i<10; i ++ ){
+ int fc = sqlite3_step( stmt );
+
+ if( fc == SQLITE_ROW ){
+ i32 time = sqlite3_column_int( stmt, 1 );
+ i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
+ u64 steamid = *((u64 *)&steamid_i64);
+
+ vg_msg_frame( &body, "" );
+ vg_msg_wkvu32( &body, "time", time );
+ vg_msg_wkvu64( &body, "steamid", steamid );
+
+ char username[32];
+ if( db_getuserinfo( steamid, username, sizeof(username), NULL ) ){
+ vg_msg_wkvstr( &body, "username", username );
+ }
+
+ vg_msg_end_frame( &body );
+ }
+ else if( fc == SQLITE_DONE ){
+ break;
+ }
+ else {
+ log_sqlite3( fc );
+ break;
+ }
+ }
+
+ sqlite3_finalize( stmt );
+ vg_msg_end_frame( &body );
+
+ 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{
+ gameserver_request_respond( k_request_status_invalid_endpoint,
+ res, NULL, msg );
+ }
+}
+
+static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg ){
+ netmsg_blank *tmp = msg->m_pData;
+
+ int client_id = gameserver_client_index( msg->m_conn );
+ if( client_id == -1 ){
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ return;
+ }
+
+ if( tmp->inetmsg_id == k_inetmsg_request ){
+ if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
+ return;
+
+ db_request *call = db_alloc_request(
+ sizeof(struct user_request_thread_data) );
+ struct user_request_thread_data *inf = (void *)call->data;
+ inf->msg = msg;
+ call->handler = gameserver_process_user_request;
+ db_send_request( call );
+ }
+ else {
+ vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
+ tmp->inetmsg_id );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ }
}
static void poll_connections(void){
if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
gameserver_rx_200_300( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ }
+ else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
+ gameserver_rx_300_400( msg );
}
else{
if( tmp->inetmsg_id == k_inetmsg_auth )
vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
tmp->inetmsg_id );
}
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
-
- SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
}
}
static void test_runner( db_request *req ){
vg_warn( "RUNNER\n" );
char table[DB_TABLE_UID_MAX];
- if( db_get_highscore_table_name( "sr003-local-mp_mtzero",
- "megapark-yellow", 302, table ) ){
+ if( db_get_highscore_table_name( "sr002-local-mp_mtzero",
+ "Coastal Run", 0, table ) ){
if( db_writeusertime( table, 76561198072130043, 232, 1 ) ){
vg_success( "Written time\n" );
i32 v = db_readusertime( table, 76561198072130043 );
vg_info( "Shutting down\n..." );
SteamGameServer_Shutdown();
+ db_kill();
db_free();
return 0;
#define DB_COURSE_UID_MAX 32
#define DB_TABLE_UID_MAX (ADDON_UID_MAX+DB_COURSE_UID_MAX+32)
-#define DB_CRASH_ON_SQLITE_ERROR
+//#define DB_CRASH_ON_SQLITE_ERROR
#define DB_LOG_SQL_STATEMENTS
#define DB_REQUEST_BUFFER_SIZE (1024*2)
/*
* Find table name from mod UID and course UID, plus the week number
*/
-static int db_get_highscore_table_name( char mod_uid[ADDON_UID_MAX],
- char run_uid[DB_COURSE_UID_MAX],
+static int db_get_highscore_table_name( const char *mod_uid,
+ const char *run_uid,
u32 week,
char table_name[DB_TABLE_UID_MAX] ){
if( !db_verify_charset( mod_uid, 13 ) ||
if( !vg_strgood(&q) ) return 0;
sqlite3_stmt *stmt = db_stmt( q.buffer );
- sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
-
if( stmt ){
+ sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
int fc = sqlite3_step( stmt );
i32 result = 0;
else return 0;
}
+/*
+ * Get user info
+ */
+static int db_getuserinfo( u64 steamid, char *out_username, u32 username_max,
+ i32 *out_type ){
+ sqlite3_stmt *stmt = db_stmt( "SELECT * FROM users WHERE steamid = ?;" );
+ if( !stmt ) return 0;
+
+ sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+ int fc = sqlite3_step( stmt );
+
+ if( fc != SQLITE_ROW ){
+ log_sqlite3( fc );
+ sqlite3_finalize( stmt );
+ return 0;
+ }
+
+ if( out_username ){
+ const char *name = (const char *)sqlite3_column_text( stmt, 1 );
+ vg_strncpy( name, out_username, username_max, k_strncpy_allow_cutoff );
+ }
+
+ if( out_type )
+ *out_type = sqlite3_column_int( stmt, 2 );
+
+ sqlite3_finalize( stmt );
+ return 1;
+}
+
static void _db_thread_end(void){
pthread_mutex_lock( &database.mux );
database.kill = 1;
pthread_mutex_unlock( &database.mux );
sqlite3_close( database.db );
- pthread_mutex_destroy( &database.mux );
}
static void *db_loop(void *_){
return result;
}
-static void db_free(void){
+static void db_kill(void){
pthread_mutex_lock( &database.mux );
database.kill = 1;
pthread_mutex_unlock( &database.mux );
+ pthread_join( database.thread, NULL );
+}
+
+static void db_free(void){
+ pthread_mutex_destroy( &database.mux );
}
static db_request *db_alloc_request( u32 size ){
#include "network_msg.h"
#include "network_common.h"
#include "player_remote.h"
+#include "world_sfd.h"
static void scores_update(void);
k_nSteamNetworkingSend_Reliable, NULL );
}
+static void network_send_request( netmsg_request *req, vg_msg *body,
+ void (*callback)( netmsg_request *res,
+ vg_msg *body )){
+ u32 len = 0;
+ if( body ){
+ len = body->len;
+ vg_info( "Request scoreboard. Info (%u):\n", body->len );
+ vg_msg_print( body );
+
+ if( body->error != k_vg_msg_error_OK ){
+ vg_error( "Body not OK\n" );
+ return;
+ }
+ }
+
+ if( callback ){
+ req->id = vg_pool_lru( &network_client.request_pool );
+ 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 );
+ pn->callback = callback;
+ pn->sendtime = vg.time_real;
+ }
+ 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 );
+}
+
+static void network_scoreboard_callback( netmsg_request *res, vg_msg *body ){
+ for( u32 i=0; i<13; i++ )
+ sfd_encode( i, "" );
+
+ if( res->status != k_request_status_ok ){
+ char buf[32];
+ vg_str s;
+ vg_strnull( &s, buf, 32 );
+ vg_strcat( &s, "Error: " );
+ vg_strcati32( &s, res->status );
+
+ sfd_encode( 4, buf );
+ return;
+ }
+
+ /* TODO: frame pointers?? */
+
+ u32 l = 0;
+ vg_msg rows = *body;
+ if( vg_msg_seekframe( &rows, "rows", k_vg_msg_first ) ){
+ vg_msg entry = rows;
+
+ while( vg_msg_seekframe( &entry, NULL, k_vg_msg_next ) ){
+ const char *username = vg_msg_seekkvstr( &entry, "username",
+ k_vg_msg_first );
+ sfd_encode( l ++, username );
+ vg_msg_skip_frame( &entry );
+ }
+ }
+}
+
+/* mod_uid: world mod uid,
+ * route_uid: run name (just a string)
+ * week: 0 for all-time, n for week #
+ */
+static void network_request_scoreboard( const char *mod_uid,
+ const char *route_uid,
+ u32 week ){
+ if( !network_client.remote )
+ return;
+
+ netmsg_request *req = alloca( sizeof(netmsg_request) + 512 );
+ req->inetmsg_id = k_inetmsg_request;
+ req->id = 0; /* TODO: pool allocatable */
+
+ vg_msg data = {0};
+ data.buf = req->q;
+ data.max = 512;
+
+ vg_msg_wkvstr( &data, "endpoint", "scoreboard" );
+ vg_msg_wkvstr( &data, "mod", mod_uid );
+ vg_msg_wkvstr( &data, "route", route_uid );
+ vg_msg_wkvu32( &data, "week", week );
+ network_send_request( req, &data, network_scoreboard_callback );
+}
+
+static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
+ netmsg_blank *tmp = msg->m_pData;
+
+ if( tmp->inetmsg_id == k_inetmsg_request ){
+
+ }
+ else if( tmp->inetmsg_id == k_inetmsg_response ){
+ netmsg_request *res = (netmsg_request *)msg->m_pData;
+
+ vg_msg *body = NULL;
+ vg_msg data = {0};
+
+ if( res->status == k_request_status_ok ){
+ data.buf = res->q;
+ data.len = msg->m_cbSize - sizeof(netmsg_request);
+ data.max = data.len;
+ vg_success( "Response to #%d:\n", (i32)res->id );
+ vg_msg_print( &data );
+ body = &data;
+ }
+ else {
+ vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
+ }
+
+ if( res->id ){
+ struct network_request *pn =
+ vg_pool_item( &network_client.request_pool, res->id );
+ pn->callback( res, body );
+ vg_pool_unwatch( &network_client.request_pool, res->id );
+ }
+ }
+}
+
static void network_send_item( enum netmsg_playeritem_type type ){
if( !network_client.remote )
return;
hSteamNetworkingSockets, network_client.remote,
item, sizeof(netmsg_playeritem)+chs+1,
k_nSteamNetworkingSend_Reliable, NULL );
-
}
static void network_disconnect(void){
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) ){
+ network_request_rx_300_400( msg );
+ }
SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
vg_console_reg_var( "network_info", &network_client.network_info,
k_var_dtype_i32, VG_VAR_PERSISTENT );
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 );
+ memset( network_client.request_buffer, 0, alloc_size );
+
+ vg_pool *pool = &network_client.request_pool;
+ pool->buffer = network_client.request_buffer;
+ pool->count = NETWORK_MAX_REQUESTS;
+ pool->stride = sizeof( struct network_request );
+ 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,
#include "highscores.h"
#include "addon_types.h"
+#define NETWORK_MAX_REQUESTS 8
+
static int network_scores_updated = 0;
/*
u32 retries;
i32 network_info;
+
+ struct network_request {
+ vg_pool_node poolnode;
+ void (*callback)( netmsg_request *res, vg_msg *body );
+ f64 sendtime;
+ }
+ *request_buffer;
+ vg_pool request_pool;
}
static network_client = {
- .state = k_ESteamNetworkingConnectionState_None,
.auth_mode = eServerModeAuthentication,
+ .state = k_ESteamNetworkingConnectionState_None,
};
static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size );
static void network_send_item( enum netmsg_playeritem_type type );
+static void network_request_scoreboard( const char *mod_uid,
+ const char *route_uid,
+ u32 week );
#endif /* NETWORK_H */
char msg[];
};
+/* requests 300 */
+typedef struct netmsg_request netmsg_request;
+enum{ k_inetmsg_request = 300, k_inetmsg_response = 301 };
+struct netmsg_request {
+ u16 inetmsg_id;
+ u8 id, status;
+ u8 q[];
+};
+
+enum request_status {
+ k_request_status_client_error = 0,
+ k_request_status_invalid_endpoint = 1,
+
+ 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
+};
+
#pragma pack(pop)
#endif /* NETWORK_MSG_H */
if( (world_sfd.active_route_board != closest) || network_scores_updated ){
network_scores_updated = 0;
world_sfd.active_route_board = closest;
-
ent_route *route = mdl_arritm( &world->ent_route, closest );
+
+ addon_reg *world_reg = world_static.addon_hub;
+ if( world_static.active_instance )
+ world_reg = world_static.addon_client;
+
+ char mod_uid[ ADDON_UID_MAX ];
+ addon_alias_uid( &world_reg->alias, mod_uid );
+
+ network_request_scoreboard(
+ mod_uid,
+ mdl_pstr( &world->meta, route->pstr_name ),
+ 0 );
+#if 0
u32 id = route->anon.official_track_id;
if( id != 0xffffffff ){
sfd_encode( 0, mdl_pstr( &world->meta, route->pstr_name ) );
sfd_encode( 1, "No data" );
}
+#endif
}
}
static world_sfd;
static void world_sfd_init(void);
+static void sfd_encode( u32 row, const char *str );
static void sfd_render( world_instance *world, camera *cam,
m4x3f transform );