gui_helper_action( button_display_string( k_srbind_mleft ), "weekly" );
gui_helper_action( button_display_string( k_srbind_mright ), "all time" );
-
gui_helper_action( button_display_string( k_srbind_mback ), "exit" );
+ if( button_down( k_srbind_mleft ) ){
+ world_sfd.view_weekly = 1;
+ world_sfd_compile_active_scores();
+ }
+
+ if( button_down( k_srbind_mright ) ){
+ world_sfd.view_weekly = 0;
+ world_sfd_compile_active_scores();
+ }
+
if( button_down( k_srbind_mback ) ){
world_entity_unfocus();
return;
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 )
+{
+ 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;
+
+ 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) )
+ return k_request_status_out_of_memory;
+
+ sqlite3_stmt *stmt = db_stmt( q.buffer );
+ if( !stmt )
+ return k_request_status_database_error;
+
+ vg_msg_frame( msg, alias );
+ 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);
+
+ if( steamid == k_connection_unauthorized )
+ continue;
+
+ vg_msg_frame( msg, "" );
+ vg_msg_wkvu32( msg, "time", time );
+ vg_msg_wkvu64( msg, "steamid", 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;
+}
+
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;
const char *route = vg_msg_getkvstr( &data, "route" );
u32 week = vg_msg_getkvu32( &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;
+ if( week == NETWORK_LEADERBOARD_CURRENT_WEEK ){
+ gameserver_cat_table( &body, mod, route,
+ gameserver_get_current_week(), "rows_weekly" );
}
-
- 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);
-
- if( steamid == k_connection_unauthorized )
- continue;
-
- 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;
- }
+ 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" );
}
-
- sqlite3_finalize( stmt );
- vg_msg_end_frame( &body );
+ else
+ gameserver_cat_table( &body, mod, route, week, "rows" );
if( body.error != k_vg_msg_error_OK ){
gameserver_request_respond( k_request_status_out_of_memory,
#include "network_msg.h"
#include "network_common.h"
#include "player_remote.h"
+#include "world.h"
#include "world_sfd.h"
+#include "world_routes.h"
static void scores_update(void);
}
static void network_send_request( netmsg_request *req, vg_msg *body,
- void (*callback)( netmsg_request *res,
- vg_msg *body )){
+ void (*callback)(
+ netmsg_request *res, vg_msg *body,
+ u64 userdata),
+ u64 userdata ){
u32 len = 0;
if( body ){
len = body->cur.co;
vg_pool_item( &network_client.request_pool, req->id );
pn->callback = callback;
pn->sendtime = vg.time_real;
+ pn->userdata = userdata;
}
else{
vg_error( "Unable to send request. Pool is full.\n" );
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, "" );
+static void network_scoreboard_callback( netmsg_request *res, vg_msg *body,
+ u64 userdata ){
+ world_instance *world = world_current_instance();
- 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;
- }
+ world_routes_recv_scoreboard( world, body, userdata, res->status );
+ if( userdata == world_sfd.active_route_board )
+ world_sfd_compile_active_scores();
+}
- u32 l = 0;
- if( vg_msg_seekframe( body, "rows" ) ){
- while( vg_msg_seekframe( body, NULL ) ){
- const char *username = vg_msg_getkvstr( body, "username" );
- if( username )
- sfd_encode( l ++, username );
- else
- sfd_encode( l ++, "UNKNOWN USER" );
-
- vg_msg_skip_frame( body );
- }
- }
-}
/* mod_uid: world mod uid,
* route_uid: run name (just a string)
- * week: 0 for all-time, n for week #
+ * week:
+ * 0 ALL TIME
+ * 1 CURRENT WEEK
+ * 2 ALL TIME + CURRENT WEEK
+ * .
+ * 10+ specific week index
*/
static void network_request_scoreboard( const char *mod_uid,
const char *route_uid,
- u32 week ){
+ u32 week, u64 userdata ){
if( !network_client.remote )
return;
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 );
+ network_send_request( req, &data, network_scoreboard_callback, userdata );
}
-static void network_publish_callback( netmsg_request *res, vg_msg *body ){
+static void network_publish_callback( netmsg_request *res, vg_msg *body,
+ u64 userdata ){
if( res->status != k_request_status_ok ){
vg_error( "Publish laptime, server error #%d\n", (i32)res->status );
}
vg_msg_wkvstr( &data, "mod", mod_uid );
vg_msg_wkvstr( &data, "route", route_uid );
vg_msg_wkvi32( &data, "time", time_centiseconds );
- network_send_request( req, &data, network_publish_callback );
+ network_send_request( req, &data, network_publish_callback, 0 );
}
static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
if( res->id ){
struct network_request *pn =
vg_pool_item( &network_client.request_pool, res->id );
- pn->callback( res, body );
+ pn->callback( res, body, pn->userdata );
vg_pool_unwatch( &network_client.request_pool, res->id );
}
}
hSteamNetworkingSockets, &remoteAddr, 0, NULL );
}
+#if 0
static void on_inet_scoreboard( SteamNetworkingMessage_t *msg ){
netmsg_scoreboard *sb = msg->m_pData;
network_scores_updated = 1;
}
+#endif
static void poll_remote_connection(void){
SteamNetworkingMessage_t *messages[32];
#define NETWORK_MAX_REQUESTS 8
-static int network_scores_updated = 0;
-
/*
* Interface
*/
struct network_request {
vg_pool_node poolnode;
- void (*callback)( netmsg_request *res, vg_msg *body );
+ void (*callback)( netmsg_request *res, vg_msg *body, u64 userdata );
f64 sendtime;
+ u64 userdata;
}
*request_buffer;
vg_pool request_pool;
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 );
+ u32 week, u64 userdata );
static void network_publish_laptime( const char *mod_uid,
const char *route_uid, f64 lap_time );
#define NETWORK_MAX_PLAYERS 20
#define NETWORK_FRAMERATE 0.1
#define NETWORK_BUFFERFRAMES 6
+#define NETWORK_LEADERBOARD_MAX_SIZE 1024
+
+#define NETWORK_LEADERBOARD_ALLTIME 0
+#define NETWORK_LEADERBOARD_CURRENT_WEEK 1
+#define NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK 2
#include "addon_types.h"
};
enum{ k_inetmsg_auth = 1 };
-typedef struct netmsg_scores_request netmsg_scores_request;
-struct netmsg_scores_request
-{
- u16 inetmsg_id;
-};
-enum{ k_inetmsg_scores_request = 2 };
-
-typedef struct netmsg_set_score netmsg_set_score;
-struct netmsg_set_score
-{
- u16 inetmsg_id;
-
- u32 record_count;
- struct netmsg_score_record
- {
- u32 trackid;
- u64 playerid;
- u16 points, time;
- }
- records[];
-};
-enum{ k_inetmsg_set_score = 6 };
-/* 31.05.23: k_inetmsg_set_score id changed from ID 3 to ID 6,
- * 3 is now INVALID */
-
-typedef struct netmsg_scoreboard netmsg_scoreboard;
-enum{ k_inetmsg_scoreboard = 5 };
-struct netmsg_scoreboard{
- u16 inetmsg_id;
-
- u32 board_count;
- struct netmsg_board
- {
- char data[27*13];
- }
- boards[ vg_list_size(track_infos) ];
-}
-static scoreboard_client_data = {
- .inetmsg_id = k_inetmsg_scoreboard,
- .board_count = vg_list_size(track_infos)
-};
-/* probably about 10k */
-
/* server control 100 */
-
/* player updates 200 */
#define NETMSG_BOUNDARY_BIT 0x8000
#include "world_routes.c"
#include "world_traffic.c"
-static void world_update( world_instance *world, v3f pos )
-{
+static void world_update( world_instance *world, v3f pos ){
world_render.sky_time += world_render.sky_rate * vg.time_delta;
world_render.sky_rate = vg_lerp( world_render.sky_rate,
world_render.sky_target_rate,
#define WORLD_H
#include "render.h"
+#include "network_msg.h"
/* types
*/
k_world_max
};
+struct leaderboard_cache {
+ enum request_status status;
+ f64 cache_time;
+ u8 *data;
+ u32 data_len;
+};
+
typedef struct world_instance world_instance;
static void skaterift_world_get_save_path( enum world_purpose which,
u32 cubemap_cooldown, cubemap_side;
rb_object rb_geo;
+
+ /* leaderboards */
+ struct leaderboard_cache *leaderboard_cache;
};
struct world_static {
world_gen_compute_light_indices( world );
mdl_close( meta );
+ /* allocate leaderboard buffers */
+ u32 bs = mdl_arrcount(&world->ent_route)*sizeof(struct leaderboard_cache);
+ world->leaderboard_cache = vg_linear_alloc( heap, bs );
+
+ for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i ++ ){
+ struct leaderboard_cache *board = &world->leaderboard_cache[i];
+ board->data = vg_linear_alloc( heap, NETWORK_LEADERBOARD_MAX_SIZE );
+ board->status = k_request_status_client_error;
+ board->cache_time = 0.0;
+ board->data_len = 0;
+ }
+
vg_async_call( async_world_postprocess, world, 0 );
vg_async_stall();
}
#include "pointcloud.h"
#include "gui.h"
#include "steam.h"
+#include "network_msg.h"
+#include "network_common.h"
#include "shaders/scene_route.h"
#include "shaders/routeui.h"
world_routes_clear( world );
}
+static void world_routes_recv_scoreboard( world_instance *world,
+ vg_msg *body, u32 route_id,
+ enum request_status status ){
+ if( route_id >= mdl_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 )
+ board->data_len = 0;
+
+ if( body->max > NETWORK_LEADERBOARD_MAX_SIZE ){
+ vg_error( "Scoreboard leaderboard too big (%u>%u)\n", body->max,
+ NETWORK_LEADERBOARD_MAX_SIZE );
+ return;
+ }
+
+ memcpy( board->data, body->buf, body->max );
+ board->data_len = body->max;
+}
+
/*
* -----------------------------------------------------------------------------
* Events
* -----------------------------------------------------------------------------
*/
-static void world_routes_init(void)
-{
+static void world_routes_init(void){
world_static.current_run_version = 200;
world_static.time = 300.0;
world_static.last_use = 0.0;
shader_routeui_register();
}
-static void world_routes_update( world_instance *world )
-{
+static void world_routes_update( world_instance *world ){
world_static.time += vg.time_delta;
for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
#define ROUTES_H
#include "world.h"
+#include "network_msg.h"
static void world_routes_init(void);
static void world_routes_fracture( world_instance *world, ent_gate *gate,
static void world_routes_update( world_instance *world );
static void world_routes_fixedupdate( world_instance *world );
static void world_routes_clear( world_instance *world );
+static void world_routes_recv_scoreboard( world_instance *world,
+ vg_msg *body, u32 route_id,
+ enum request_status status );
#endif /* ROUTES_H */
#include "shaders/scene_vertex_blend.h"
#include "network.h"
#include "entity.h"
+#include "network_common.h"
+#include "world_routes.h"
static f32 sfd_encode_glyph( char c ){
int value = 0;
}
}
+static void world_sfd_compile_scores( struct leaderboard_cache *board ){
+ for( u32 i=0; i<13; i++ )
+ sfd_encode( i, "" );
+
+ if( !board ){
+ sfd_encode( 4, "Error out of range" );
+ return;
+ }
+
+ if( !network_client.remote ){
+ sfd_encode( 4, "Offline" );
+ return;
+ }
+
+ if( board->status == k_request_status_not_found ){
+ sfd_encode( 4, "No records" );
+ return;
+ }
+
+ if( board->status != k_request_status_ok ){
+ char buf[32];
+ vg_str s;
+ vg_strnull( &s, buf, 32 );
+ vg_strcat( &s, "Error: " );
+ vg_strcati32( &s, board->status );
+ sfd_encode( 4, buf );
+ return;
+ }
+
+ vg_msg body;
+ vg_msg_init( &body, board->data, board->data_len );
+
+ const char *alias = "rows";
+
+ if( world_sfd.view_weekly ){
+ alias = "rows_weekly";
+ sfd_encode( 0, "Weekly" );
+ }
+ else {
+ sfd_encode( 0, "All-Time" );
+ }
+
+ u32 l = 1;
+ if( vg_msg_seekframe( &body, alias ) ){
+ while( vg_msg_seekframe( &body, NULL ) ){
+ const char *username = vg_msg_getkvstr( &body, "username" );
+
+ if( username )
+ sfd_encode( l ++, username );
+ else
+ sfd_encode( l ++, "UNKNOWN USER" );
+
+ vg_msg_skip_frame( &body );
+ }
+ }
+ else {
+ sfd_encode( 4, "No records" );
+ }
+}
+
+static void world_sfd_compile_active_scores(void){
+ world_instance *world = world_current_instance();
+
+ struct leaderboard_cache *board = NULL;
+
+ if( world_sfd.active_route_board < mdl_arrcount( &world->ent_route ) )
+ board = &world->leaderboard_cache[ world_sfd.active_route_board ];
+
+ world_sfd_compile_scores( board );
+}
+
static void world_sfd_update( world_instance *world, v3f pos ){
if( mdl_arrcount( &world->ent_route ) ){
u32 closest = 0;
}
}
- 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 );
+ struct leaderboard_cache *board = &world->leaderboard_cache[ closest ];
- addon_reg *world_reg =
- world_static.instance_addons[ world_static.active_instance ];
+ /* request new board if cache expires */
+ if( network_client.remote ){
+ f64 delta = vg.time_real - board->cache_time;
+ if( (delta > 45.0) || (board->cache_time == 0.0) ){
+ board->cache_time = vg.time_real;
+ ent_route *route = mdl_arritm( &world->ent_route, closest );
+ addon_reg *world_reg =
+ world_static.instance_addons[ world - world_static.instances ];
- char mod_uid[ ADDON_UID_MAX ];
- addon_alias_uid( &world_reg->alias, mod_uid );
+ 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 );
+ network_request_scoreboard(
+ mod_uid,
+ mdl_pstr( &world->meta, route->pstr_name ),
+ NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK, closest );
+ }
}
+
+ /* compile board text if we changed. */
+ if( world_sfd.active_route_board != closest ){
+ world_sfd_compile_active_scores();
+ }
+
+ world_sfd.active_route_board = closest;
}
for( int i=0; i<world_sfd.w*world_sfd.h; i++ ){
#define SFD_H
#include "world.h"
+#include "world_routes.h"
struct world_sfd{
GLuint tex_scoretex;
glmesh mesh_base, mesh_display;
mdl_submesh sm_base;
+
u32 active_route_board;
scene_context scene;
+ u32 view_weekly;
+
u32 w, h;
float *buffer;
}
static void sfd_encode( u32 row, const char *str );
static void sfd_render( world_instance *world, camera *cam,
m4x3f transform );
+static void world_sfd_compile_scores( struct leaderboard_cache *leaderboard );
+static void world_sfd_compile_active_scores(void);
#endif /* SFD_H */