A complete workshop implementation, I guess
authorhgn <hgodden00@gmail.com>
Mon, 8 May 2023 23:17:06 +0000 (00:17 +0100)
committerhgn <hgodden00@gmail.com>
Mon, 8 May 2023 23:17:06 +0000 (00:17 +0100)
ent_skateshop.c
ent_skateshop.h
model.h
workshop.c
workshop.h

index 708e6483e40e9bad3edc4f365dff61b913473220..aeeaf702f76a11f6be6a175f3a07b853c44bab71 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef ENT_SKATESHOP_C
 #define ENT_SKATESHOP_C
 
+#define VG_GAME
+#include "vg/vg.h"
+#include "vg/vg_steam_ugc.h"
 #include "ent_skateshop.h"
 #include "world.h"
 #include "player.h"
@@ -250,9 +253,13 @@ VG_STATIC void workshop_scan_thread( void *_args )
 
          reg->cache_ptr = NULL;
          vg_strncpy( file.name, reg->filename, 64, k_strncpy_always_add_null );
+         vg_strncpy( file.name, reg->workshop.title,
+                     64, k_strncpy_always_add_null );
          reg->filename_hash = hash;
          reg->workshop_id = 0;
          reg->state = k_registry_board_state_indexed;
+         reg->workshop.author = 0;
+         strcpy( reg->workshop.author_name, "custom" );
       }
 
 next_file: tinydir_next( &dir );
@@ -303,6 +310,25 @@ next_file: tinydir_next( &dir );
       reg->workshop_id = id;
       reg->state = k_registry_board_state_indexed;
 
+      workshop_file_info_clear( &reg->workshop );
+      strcpy( reg->workshop.title, "Workshop file" );
+
+      /* load the metadata off the disk */
+      vg_async_item *call = 
+         vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
+
+      const char *meta_file = "/board.mdl.inf";
+      char path[ 1024 ];
+      struct async_workshop_filepath_info *info = call->payload;
+      info->buf = path;
+      info->id = reg->workshop_id;
+      info->len = vg_list_size(path) - strlen(meta_file)-1;
+      vg_async_dispatch( call, async_workshop_get_filepath );
+      vg_async_stall(); /* too bad! */
+
+      strcat( path, meta_file );
+      workshop_load_metadata( path, &reg->workshop );
+
 next_file_workshop:;
    }
    
@@ -348,6 +374,27 @@ VG_STATIC void unwatch_cache_board( struct cache_board *ptr )
    ptr->ref_count --;
 }
 
+/*
+ * Callback handler for persona state changes,
+ * it sets the author names on the registries
+ */
+VG_STATIC void callback_persona_statechange( CallbackMsg_t *msg )
+{
+   PersonaStateChange_t *info = (PersonaStateChange_t *)msg->m_pubParam;
+   ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
+
+   if( info->m_nChangeFlags & k_EPersonaChangeName ){
+      for( u32 i=0; i<global_skateshop.registry_count; i++ ){
+         struct registry_board *reg = &global_skateshop.registry[i];
+         if( reg->workshop.author == info->m_ulSteamID ){
+            const char *name = SteamAPI_ISteamFriends_GetFriendPersonaName( 
+                                    hSteamFriends, info->m_ulSteamID );
+            str_utf8_collapse( name, reg->workshop.author_name, 32 );
+         }
+      }
+   }
+}
+
 /*
  * VG event init
  */
@@ -369,6 +416,11 @@ VG_STATIC void skateshop_init(void)
       board->last_use_time = -99999.9;
       board->ref_count = 0;
    }
+
+   if( steam_ready ){
+      steam_register_callback( k_iPersonaStateChange, 
+                               callback_persona_statechange );
+   }
 }
 
 VG_STATIC struct cache_board *skateshop_selected_cache_if_loaded(void)
@@ -460,8 +512,6 @@ VG_STATIC void global_skateshop_preupdate(void)
    }
 
    if( moved ){
-      vg_info( "Select registry: %u\n", 
-               global_skateshop.selected_registry_id );
       global_skateshop.interaction_cooldown = 0.125f;
       return;
    }
@@ -556,8 +606,10 @@ fade_out:;
    mdl_transform_m4x3( &mark_info->transform, mtext );
    mdl_transform_m4x3( &mark_rack->transform, mrack );
 
+#if 0
    const char *text_title = "Fish - Title";
    const char *text_author = "by Shaniqua";
+#endif
 
    m4x3f mlocal, mmdl;
    m4x3_identity( mlocal );
@@ -596,24 +648,31 @@ fade_out:;
    struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded();
    if( !cache_ptr ) return;
 
+   struct registry_board *reg = 
+      &global_skateshop.registry[cache_ptr->registry_id];
+   struct workshop_file_info *info = &reg->workshop;
+
    /* Skin title
     * ----------------------------------------------------------------- */
+   m3x3_zero( mlocal );
    m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
-   mlocal[3][0] = -font3d_string_width(&world_global.font,0,text_title);
+   mlocal[3][0] = -font3d_string_width( &world_global.font, 0, info->title );
    mlocal[3][0] *= scale*0.5f;
    mlocal[3][1] = 0.1f;
    m4x3_mul( mtext, mlocal, mmdl );
-   font3d_simple_draw( &world_global.font, 0, text_title, &main_camera, mmdl );
+   font3d_simple_draw( &world_global.font, 0, info->title, &main_camera, mmdl );
 
    /* Author name
     * ----------------------------------------------------------------- */
    scale *= 0.4f;
    m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
-   mlocal[3][0] = -font3d_string_width(&world_global.font,0,text_author);
+   mlocal[3][0] = -font3d_string_width( &world_global.font, 0, 
+                                        info->author_name );
    mlocal[3][0] *= scale*0.5f;
    mlocal[3][1] = 0.0f;
    m4x3_mul( mtext, mlocal, mmdl );
-   font3d_simple_draw( &world_global.font, 0, text_author, &main_camera, mmdl );
+   font3d_simple_draw( &world_global.font, 0, 
+                       info->author_name, &main_camera, mmdl );
 }
 
 /*
index e68816861cd9a583f29d25b8f44d1954d87e19f1..4f84f7ae03dc68ddfd009cfc7b9dc387bb09b359 100644 (file)
@@ -43,6 +43,9 @@ struct{
 
    struct registry_board{
       PublishedFileId_t workshop_id;
+
+      /* only for steam workshop files */
+      struct workshop_file_info workshop;
       struct cache_board *cache_ptr;
 
       char filename[64]; /* if workshop, string version of its published ID. */
diff --git a/model.h b/model.h
index 811f3a12034c92634abc504902e02bfbceb179ed..664950fc0191b7bf6f9e7f95e3ce6d4cffd3bb25 100644 (file)
--- a/model.h
+++ b/model.h
@@ -274,8 +274,7 @@ VG_STATIC u32 mdl_query_array_size( mdl_array *arr )
       u32 size = arr->item_size*arr->item_count;
       return vg_align8(size);
    }
-   else
-      return 0;
+   else return 0;
 }
 
 VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr );
@@ -290,21 +289,18 @@ void mdl_fread_pack_file( mdl_context *mdl, mdl_file *info, void *dst )
    fseek( mdl->file, mdl->pack_base_offset+info->pack_offset, SEEK_SET );
    u64 l = fread( dst, info->pack_size, 1, mdl->file );
 
-   if( l != 1 )
-      mdl_load_fatal_corrupt( mdl );
+   if( l != 1 ) mdl_load_fatal_corrupt( mdl );
 }
 
 /* TODO: Rename these */
-VG_STATIC 
-void mdl_load_array_file_buffer( mdl_context *mdl, mdl_array *arr, 
-                                 void *buffer )
+VG_STATIC void mdl_load_array_file_buffer( mdl_context *mdl, mdl_array *arr, 
+                                           void *buffer )
 {
    if( arr->item_count ){
       fseek( mdl->file, arr->file_offset, SEEK_SET );
       u64 l = fread( buffer, arr->item_size*arr->item_count, 1, mdl->file );
 
-      if( l != 1 )
-         mdl_load_fatal_corrupt( mdl );
+      if( l != 1 ) mdl_load_fatal_corrupt( mdl );
    }
 }
 
index c82ca6ef563a12167635daab87db2ab1ae3dbd9c..30aaf7a376b5be2fe1933fb46cb36ed50bcf4bdb 100644 (file)
@@ -42,7 +42,6 @@ struct workshop_form{
     */
 
    char model_path[128];
-
    struct player_board board_model;
 
    /* what does the user want to do with the image preview? */
@@ -298,6 +297,9 @@ VG_STATIC void workshop_form_async_package_complete( void *data, u32 size )
  */
 struct workshop_package_thread_args{
    PublishedFileId_t file_id;
+
+   u64_steamid steamid;
+   char username[32];
 };
 VG_STATIC void _workshop_package_thread( void *_args )
 {
@@ -340,6 +342,40 @@ VG_STATIC void _workshop_package_thread( void *_args )
       return;
    }
 
+   /* write the metadata file */
+   struct workshop_file_info meta;
+   meta.author = args->steamid;
+   vg_strncpy( args->username, meta.author_name, vg_list_size(meta.author_name),
+               k_strncpy_always_add_null );
+   vg_strncpy( workshop_form.submission.title, meta.title, 
+               vg_list_size(meta.title), k_strncpy_always_add_null );
+
+   char _path[1024];
+   vg_str path;
+   vg_strnull( &path, _path, vg_list_size( _path ) );
+   vg_strcat( &path, info->abs_content_file );
+   vg_strcat( &path, ".inf" );
+   
+   if( vg_strgood( &path ) ){
+      FILE *fp = fopen( _path, "wb" );
+
+      if( fp ){
+         fwrite( &meta, sizeof(struct workshop_file_info), 1, fp );
+         fclose( fp );
+      }
+      else{
+         info->success = 0;
+         info->failure_reason = "Cant write .inf file";
+         vg_async_dispatch( call, workshop_form_async_package_complete );
+      }
+   }
+   else{
+      info->success = 0;
+      info->failure_reason = "Path too long";
+      vg_async_dispatch( call, workshop_form_async_package_complete );
+      return;
+   }
+
    info->success = 1;
    vg_async_dispatch( call, workshop_form_async_package_complete );
 }
@@ -354,6 +390,16 @@ VG_STATIC void workshop_package_submission( PublishedFileId_t file_id )
       vg_linear_alloc( vg_mem.scratch, 
                        sizeof(struct workshop_package_thread_args));
 
+   ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
+   ISteamUser *hSteamUser = SteamAPI_SteamUser();
+
+   args->steamid = SteamAPI_ISteamUser_GetSteamID( hSteamUser );
+
+   const char *username = SteamAPI_ISteamFriends_GetPersonaName(hSteamFriends);
+   str_utf8_collapse( username, args->username, vg_list_size( args->username ));
+   vg_info( "Steamid: "PRINTF_U64", Name: %s(%s)\n", 
+               args->steamid, username, args->username );
+
    args->file_id = file_id;
    vg_loader_start( _workshop_package_thread, args );
 }
@@ -517,13 +563,29 @@ VG_STATIC void workshop_form_loadmodel_async_complete( void *payload, u32 size )
    workshop_end_op();
 }
 
+/*
+ * Reciever for failure to load
+ */
+VG_STATIC void workshop_form_loadmodel_async_error( void *payload, u32 size )
+{
+   workshop_end_op();
+}
+
 /*
  * Thread which loads the model from the disk 
  */
 VG_STATIC void _workshop_form_load_thread( void *data )
 {
-   player_board_load( &workshop_form.board_model, workshop_form.model_path );
-   vg_async_call( workshop_form_loadmodel_async_complete, NULL, 0 );
+   FILE *test = fopen( workshop_form.model_path, "rb" );
+   if( test ){
+      fclose( test );
+      player_board_load( &workshop_form.board_model, workshop_form.model_path );
+      vg_async_call( workshop_form_loadmodel_async_complete, NULL, 0 );
+   }
+   else{
+      vg_error( "workshop async load failed: file not found\n" );
+      vg_async_call( workshop_form_loadmodel_async_error, NULL, 0 );
+   }
 }
 
 /*
@@ -1083,7 +1145,6 @@ VG_STATIC void workshop_form_gui_edit_page( ui_rect content )
          .change = workshop_changed_description
       };
       ui_rect desc_entry;
-      /* TODO: Tommora, new split_px_gap and split_px() */
       ui_split( content, k_ui_axis_h, 8, 0, null, content );
       ui_split( content, k_ui_axis_h, 28, 0, label, content );
       ui_split( content, k_ui_axis_h, 28*4, 0, desc_entry, content );
@@ -1165,8 +1226,6 @@ VG_STATIC void workshop_form_gui_sidebar( ui_rect sidebar )
    char buf[32];
    strcpy( buf, "page " );
    int i  = 5;
-
-   /* TODO: for what it is, this code is getting a bit.. special */
        i += highscore_intl( buf+i, workshop_form.view_published_page_id+1, 4 );
             buf[ i ++ ] = '/';
        i += highscore_intl( buf+i, workshop_form.view_published_page_count, 4 );
@@ -1368,4 +1427,32 @@ VG_STATIC void async_workshop_get_installed_files( void *data, u32 len )
    *info->len = j;
 }
 
+VG_STATIC void vg_strsan_ascii( char *buf, u32 len )
+{
+   for( u32 i=0; i<len-1; i ++ ){
+      if( buf[i] == 0 ) return;
+
+      if( buf[i] < 32 || buf[i] > 126 ){
+         buf[i] = '?';
+      }
+   }
+   buf[len-1] = '\0';
+}
+
+#define VG_STRSAN_ASCII( X ) vg_strsan_ascii( X, vg_list_size(X) )
+
+VG_STATIC void workshop_load_metadata( const char *path,
+                                       struct workshop_file_info *info )
+{
+   FILE *fp = fopen( path, "rb" );
+   
+   if( fp ){
+      if( fread( info, sizeof( struct workshop_file_info ), 1, fp ) ){
+         VG_STRSAN_ASCII( info->author_name );
+         VG_STRSAN_ASCII( info->title );
+      }
+      fclose( fp );
+   }
+}
+
 #endif /* WORKSHOP_C */
index a08bc4d25602ad631880b5f04e3dc741d29ad19a..4a0f8b27008347d74cdc574d53fb790ea23b8162 100644 (file)
@@ -18,6 +18,12 @@ struct workshop{
 }
 static workshop;
 
+struct workshop_file_info{
+   u64 author;
+   char author_name[32];
+   char title[64];
+};
+
 struct async_workshop_filepath_info{
    PublishedFileId_t id;
    char *buf;
@@ -29,8 +35,22 @@ struct async_workshop_installed_files_info{
    u32 *len; /* inout */
 };
 
+struct async_workshop_metadata_info{
+   struct workshop_file_info *info;
+   const char *path;
+};
+
+VG_STATIC void workshop_file_info_clear( struct workshop_file_info *info )
+{
+   info->author = 0ul;
+   info->author_name[0] = '\0';
+   info->title[0] = '\0';
+}
+
 VG_STATIC void async_workshop_get_filepath( void *data, u32 len );
 VG_STATIC void async_workshop_get_installed_files( void *data, u32 len );
+VG_STATIC void workshop_load_metadata( const char *path,
+                                       struct workshop_file_info *info );
 
 /*
  * Start a new operation and crash if we are already running one.