From c842298de5c6d85d228b856da531a48c90379dbf Mon Sep 17 00:00:00 2001 From: hgn Date: Thu, 10 Apr 2025 21:15:21 +0100 Subject: [PATCH] various stuff --- src/addon.c | 30 ++-- src/addon.h | 3 +- src/ent_route.c | 20 +-- src/gameserver.c | 7 +- src/gameserver_database.c | 16 +-- src/gameserver_database.h | 2 +- src/gameserver_replay.c | 142 +++++++++++-------- src/gameserver_replay.h | 5 +- src/gameserver_requests.c | 33 +++-- src/network_requests.c | 3 + src/player.c | 6 + src/player_remote.c | 288 ++++++++++++++++++++++++------------- src/player_remote.h | 6 + src/player_render.c | 6 +- src/player_replay.c | 3 +- src/replay2.c | 290 +++++++++++++++++++++++++++++++++----- src/replay2.h | 20 +++ src/save.c | 17 +-- src/world_render.c | 2 + 19 files changed, 628 insertions(+), 271 deletions(-) diff --git a/src/addon.c b/src/addon.c index 347ad36..2ac69d1 100644 --- a/src/addon.c +++ b/src/addon.c @@ -741,14 +741,16 @@ void *addon_cache_item_arena( enum addon_type type, u16 id ) */ void *addon_cache_item_if_loaded( enum addon_type type, u16 id ) { - if( !id ) return NULL; + if( !id ) + return NULL; struct addon_cache *cache = &addon_system.cache[type]; struct addon_cache_entry *entry = vg_pool_item( &cache->pool, id ); if( entry->state == k_addon_cache_state_loaded ) return addon_cache_item( type, id ); - else return NULL; + else + return NULL; } /* @@ -846,8 +848,7 @@ static void T1_addon_cache_load_loop(void *_) SDL_LockMutex( addon_system.cache_lock ); if( entry->state == k_addon_cache_state_load_request ) { - vg_info( "process cache load request (%u#%u, reg:%u)\n", - type, id, entry->reg_index ); + vg_info( "process cache load request (%u#%u, reg:%u)\n", type, id, entry->reg_index ); if( entry->reg_index >= addon_count(type,0,0) ) { @@ -869,8 +870,7 @@ static void T1_addon_cache_load_loop(void *_) { if( addon_cache_load_request( type, id, reg, folder ) ) { - vg_async_call( async_addon_setstate, - entry, k_addon_cache_state_loaded ); + vg_async_call( async_addon_setstate, entry, k_addon_cache_state_loaded ); continue; } } @@ -942,22 +942,23 @@ u16 addon_cache_create_viewer( enum addon_type type, u16 reg_id ) return cache_id; } -u16 addon_cache_create_viewer_from_uid( enum addon_type type, - char uid[ADDON_UID_MAX] ) +u16 addon_cache_create_viewer_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] ) { addon_alias q; - if( !addon_uid_to_alias( uid, &q ) ) return 0; - if( q.type != type ) return 0; + if( !addon_uid_to_alias( uid, &q ) ) + return 0; + if( q.type != type ) + return 0; u32 reg_id = addon_match( &q ); - if( reg_id == 0xffffffff ){ + if( reg_id == 0xffffffff ) + { vg_warn( "We dont have the addon '%s' installed.\n", uid ); return 0; } - else { + else return addon_cache_create_viewer( type, reg_id ); - } } void addon_cache_watch( enum addon_type type, u16 cache_id ) @@ -971,7 +972,8 @@ void addon_cache_watch( enum addon_type type, u16 cache_id ) void addon_cache_unwatch( enum addon_type type, u16 cache_id ) { - if( !cache_id ) return; + if( !cache_id ) + return; struct addon_cache *cache = &addon_system.cache[type]; vg_pool *pool = &cache->pool; diff --git a/src/addon.h b/src/addon.h index 8725994..c65524b 100644 --- a/src/addon.h +++ b/src/addon.h @@ -91,5 +91,4 @@ u16 addon_cache_create_viewer( enum addon_type type, u16 reg_id); void addon_cache_watch( enum addon_type type, u16 cache_id ); void addon_cache_unwatch( enum addon_type type, u16 cache_id ); -u16 addon_cache_create_viewer_from_uid( enum addon_type type, - char uid[ADDON_UID_MAX] ); +u16 addon_cache_create_viewer_from_uid( enum addon_type type, const char uid[ADDON_UID_MAX] ); diff --git a/src/ent_route.c b/src/ent_route.c index ad19724..7dd48d0 100644 --- a/src/ent_route.c +++ b/src/ent_route.c @@ -108,39 +108,33 @@ void ent_route_preupdate(void) u32 centiseconds; vg_msg_getkvintg( &body, "time", k_vg_msg_u32, ¢iseconds, NULL ); - u32 lastmin; - vg_msg_getkvintg( &body, "lastmin", k_vg_msg_u32, &lastmin, NULL ); + i64 lastsec; + vg_msg_getkvintg( &body, "lastsec", k_vg_msg_i64, &lastsec, NULL ); u64 steamid; vg_msg_getkvintg( &body, "steamid", k_vg_msg_u64, &steamid, NULL ); + u32 last_minute = (u32)( lastsec / 60 ); + // TODO: put this in a header u32 minutes_span = (centiseconds+(200*60)) / (100*60); - vg_queue_clear( &_remote_replay.replay.buffer ); _remote_replay.min_frame_t = 0.0; _remote_replay.total_chunks = minutes_span; _remote_replay.chunks_downloaded = 0; _remote_replay.steamid = steamid; - _remote_replay.state = k_remote_replay_state_downloading; + _remote_replay.state = k_remote_replay_state_init; + _remote_replay.last_second = lastsec; for( u32 i=0; iminute = lastmin - minutes_span + 1 + i; + chunk->minute = last_minute - minutes_span + 1 + i; chunk->state = k_chunk_state_none; - - // TODO: cache wrapper - // - // reset_replay_cache( span, callback_when_complete, callback_when_fail ) - // get_cache_remote_replay( ... ) x3 - // - //network_download_replay( steamid, want_minute ); } // TODO: UI overlay for downlaoding sfd_encode( (v2i){0,6}, "Downloading ...", k_world_sfd_center ); - break; } else diff --git a/src/gameserver.c b/src/gameserver.c index 307e2b6..d73dc4a 100644 --- a/src/gameserver.c +++ b/src/gameserver.c @@ -25,7 +25,7 @@ struct _gameserver _gameserver = .auth_mode = eServerModeAuthentication, .tasks = { - .upper_memory_limit = 1024*1024*8 + .buffer_size = 1024*1024*8 } }; @@ -623,10 +623,6 @@ static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg ) //_gs_start_transfer( client_id, "/tmp/server-replay.replay" ); //_gs_test_replay( client_id, "/tmp/server-replay.replay" ); } - else if( !strcmp( prop->msg, "$savemin" ) ) - { - _gs_replay_request_save( client_id, client->steamid, _gs_replay.current_minute, 2, 0 ); - } gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l+1, k_nSteamNetworkingSend_Reliable ); } @@ -768,6 +764,7 @@ int main( int argc, char *argv[] ) goto E0; _gs_requests_init(); + _gs_replay_init(); /* steamworks init * --------------------------------------------------------------- */ diff --git a/src/gameserver_database.c b/src/gameserver_database.c index 01acb5f..479f263 100644 --- a/src/gameserver_database.c +++ b/src/gameserver_database.c @@ -5,7 +5,7 @@ struct _gs_db _gs_db = { .tasks = { - .upper_memory_limit = 1024*1024*40 + .buffer_size = 1024*1024*20 } }; @@ -196,7 +196,7 @@ i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ) return 0; } -bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster, i32 last_minute ) +bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, i64 last_second ) { THREAD_1; @@ -207,7 +207,7 @@ bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, boo 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, lastmin INT);" ); + vg_strcat( &q, "\"\n (steamid BIGINT UNIQUE, time INT, lastsec BIGINT);" ); if( !vg_strgood(&q) ) return 0; sqlite3_stmt *create_table = db_stmt( q.buffer ); @@ -235,7 +235,6 @@ bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, boo sqlite3_step( s2 ); sqlite3_finalize( s2 ); } -#endif if( only_if_faster ) { @@ -243,13 +242,14 @@ bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, boo if( (current != 0) && (score > current) ) return 1; } +#endif /* insert score * -------------------------------------------------*/ vg_strnull( &q, buf, 512 ); vg_strcat( &q, "REPLACE INTO \"" ); vg_strcat( &q, table ); - vg_strcat( &q, "\"(steamid,time,lastmin)\n VALUES (?,?,?);" ); + vg_strcat( &q, "\"(steamid,time,lastsec)\n VALUES (?,?,?);" ); if( !vg_strgood(&q) ) return 0; @@ -259,7 +259,7 @@ bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, boo { sqlite3_bind_int64( stmt, 1, *((i64 *)&steamid) ); sqlite3_bind_int( stmt, 2, score ); - sqlite3_bind_int( stmt, 3, last_minute ); + sqlite3_bind_int64( stmt, 3, last_second ); int fc = sqlite3_step( stmt ); sqlite3_finalize( stmt ); @@ -402,7 +402,7 @@ enum request_status gameserver_cat_table( vg_msg *msg, const char *mod, const ch if( fc == SQLITE_ROW ) { i32 time = sqlite3_column_int( stmt, 1 ); - i32 last_minute = sqlite3_column_int( stmt, 2 ); + i64 last_second = sqlite3_column_int64( stmt, 2 ); i64 steamid_i64 = sqlite3_column_int64( stmt, 0 ); u64 steamid = *((u64 *)&steamid_i64); @@ -412,7 +412,7 @@ enum request_status gameserver_cat_table( vg_msg *msg, const char *mod, const ch 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 ); - vg_msg_wkvnum( msg, "lastmin", k_vg_msg_u32, 1, &last_minute ); + vg_msg_wkvnum( msg, "lastsec", k_vg_msg_i64, 1, &last_second ); char username[32]; if( db_getuserinfo( steamid, username, sizeof(username), NULL ) ) diff --git a/src/gameserver_database.h b/src/gameserver_database.h index 091c6e5..e9ab0c4 100644 --- a/src/gameserver_database.h +++ b/src/gameserver_database.h @@ -32,7 +32,7 @@ extern _gs_db; void db_action_set_username( u64 steamid, const char *username ); bool db_get_highscore_table_name( const char *mod_uid, const char *run_uid, u32 week, char table_name[DB_TABLE_UID_MAX] ); i32 db_readusertime( char table[DB_TABLE_UID_MAX], u64 steamid ); -bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, bool only_if_faster, i32 last_minute ); +bool db_writeusertime( char table[DB_TABLE_UID_MAX], u64 steamid, i32 score, i64 last_second ); #if 0 /* diff --git a/src/gameserver_replay.c b/src/gameserver_replay.c index 222e16a..ec87189 100644 --- a/src/gameserver_replay.c +++ b/src/gameserver_replay.c @@ -3,11 +3,6 @@ struct _gs_replay _gs_replay; -u32 _gs_replay_get_minute(void) -{ - return time(NULL) / 60; -} - struct serialized_replay { u64 steamid; @@ -139,41 +134,58 @@ void _gs_replay_client_disconnect( u32 client_id ) vg_queue_clear( &dest_replay->ring_buffer ); } +struct replay_descriptor_info +{ + i64 last_second; + u64 steamid; + + u32 message_length; + u8 message_buffer[]; +}; +static void _gs_replay_write_descriptor( vg_async_task *task ) +{ + struct replay_descriptor_info *info = (void *)task->data; + + char path[1024]; + snprintf( path, sizeof(path), "replaydata/%lx@%lx.bkv", info->steamid, info->last_second ); + + FILE *fp = fopen( path, "wb" ); + if( !fp ) + { + vg_error( "Can't open '%s'\n", path ); + return; + } + fwrite( info->message_buffer, info->message_length, 1, fp ); + fclose( fp ); + vg_info( "descriptor written to %s\n", path ); +} + struct request_save_info { u32 client_id; u64 steam_id; - u32 start_minute, length; + u32 centiseconds; + i64 last_second; }; -void _gs_replay_request_save( u32 client_id, u64 steamid, u32 start_minute, u32 length, bool async ); +void _gs_replay_request_save( u32 client_id, u64 steamid, i64 last_second, u32 centiseconds, bool async ); static void _gs_replay_async_request_save( vg_async_task *task ) { struct request_save_info *info = (void *)task->data; - _gs_replay_request_save( info->client_id, info->steam_id, info->start_minute, info->length, 0 ); + _gs_replay_request_save( info->client_id, info->steam_id, info->last_second, info->centiseconds, 0 ); } -void _gs_replay_request_save( u32 client_id, u64 steamid, u32 start_minute, u32 length, bool async ) +void _gs_replay_request_save( u32 client_id, u64 steamid, i64 last_second, u32 centiseconds, bool async ) { if( async ) { THREAD_1; - /* C0: In the future, this is like just storing all the call parameters to a struct, and then recalling the - * function later. Really, we could auto-generate such patterns. - * - * void( u32 id ) - * { - * #c0_recall( thread0 ); - * - * .. from here on out we are thread0 - * } - */ vg_async_task *task = vg_allocate_async_task( &_gameserver.tasks, sizeof(struct request_save_info), 1 ); struct request_save_info *info = (void *)task->data; info->client_id = client_id; info->steam_id = steamid; - info->start_minute = start_minute; - info->length = length; + info->last_second = last_second; + info->centiseconds = centiseconds; task->handler = _gs_replay_async_request_save; vg_async_task_dispatch( &_gameserver.tasks, task ); return; @@ -181,24 +193,57 @@ void _gs_replay_request_save( u32 client_id, u64 steamid, u32 start_minute, u32 THREAD_0; - if( _gameserver.clients[ client_id ].steamid != steamid ) + struct gameserver_client *client = &_gameserver.clients[ client_id ]; + if( client->steamid != steamid ) { /* Probably very rare */ vg_error( "Tried to request save for a client, but the steamid doesnt match from when it was called.\n" ); return; } + u32 start_minute = last_second/60, + minutes_span = (centiseconds+(200*60)) / (100*60); + gs_replay *replay = &_gs_replay.replays[ client_id ]; - for( u32 i=0; iminute_flags[ minute_index ] |= k_replay_minute_save_request; } + + vg_info( "Preparing descriptor file\n" ); + u32 descriptor_size = 1024; + vg_async_task *replay_descriptor_task = + vg_allocate_async_task( &_gs_db.tasks, sizeof(struct replay_descriptor_info) + descriptor_size, 1 ); + struct replay_descriptor_info *descriptor_info = (void *)replay_descriptor_task->data; + + vg_msg msg; + vg_msg_init( &msg, descriptor_info->message_buffer, descriptor_size ); + vg_msg_wkvnum( &msg, "centiseconds", k_vg_msg_u32, 1, ¢iseconds ); + u32 end_offset = (u32)((((i64)start_minute+1) * 60) - last_second); + vg_msg_wkvnum( &msg, "end_offset", k_vg_msg_u32, 1, &end_offset ); + vg_msg_wkvstr( &msg, "world", client->items[ k_netmsg_playeritem_world0 ].uid ); + + vg_msg_frame( &msg, "playerinfo" ); + { + vg_msg_wkvnum( &msg, "steamid", k_vg_msg_u64, 1, &steamid ); + vg_msg_wkvstr( &msg, "board", client->items[ k_netmsg_playeritem_board ].uid ); + vg_msg_wkvstr( &msg, "player", client->items[ k_netmsg_playeritem_player ].uid ); + } + vg_msg_end_frame( &msg ); + descriptor_info->message_length = msg.cur.co; + descriptor_info->last_second = last_second; + descriptor_info->steamid = steamid; + + vg_info( "..1\n" ); + replay_descriptor_task->handler = _gs_replay_write_descriptor; + vg_async_task_dispatch( &_gs_db.tasks, replay_descriptor_task ); + vg_info( "...\n" ); } void _gs_replay_server_tick(void) { - u32 new_minute = _gs_replay_get_minute(); + u32 new_minute = time(NULL)/60; if( new_minute > _gs_replay.current_minute ) { @@ -215,6 +260,17 @@ void _gs_replay_server_tick(void) } } +void _gs_replay_init(void) +{ + for( u32 i=0; iring_buffer; + ring->buffer = malloc( REPLAY_MEMORY_SIZE ); + ring->size = REPLAY_MEMORY_SIZE; + } +} + void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_size ) { gs_replay *dest_replay = &_gs_replay.replays[ client_id ]; @@ -256,31 +312,12 @@ void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_s } dest_frame = vg_queue_alloc( ring, total_size, &dest_item_id ); - if( !dest_frame ) - { - /* policy 3: grow (double size) */ - u32 cur_size = ring->size; - if( cur_size < 1024 ) - cur_size = 1024; - u32 new_size = cur_size * 2; - - if( new_size > REPLAY_SIZE_LIMIT ) - new_size = REPLAY_SIZE_LIMIT; - - 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 ); - } - } + if( !dest_frame ) + { + vg_error( "Unable to allocate replay frame for player #%u\n", (u32)client_id ); + return; } memcpy( &dest_frame->network_frame, frame, frame_size ); @@ -291,12 +328,3 @@ void _gs_replay_save_frame( u8 client_id, netmsg_playerframe *frame, u32 frame_s dest_frame->minute = _gs_replay.current_minute; dest_replay->minute_flags[ _gs_replay.current_minute % REPLAY_MINUTE_COUNT ] |= k_replay_minute_has_data; } - -#if 0 - -u64 _gs_write_replay_to_disk( u8 client_id, f64 server_duration ) -{ - THREAD_0; - -} -#endif diff --git a/src/gameserver_replay.h b/src/gameserver_replay.h index 327c25c..247c0dd 100644 --- a/src/gameserver_replay.h +++ b/src/gameserver_replay.h @@ -1,7 +1,7 @@ #pragma once #include "network_msg.h" -#define REPLAY_SIZE_LIMIT 1024*1024*2 +#define REPLAY_MEMORY_SIZE 1024*1024*2 #define REPLAY_MINUTE_COUNT 8 typedef struct gs_replay_frame gs_replay_frame; @@ -48,4 +48,5 @@ void _gs_transfer_tick(void); u32 _gs_replay_get_minute(void); void _gs_replay_client_disconnect( u32 client_id ); -void _gs_replay_request_save( u32 client_id, u64 steamid, u32 start_minute, u32 length, bool async ); +void _gs_replay_request_save( u32 client_id, u64 steamid, i64 last_second, u32 centiseconds, bool async ); +void _gs_replay_init(void); diff --git a/src/gameserver_requests.c b/src/gameserver_requests.c index 93b1414..d77b0aa 100644 --- a/src/gameserver_requests.c +++ b/src/gameserver_requests.c @@ -291,13 +291,26 @@ static void task_request_run( vg_async_task *task ) if( steamid == 0 ) steamid = req->user_steamid; + char path[1024]; + u32 minute; vg_msg_getkvintg( &client_msg, "minute", k_vg_msg_u32, &minute, NULL ); - if( minute == 0 ) - minute = _gs_replay_get_minute()-1; - - char path[1024]; - snprintf( path, sizeof(path), "replaydata/%lx@%x", steamid, minute ); + if( minute ) + snprintf( path, sizeof(path), "replaydata/%lx@%x", steamid, minute ); + else + { + i64 last_second; + vg_msg_getkvintg( &client_msg, "lastsec", k_vg_msg_i64, &last_second, NULL ); + if( last_second ) + { + snprintf( path, sizeof(path), "replaydata/%lx@%lx.bkv", steamid, last_second ); + } + else + { + req->status = k_request_status_client_error; + goto E0; + } + } FILE *fp = fopen( path, "rb" ); if( !fp ) @@ -346,18 +359,14 @@ static void task_request_run( vg_async_task *task ) goto E0; } - u32 last_minute = 0; i32 current_time = db_readusertime( table, req->user_steamid ); if( (current_time == 0) || (centiseconds < current_time) ) { - u32 current_minute = _gs_replay_get_minute(), - minutes_span = (centiseconds+(200*60)) / (100*60); - _gs_replay_request_save( req->client_id, req->user_steamid, current_minute, minutes_span, 1 ); - - last_minute = current_minute; + i64 last_second = time(NULL); + _gs_replay_request_save( req->client_id, req->user_steamid, last_second, centiseconds, 1 ); + db_writeusertime( table, req->user_steamid, centiseconds, last_second ); } - db_writeusertime( table, req->user_steamid, centiseconds, 1, last_minute ); //db_writeusertime( weekly_table, req->user_steamid, centiseconds, 1, last_minute ); } else diff --git a/src/network_requests.c b/src/network_requests.c index 924a93b..5f76a88 100644 --- a/src/network_requests.c +++ b/src/network_requests.c @@ -160,6 +160,9 @@ void network_publish_laptime( const char *mod_uid, const char *route_uid, f64 la 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 ); + + f64 time_real = vg.time_real; + vg_msg_wkvnum( &data, "end", k_vg_msg_f64, 1, &time_real ); network_send_request( packet, &data, NULL, 0 ); } diff --git a/src/player.c b/src/player.c index a34705e..c868191 100644 --- a/src/player.c +++ b/src/player.c @@ -235,11 +235,17 @@ void player__pass_gate( u32 id ) skaterift_record_frame( &player_replay.local, 1 ); + /* I genuinely have no idea why this code was like it was, I don't think it's right. Maybe an artifact of editing + * this area some time ago. Keeping it but zero'd out in case I remember in the distant futre. */ +#if 0 /* update boundary hash (network animation) */ u16 index = mdl_entity_id_id(id) & ~NETMSG_BOUNDARY_MASK; localplayer.boundary_hash ^= NETMSG_GATE_BOUNDARY_BIT; localplayer.boundary_hash &= ~NETMSG_BOUNDARY_MASK; localplayer.boundary_hash |= index; +#else + localplayer.boundary_hash ^= NETMSG_GATE_BOUNDARY_BIT; +#endif localplayer.gate_waiting = gate; localplayer.deferred_frame_record = 1; diff --git a/src/player_remote.c b/src/player_remote.c index dcbd849..76d0bf3 100644 --- a/src/player_remote.c +++ b/src/player_remote.c @@ -61,6 +61,83 @@ void player_remote_update_friendflags( struct network_player *remote ) remote->steamid, k_EFriendFlagBlocked ); } +void decode_playerframe( netmsg_playerframe *frame, u32 data_length, struct interp_frame *dest ) +{ + dest->active = 1; + dest->subsystem = frame->subsystem; + dest->flags = frame->flags; + + bitpack_ctx ctx = + { + .mode = k_bitpack_decompress, + .buffer = frame->animdata, + .buffer_len = data_length, + .bytes = 0, + }; + + /* camera */ + v2f _null_v2f = {0,0}; + bitpack_qv2f( &ctx, 8, 0.0f, VG_TAUf, _null_v2f ); + + /* animation + * -------------------------------------------------------------*/ + dest->timestamp = frame->timestamp; + dest->boundary_hash = frame->boundary_hash; + + struct network_player *player = &netplayers.list[ frame->client ]; + struct player_subsystem_interface *sys = player_subsystems[ frame->subsystem ]; + + memset( &dest->data, 0, sys->animator_size ); + if( sys->network_animator_exchange ) + sys->network_animator_exchange( &ctx, &dest->data ); + else + bitpack_bytes( &ctx, sys->animator_size, sys->animator_data ); + + /* sfx + * -------------------------------------------------------------*/ + for( u32 i=0; isound_effects; i ++ ) + { + struct net_sfx sfx; + net_sfx_exchange( &ctx, &sfx ); + +#if 0 + f64 t = (frame->timestamp - NETWORK_FRAMERATE) + (sfx.subframe*NETWORK_FRAMERATE); + f32 remaining = t - ib->t; + + if( remaining <= 0.0f ) + net_sfx_play( &sfx ); + else + { + struct net_sfx *dst = NULL; + for( u32 j=0; jsystem == k_player_subsystem_invalid ) + { + dst = sj; + break; + } + + if( sj->priority < sfx.priority ) + dst = sj; + } + + *dst = sfx; + dst->subframe = remaining; + } +#endif + } + + /* glider + * -------------------------------------------------------------*/ + + memset( &dest->data_glider, 0, sizeof(struct remote_glider_animator) ); + if( dest->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ) + { + player_glide_remote_animator_exchange( &ctx, &dest->data_glider ); + } +} + void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ) { netmsg_blank *tmp = msg->m_pData; @@ -124,22 +201,25 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ) vg_error( "inetmsg_playerleave: player index out of range\n" ); } } - else if( tmp->inetmsg_id == k_inetmsg_playerframe ){ + else if( tmp->inetmsg_id == k_inetmsg_playerframe ) + { u32 datasize = msg->m_cbSize - sizeof(netmsg_playerframe); - - if( datasize > sizeof(union interp_animdata) ){ + if( datasize > sizeof(union interp_animdata) ) + { vg_error( "Player frame data exceeds animdata size\n" ); return; } netmsg_playerframe *frame = msg->m_pData; - if( frame->client >= VG_ARRAY_LEN(netplayers.list) ){ + if( frame->client >= VG_ARRAY_LEN(netplayers.list) ) + { vg_error( "inetmsg_playerframe: player index out of range\n" ); return; } - if( frame->subsystem >= k_player_subsystem_max ){ + if( frame->subsystem >= k_player_subsystem_max ) + { vg_error( "inetmsg_playerframe: subsystem out of range\n" ); return; } @@ -148,15 +228,18 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ) struct interp_frame *dest = NULL; f64 min_time = INFINITY; - for( u32 i=0; iframes); i++ ){ + for( u32 i=0; iframes); i++ ) + { struct interp_frame *ifr = &ib->frames[i]; - if( !ifr->active ){ + if( !ifr->active ) + { dest = ifr; break; } - if( ifr->timestamp < min_time ){ + if( ifr->timestamp < min_time ) + { min_time = ifr->timestamp; dest = ifr; } @@ -235,21 +318,25 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ) player_glide_remote_animator_exchange( &ctx, &dest->data_glider ); } + // FIXME: these frames might be out of order! Not good to set this player->subsystem = frame->subsystem; player->down_bytes += msg->m_cbSize; } - else if( tmp->inetmsg_id == k_inetmsg_playeritem ){ + else if( tmp->inetmsg_id == k_inetmsg_playeritem ) + { netmsg_playeritem *item = msg->m_pData; - if( !packet_minsize( msg, sizeof(*item)+1 )) return; + if( !packet_minsize( msg, sizeof(*item)+1 )) + return; - if( item->client >= VG_ARRAY_LEN(netplayers.list) ){ + if( item->client >= VG_ARRAY_LEN(netplayers.list) ) + { vg_error( "inetmsg_playerframe: player index out of range\n" ); return; } - if( item->type_index >= k_netmsg_playeritem_max ){ - vg_warn( "Client #%d invalid equip type %u\n", - (i32)item->client, (u32)item->type_index ); + if( item->type_index >= k_netmsg_playeritem_max ) + { + vg_warn( "Client #%d invalid equip type %u\n", (i32)item->client, (u32)item->type_index ); return; } @@ -263,27 +350,26 @@ void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ) struct network_player *player = &netplayers.list[ item->client ]; char *uid = player->items[ item->type_index ]; + network_msgstring( item->uid, msg->m_cbSize, sizeof(*item), uid, ADDON_UID_MAX ); - network_msgstring( item->uid, msg->m_cbSize, sizeof(*item), - uid, ADDON_UID_MAX ); - - if( item->type_index == k_netmsg_playeritem_board ){ + if( item->type_index == k_netmsg_playeritem_board ) + { addon_cache_unwatch( k_addon_type_board, player->board_view_slot ); - player->board_view_slot = - addon_cache_create_viewer_from_uid( k_addon_type_board, uid ); + player->board_view_slot = addon_cache_create_viewer_from_uid( k_addon_type_board, uid ); } - else if( item->type_index == k_netmsg_playeritem_player ){ - addon_cache_unwatch( k_addon_type_player, - player->playermodel_view_slot ); - player->playermodel_view_slot = - addon_cache_create_viewer_from_uid( k_addon_type_player, uid ); + else if( item->type_index == k_netmsg_playeritem_player ) + { + addon_cache_unwatch( k_addon_type_player, player->playermodel_view_slot ); + player->playermodel_view_slot = addon_cache_create_viewer_from_uid( k_addon_type_player, uid ); } else if( (item->type_index == k_netmsg_playeritem_world0) || - (item->type_index == k_netmsg_playeritem_world1) ){ + (item->type_index == k_netmsg_playeritem_world1) ) + { relink_remote_player_worlds( item->client ); } } - else if( tmp->inetmsg_id == k_inetmsg_chat ){ + else if( tmp->inetmsg_id == k_inetmsg_chat ) + { netmsg_chat *chat = msg->m_pData; struct network_player *player = &netplayers.list[ chat->client ]; @@ -390,98 +476,89 @@ static void remote_player_effect( struct network_player *player, /* * write the remote players final_mtx */ -static void pose_remote_player( u32 index, - struct interp_frame *f0, - struct interp_frame *f1 ){ - - struct network_player *player = &netplayers.list[ index ]; - - struct interp_buffer *buf = &netplayers.interp_data[ index ]; +void pose_remote_player( f64 pose_time, struct interp_frame *f0, struct interp_frame *f1, struct player_board *board, + + m4x3f *out_final_mtx, v3f *out_glider_mtx, + struct player_board_pose *out_board_pose, + struct player_effects_data *out_effects, + bool *out_render_glider ) +{ struct skeleton *sk = &localplayer.skeleton; - m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_count*index ]; - struct player_board_pose *board_pose = &netplayers.board_poses[index]; - struct player_subsystem_interface *sys0 = player_subsystems[f0->subsystem], *sys1 = NULL; - struct player_board *board = - addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); - player_pose pose0, pose1, posed; sys0->pose( &f0->data, &pose0 ); - f32 t = 0.0f; - if( f1 ) { - t = (buf->t - f0->timestamp) / (f1->timestamp - f0->timestamp); + t = (pose_time - f0->timestamp) / (f1->timestamp - f0->timestamp); t = vg_clampf( t, 0.0f, 1.0f ); sys1 = player_subsystems[f1->subsystem]; sys1->pose( &f1->data, &pose1 ); u16 bounds = f0->boundary_hash^f1->boundary_hash; - if( bounds & NETMSG_BOUNDARY_BIT ) t = 1.0f; - if( bounds & NETMSG_GATE_BOUNDARY_BIT ) { - /* TODO: Extra work retransforming the root_co, instance_id.. etc */ + /* NOTE: This could be interpolated more correctly instead of being a 'hard' boundary. + * Maybe just extrapolating up until the boundary cutoff? */ t = 1.0f; } lerp_player_pose( &pose0, &pose1, t, &posed ); - effect_blink_apply( &player->effect_data.blink, &posed, vg.time_delta ); - - apply_full_skeleton_pose( sk, &posed, final_mtx ); + effect_blink_apply( &out_effects->blink, &posed, vg.time_delta ); + apply_full_skeleton_pose( sk, &posed, out_final_mtx ); - if( t < 0.5f ){ + if( t < 0.5f ) + { if( sys0->effects ) - sys0->effects( &f0->data, final_mtx, board, &player->effect_data ); + sys0->effects( &f0->data, out_final_mtx, board, out_effects ); } - else{ + else + { if( sys1->effects ) - sys1->effects( &f1->data, final_mtx, board, &player->effect_data ); + sys1->effects( &f1->data, out_final_mtx, board, out_effects ); } - - memcpy( board_pose, &posed.board, sizeof(*board_pose) ); + memcpy( out_board_pose, &posed.board, sizeof(struct player_board_pose) ); } else { - effect_blink_apply( &player->effect_data.blink, &pose0, vg.time_delta ); - apply_full_skeleton_pose( sk, &pose0, final_mtx ); + effect_blink_apply( &out_effects->blink, &pose0, vg.time_delta ); + apply_full_skeleton_pose( sk, &pose0, out_final_mtx ); if( sys0->effects ) - sys0->effects( &f0->data, final_mtx, board, &player->effect_data ); - memcpy( board_pose, &pose0.board, sizeof(*board_pose) ); + sys0->effects( &f0->data, out_final_mtx, board, out_effects ); + memcpy( out_board_pose, &pose0.board, sizeof(struct player_board_pose) ); } - if( f0->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER| - NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ){ - player->render_glider = 1; - + if( f0->flags & (NETMSG_PLAYERFRAME_HAVE_GLIDER|NETMSG_PLAYERFRAME_GLIDER_ORPHAN) ) + { + *out_render_glider = 1; v3f co; v4f q; f32 s; - if( f1 ){ - v3_lerp( f0->data_glider.root_co, f1->data_glider.root_co, t, co ); - q_nlerp( f0->data_glider.root_q, f1->data_glider.root_q, t, q ); + if( f1 ) + { + v3_lerp( f0->data_glider.root_co,f1->data_glider.root_co, t, co ); + q_nlerp( f0->data_glider.root_q, f1->data_glider.root_q, t, q ); s = vg_lerpf( f0->data_glider.s, f1->data_glider.s, t ); } - else { + else + { v3_copy( f0->data_glider.root_co, co ); v4_copy( f0->data_glider.root_q, q ); s = f0->data_glider.s; } - v3f *mtx = netplayers.glider_mtx[ index ]; - q_m3x3( q, mtx ); - m3x3_scalef( mtx, s ); - v3_copy( co, mtx[3] ); + q_m3x3( q, out_glider_mtx ); + m3x3_scalef( out_glider_mtx, s ); + v3_copy( co, out_glider_mtx[3] ); } else - player->render_glider = 0; + *out_render_glider = 0; } /* @@ -501,32 +578,45 @@ void animate_remote_player( u32 index ) *abs_max_frame = NULL; struct interp_buffer *buf = &netplayers.interp_data[index]; - for( u32 i=0; iframes); i ++ ){ + for( u32 i=0; iframes); i ++ ) + { struct interp_frame *ifr = &buf->frames[i]; - if( ifr->active ){ - if( (ifr->timestamp > min_time) && (ifr->timestamp < buf->t) ){ + if( ifr->active ) + { + if( (ifr->timestamp > min_time) && (ifr->timestamp < buf->t) ) + { min_time = ifr->timestamp; minframe = ifr; } - if( (ifr->timestamp < max_time) && (ifr->timestamp > buf->t) ){ + if( (ifr->timestamp < max_time) && (ifr->timestamp > buf->t) ) + { max_time = ifr->timestamp; maxframe = ifr; } - if( ifr->timestamp > abs_max_time ){ + if( ifr->timestamp > abs_max_time ) + { abs_max_time = ifr->timestamp; abs_max_frame = ifr; } } } + // FIXME: Need to redo addon caching. board can be in race condition here! struct network_player *player = &netplayers.list[ index ]; + struct skeleton *sk = &localplayer.skeleton; + m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_count*index ]; + v3f *glider_mtx = netplayers.glider_mtx[ index ]; + struct player_board_pose *board_pose = &netplayers.board_poses[index]; + struct player_board *board = addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); + struct player_effects_data *effects = &player->effect_data; + bool *glider_flag = &player->render_glider; if( minframe && maxframe ) { - pose_remote_player( index, minframe, maxframe ); + pose_remote_player( buf->t, minframe, maxframe, board, final_mtx, glider_mtx, board_pose, effects, glider_flag ); buf->t += vg.time_frame_delta; } else @@ -534,7 +624,7 @@ void animate_remote_player( u32 index ) buf->t = abs_max_time - 0.25; if( abs_max_frame ) - pose_remote_player( index, abs_max_frame, NULL ); + pose_remote_player( buf->t, abs_max_frame, NULL, board, final_mtx, glider_mtx, board_pose, effects, glider_flag ); else return; } @@ -545,9 +635,11 @@ void animate_remote_player( u32 index ) */ void animate_remote_players(void) { - for( u32 i=0; iactive ) continue; + if( !player->active ) + continue; animate_remote_player( i ); } @@ -569,8 +661,7 @@ void render_remote_players( world_instance *world, vg_camera *cam ) if( !player->same_world ) continue; #if 0 - if( !player->isfriend && - (world-_world.instances == k_world_purpose_hub)) continue; + if( !player->isfriend && (world-_world.instances == k_world_purpose_hub)) continue; #endif draw_list[draw_list_count ++] = i; @@ -581,36 +672,29 @@ void render_remote_players( world_instance *world, vg_camera *cam ) struct skeleton *sk = &localplayer.skeleton; + // TODO: Make this a reference count instead of mutex? SDL_LockMutex( addon_system.cache_lock ); - - for( u32 j=0; jbone_count*index ]; - struct player_model *model = - addon_cache_item_if_loaded( k_addon_type_player, - player->playermodel_view_slot ); + struct player_model *model = addon_cache_item_if_loaded( k_addon_type_player, player->playermodel_view_slot ); - if( !model ) model = &localplayer.fallback_model; + if( !model ) + model = &localplayer.fallback_model; render_playermodel( cam, world, 0, model, sk, final_mtx ); - struct player_board *board = - addon_cache_item_if_loaded( k_addon_type_board, - player->board_view_slot ); - render_board( cam, world, board, final_mtx[localplayer.id_board], - &netplayers.board_poses[ index ], k_board_shader_player ); + struct player_board *board = addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); + render_board( cam, world, board, final_mtx[localplayer.id_board], + &netplayers.board_poses[ index ], k_board_shader_player ); } - SDL_UnlockMutex( addon_system.cache_lock ); if( !gliders ) return; - - /* TODO: we really, really only need to do all this once. at some point - * PLEASE figure out a good place to do this once per frame! - */ shader_model_entity_use(); shader_model_entity_uTexMain( 0 ); @@ -619,13 +703,15 @@ void render_remote_players( world_instance *world, vg_camera *cam ) WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_entity ); - for( u32 j=0; jrender_glider ) continue; - if( player->render_glider ){ + if( player->render_glider ) + { v3f *glider_mtx = netplayers.glider_mtx[ index ]; render_glider_model( cam, world, glider_mtx, k_board_shader_entity ); } diff --git a/src/player_remote.h b/src/player_remote.h index 8857964..a6bd72c 100644 --- a/src/player_remote.h +++ b/src/player_remote.h @@ -103,3 +103,9 @@ void remote_players_imgui_world( ui_context *ctx, world_instance *world, m4x4f pv, f32 max_dist, int geo_cull ); void remote_players_imgui_lobby( ui_context *ctx ); void remote_players_chat_imgui( ui_context *ctx ); +void pose_remote_player( f64 pose_time, struct interp_frame *f0, struct interp_frame *f1, struct player_board *board, + + m4x3f *out_final_mtx, v3f *out_glider_mtx, + struct player_board_pose *out_board_pose, + struct player_effects_data *out_effects, + bool *out_render_glider ); diff --git a/src/player_render.c b/src/player_render.c index 48d8285..9bbb6ca 100644 --- a/src/player_render.c +++ b/src/player_render.c @@ -109,10 +109,8 @@ void player_load_animation_reference( const char *path ) /* allocate matrix buffers for localplayer and remote players */ u32 mtx_size = sizeof(m4x3f)*sk->bone_count; localplayer.final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size ); - netplayers.final_mtx = vg_linear_alloc( vg_mem.rtmemory, - mtx_size*NETWORK_MAX_PLAYERS ); - netplayers.glider_mtx = vg_linear_alloc( vg_mem.rtmemory, - sizeof(m4x3f)*NETWORK_MAX_PLAYERS ); + netplayers.final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size*NETWORK_MAX_PLAYERS ); + netplayers.glider_mtx = vg_linear_alloc( vg_mem.rtmemory, sizeof(m4x3f)*NETWORK_MAX_PLAYERS ); } /* TODO: allow error handling */ diff --git a/src/player_replay.c b/src/player_replay.c index 4c1427c..c8666be 100644 --- a/src/player_replay.c +++ b/src/player_replay.c @@ -419,8 +419,7 @@ void skaterift_record_frame( replay_buffer *replay, int force_gamestate ) [k_player_subsystem_walk ] = sizeof(struct player_walk_state), [k_player_subsystem_drive] = 0, [k_player_subsystem_skate] = sizeof(struct player_skate_state), - [k_player_subsystem_dead ] = localplayer.ragdoll.part_count * - sizeof(struct replay_rb), + [k_player_subsystem_dead ] = localplayer.ragdoll.part_count * sizeof(struct replay_rb), [k_player_subsystem_glide] = sizeof(struct replay_rb), }[ localplayer.subsystem ]; } diff --git a/src/replay2.c b/src/replay2.c index be53cc8..8ce5132 100644 --- a/src/replay2.c +++ b/src/replay2.c @@ -10,6 +10,10 @@ void _replay2_init(void) _remote_replay.replay.buffer.buffer = vg_linear_alloc( vg_mem.rtmemory, size ); _remote_replay.replay.buffer.size = size; + + struct skeleton *sk = &localplayer.skeleton; + u32 mtx_size = sizeof(m4x3f)*sk->bone_count; + _replay_player.final_mtx = vg_linear_alloc( vg_mem.rtmemory, mtx_size ); } void _replay2_open_player( replay2 *replay, bool end ) @@ -37,6 +41,14 @@ void _replay2_open_player( replay2 *replay, bool end ) _replay_player.cursor = frame->time; } +void replay2_close_player(void) +{ + addon_cache_unwatch( k_addon_type_player, _replay_player.playermodel_cache_id ); + addon_cache_unwatch( k_addon_type_board, _replay_player.board_cache_id ); + _replay_player.playermodel_cache_id = 0; + _replay_player.board_cache_id = 0; +} + /* remote replay downloader * ------------------------------------------------------------------ */ @@ -51,7 +63,10 @@ static void replay_download_callback( void *data, u32 data_size, u64 userdata, e if( is_from_network ) { char path[1024]; - snprintf( path, sizeof(path), "replaydata/%lx@%x", _remote_replay.steamid, chunk->minute ); + if( _remote_replay.state == k_remote_replay_state_getinfo ) + snprintf( path, sizeof(path), "replaydata/%lx@%lx.bkv", _remote_replay.steamid, _remote_replay.last_second ); + else + snprintf( path, sizeof(path), "replaydata/%lx@%x", _remote_replay.steamid, chunk->minute ); FILE *fp = fopen( path, "wb" ); if( fp ) @@ -66,6 +81,48 @@ static void replay_download_callback( void *data, u32 data_size, u64 userdata, e } } + if( _remote_replay.state == k_remote_replay_state_getinfo ) + { + vg_msg kvs; + vg_msg_init( &kvs, data, data_size ); + + u32 centiseconds = 0; + vg_msg_getkvintg( &kvs, "centiseconds", k_vg_msg_u32, ¢iseconds, NULL ); + u32 end_offset_seconds = 0; + vg_msg_getkvintg( &kvs, "end_offset", k_vg_msg_u32, &end_offset_seconds, NULL ); + _remote_replay.end_offset = end_offset_seconds; + _remote_replay.start_offset = _remote_replay.end_offset + ((f64)centiseconds / 100.0); + + vg_info( "end_offset: -%f\n", _remote_replay.end_offset ); + vg_info( "start_offset: -%f\n", _remote_replay.start_offset ); + + vg_msg_cursor orig = kvs.cur; + if( vg_msg_seekframe( &kvs, "playerinfo" ) ) + { + const char *board_str = vg_msg_getkvstr( &kvs, "board" ); + const char *playermodel_str = vg_msg_getkvstr( &kvs, "player" ); + + vg_info( "board_str: %s\n", board_str ); + vg_info( "playermodel_str: %s\n", playermodel_str ); + + if( playermodel_str ) + { + addon_cache_unwatch( k_addon_type_player, _replay_player.playermodel_cache_id ); + _replay_player.playermodel_cache_id = + addon_cache_create_viewer_from_uid( k_addon_type_player, playermodel_str ); + } + + if( board_str ) + { + addon_cache_unwatch( k_addon_type_board, _replay_player.board_cache_id ); + _replay_player.board_cache_id = addon_cache_create_viewer_from_uid( k_addon_type_board, board_str ); + } + } + + _remote_replay.state = k_remote_replay_state_downloading; + return; + } + vg_info( "Processing %u bytes of network frames\n", data_size ); u32 offset = 0; @@ -92,7 +149,7 @@ static void replay_download_callback( void *data, u32 data_size, u64 userdata, e if( playerframe->timestamp <= _remote_replay.min_frame_t ) { vg_warn( "Dropping packet with lesser timestamp (%f). (FIXME)\n", playerframe->timestamp ); - goto E1; + goto S0; } _remote_replay.min_frame_t = playerframe->timestamp; @@ -107,7 +164,7 @@ static void replay_download_callback( void *data, u32 data_size, u64 userdata, e memcpy( &dst_frame->net_frame, playerframe, snm->msg_size ); } -E1: offset += sizeof(serialized_netmsg) + snm->msg_size; +S0: offset += sizeof(serialized_netmsg) + snm->msg_size; if( offset == data_size ) break; } @@ -120,14 +177,49 @@ E1: offset += sizeof(serialized_netmsg) + snm->msg_size; _remote_replay.state = k_remote_replay_state_ready; vg_success( "Winner! (%u total frames)\n", replay->buffer.allocation_count ); - _replay2_open_player( replay, 0 ); + replay2_frame *frame_start = vg_queue_data( &replay->buffer, replay->buffer.tail_offset ), + *frame_end = vg_queue_data( &replay->buffer, replay->buffer.head_offset ); + + if( 1 /* remote replay specific */ ) + { + _replay2_open_player( replay, 0 ); + + f64 highlight_start = (frame_end->time - _remote_replay.start_offset), + highlight_end = (frame_end->time - _remote_replay.end_offset), + start = highlight_start - 5.0, + end = highlight_end + 5.0; + + if( start < frame_start->time ) + start = frame_start->time; + if( end > frame_end->time ) + end = frame_end->time; + + _replay_player.start_t = start; + _replay_player.end_t = end; + _replay_player.highlight_start = highlight_start - start; + _replay_player.highlight_length = highlight_end - highlight_start; + _replay_player.highlight = 1; + _replay2_seek( start, 0 ); + } + else + { + _replay2_open_player( replay, 1 ); + _replay_player.start_t = frame_start->time; + _replay_player.end_t = frame_end->time; + _replay2_seek( frame_end->time, 0 ); + } } return; } + else + { + if( _remote_replay.state == k_remote_replay_state_getinfo ) + goto E1; + } E0:chunk->state = k_chunk_state_broken; - _remote_replay.state = k_remote_replay_state_failed; +E1:_remote_replay.state = k_remote_replay_state_failed; } struct async_cache_check_result @@ -141,8 +233,7 @@ struct async_cache_check_result static void async_cache_check_result( void *payload, u32 size ) { struct async_cache_check_result *result = payload; - struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; - + if( result->found_in_cache ) { if( result->broken ) @@ -155,7 +246,6 @@ static void async_cache_check_result( void *payload, u32 size ) if( !network_connected() ) return; - chunk->state = k_chunk_state_downloading; vg_make_directory( "replaydata" ); netmsg_request *packet = alloca( sizeof(netmsg_request) + 512 ); @@ -164,18 +254,35 @@ static void async_cache_check_result( void *payload, u32 size ) vg_msg data; vg_msg_init( &data, packet->buffer, 512 ); vg_msg_wkvstr( &data, "endpoint", "replay" ); - vg_msg_wkvnum( &data, "minute", k_vg_msg_u32, 1, &chunk->minute ); vg_msg_wkvnum( &data, "steamid", k_vg_msg_u64, 1, &_remote_replay.steamid ); + if( _remote_replay.state == k_remote_replay_state_downloading ) + { + struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; + chunk->state = k_chunk_state_downloading; + vg_msg_wkvnum( &data, "minute", k_vg_msg_u32, 1, &chunk->minute ); + } + else + vg_msg_wkvnum( &data, "lastsec", k_vg_msg_i64, 1, &_remote_replay.last_second ); network_send_request( packet, &data, replay_download_callback, 1 ); } } +// ignore this +static const u32 k_download_option_preparing = 0x00; +static const u32 k_download_option_data = 0x00; + static void _remote_replay_cache_check( void *_ ) { - struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; - char path[1024]; - snprintf( path, sizeof(path), "replaydata/%lx@%x", _remote_replay.steamid, chunk->minute ); + if( _remote_replay.state == k_remote_replay_state_getinfo ) + { + snprintf( path, sizeof(path), "replaydata/%lx@%lx.bvk", _remote_replay.steamid, _remote_replay.last_second ); + } + else + { + struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; + snprintf( path, sizeof(path), "replaydata/%lx@%x", _remote_replay.steamid, chunk->minute ); + } vg_async_item *item = vg_async_alloc( sizeof(struct async_cache_check_result) + 20*1024*1024 ); struct async_cache_check_result *result = item->payload; @@ -199,33 +306,130 @@ static void _remote_replay_cache_check( void *_ ) } vg_async_dispatch( item, async_cache_check_result ); + vg_async_stall(); } static void _remote_replay_pre_update(void) { - if( _remote_replay.state == k_remote_replay_state_none ) - return; - - if( _remote_replay.state == k_remote_replay_state_ready ) - return; - - if( _remote_replay.state == k_remote_replay_state_failed ) - return; - - struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; - if( chunk->state == k_chunk_state_none ) + if( _remote_replay.state == k_remote_replay_state_init ) { if( vg_loader_availible() ) { - chunk->state = k_chunk_state_cache_check; + _remote_replay.state = k_remote_replay_state_getinfo; vg_loader_start( _remote_replay_cache_check, NULL ); } } + else if( _remote_replay.state == k_remote_replay_state_downloading ) + { + struct remote_replay_chunk *chunk = &_remote_replay.chunks[ _remote_replay.chunks_downloaded ]; + if( chunk->state == k_chunk_state_none ) + { + if( vg_loader_availible() ) + { + chunk->state = k_chunk_state_cache_check; + vg_loader_start( _remote_replay_cache_check, NULL ); + } + } + } } void _replay2_pre_update(void) { _remote_replay_pre_update(); + + if( skaterift.activity != k_skaterift_replay ) + return; + + if( _replay_player.animation_dirty ) + { + _replay_player.animation_dirty = 0; + + replay2 *replay = _replay_player.replay; + replay2_frame *current_frame = vg_queue_data( &replay->buffer, _replay_player.cursor_frame_offset ), + *next_frame = NULL; + + u32 next_frame_offset; + if( vg_queue_next( &replay->buffer, _replay_player.cursor_frame_offset, &next_frame_offset ) ) + { + next_frame = vg_queue_data( &replay->buffer, next_frame_offset ); + } + + /* decode frames */ + struct interp_frame interp_frame0, interp_frame1; + u32 s0 = current_frame->net_frame_size - sizeof(netmsg_playerframe); + decode_playerframe( ¤t_frame->net_frame, s0, &interp_frame0 ); + + + m4x3f *final_mtx = _replay_player.final_mtx; + v3f *glider_mtx = _replay_player.glider_mtx; + struct player_board_pose *board_pose = &_replay_player.board_pose; + struct player_board *board = &localplayer.fallback_board; + //struct player_board *board = addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); + struct player_effects_data *effects = &_replay_player.effect_data; + bool *glider_flag = &_replay_player.render_glider; + f64 frame_time = _replay_player.cursor; + + if( next_frame ) + { + u32 s1 = next_frame->net_frame_size - sizeof(netmsg_playerframe); + decode_playerframe( &next_frame->net_frame, s1, &interp_frame1 ); + pose_remote_player( frame_time, &interp_frame0, &interp_frame1, board, final_mtx, glider_mtx, + board_pose, effects, glider_flag ); + } + else + { + pose_remote_player( frame_time, &interp_frame0, NULL, board, final_mtx, glider_mtx, board_pose, + effects, glider_flag ); + } + } +} + +void _replay2_render_player( world_instance *world, vg_camera *cam ) +{ + if( skaterift.activity != k_skaterift_replay ) + return; + + SDL_LockMutex( addon_system.cache_lock ); + + //struct player_model *model = addon_cache_item_if_loaded( k_addon_type_player, player->playermodel_view_slot ); + //if( !model ) + // model = &localplayer.fallback_model; + struct skeleton *sk = &localplayer.skeleton; + struct player_model *model = &localplayer.fallback_model; + render_playermodel( cam, world, 0, model, sk, _replay_player.final_mtx ); + + //struct player_board *board = addon_cache_item_if_loaded( k_addon_type_board, player->board_view_slot ); + struct player_board *board = &localplayer.fallback_board; + render_board( cam, world, board, _replay_player.final_mtx[localplayer.id_board], + &_replay_player.board_pose, k_board_shader_player ); + + SDL_UnlockMutex( addon_system.cache_lock ); + +#if 0 + if( !gliders ) + return; + + shader_model_entity_use(); + shader_model_entity_uTexMain( 0 ); + shader_model_entity_uCamera( cam->transform[3] ); + shader_model_entity_uPv( cam->mtx.pv ); + + WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_entity ); + + for( u32 j=0; jrender_glider ) continue; + + if( player->render_glider ) + { + v3f *glider_mtx = netplayers.glider_mtx[ index ]; + render_glider_model( cam, world, glider_mtx, k_board_shader_entity ); + } + } +#endif } void _replay2_seek( f64 t, bool play_sounds ) @@ -250,30 +454,31 @@ void _replay2_seek( f64 t, bool play_sounds ) { if( dir < 0.0 ) { - if( t > current_frame->time ) + if( current_frame->time < t ) { break; } } bool next = 0; - if( dir > 0.0 ) - next = vg_queue_next( &replay->buffer, current_offset, ¤t_offset ); - else - next = vg_queue_previous( &replay->buffer, current_offset, ¤t_offset ); - + u32 next_offset; + if( dir > 0.0 ) next = vg_queue_next( &replay->buffer, current_offset, &next_offset ); + else next = vg_queue_previous( &replay->buffer, current_offset, &next_offset ); if( !next ) break; - current_frame = vg_queue_data( &replay->buffer, current_offset ); + replay2_frame *next_frame = vg_queue_data( &replay->buffer, next_offset ); if( dir > 0.0 ) { - if( t < current_frame->time ) + if( next_frame->time > t ) { break; } } + current_offset = next_offset; + current_frame = next_frame; + if( play_sounds ) { //replay_emit_frame_sounds( next ); @@ -281,7 +486,8 @@ void _replay2_seek( f64 t, bool play_sounds ) } _replay_player.cursor_frame_offset = current_offset; - _replay_player.cursor = current_frame->time; + _replay_player.cursor = t; + _replay_player.animation_dirty = 1; } void _replay2_imgui( ui_context *ctx ) @@ -300,12 +506,9 @@ void _replay2_imgui( ui_context *ctx ) replay2 *replay = _replay_player.replay; VG_ASSERT( replay ); - f64 start = _replay_player.cursor, - end = start; - replay2_frame *frame_start = vg_queue_data( &replay->buffer, replay->buffer.tail_offset ), - *frame_end = vg_queue_data( &replay->buffer, replay->buffer.head_offset ); - start = frame_start->time; - end = frame_end->time; + f64 start = _replay_player.start_t, + end = _replay_player.end_t; + f64 len = end - start, cur = (_replay_player.cursor - start) / len; @@ -322,6 +525,15 @@ void _replay2_imgui( ui_context *ctx ) ui_fill( ctx, timeline, ui_colour( ctx, k_ui_bg ) ); + /* for remote replays */ + if( _replay_player.highlight ) + { + f64 x = _replay_player.highlight_start / len, + w = _replay_player.highlight_length / len; + ui_rect highlight = { timeline[0] + x*(f64)timeline[2], timeline[1] + timeline[3] -1, (f64)timeline[2]*w, 2 }; + ui_fill( ctx, highlight, ui_colour( ctx, k_ui_green ) ); + } + /* cursor frame block */ replay2_frame *frame_cursor = vg_queue_data( &replay->buffer, _replay_player.cursor_frame_offset ); u32 next_frame_offset; diff --git a/src/replay2.h b/src/replay2.h index fe4c637..879cec8 100644 --- a/src/replay2.h +++ b/src/replay2.h @@ -1,5 +1,6 @@ #pragma once #include "vg/vg_mem_queue.h" +#include "player_effects.h" typedef struct replay2 replay2; struct replay2 @@ -47,11 +48,15 @@ struct _remote_replay enum remote_replay_state { k_remote_replay_state_none, + k_remote_replay_state_init, + k_remote_replay_state_getinfo, k_remote_replay_state_downloading, k_remote_replay_state_ready, k_remote_replay_state_failed } state; + i64 last_second; + f64 end_offset, start_offset; /* from the download */ replay2 replay; } @@ -72,6 +77,19 @@ struct _replay_player bool use_freecam; bool hide_ui; v3f freecam_v, freecam_w; + + f64 start_t, end_t; + bool highlight; + f64 highlight_start, highlight_length; + + bool animation_dirty; + u16 board_cache_id, + playermodel_cache_id; + + struct player_effects_data effect_data; + bool render_glider; + m4x3f *final_mtx, glider_mtx; + struct player_board_pose board_pose; } extern _replay_player; @@ -79,3 +97,5 @@ void _replay2_init(void); void _replay2_pre_update(void); void _replay2_imgui( ui_context *ctx ); void _replay2_open_player( replay2 *replay, bool end ); +void _replay2_render_player( world_instance *world, vg_camera *cam ); +void _replay2_seek( f64 t, bool play_sounds ); diff --git a/src/save.c b/src/save.c index d1cc3a8..af89885 100644 --- a/src/save.c +++ b/src/save.c @@ -45,16 +45,16 @@ void savedata_file_read( savedata_file *file ) } } -static void skaterift_write_addon_alias( vg_msg *msg, const char *key, - addon_alias *alias ){ +static void skaterift_write_addon_alias( vg_msg *msg, const char *key, addon_alias *alias ) +{ if( alias->workshop_id ) vg_msg_wkvnum( msg, key, k_vg_msg_u64, 1, &alias->workshop_id ); else vg_msg_wkvstr( msg, key, alias->foldername ); } -static void skaterift_write_viewslot( vg_msg *msg, const char *key, - enum addon_type type, u16 cache_id ){ +static void skaterift_write_viewslot( vg_msg *msg, const char *key, enum addon_type type, u16 cache_id ) +{ if( !cache_id ) return; struct addon_cache *cache = &addon_system.cache[type]; @@ -65,9 +65,7 @@ static void skaterift_write_viewslot( vg_msg *msg, const char *key, skaterift_write_addon_alias( msg, key, ®->alias ); } -static bool skaterift_read_addon_alias( vg_msg *msg, const char *key, - enum addon_type type, - addon_alias *alias ) +static bool skaterift_read_addon_alias( vg_msg *msg, const char *key, enum addon_type type, addon_alias *alias ) { alias->foldername[0] = '\0'; alias->workshop_id = 0; @@ -77,10 +75,7 @@ static bool skaterift_read_addon_alias( vg_msg *msg, const char *key, if( vg_msg_getkvcmd( msg, key, &kv ) ) { if( kv.code == k_vg_msg_kvstring ) - { - vg_strncpy( kv.value, alias->foldername, sizeof(alias->foldername), - k_strncpy_allow_cutoff ); - } + vg_strncpy( kv.value, alias->foldername, sizeof(alias->foldername), k_strncpy_allow_cutoff ); else vg_msg_cast( kv.value, kv.code, &alias->workshop_id, k_vg_msg_u64 ); diff --git a/src/world_render.c b/src/world_render.c index 46accf1..2e42ba8 100644 --- a/src/world_render.c +++ b/src/world_render.c @@ -1182,6 +1182,8 @@ void render_world( world_instance *world, vg_camera *cam, render_remote_players( world, cam ); render_other_entities( world, cam ); + _replay2_render_player( world, cam ); + _board_maker_render( world, cam ); if( stenciled ) -- 2.25.1