X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=addon.c;h=ef92b0083ce4f1ba1c6a49a39d2a0e4c4a191aea;hb=137d40d96fe923600d8378b8e138e3c276f27ff4;hp=b599c304a04e2c91f3965460774db4ba22bd6864;hpb=dbdb031ee290892468d24b6dd576fe62c47e778a;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/addon.c b/addon.c index b599c30..ef92b00 100644 --- a/addon.c +++ b/addon.c @@ -2,21 +2,21 @@ #define ADDON_C #include "addon.h" +#include "addon_types.h" #include "vg/vg_msg.h" #include "steam.h" -#include "workshop_types.h" #include "workshop.h" -static u32 addon_count( enum workshop_file_type type ){ +static u32 addon_count( enum addon_type type ){ return addon_system.registry_type_counts[ type ]; } /* these kind of suck, oh well. */ -static addon_reg *get_addon_from_index(enum workshop_file_type type, u32 index){ +static addon_reg *get_addon_from_index(enum addon_type type, u32 index){ u32 count = 0; for( u32 i=0; counttype == type ){ + if( reg->alias.type == type ){ if( index == count ) return reg; @@ -27,11 +27,11 @@ static addon_reg *get_addon_from_index(enum workshop_file_type type, u32 index){ return NULL; } -static u32 get_index_from_addon( enum workshop_file_type type, addon_reg *a ){ +static u32 get_index_from_addon( enum addon_type type, addon_reg *a ){ u32 count = 0; for( u32 i=0; counttype == type ){ + if( reg->alias.type == type ){ if( reg == a ) return count; @@ -42,22 +42,25 @@ static u32 get_index_from_addon( enum workshop_file_type type, addon_reg *a ){ return 0xffffffff; } -static u32 addon_match( enum workshop_file_type type, - u64 workshop_id, const char *foldername ){ - u32 foldername_djb2 = vg_strdjb2( foldername ); +static u32 addon_match( addon_alias *alias ){ + if( alias->type == k_addon_type_none ) return 0xffffffff; + + u32 foldername_djb2 = 0; + if( !alias->workshop_id ) + foldername_djb2 = vg_strdjb2( alias->foldername ); u32 count = 0; - for( u32 i=0; counttype]; i++ ){ addon_reg *reg = &addon_system.registry[i]; - if( reg->type == type ){ + if( reg->alias.type == alias->type ){ - if( workshop_id ){ - if( workshop_id == reg->workshop_id ) + if( alias->workshop_id ){ + if( alias->workshop_id == reg->alias.workshop_id ) return count; } else{ if( reg->foldername_hash == foldername_djb2 ){ - if( !strcmp( reg->foldername, foldername ) ){ + if( !strcmp( reg->alias.foldername, alias->foldername ) ){ return count; } } @@ -70,9 +73,123 @@ static u32 addon_match( enum workshop_file_type type, return 0xffffffff; } +/* + * Create a string version of addon alias in buf + */ +static void addon_alias_uid( addon_alias *alias, char buf[ADDON_UID_MAX] ){ + if( alias->workshop_id ){ + snprintf( buf, 128, "sr%03d-steam-"PRINTF_U64, + alias->type, alias->workshop_id ); + } + else { + snprintf( buf, 128, "sr%03d-local-%s", + alias->type, alias->foldername ); + } +} + +/* + * equality check + */ +static int addon_alias_eq( addon_alias *a, addon_alias *b ){ + if( a->type == b->type ){ + if( a->workshop_id == b->workshop_id ){ + if( a->workshop_id ) + return 1; + else + return !strcmp( a->foldername, b->foldername ); + } + else + return 0; + } + else return 0; +} + +/* + * make alias represent NULL. + */ +static void invalidate_addon_alias( addon_alias *alias ){ + alias->type = k_addon_type_none; + alias->workshop_id = 0; + alias->foldername[0] = '\0'; +} + +/* + * parse uid to alias. returns 1 if successful + */ +static int addon_uid_to_alias( char uid[ADDON_UID_MAX], addon_alias *alias ){ +/* 1 + * 01234567890123 + * sr&&&-@@@@@-#* + * | | | + * type | id + * | + * location + */ + if( strlen(uid) < 13 ){ + invalidate_addon_alias( alias ); + return 0; + } + if( !((uid[0] == 's') && (uid[1] == 'r')) ){ + invalidate_addon_alias( alias ); + return 0; + } + + char type[4]; + memcpy( type, uid+2, 3 ); + type[3] = '\0'; + alias->type = atoi(type); + + char location[6]; + memcpy( location, uid+6, 5 ); + location[5] = '\0'; + + if( !strcmp(location,"steam") ) + alias->workshop_id = atoll( uid+12 ); + else if( !strcmp(location,"local") ){ + alias->workshop_id = 0; + vg_strncpy( uid+12, alias->foldername, 64, k_strncpy_always_add_null ); + } + else{ + invalidate_addon_alias( alias ); + return 0; + } + + return 1; +} + static void addon_system_init( void ){ u32 reg_size = sizeof(addon_reg)*ADDON_MOUNTED_MAX; addon_system.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size ); + + for( u32 type=0; typecache_count ){ + /* create the allocations pool */ + u32 alloc_size = sizeof(struct addon_cache_entry)*inf->cache_count; + cache->allocs = vg_linear_alloc( vg_mem.rtmemory, alloc_size ); + memset( cache->allocs, 0, alloc_size ); + + cache->pool.buffer = cache->allocs; + cache->pool.count = inf->cache_count; + cache->pool.stride = sizeof( struct addon_cache_entry ); + cache->pool.offset = offsetof( struct addon_cache_entry, poolnode ); + vg_pool_init( &cache->pool ); + + /* create the real memory */ + u32 cache_size = inf->cache_stride*inf->cache_count; + cache->items = vg_linear_alloc( vg_mem.rtmemory, cache_size ); + cache->stride = inf->cache_stride; + memset( cache->items, 0, cache_size ); + + for( i32 j=0; jcache_count; j++ ){ + struct addon_cache_entry *alloc = &cache->allocs[j]; + alloc->reg_ptr = NULL; + alloc->reg_index = 0xffffffff; + } + } + } } /* @@ -83,29 +200,30 @@ static void addon_system_init( void ){ /* * Reciever for scan completion. copies the registry counts back into main fred */ -VG_STATIC void async_addon_reg_update( void *data, u32 size ) +static void async_addon_reg_update( void *data, u32 size ) { vg_info( "Registry update notify\n" ); - for( u32 i=0; ifoldername, 64, k_strncpy_always_add_null ); - reg->foldername_hash = vg_strdjb2( reg->foldername ); +static void addon_set_foldername( addon_reg *reg, const char name[64] ){ + vg_strncpy( name, reg->alias.foldername, 64, k_strncpy_always_add_null ); + reg->foldername_hash = vg_strdjb2( reg->alias.foldername ); } /* * Create a new registry */ -VG_STATIC addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id, - enum workshop_file_type type ){ +static addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id, + enum addon_type type ){ if( addon_system.registry_count == ADDON_MOUNTED_MAX ){ vg_error( "You have too many addons installed!\n" ); return NULL; @@ -113,11 +231,11 @@ VG_STATIC addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id, addon_reg *reg = &addon_system.registry[ addon_system.registry_count ]; reg->metadata_len = 0; - reg->userdata = NULL; + reg->cache_id = 0; reg->state = k_addon_state_indexed; - reg->workshop_id = workshop_id; - reg->foldername[0] = '\0'; - reg->type = type; + reg->alias.workshop_id = workshop_id; + reg->alias.foldername[0] = '\0'; + reg->alias.type = type; if( workshop_id ){ char foldername[64]; @@ -130,7 +248,7 @@ VG_STATIC addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id, /* * If the addon.inf exists int the folder, load into the reg */ -VG_STATIC int addon_try_load_metadata( addon_reg *reg, vg_str folder_path ){ +static int addon_try_load_metadata( addon_reg *reg, vg_str folder_path ){ vg_str meta_path = folder_path; vg_strcat( &meta_path, "/addon.inf" ); if( !vg_strgood( &meta_path ) ){ @@ -157,50 +275,50 @@ VG_STATIC int addon_try_load_metadata( addon_reg *reg, vg_str folder_path ){ return 1; } -VG_STATIC void addon_print_info( addon_reg *reg ){ +static void addon_print_info( addon_reg *reg ){ vg_info( "addon_reg #%u{\n", addon_system.registry_count ); - vg_info( " type: %d\n", reg->type ); - vg_info( " workshop_id: " PRINTF_U64 "\n", reg->workshop_id ); - vg_info( " folder: [%u]%s\n", reg->foldername_hash, reg->foldername ); + vg_info( " type: %d\n", reg->alias.type ); + vg_info( " workshop_id: " PRINTF_U64 "\n", reg->alias.workshop_id ); + vg_info( " folder: [%u]%s\n", reg->foldername_hash, reg->alias.foldername ); vg_info( " metadata_len: %u\n", reg->metadata_len ); - vg_info( " userdata: %p\n", reg->userdata ); + vg_info( " cache_id: %hu\n", reg->cache_id ); vg_info( "}\n" ); } -VG_STATIC void addon_mount_finish( addon_reg *reg ){ +static void addon_mount_finish( addon_reg *reg ){ +#if 0 + addon_print_info( reg ); +#endif addon_system.registry_count ++; } /* * Mount a fully packaged addon, one that certainly has a addon.inf */ -VG_STATIC addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id, +static addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id, vg_str folder_path ) { - addon_reg *reg = addon_alloc_reg( workshop_id, k_workshop_file_type_none ); + addon_reg *reg = addon_alloc_reg( workshop_id, k_addon_type_none ); if( !reg ) return NULL; if( !addon_try_load_metadata( reg, folder_path ) ){ return NULL; } - enum workshop_file_type type = k_workshop_file_type_none; - vg_msg root = {0}; - root.buf = reg->metadata; - root.len = reg->metadata_len; - root.max = sizeof(reg->metadata); + enum addon_type type = k_addon_type_none; + vg_msg msg; + vg_msg_init( &msg, reg->metadata, reg->metadata_len ); - vg_msg workshop = root; - if( vg_msg_seekframe( &workshop, "workshop", k_vg_msg_first )){ - type = vg_msg_seekkvu32( &workshop, "type", k_vg_msg_first ); + if( vg_msg_seekframe( &msg, "workshop" )){ + type = vg_msg_getkvu32( &msg, "type", 0 ); } - if( type == k_workshop_file_type_none ){ + if( type == k_addon_type_none ){ vg_error( "Cannot determine addon type\n" ); return NULL; } - reg->type = type; + reg->alias.type = type; addon_mount_finish( reg ); return reg; } @@ -208,8 +326,8 @@ VG_STATIC addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id, /* * Mount a local folder. may or may not have addon.inf */ -VG_STATIC addon_reg *addon_mount_local_addon( const char *folder, - enum workshop_file_type type, +static addon_reg *addon_mount_local_addon( const char *folder, + enum addon_type type, const char *content_ext ) { char folder_path_buf[4096]; @@ -222,8 +340,8 @@ VG_STATIC addon_reg *addon_mount_local_addon( const char *folder, for( u32 i=0; itype == type) && (reg->foldername_hash == folder_hash) ){ - if( !strcmp( reg->foldername, folder_name ) ){ + if( (reg->alias.type == type) && (reg->foldername_hash == folder_hash) ){ + if( !strcmp( reg->alias.foldername, folder_name ) ){ reg->state = k_addon_state_indexed; return NULL; } @@ -237,10 +355,8 @@ VG_STATIC addon_reg *addon_mount_local_addon( const char *folder, if( reg->metadata_len == 0 ){ /* create our own content commands */ - vg_msg msg = {0}; - msg.buf = reg->metadata; - msg.len = 0; - msg.max = sizeof(reg->metadata); + vg_msg msg; + vg_msg_init( &msg, reg->metadata, sizeof(reg->metadata) ); u32 content_count = 0; @@ -273,7 +389,7 @@ VG_STATIC addon_reg *addon_mount_local_addon( const char *folder, if( !content_count ) return NULL; if( msg.error == k_vg_msg_error_OK ) - reg->metadata_len = msg.cur; + reg->metadata_len = msg.cur.co; else{ vg_error( "Error creating metadata: %d\n", msg.error ); return NULL; @@ -287,7 +403,7 @@ VG_STATIC addon_reg *addon_mount_local_addon( const char *folder, /* * Check all subscribed items */ -VG_STATIC void addon_mount_workshop_items(void){ +static void addon_mount_workshop_items(void){ if( !steam_ready ) return; /* * Steam workshop scan @@ -311,7 +427,7 @@ VG_STATIC void addon_mount_workshop_items(void){ for( u32 i=0; iworkshop_id == id ){ + if( reg->alias.workshop_id == id ){ reg->state = k_addon_state_indexed; goto next_file_workshop; } @@ -339,7 +455,7 @@ next_file_workshop:; * Scan a local content folder for addons. It must find at least one file with * the specified content_ext to be considered. */ -VG_STATIC void addon_mount_content_folder( enum workshop_file_type type, +static void addon_mount_content_folder( enum addon_type type, const char *base_folder, const char *content_ext ) { @@ -354,7 +470,6 @@ VG_STATIC void addon_mount_content_folder( enum workshop_file_type type, vg_dir dir; if( !vg_dir_open(&dir,path.buffer) ){ vg_error( "vg_dir_open('%s') failed\n", path.buffer ); - vg_async_call( workshop_async_any_complete, NULL, 0 ); return; } @@ -379,19 +494,22 @@ VG_STATIC void addon_mount_content_folder( enum workshop_file_type type, vg_dir_close(&dir); } +/* + * write the full path of the addon's folder into the vg_str + */ static int addon_get_content_folder( addon_reg *reg, vg_str *folder ){ - if( reg->workshop_id ){ + if( reg->alias.workshop_id ){ vg_async_item *call = vg_async_alloc( sizeof(struct async_workshop_filepath_info) ); struct async_workshop_filepath_info *info = call->payload; info->buf = folder->buffer; - info->id = reg->workshop_id; + info->id = reg->alias.workshop_id; info->len = folder->len; vg_async_dispatch( call, async_workshop_get_filepath ); vg_async_stall(); /* too bad! */ if( info->buf[0] == '\0' ){ vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n", - reg->workshop_id ); + reg->alias.workshop_id ); return 0; } folder->i = strlen( folder->buffer ); @@ -399,15 +517,268 @@ static int addon_get_content_folder( addon_reg *reg, vg_str *folder ){ } else{ folder->i = 0; - if( reg->type == k_workshop_file_type_board ) - vg_strcat( folder, "boards/" ); - else if( reg->type == k_workshop_file_type_world ) - vg_strcat( folder, "maps/" ); - else return 0; + + const char *local_folder = + addon_type_infos[reg->alias.type].local_content_folder; + + if( !local_folder ) return 0; + vg_strcat( folder, local_folder ); + vg_strcat( folder, reg->alias.foldername ); + return 1; + } +} + +/* + * Return existing cache id if reg_index points to a registry with its cache + * already set. + */ +static u16 addon_cache_fetch( enum addon_type type, u32 reg_index ){ + addon_reg *reg = NULL; + + if( reg_index < addon_count( type ) ){ + reg = get_addon_from_index( type, reg_index ); + if( reg->cache_id ) + return reg->cache_id; + } + + return 0; +} + +/* + * Allocate a new cache item from the pool + */ +static u16 addon_cache_alloc( enum addon_type type, u32 reg_index ){ + struct addon_cache *cache = &addon_system.cache[ type ]; + + u16 new_id = vg_pool_lru( &cache->pool ); + struct addon_cache_entry *new_entry = vg_pool_item( &cache->pool, new_id ); + + addon_reg *reg = NULL; + if( reg_index < addon_count( type ) ) + reg = get_addon_from_index( type, reg_index ); + + if( new_entry ){ + if( new_entry->reg_ptr ) + new_entry->reg_ptr->cache_id = 0; + + if( reg ) + reg->cache_id = new_id; + + new_entry->reg_ptr = reg; + new_entry->reg_index = reg_index; + return new_id; + } + else{ + vg_error( "cache full (type: %u)!\n", type ); + return 0; + } +} + +/* + * Get the real item data for cache id + */ +static void *addon_cache_item( enum addon_type type, u16 id ){ + if( !id ) return NULL; + + struct addon_cache *cache = &addon_system.cache[type]; + return cache->items + ((size_t)(id-1) * cache->stride); +} + +/* + * Get the real item data for cache id ONLY if the item is completely loaded. + */ +static void *addon_cache_item_if_loaded( enum addon_type type, u16 id ){ + 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; +} + +/* + * Updates the item state from the main thread + */ +static void async_addon_setstate( void *_entry, u32 _state ){ + addon_cache_entry *entry = _entry; + SDL_AtomicLock( &addon_system.sl_cache_using_resources ); + entry->state = _state; + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + vg_success( " loaded (%s)\n", entry->reg_ptr->alias.foldername ); +} - vg_strcat( folder, reg->foldername ); +/* + * Handles the loading of an individual item + */ +static int addon_cache_load_request( enum addon_type type, u16 id, + addon_reg *reg, vg_str folder ){ + + /* load content files + * --------------------------------- */ + vg_str content_path = folder; + + vg_msg msg; + vg_msg_init( &msg, reg->metadata, reg->metadata_len ); + + const char *kv_content = vg_msg_getkvstr( &msg, "content" ); + if( kv_content ){ + vg_strcat( &content_path, "/" ); + vg_strcat( &content_path, kv_content ); + } + else{ + vg_error( " No content paths in metadata\n" ); + return 0; + } + + if( !vg_strgood( &content_path ) ) { + vg_error( " Metadata path too long\n" ); + return 0; + } + + if( type == k_addon_type_board ){ + struct player_board *board = addon_cache_item( type, id ); + player_board_load( board, content_path.buffer ); + return 1; + } + else if( type == k_addon_type_player ){ + struct player_model *model = addon_cache_item( type, id ); + player_model_load( model, content_path.buffer ); return 1; } + else { + return 0; + } + + return 0; +} + +static void addon_cache_free_item( enum addon_type type, u16 id ){ + if( type == k_addon_type_board ){ + struct player_board *board = addon_cache_item( type, id ); + player_board_unload( board ); + } + else if( type == k_addon_type_player ){ + struct player_model *model = addon_cache_item( type, id ); + player_model_unload( model ); + } +} + +/* + * Goes over cache item load requests and calls the above ^ + */ +static void addon_cache_load_loop(void){ + vg_info( "Running load loop\n" ); + char path_buf[4096]; + + for( u32 type=0; typepool.count; id++ ){ + addon_cache_entry *entry = vg_pool_item( &cache->pool, id ); + + SDL_AtomicLock( &addon_system.sl_cache_using_resources ); + 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 ); + + if( entry->reg_index >= addon_count(type) ){ + /* should maybe have a different value for this case */ + entry->state = k_addon_cache_state_none; + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + continue; + } + + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + + /* continue with the request */ + addon_reg *reg = get_addon_from_index( type, entry->reg_index ); + entry->reg_ptr = reg; + + vg_str folder; + vg_strnull( &folder, path_buf, 4096 ); + if( addon_get_content_folder( reg, &folder ) ){ + if( addon_cache_load_request( type, id, reg, folder ) ){ + vg_async_call( async_addon_setstate, + entry, k_addon_cache_state_loaded ); + continue; + } + } + + vg_warn( "cache item did not load (%u#%u)\n", type, id ); + SDL_AtomicLock( &addon_system.sl_cache_using_resources ); + entry->state = k_addon_cache_state_none; + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + } + else + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + } + } +} + +/* + * Perform the cache interactions required to create a viewslot which will + * eventually be loaded by other parts of the system. + */ +static u16 addon_cache_create_viewer( enum addon_type type, u16 reg_id ){ + struct addon_cache *cache = &addon_system.cache[type]; + vg_pool *pool = &cache->pool; + + u16 cache_id = addon_cache_fetch( type, reg_id ); + if( !cache_id ){ + cache_id = addon_cache_alloc( type, reg_id ); + + if( cache_id ){ + SDL_AtomicLock( &addon_system.sl_cache_using_resources ); + addon_cache_entry *entry = vg_pool_item( pool, cache_id ); + + if( entry->state == k_addon_cache_state_loaded ){ + addon_cache_free_item( type, cache_id ); + } + + entry->state = k_addon_cache_state_load_request; + SDL_AtomicUnlock( &addon_system.sl_cache_using_resources ); + } + } + + if( cache_id ) + vg_pool_watch( pool, cache_id ); + + return cache_id; +} + +static u16 addon_cache_create_viewer_from_uid( enum addon_type type, + char uid[ADDON_UID_MAX] ){ + addon_alias q; + 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 ){ + vg_warn( "We dont have the addon '%s' installed.\n", uid ); + return 0; + } + else { + return addon_cache_create_viewer( type, reg_id ); + } +} + +static void addon_cache_watch( enum addon_type type, u16 cache_id ){ + if( !cache_id ) return; + + struct addon_cache *cache = &addon_system.cache[type]; + vg_pool *pool = &cache->pool; + vg_pool_watch( pool, cache_id ); +} + +static void addon_cache_unwatch( enum addon_type type, u16 cache_id ){ + if( !cache_id ) return; + + struct addon_cache *cache = &addon_system.cache[type]; + vg_pool *pool = &cache->pool; + vg_pool_unwatch( pool, cache_id ); } #endif /* ADDON_C */