various stuff
authorhgn <hgodden00@gmail.com>
Thu, 10 Apr 2025 20:15:21 +0000 (21:15 +0100)
committerhgn <hgodden00@gmail.com>
Thu, 10 Apr 2025 20:15:21 +0000 (21:15 +0100)
19 files changed:
src/addon.c
src/addon.h
src/ent_route.c
src/gameserver.c
src/gameserver_database.c
src/gameserver_database.h
src/gameserver_replay.c
src/gameserver_replay.h
src/gameserver_requests.c
src/network_requests.c
src/player.c
src/player_remote.c
src/player_remote.h
src/player_render.c
src/player_replay.c
src/replay2.c
src/replay2.h
src/save.c
src/world_render.c

index 347ad36d034aeca5294025cbfab40bcb2ae8da92..2ac69d1e8c917a7686de3ce7f35cae338463e3eb 100644 (file)
@@ -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;
index 8725994ba0f39ee0ce75caff94f61e5c1c3a88d3..c65524babf66b7433b846f1dc7a6dbb8394401f6 100644 (file)
@@ -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] );
index ad19724ed1a0e9a0b11270040bc757b0f57f9586..7dd48d057da2e56b9e740d6973cf49ee095cf229 100644 (file)
@@ -108,39 +108,33 @@ void ent_route_preupdate(void)
                u32 centiseconds;
                vg_msg_getkvintg( &body, "time", k_vg_msg_u32, &centiseconds, 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
index 307e2b651df0ce7c2deb91b3ce5eda4fbc5ecd3d..d73dc4aa032cbd75feee51002412c7eb6361b162 100644 (file)
@@ -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 
     * --------------------------------------------------------------- */
index 01acb5f416d961d2efcc510675a36c8420ea4acd..479f263ddb63f9e23276e699fcd8cd913dd40e5e 100644 (file)
@@ -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 ) )
index 091c6e5cc0915f7dcaeacf5a18895a31354bda24..e9ab0c4a511b22548812cb82d58ac953e8b40442 100644 (file)
@@ -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
 /*
index 222e16ab7b6bb294d32110d1aa06b6028efa7a54..ec87189669eccb4fed67883427073559c8070786 100644 (file)
@@ -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; 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, &centiseconds );
+   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; 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 ];
@@ -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
index 327c25c5d144e36deb590cc89017c461fc9706be..247c0ddf01811eacea5f81b24873800c16552e1e 100644 (file)
@@ -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);
index 93b14149357b680a79eb296ce05acd0c4056dd53..d77b0aadfc356ca47917a24272159caa74170614 100644 (file)
@@ -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
index 924a93b8a898e7c9c0dca4a7265614ad57325201..5f76a8808f8a5c97d4a815ec6596278eb88eaf6e 100644 (file)
@@ -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 );
 }
 
index a34705e1e4cd7b4c1194b3579517bacdc4a5f0a1..c8681915e333e5ca91e77a3af2a07f137aeff956 100644 (file)
@@ -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;
index dcbd849f6bb1a3c534784aea387bc8864d39623d..76d0bf3527086655efe8fa16ea7e95803af54866 100644 (file)
@@ -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; 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;
@@ -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; 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;
          }
@@ -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; 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 
@@ -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; 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 );
    }
@@ -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; 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 );
@@ -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; 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 );
       }
index 8857964c84a9e6a92bc1e95cf85d62e1d7ee0bd5..a6bd72cc1252a29db3d61705781799aa07ea65cb 100644 (file)
@@ -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 );
index 48d82851f6a1ce91b5f85a0271274d8651453fd4..9bbb6ca59ad5b55073404e8c0159f9f755af5636 100644 (file)
@@ -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 */
index 4c1427ccca5b75ea345d7be3f8dc50d95495ba00..c8666bea76ae40bf6dd6559867817d4bb4d019a4 100644 (file)
@@ -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 ];
    }
index be53cc8544b190bf3f4688a448fc5c682cb7f9f2..8ce513291774fc5af082359413dc4d1bd5102385 100644 (file)
@@ -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, &centiseconds, 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( &current_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 )
@@ -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, &current_offset );
-      else 
-         next = vg_queue_previous( &replay->buffer, current_offset, &current_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;
index fe4c637fadb58834f8664f441486b5b0b9f1b9d6..879cec8b05ccb65429330121cf133ddb6774ae29 100644 (file)
@@ -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 );
index d1cc3a8678f675f5ef5db018015c0a5ee62a17f9..af89885fe26ffedd85598e2dfa02671ddc5d46f9 100644 (file)
@@ -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, &reg->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 );
 
index 46accf1cd2e72239ba78aaced1b8b71764d33cd6..2e42ba8e47a23099351f5c291e0fe5c791b77754 100644 (file)
@@ -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 )