*/
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;
}
/*
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) )
{
{
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;
}
}
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 )
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;
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] );
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; i<minutes_span; i ++ )
{
struct remote_replay_chunk *chunk = &_remote_replay.chunks[i];
- chunk->minute = 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
.auth_mode = eServerModeAuthentication,
.tasks =
{
- .upper_memory_limit = 1024*1024*8
+ .buffer_size = 1024*1024*8
}
};
//_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 );
}
goto E0;
_gs_requests_init();
+ _gs_replay_init();
/* steamworks init
* --------------------------------------------------------------- */
{
.tasks =
{
- .upper_memory_limit = 1024*1024*40
+ .buffer_size = 1024*1024*20
}
};
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;
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 );
sqlite3_step( s2 );
sqlite3_finalize( s2 );
}
-#endif
if( only_if_faster )
{
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;
{
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 );
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);
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 ) )
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
/*
struct _gs_replay _gs_replay;
-u32 _gs_replay_get_minute(void)
-{
- return time(NULL) / 60;
-}
-
struct serialized_replay
{
u64 steamid;
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;
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; i<length; i ++ )
+ for( u32 i=0; i<minutes_span; i ++ )
{
u32 minute_index = (start_minute - i) % REPLAY_MINUTE_COUNT;
replay->minute_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 )
{
}
}
+void _gs_replay_init(void)
+{
+ for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ )
+ {
+ gs_replay *dest_replay = &_gs_replay.replays[ i ];
+ vg_queue *ring = &dest_replay->ring_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 ];
}
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 );
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
#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;
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);
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 )
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
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 );
}
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;
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; i<frame->sound_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; j<NETWORK_SFX_QUEUE_LENGTH; j ++ )
+ {
+ struct net_sfx *sj = &netplayers.sfx_queue[j];
+ if( sj->system == 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;
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;
}
struct interp_frame *dest = NULL;
f64 min_time = INFINITY;
- for( u32 i=0; i<VG_ARRAY_LEN(ib->frames); i++ ){
+ for( u32 i=0; i<VG_ARRAY_LEN(ib->frames); 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;
}
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;
}
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 ];
/*
* 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;
}
/*
*abs_max_frame = NULL;
struct interp_buffer *buf = &netplayers.interp_data[index];
- for( u32 i=0; i<VG_ARRAY_LEN(buf->frames); i ++ ){
+ for( u32 i=0; i<VG_ARRAY_LEN(buf->frames); 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
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;
}
*/
void animate_remote_players(void)
{
- for( u32 i=0; i<VG_ARRAY_LEN(netplayers.list); i ++ ){
+ for( u32 i=0; i<VG_ARRAY_LEN(netplayers.list); i ++ )
+ {
struct network_player *player = &netplayers.list[i];
- if( !player->active ) continue;
+ if( !player->active )
+ continue;
animate_remote_player( i );
}
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;
struct skeleton *sk = &localplayer.skeleton;
+ // TODO: Make this a reference count instead of mutex?
SDL_LockMutex( addon_system.cache_lock );
-
- for( u32 j=0; j<draw_list_count; j ++ ){
+ for( u32 j=0; j<draw_list_count; j ++ )
+ {
u32 index = draw_list[j];
struct network_player *player = &netplayers.list[index];
m4x3f *final_mtx = &netplayers.final_mtx[ sk->bone_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 );
WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_entity );
- for( u32 j=0; j<draw_list_count; j ++ ){
+ for( u32 j=0; j<draw_list_count; j ++ )
+ {
u32 index = draw_list[j];
struct network_player *player = &netplayers.list[index];
if( !player->render_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 );
}
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 );
/* 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 */
[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 ];
}
_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 )
_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
* ------------------------------------------------------------------ */
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 )
}
}
+ 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;
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;
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;
}
_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
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 )
if( !network_connected() )
return;
- chunk->state = k_chunk_state_downloading;
vg_make_directory( "replaydata" );
netmsg_request *packet = alloca( sizeof(netmsg_request) + 512 );
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;
}
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; j<draw_list_count; j ++ )
+ {
+ u32 index = draw_list[j];
+
+ struct network_player *player = &netplayers.list[index];
+ if( !player->render_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 )
{
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 );
}
_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 )
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;
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;
#pragma once
#include "vg/vg_mem_queue.h"
+#include "player_effects.h"
typedef struct replay2 replay2;
struct replay2
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;
}
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;
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 );
}
}
-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];
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;
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 );
render_remote_players( world, cam );
render_other_entities( world, cam );
+ _replay2_render_player( world, cam );
+
_board_maker_render( world, cam );
if( stenciled )