compile_server( &test_proj, &vg_test_env );
}
+void s_queuetest_build(void)
+{
+ vg_info( "running script: s_queuetest_build(void)\n" );
+
+ struct vg_project test_proj;
+ vg_project_init( &test_proj, "bin", "queuetest", &vg_test_env, 0 );
+
+ vg_str sources = {0};
+ vg_strcat( &sources, "src/queuetest.c vg/vg_tool.c \\\n " );
+
+ struct vg_compiler_conf conf = {0};
+ vg_strcat( &conf.include, "-Isrc -I./dep " );
+ vg_strcat( &conf.include, "-I. -I./vg -I./vg/dep " );
+ vg_strcat( &conf.library, "-L./vg/dep/steam " );
+ vg_strcat( &conf.link, "-ldl -lpthread -lm " );
+ vg_compiler_run( &test_proj, &vg_test_env, &conf, sources.buffer, "qtest", k_obj_type_exe );
+}
+
int main( int argc, char *argv[] )
{
vg_log_init();
if( vg_long_opt( "testing-server", NULL ) )
s_testing_server();
+ if( vg_long_opt( "queuetest", NULL ) )
+ s_queuetest_build();
+
if( vg_long_opt( "tools", NULL ) )
s_compile_tools();
#include "gameserver.h"
#include "vg/vg_opt.h"
#include "network_common.h"
-#include "gameserver_db.h"
+#include "gameserver_database.h"
#include "vg/vg_m.h"
#include "vg/vg_msg.h"
#include "gameserver_replay.h"
+#include "gameserver_transfer.h"
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 )
{
- msg->m_nUserData --;
-
- if( msg->m_nUserData == 0 )
- SteamAPI_SteamNetworkingMessage_t_Release( msg );
+ SteamAPI_SteamNetworkingMessage_t_Release( msg );
}
/*
* Send message to single client, with authentication checking
*/
-static void gameserver_send_to_client( i32 client_id,
- const void *pData, u32 cbData,
- int nSendFlags )
+static void gameserver_send_to_client( i32 client_id, const void *pData, u32 cbData, int nSendFlags )
{
struct gameserver_client *client = &gameserver.clients[ client_id ];
- if( gameserver.loopback_test && !client->connection )
- return;
-
if( !client->steamid )
return;
- SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
- hSteamNetworkingSockets, client->connection,
- pData, cbData, nSendFlags, NULL );
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, client->connection,
+ pData, cbData, nSendFlags, NULL );
}
/*
{
struct gameserver_client *client = &gameserver.clients[index];
- if( gameserver.loopback_test && !client->connection )
- return;
-
netmsg_version version;
version.inetmsg_id = k_inetmsg_version;
version.version = NETWORK_SKATERIFT_VERSION;
static void gameserver_player_join( int index )
{
struct gameserver_client *joiner = &gameserver.clients[index];
-
+
+ SteamAPI_ISteamNetworkingSockets_ConfigureConnectionLanes( hSteamNetworkingSockets, joiner->connection, 2,
+ (int[]){ 0,0 }, (u16[]){ 10, 1 } );
+
netmsg_playerjoin join = { .inetmsg_id = k_inetmsg_playerjoin,
.index = index,
.steamid = joiner->steamid };
- gameserver_send_to_all( index, &join, sizeof(join),
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_all( index, &join, sizeof(join), k_nSteamNetworkingSend_Reliable );
/*
* update the joining user about current connections and our version
*/
gameserver_send_version_to_client( index );
- netmsg_playerusername *username =
- alloca( sizeof(netmsg_playerusername) + NETWORK_USERNAME_MAX );
+ netmsg_playerusername *username = alloca( sizeof(netmsg_playerusername) + NETWORK_USERNAME_MAX );
username->inetmsg_id = k_inetmsg_playerusername;
- netmsg_playeritem *item =
- alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
+ netmsg_playeritem *item = alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
item->inetmsg_id = k_inetmsg_playeritem;
netmsg_region *region = alloca( sizeof(netmsg_region) + NETWORK_REGION_MAX );
netmsg_playerjoin init = { .inetmsg_id = k_inetmsg_playerjoin,
.index = i,
.steamid = client->steamid };
- gameserver_send_to_client( index, &init, sizeof(init),
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_client( index, &init, sizeof(init), k_nSteamNetworkingSend_Reliable );
/* username */
username->index = i;
- u32 chs = vg_strncpy( client->username, username->name,
- NETWORK_USERNAME_MAX,
- k_strncpy_always_add_null );
+ u32 chs = vg_strncpy( client->username, username->name, NETWORK_USERNAME_MAX, k_strncpy_always_add_null );
u32 size = sizeof(netmsg_playerusername) + chs + 1;
- gameserver_send_to_client( index, username, size,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_client( index, username, size, k_nSteamNetworkingSend_Reliable );
/* items */
for( int j=0; j<k_netmsg_playeritem_max; j++ )
{
- chs = vg_strncpy( client->items[j].uid, item->uid, ADDON_UID_MAX,
- k_strncpy_always_add_null );
+ chs = vg_strncpy( client->items[j].uid, item->uid, ADDON_UID_MAX, k_strncpy_always_add_null );
item->type_index = j;
item->client = i;
size = sizeof(netmsg_playeritem) + chs + 1;
- gameserver_send_to_client( index, item, size,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_client( index, item, size, k_nSteamNetworkingSend_Reliable );
}
/* region */
-
region->client = i;
region->flags = client->region_flags;
- u32 l = vg_strncpy( client->region, region->loc, NETWORK_REGION_MAX,
- k_strncpy_always_add_null );
+ u32 l = vg_strncpy( client->region, region->loc, NETWORK_REGION_MAX, k_strncpy_always_add_null );
size = sizeof(netmsg_region) + l + 1;
-
- gameserver_send_to_client( index, region, size,
- k_nSteamNetworkingSend_Reliable );
+ gameserver_send_to_client( index, region, size, k_nSteamNetworkingSend_Reliable );
}
}
SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup( hSteamNetworkingSockets, conn, gameserver.client_group );
SteamAPI_ISteamNetworkingSockets_SetConnectionUserData( hSteamNetworkingSockets, conn, index );
-
- if( gameserver.loopback_test )
- {
- vg_warn( "[DEV] Creating loopback client\n" );
- struct gameserver_client *loopback = &gameserver.clients[1];
- loopback->active = 1;
- loopback->connection = 0;
- }
}
else
{
i64 id;
if( hconn == 0 )
- {
- if( gameserver.loopback_test )
- return 1;
- else
- return -1;
- }
+ return -1;
else
id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData( hSteamNetworkingSockets, hconn );
{
gameserver_player_leave( client_id );
remove_client( client_id );
-
- if( gameserver.loopback_test )
- {
- gameserver_player_leave( 1 );
- remove_client( 1 );
- }
}
else
{
{
client->steamid = k_steamid_max;
gameserver_player_join( client_id );
-
- if( gameserver.loopback_test )
- {
- struct gameserver_client *loopback = &gameserver.clients[1];
- loopback->steamid = k_steamid_max;
- gameserver_player_join( 1 );
- }
}
}
basic->sound_effects = 0;
struct gameserver_client *c0 = &gameserver.clients[client_id];
- c0->instance = frame->flags & NETMSG_PLAYERFRAME_INSTANCE_ID;
for( int i=0; i<NETWORK_MAX_PLAYERS; i++ )
{
continue;
struct gameserver_client *ci = &gameserver.clients[i];
-
int send_full = 0;
- if( c0->instance == ci->instance )
- {
- u32 k_index = network_pair_index( client_id, i );
- u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
-
- if( (k_mask & (CLIENT_KNOWLEDGE_SAME_WORLD0<<c0->instance)) )
- send_full = 1;
- }
+ u32 k_index = network_pair_index( client_id, i );
+ u8 k_mask = gameserver.client_knowledge_mask[ k_index ];
+ if( (k_mask & CLIENT_KNOWLEDGE_SAME_WORLD0) )
+ send_full = 1;
if( send_full )
gameserver_send_to_client( i, full, size, k_nSteamNetworkingSend_Unreliable );
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;
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
}
else if( tmp->inetmsg_id == k_inetmsg_playerframe )
{
{
_gs_write_replay_to_disk( client_id, 1.0*60.0, "/tmp/server-replay.replay" );
}
- else if( !strcmp( prop->msg, "play" ) )
+ else if( !strcmp( prop->msg, "transfer" ) )
{
- _gs_test_replay( "/tmp/server-replay.replay" );
+ _gs_start_transfer( client_id, "/tmp/server-replay.replay" );
+ //_gs_test_replay( client_id, "/tmp/server-replay.replay" );
}
gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, k_nSteamNetworkingSend_Reliable );
res->status = status;
- if( gameserver.loopback_test && !msg->m_conn )
- {
- release_message( msg );
- return;
- }
-
SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
hSteamNetworkingSockets, msg->m_conn,
res, sizeof(netmsg_request) + len,
if( tmp->inetmsg_id == k_inetmsg_request )
{
- if( gameserver.loopback_test && (client_id == 1) )
- {
- release_message( msg );
- return;
- }
-
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
}
else
{
{
gameserver_rx_300_400( msg );
}
- else{
+ else
+ {
if( tmp->inetmsg_id == k_inetmsg_auth )
gameserver_rx_auth( msg );
- else if( tmp->inetmsg_id == k_inetmsg_version ){
+ else if( tmp->inetmsg_id == k_inetmsg_version )
gameserver_rx_version( msg );
- }
- else {
- vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
- tmp->inetmsg_id );
- }
+ else
+ vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n", tmp->inetmsg_id );
release_message( msg );
}
}
for( int i=0; i<len; i++ )
{
SteamNetworkingMessage_t *msg = messages[i];
- msg->m_nUserData = 1;
-
- if( gameserver.loopback_test )
- {
- 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 );
}
}
if( vg_long_opt( "noauth", "Disable server authentication" ) )
gameserver.auth_mode = eServerModeNoAuthentication;
- 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 );
vg_alloc_quota();
- db_init();
+ if( !db_init() )
+ {
+ return 0;
+ }
/* steamworks init
* --------------------------------------------------------------- */
SteamAPI_ManualDispatch_Init();
HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
hSteamNetworkingSockets = SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
+ hSteamNetworkingUtils = SteamAPI_SteamNetworkingUtils_SteamAPI();
steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack, on_connect_status );
steamworks_event_loop( hsteampipe );
poll_connections();
_gs_replay_server_tick();
+ _gs_transfer_tick();
usleep(10000);
gameserver.ticks ++;
- if( db_killed() )
- break;
+ /* consume tasks here */
}
SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, gameserver.client_group );
vg_info( "Shutting down\n..." );
SteamGameServer_Shutdown();
- db_kill();
db_free();
-
return 0;
}
#include "gameserver_replay.c"
+#include "gameserver_transfer.c"
+#include "gameserver_database.c"
HSteamNetPollGroup client_group;
EServerMode auth_mode;
- struct gameserver_client {
- int active;
+ struct gameserver_client
+ {
+ bool active, authenticated;
u32 version;
- int authenticated;
HSteamNetConnection connection;
char username[ NETWORK_USERNAME_MAX ];
- u8 instance;
-
- struct gameserver_item {
+ struct gameserver_item
+ {
char uid[ADDON_UID_MAX];
u32 hash;
}
u8 client_knowledge_mask[ (NETWORK_MAX_PLAYERS*(NETWORK_MAX_PLAYERS-1))/2 ];
u8 app_symmetric_key[ k_nSteamEncryptedAppTicketSymmetricKeyLen ];
-
- bool loopback_test;
u64 ticks;
}
static gameserver = {
.auth_mode = eServerModeAuthentication
};
-static ISteamNetworkingSockets *hSteamNetworkingSockets = NULL;
+extern ISteamNetworkingSockets *hSteamNetworkingSockets;
+extern ISteamNetworkingUtils *hSteamNetworkingUtils;
u64 seconds_to_server_ticks( f64 s );
--- /dev/null
+#include "gameserver_database.h"
+
+struct _gs_db _gs_db;
+
+/*
+ * Log the error code (or carry on if its OK).
+ */
+void log_sqlite3( int code )
+{
+ if( code == SQLITE_OK )
+ return;
+
+ vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
+}
+
+sqlite3_stmt *db_stmt( const char *code )
+{
+#ifdef DB_LOG_SQL_STATEMENTS
+ vg_low( code );
+#endif
+
+ sqlite3_stmt *stmt;
+ int fc = sqlite3_prepare_v2( _gs_db.db, code, -1, &stmt, NULL );
+
+ if( fc != SQLITE_OK )
+ {
+ log_sqlite3( fc );
+ sqlite3_finalize( stmt );
+ return NULL;
+ }
+
+ return stmt;
+}
+
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz )
+{
+ return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
+}
+
+/*
+ * Allowed characters in sqlite table names. We use "" as delimiters.
+ */
+static int db_verify_charset( const char *str, int mincount )
+{
+ for( int i=0; ; i++ )
+ {
+ char c = str[i];
+ if( c == '\0' )
+ {
+ if( i < mincount ) return 0;
+ else return 1;
+ }
+
+ if( !((c==' ')||(c=='!')||(c>='#'&&c<='~')) )
+ return 0;
+ }
+
+ return 0;
+}
+
+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 ) )
+ return 0;
+
+ vg_str a;
+ vg_strnull( &a, table_name, DB_TABLE_UID_MAX );
+ vg_strcat( &a, mod_uid );
+ vg_strcat( &a, ":" );
+ vg_strcat( &a, run_uid );
+
+ if( week ){
+ vg_strcat( &a, "#" );
+ vg_strcati32( &a, week );
+ }
+
+ return vg_strgood( &a );
+}
+
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid )
+{
+ char buf[ 512 ];
+ vg_str q;
+ vg_strnull( &q, buf, 512 );
+ vg_strcat( &q, "SELECT time FROM \"" );
+ vg_strcat( &q, table );
+ vg_strcat( &q, "\" WHERE steamid = ?;" );
+ if( !vg_strgood(&q) )
+ return 0;
+
+ sqlite3_stmt *stmt = db_stmt( q.buffer );
+ if( stmt )
+ {
+ sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+ int fc = sqlite3_step( stmt );
+
+ i32 result = 0;
+
+ if( fc == SQLITE_ROW )
+ result = sqlite3_column_int( stmt, 0 );
+ else if( fc != SQLITE_DONE )
+ log_sqlite3(fc);
+
+ sqlite3_finalize( stmt );
+ return result;
+ }
+ else
+ return 0;
+}
+
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster )
+{
+ /* auto create table
+ * ------------------------------------------*/
+ char buf[ 512 ];
+ vg_str q;
+ vg_strnull( &q, buf, 512 );
+ vg_strcat( &q, "CREATE TABLE IF NOT EXISTS \n \"" );
+ vg_strcat( &q, table );
+ vg_strcat( &q, "\"\n (steamid BIGINT UNIQUE, time INT);" );
+ if( !vg_strgood(&q) )
+ return 0;
+
+ vg_str str;
+ sqlite3_stmt *create_table = db_stmt( q.buffer );
+
+ if( create_table )
+ {
+ db_sqlite3_bind_sz( create_table, 1, table );
+
+ int fc = sqlite3_step( create_table );
+ sqlite3_finalize( create_table );
+ if( fc != SQLITE_DONE )
+ return 0;
+ }
+ else
+ return 0;
+
+ if( only_if_faster )
+ {
+ i32 current = db_readusertime( table, steamid );
+ if( (current != 0) && (score > current) )
+ return 1;
+ }
+
+ /* insert score
+ * -------------------------------------------------*/
+ vg_strnull( &q, buf, 512 );
+ vg_strcat( &q, "REPLACE INTO \"" );
+ vg_strcat( &q, table );
+ vg_strcat( &q, "\"(steamid,time)\n VALUES (?,?);" );
+ if( !vg_strgood(&q) )
+ return 0;
+
+ sqlite3_stmt *stmt = db_stmt( q.buffer );
+
+ if( stmt )
+ {
+ sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
+ sqlite3_bind_int( stmt, 2, score );
+
+ int fc = sqlite3_step( stmt );
+ sqlite3_finalize( stmt );
+ if( fc != SQLITE_DONE )
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+bool db_updateuser( u64 steamid, const char *username, int admin )
+{
+ sqlite3_stmt *stmt = db_stmt( "INSERT OR REPLACE INTO users (steamid, name, type) VALUES (?,?,?);" );
+
+ if( stmt )
+ {
+ sqlite3_bind_int64( stmt, 1, *((i64*)(&steamid)) );
+ db_sqlite3_bind_sz( stmt, 2, username );
+ sqlite3_bind_int( stmt, 3, admin );
+
+ int fc = sqlite3_step( stmt );
+ sqlite3_finalize(stmt);
+
+ if( fc == SQLITE_DONE )
+ {
+ vg_success( "Inserted %lu (%s), type: %d\n", steamid, username, admin );
+ return 1;
+ }
+ else
+ {
+ log_sqlite3( fc );
+ return 0;
+ }
+ }
+ else
+ return 0;
+}
+
+bool 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;
+}
+
+#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
+
+static void *database_worker_thread(void *_)
+{
+ 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)
+ {
+ pthread_mutex_lock( &_gs_db.mux );
+
+#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);
+ }
+
+ vg_low( "Database thread terminates.\n" );
+ return NULL;
+}
+
+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;
+
+ if( pthread_mutex_init( &_gs_db.mux, NULL ) )
+ return 0;
+
+ if( pthread_create( &_gs_db.thread, NULL, _gs_db.worker_thread, NULL ) )
+ return 0;
+
+ return 1;
+}
+
+void db_free(void)
+{
+ pthread_join( _gs_db.thread, NULL );
+ pthread_mutex_destroy( &_gs_db.mux );
+}
--- /dev/null
+#pragma once
+
+#include "vg/vg_log.h"
+#include "vg/vg_mem_queue.h"
+#include "network_common.h"
+#include "dep/sqlite3/sqlite3.h"
+#include <pthread.h>
+#include <unistd.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
+{
+ void (*handler)( db_request *req );
+ u32 size,_;
+ u8 data[];
+};
+
+struct _gs_db
+{
+ sqlite3 *db;
+ pthread_t thread;
+ pthread_mutex_t mux;
+
+ vg_queue queue;
+ int kill;
+}
+extern _gs_db;
+
+/*
+ * Perpare statement and auto throw away if fails. Returns NULL on failure.
+ */
+sqlite3_stmt *db_stmt( const char *code );
+void log_sqlite3( int code );
+
+/*
+ * bind zero terminated string
+ */
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz );
+
+/*
+ * Find table name from mod UID and course UID, plus the week number
+ */
+bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] );
+
+/*
+ * Read value from highscore table. If not found or error, returns 0
+ */
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid );
+
+/*
+ * Write to highscore table
+ */
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster );
+
+/*
+ * Set username and type
+ */
+bool db_updateuser( u64 steamid, const char *username, int admin );
+
+/*
+ * Get user info
+ */
+bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type );
+
+/*
+ * 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
-#ifndef GAMESERVER_DB_H
-#define GAMESERVER_DB_H
+#pragma once
#include "vg/vg_log.h"
#include "vg/vg_mem_queue.h"
#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_LOG_SQL_STATEMENTS
#define DB_REQUEST_BUFFER_SIZE (1024*2)
typedef struct db_request db_request;
-struct db_request {
+struct db_request
+{
void (*handler)( db_request *req );
u32 size,_;
u8 data[];
};
-struct {
+struct _gs_db
+{
sqlite3 *db;
pthread_t thread;
pthread_mutex_t mux;
vg_queue queue;
int kill;
}
-static database;
-
-/*
- * Log the error code (or carry on if its OK).
- */
-static void log_sqlite3( int code )
-{
- if( code == SQLITE_OK ) return;
- //vg_print_backtrace();
- vg_error( "sqlite3(%d): %s\n", code, sqlite3_errstr(code) );
-
-#ifdef DB_CRASH_ON_SQLITE_ERROR
- int crash = *((int*)2);
-#endif
-}
+extern _gs_db;
/*
* Perpare statement and auto throw away if fails. Returns NULL on failure.
*/
-static sqlite3_stmt *db_stmt( const char *code ){
-#ifdef DB_LOG_SQL_STATEMENTS
- vg_low( code );
-#endif
-
- sqlite3_stmt *stmt;
- int fc = sqlite3_prepare_v2( database.db, code, -1, &stmt, NULL );
-
- if( fc != SQLITE_OK ){
- log_sqlite3( fc );
- sqlite3_finalize( stmt );
- return NULL;
- }
-
- return stmt;
-}
+sqlite3_stmt *db_stmt( const char *code );
/*
* bind zero terminated string
*/
-static int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz ){
- return sqlite3_bind_text( stmt, pos, sz, -1, SQLITE_STATIC );
-}
-
-/*
- * Allowed characters in sqlite table names. We use "" as delimiters.
- */
-static int db_verify_charset( const char *str, int mincount ){
- for( int i=0; ; i++ ){
- char c = str[i];
- if( c == '\0' ){
- if( i < mincount ) return 0;
- else return 1;
- }
-
- if( !((c==' ')||(c=='!')||(c>='#'&&c<='~')) ) return 0;
- }
-
- return 0;
-}
+int db_sqlite3_bind_sz( sqlite3_stmt *stmt, int pos, const char *sz );
/*
* Find table name from mod UID and course UID, plus the week number
*/
-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 ) ||
- !db_verify_charset( run_uid, 1 ) ) return 0;
-
- vg_str a;
- vg_strnull( &a, table_name, DB_TABLE_UID_MAX );
- vg_strcat( &a, mod_uid );
- vg_strcat( &a, ":" );
- vg_strcat( &a, run_uid );
-
- if( week ){
- vg_strcat( &a, "#" );
- vg_strcati32( &a, week );
- }
-
- return vg_strgood( &a );
-}
+bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] );
/*
* Read value from highscore table. If not found or error, returns 0
*/
-static i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ){
- char buf[ 512 ];
- vg_str q;
- vg_strnull( &q, buf, 512 );
- vg_strcat( &q, "SELECT time FROM \"" );
- vg_strcat( &q, table );
- vg_strcat( &q, "\" WHERE steamid = ?;" );
- if( !vg_strgood(&q) ) return 0;
-
- sqlite3_stmt *stmt = db_stmt( q.buffer );
- if( stmt ){
- sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
- int fc = sqlite3_step( stmt );
-
- i32 result = 0;
-
- if( fc == SQLITE_ROW )
- result = sqlite3_column_int( stmt, 0 );
- else if( fc != SQLITE_DONE )
- log_sqlite3(fc);
-
- sqlite3_finalize( stmt );
- return result;
- }
- else return 0;
-}
+i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid );
/*
* Write to highscore table
*/
-static int db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid,
- i32 score, int only_if_faster ){
- /* auto create table
- * ------------------------------------------*/
- char buf[ 512 ];
- vg_str q;
- vg_strnull( &q, buf, 512 );
- vg_strcat( &q, "CREATE TABLE IF NOT EXISTS \n \"" );
- vg_strcat( &q, table );
- vg_strcat( &q, "\"\n (steamid BIGINT UNIQUE, time INT);" );
- if( !vg_strgood(&q) ) return 0;
-
- vg_str str;
- sqlite3_stmt *create_table = db_stmt( q.buffer );
-
- if( create_table ){
- db_sqlite3_bind_sz( create_table, 1, table );
-
- int fc = sqlite3_step( create_table );
- sqlite3_finalize( create_table );
- if( fc != SQLITE_DONE )
- return 0;
- }
- else return 0;
-
- if( only_if_faster ){
- i32 current = db_readusertime( table, steamid );
- if( (current != 0) && (score > current) )
- return 1;
- }
-
- /* insert score
- * -------------------------------------------------*/
- vg_strnull( &q, buf, 512 );
- vg_strcat( &q, "REPLACE INTO \"" );
- vg_strcat( &q, table );
- vg_strcat( &q, "\"(steamid,time)\n VALUES (?,?);" );
- if( !vg_strgood(&q) ) return 0;
-
- sqlite3_stmt *stmt = db_stmt( q.buffer );
-
- if( stmt ){
- sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) );
- sqlite3_bind_int( stmt, 2, score );
-
- int fc = sqlite3_step( stmt );
- sqlite3_finalize( stmt );
- if( fc != SQLITE_DONE )
- return 0;
- else
- return 1;
- }
- else return 0;
-}
+bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster );
/*
* Set username and type
*/
-static int db_updateuser( u64 steamid, const char *username, int admin ){
- sqlite3_stmt *stmt = db_stmt(
- "INSERT OR REPLACE INTO users (steamid, name, type) "
- "VALUES (?,?,?);" );
-
- if( stmt ){
- sqlite3_bind_int64( stmt, 1, *((i64*)(&steamid)) );
- db_sqlite3_bind_sz( stmt, 2, username );
- sqlite3_bind_int( stmt, 3, admin );
-
- int fc = sqlite3_step( stmt );
- sqlite3_finalize(stmt);
-
- if( fc == SQLITE_DONE ){
- vg_success( "Inserted %lu (%s), type: %d\n",
- steamid, username, admin );
- return 1;
- }
- else{
- log_sqlite3( fc );
- return 0;
- }
- }
- else return 0;
-}
+bool db_updateuser( u64 steamid, const char *username, int admin );
/*
* 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 );
-}
-
-static void *db_loop(void *_){
- int rc = sqlite3_open( "highscores.db", &database.db );
-
- if( rc ){
- vg_error( "database failure: %s\n", sqlite3_errmsg(database.db) );
- _db_thread_end();
- 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 );
- _db_thread_end();
- return NULL;
- }
- }
- else {
- _db_thread_end();
- return NULL;
- }
-
- /*
- * Request processing loop
- */
- while(1)
- {
- pthread_mutex_lock( &database.mux );
-
- if( database.kill )
- {
- pthread_mutex_unlock( &database.mux );
- _db_thread_end();
- break;
- }
-
- u32 processed = 0;
-
- for( u32 i=0; i<16; i ++ )
- {
- db_request *req = vg_queue_tail_data( &database.queue );
- if( !req )
- break;
- pthread_mutex_unlock( &database.mux );
-
- req->handler( req );
- processed ++;
-
- pthread_mutex_lock( &database.mux );
- vg_queue_pop( &database.queue );
- }
-
- pthread_mutex_unlock( &database.mux );
-
- if( processed )
- vg_low( "Processed %u database requests.\n", processed );
-
- usleep(50000);
- }
-
- vg_low( "Database thread terminates.\n" );
- return NULL;
-}
+bool db_getuserinfo( u64 steamid, char *out_username, u32 username_max, i32 *out_type );
/*
* Create database connection and users table
*/
-static int db_init(void)
-{
- database.queue.buffer = (u8 *)vg_linear_alloc( vg_mem.rtmemory, DB_REQUEST_BUFFER_SIZE ),
- database.queue.size = DB_REQUEST_BUFFER_SIZE;
-
- if( pthread_mutex_init( &database.mux, NULL ) )
- return 0;
-
- if( pthread_create( &database.thread, NULL, db_loop, NULL ) )
- return 0;
-
- return 1;
-}
-
-static int db_killed(void)
-{
- pthread_mutex_lock( &database.mux );
- int result = database.kill;
- pthread_mutex_unlock( &database.mux );
- return result;
-}
-
-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 );
-}
+bool db_init(void);
+void db_free(void);
+#if 0
static db_request *db_alloc_request( u32 size )
{
u32 total = sizeof(db_request) + size;
{
pthread_mutex_unlock( &database.mux );
}
-
-#endif /* GAMESERVER_DB_H */
+#endif
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 )
return;
}
- /* FIXME: Should we do this on another thread */
+ /* 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 )
{
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 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 ++;
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 );
+
fclose( fp );
vg_success( "Written %u frames to disk.\n", frame_count );
}
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
{
struct gs_replay
{
u32 frame_count,
- head_item;
+ head_item;
}
replays[ NETWORK_MAX_PLAYERS ];
+
struct vg_queue ring_buffer;
bool print_info;
u64 print_ticker;
-
- u32 playback_buffer_len,
- playback_buffer_offset;
- void *playback_buffer;
}
extern _gs_replay;
void _gs_replay_server_tick(void);
void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_size );
void _gs_write_replay_to_disk( u8 client_id, f64 server_duration, const char *path );
-void _gs_test_replay( const char *path );
+void _gs_test_replay( u8 client_id, const char *path );
+void _gs_transfer_tick(void);
--- /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 );
SteamNetworkingMessage_t *messages[32];
int len;
- for( int i=0; i<10; i++ ){
+ for( int i=0; i<10; i++ )
+ {
len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
hSteamNetworkingSockets, network_client.remote,
messages, VG_ARRAY_LEN(messages));
if( len <= 0 )
return;
- for( int i=0; i<len; i++ ){
+ for( int i=0; i<len; i++ )
+ {
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;
ctx->bytes += bytes;
}
-static u32 bitpack_qf32( bitpack_ctx *ctx, u32 bits,
- f32 min, f32 max, f32 *v ){
- u32 mask = (0x1 << bits) - 1;
-
- if( ctx->mode == k_bitpack_compress ){
+static u32 bitpack_qf32( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, f32 *v )
+{
+ if( ctx->mode == k_bitpack_compress )
+ {
u32 a = vg_quantf( *v, bits, min, max );
bitpack_bytes( ctx, bits/8, &a );
return a;
}
- else {
+ else
+ {
u32 a = 0;
bitpack_bytes( ctx, bits/8, &a );
*v = vg_dequantf( a, bits, min, max );
}
}
-static void bitpack_qv2f( bitpack_ctx *ctx, u32 bits,
- f32 min, f32 max, v2f v ){
+static void bitpack_qv2f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v2f v )
+{
for( u32 i=0; i<2; i ++ )
bitpack_qf32( ctx, bits, min, max, v+i );
}
-static void bitpack_qv3f( bitpack_ctx *ctx, u32 bits,
- f32 min, f32 max, v3f v ){
+static void bitpack_qv3f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v3f v )
+{
for( u32 i=0; i<3; i ++ )
bitpack_qf32( ctx, bits, min, max, v+i );
}
-static void bitpack_qv4f( bitpack_ctx *ctx, u32 bits,
- f32 min, f32 max, v4f v ){
+static void bitpack_qv4f( bitpack_ctx *ctx, u32 bits, f32 min, f32 max, v4f v )
+{
for( u32 i=0; i<4; i ++ )
bitpack_qf32( ctx, bits, min, max, v+i );
}
-static void bitpack_qquat( bitpack_ctx *ctx, v4f quat ){
+static void bitpack_qquat( bitpack_ctx *ctx, v4f quat )
+{
const f32 k_domain = 0.70710678118f;
- if( ctx->mode == k_bitpack_compress ){
+ if( ctx->mode == k_bitpack_compress )
+ {
v4f qabs;
for( u32 i=0; i<4; i++ )
qabs[i] = fabsf(quat[i]);
#define NETMSG_BOUNDARY_BIT 0x8000
#define NETMSG_GATE_BOUNDARY_BIT 0x4000
+#define NETMSG_CAMERA_BOUNDARY_BIT 0x2000
+
#define NETMSG_BOUNDARY_MASK (NETMSG_BOUNDARY_BIT|NETMSG_GATE_BOUNDARY_BIT)
-#define NETMSG_PLAYERFRAME_INSTANCE_ID 0x3
+#define NETMSG_PLAYERFRAME_REMOVED0 0x3
#define NETMSG_PLAYERFRAME_HAVE_GLIDER 0x4
#define NETMSG_PLAYERFRAME_GLIDER_ORPHAN 0x8
struct netmsg_playerframe
{
u16 inetmsg_id;
- u16 boundary_hash; /* used for animating correctly through gates, teleport..
- msb is a flip flop for teleporting
- second msb is flip flop for gate */
+ u16 boundary_hash;
u8 client, subsystem,
flags, sound_effects;
f64 timestamp;
-
u8 animdata[];
};
void remote_player_send_playerframe(void)
{
u8 sysid = localplayer.subsystem;
- if( sysid >= k_player_subsystem_max ) return;
+ if( sysid >= k_player_subsystem_max )
+ return;
struct player_subsystem_interface *sys = player_subsystems[sysid];
- if( sys->animator_size ){
+ if( sys->animator_size )
+ {
u32 max_buf_size = sys->animator_size + sizeof(localplayer.sfx_buffer),
base_size = sizeof(struct netmsg_playerframe),
max_packet = base_size + max_buf_size;
.bytes = 0
};
+ frame->timestamp = vg.time_real;
+ frame->boundary_hash = localplayer.boundary_hash;
+
+ /* camera
+ * -------------------------------- */
+ bitpack_qv2f( &ctx, 8, 0.0f, VG_TAUf, (v2f){ fmod( localplayer.angles[0], VG_TAUf ), localplayer.angles[1] } );
+
/* animation
* -----------------------------------------------*/
- frame->timestamp = vg.time_real;
- frame->boundary_hash = localplayer.boundary_hash;
if( sys->network_animator_exchange )
sys->network_animator_exchange( &ctx, sys->animator_data );
else
/* glider
* -------------------------------------------------------------*/
- if( localplayer.have_glider ||
- (localplayer.subsystem == k_player_subsystem_glide) ) {
+ if( localplayer.have_glider || (localplayer.subsystem == k_player_subsystem_glide) )
frame->flags |= NETMSG_PLAYERFRAME_HAVE_GLIDER;
- }
if( localplayer.glider_orphan )
frame->flags |= NETMSG_PLAYERFRAME_GLIDER_ORPHAN;
- if( frame->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|
- NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){
- player_glide_remote_animator_exchange( &ctx,
- &player_glide.remote_animator );
- }
+ if( frame->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|NETMSG_PLAYERFRAME_GLIDER_ORPHAN) )
+ player_glide_remote_animator_exchange( &ctx, &player_glide.remote_animator );
/* ------- */
u32 wire_size = base_size + ctx.bytes;
netplayers.up_bytes += wire_size;
-
- SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
- hSteamNetworkingSockets, network_client.remote,
- frame, wire_size,
- k_nSteamNetworkingSend_Unreliable, NULL );
+ SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, network_client.remote,
+ frame, wire_size, k_nSteamNetworkingSend_Unreliable,
+ NULL );
}
}
--- /dev/null
+#define _DEFAULT_SOURCE
+#include <signal.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+
+#include "vg/vg_platform.h"
+#include "vg/vg_async2.h"
+#include "vg/vg_log.h"
+#include "vg/vg_m.h"
+
+vg_async_queue t0_q, t1_q = { .upper_memory_limit = 1024*1024*100 };
+pthread_t worker_thread, other_thread;
+pthread_mutex_t print_mutex;
+vg_rand rand2,rand3;
+
+volatile sig_atomic_t sig_stop;
+static void inthandler( int signum )
+{
+ sig_stop = 1;
+}
+
+static void siesta( u32 amt )
+{
+ //usleep( amt & 0x3 );
+ //usleep(amt);
+}
+
+static void some_callback( vg_async_task *task )
+{
+ //u32 us = vg_randu32( &rand2 ) % 1000;
+ pthread_mutex_lock( &print_mutex );
+ //vg_low( "message %u\n", us );
+ pthread_mutex_unlock( &print_mutex );
+ siesta( 1000 );
+}
+
+static void *worker_thread_func( void *_ )
+{
+ static u32 slower = 0;
+
+ while(1)
+ {
+ siesta(10000);
+ usleep(1);
+
+ slower ++;
+
+ if( slower >= 10000 )
+ {
+ slower = 0;
+ pthread_mutex_lock( &t1_q.lock );
+ u32 bytes = vg_queue_usage( &t1_q.queue );
+
+ f32 cap = t1_q.upper_memory_limit,
+ usage = bytes,
+ pc = usage / cap;
+
+ char bar[ 21 ];
+ bar[20] = '\0';
+
+ for( u32 i=0; i<20; i ++ )
+ {
+ f32 v = (f32)i / 20.0f;
+ if( pc > v )
+ bar[i] = '#';
+ else
+ bar[i] = ' ';
+ }
+
+ pthread_mutex_lock( &print_mutex );
+ if( pc < 0.9f )
+ vg_info( "Usage: %08u, %03u %08u |%s|\n", bytes, t1_q.queue.allocation_count, t1_q.queue.head_offset, bar );
+ else
+ vg_warn( "Usage: %08u, %03u %08u |%s|\n", bytes, t1_q.queue.allocation_count, t1_q.queue.head_offset, bar );
+ pthread_mutex_unlock( &print_mutex );
+ pthread_mutex_unlock( &t1_q.lock );
+ }
+
+ if( vg_async_consume( &t1_q, 100 ) )
+ break;
+ }
+
+ return NULL;
+}
+
+static void *other_thread_func( void *_ )
+{
+ while( !sig_stop )
+ {
+ siesta(100000);
+ vg_async_task *task = vg_allocate_async_task( &t1_q, vg_randu32( &rand3 ) % (20*1024), 1 );
+ task->handler = some_callback;
+ vg_async_task_dispatch( &t1_q, task );
+ siesta(100 + (vg_randu32( &rand3 ) % 100) );
+ }
+
+ vg_info( "dies\n" );
+
+ return NULL;
+}
+
+int main( int argc, char *argv[] )
+{
+ vg_log_init();
+
+ signal( SIGINT, inthandler );
+ signal( SIGQUIT, inthandler );
+ signal( SIGPIPE, SIG_IGN );
+
+ vg_rand rand;
+ vg_rand_seed( &rand, 34 );
+ vg_rand_seed( &rand2, 38 );
+ vg_rand_seed( &rand3, 384 );
+
+ if( !vg_init_async_queue( &t0_q ) )
+ return 0;
+
+ if( !vg_init_async_queue( &t1_q ) )
+ return 0;
+
+ if( pthread_create( &worker_thread, NULL, worker_thread_func, NULL ) )
+ return 0;
+
+ if( pthread_create( &other_thread, NULL, other_thread_func, NULL ) )
+ return 0;
+
+ if( pthread_mutex_init( &print_mutex, NULL ) )
+ return 0;
+
+ vg_low( "Started\n" );
+
+ u32 tocker = 0;
+
+ while( !sig_stop )
+ {
+ vg_async_task *task = vg_allocate_async_task( &t1_q, vg_randu32( &rand ) % (70*1024), 1 );
+ task->handler = some_callback;
+ vg_async_task_dispatch( &t1_q, task );
+
+ siesta(1 + (vg_randu32( &rand ) % 10) + tocker );
+
+ i32 mv = vg_randu32(&rand) % 10;
+ mv -= 5;
+
+ if( tocker < 10 )
+ tocker = 10;
+ else
+ tocker += mv;
+ }
+
+ vg_async_queue_end( &t1_q, k_async_quit_when_empty );
+ pthread_join( worker_thread, NULL );
+ pthread_join( other_thread, NULL );
+
+ vg_low( "Stopted\n" );
+ return 1;
+}
+
+#include "vg/vg_async2.c"