static void addon_system_init( void );
static u32 addon_count( enum addon_type type );
-static addon_reg *get_addon_from_index(enum addon_type type, u32 index);
+static addon_reg *get_addon_from_index( enum addon_type type, u32 index );
static u32 get_index_from_addon( enum addon_type type, addon_reg *a );
static int addon_get_content_folder( addon_reg *reg, vg_str *folder );
vg_build_symbolic_link( "sound_src", "sound" );
vg_build_symbolic_link( "playermodels_src", "playermodels" );
vg_build_syscall( "mkdir -p %s/cfg", vg_compiler.build_dir );
+ vg_build_syscall( "mkdir -p %s/savedata", vg_compiler.build_dir );
vg_build_syscall( "mkdir -p %s/tools", vg_compiler.build_dir );
vg_build_syscall( "cp blender_export.py %s/tools/", vg_compiler.build_dir );
localplayer.board_view_slot = cache_id;
global_skateshop_exit();
- skaterift_write_main_savedata();
+ skaterift_autosave(1);
return;
}
}
#define SAVE_C
#include "save.h"
+#include "addon.h"
#include "vg/vg_msg.h"
+#include "vg/vg_log.h"
+#include "world.h"
-static void savedata_write_thread( void *file ){
- struct savedata *sav = file;
+static void savedata_file_write( savedata_file *file ){
+ savedata_file *sav = file;
FILE *fp = fopen( sav->path, "wb" );
if( fp ){
fwrite( sav->buf, sav->len, 1, fp );
fclose( fp );
+ vg_success( "savedata written to '%s'\n", sav->path );
}
else {
vg_error( "Error writing savedata (%s)\n", sav->path );
}
}
-static void savedata_read( struct savedata *sav ){
- FILE *fp = fopen( sav->path, "rb" );
+static void savedata_group_write( savedata_group *group ){
+ for( u32 i=0; i<group->file_count; i++ ){
+ savedata_file_write( &group->files[i] );
+ }
+}
+
+static void savedata_file_read( savedata_file *file ){
+ FILE *fp = fopen( file->path, "rb" );
if( fp ){
- sav->len = fread( sav->buf, 1, sizeof(sav->buf), fp );
+ file->len = fread( file->buf, 1, sizeof(file->buf), fp );
fclose( fp );
}
else{
- sav->len = 0;
- vg_warn( "Error reading savedata (%s)\n", sav->path );
+ file->len = 0;
+ vg_warn( "Error reading savedata (%s)\n", file->path );
}
}
static void 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;
alias->type = type;
alias->workshop_id = vg_msg_read_as_u64( &kv );
}
-static void skaterift_write_main_savedata(void){
- if( !vg_loader_availible() ) return;
+static void skaterift_populate_world_savedata( savedata_file *file,
+ enum world_purpose which ){
+ file->path[0] = '\0';
+ file->len = 0;
+ addon_reg *reg = NULL;
+ if( which == k_world_purpose_hub ) reg = world_static.addon_hub;
+ else reg = world_static.addon_client;
+
+ if( !reg ){
+ vg_error( "Tried to save unspecified world (reg was null)\n" );
+ return;
+ }
- vg_linear_clear( vg_async.buffer );
- struct savedata *sav = vg_linear_alloc( vg_async.buffer,
- vg_align8(sizeof(struct savedata)) );
+ skaterift_world_get_save_path( which, file->path );
+
+ vg_msg sav = {0};
+ sav.buf = file->buf;
+ sav.max = sizeof(file->buf);
+
+ if( which == k_world_purpose_hub ){
+ if( world_static.instances[0].status == k_world_status_loaded )
+ world_entity_serialize( &world_static.instances[0], &sav );
+ }
+ else {
+ for( u32 i=1; i<vg_list_size(world_static.instances); i++ ){
+ world_instance *instance = &world_static.instances[i];
+ if( instance->status == k_world_status_loaded ){
+ world_entity_serialize( instance, &sav );
+ }
+ }
+ }
+
+ file->len = sav.len;
+}
- strcpy( sav->path, "save.bkv" );
+static void skaterift_populate_main_savedata( savedata_file *file ){
+ strcpy( file->path, str_skaterift_main_save );
- vg_msg kvsav = {0};
- kvsav.buf = sav->buf;
- kvsav.max = sizeof(sav->buf);
+ vg_msg sav = {0};
+ sav.buf = file->buf;
+ sav.max = sizeof(file->buf);
- vg_msg_frame( &kvsav, "player" );
+ vg_msg_frame( &sav, "player" );
{
- skaterift_write_viewslot( &kvsav, "board", k_addon_type_board,
+ skaterift_write_viewslot( &sav, "board", k_addon_type_board,
localplayer.board_view_slot );
- skaterift_write_viewslot( &kvsav, "playermodel", k_addon_type_player,
+ skaterift_write_viewslot( &sav, "playermodel", k_addon_type_player,
localplayer.playermodel_view_slot );
}
- vg_msg_end_frame( &kvsav );
+ vg_msg_end_frame( &sav );
- vg_msg_frame( &kvsav, "world" );
+ vg_msg_frame( &sav, "world" );
{
addon_reg *reg = world_static.addon_client;
if( reg && (world_static.active_instance > 0) ){
- skaterift_write_addon_alias( &kvsav, "alias", ®->alias );
- vg_msg_wkvu32( &kvsav, "index", world_static.active_instance );
- vg_msg_wkvnum( &kvsav, "position", k_vg_msg_float|k_vg_msg_32b, 3,
+ skaterift_write_addon_alias( &sav, "alias", ®->alias );
+ vg_msg_wkvu32( &sav, "index", world_static.active_instance );
+ vg_msg_wkvnum( &sav, "position", k_vg_msg_float|k_vg_msg_32b, 3,
localplayer.rb.co );
}
}
- vg_msg_end_frame( &kvsav );
+ vg_msg_end_frame( &sav );
+
+ file->len = sav.len;
+}
+
+static int skaterift_autosave( int async ){
+ if( async )
+ if( !vg_loader_availible() ) return 0;
+
+ u32 save_files = 2;
+ if( world_static.addon_client )
+ save_files ++;
+
+ vg_linear_clear( vg_async.buffer );
+ u32 size = sizeof(savedata_group) + sizeof(savedata_file) * save_files;
+
+ savedata_group *group;
+ if( async ){
+ size = vg_align8( size );
+ group = vg_linear_alloc( vg_async.buffer, size );
+ }
+ else
+ group = alloca( size );
+
+ group->file_count = save_files;
+ skaterift_populate_main_savedata( &group->files[0] );
+ skaterift_populate_world_savedata( &group->files[1], k_world_purpose_hub );
+
+ if( world_static.addon_client )
+ skaterift_populate_world_savedata( &group->files[2],
+ k_world_purpose_client );
+
+ if( async )
+ vg_loader_start( (void *)savedata_group_write, group );
+ else
+ savedata_group_write( group );
- sav->len = kvsav.len;
- vg_loader_start( savedata_write_thread, sav );
+ return 1;
}
-static void skaterift_read_main_savedata( struct savedata *sav ){
- strcpy( sav->path, "save.bkv" );
- savedata_read( sav );
+static void skaterift_autosave_synchronous(void){
+ skaterift_autosave(0);
}
#endif /* SAVE_C */
#include "vg/vg_stdint.h"
-struct savedata {
- char path[128];
- u8 buf[1024];
- u32 len;
+static const char *str_skaterift_main_save = "save.bkv";
+static f64 skaterift_last_autosave = 0.0;
+
+typedef struct savedata_file savedata_file;
+typedef struct savedata_group savedata_group;
+
+struct savedata_group {
+ u32 file_count;
+ struct savedata_file {
+ char path[128];
+ u8 buf[1024];
+ u32 len;
+ }
+ files[];
};
-#include "ent_skateshop.h"
+static void savedata_file_read( savedata_file *file );
+static void savedata_file_write( savedata_file *file );
+static void savedata_group_write( savedata_group *group );
-static void savedata_read( struct savedata *sav );
-static void savedata_write_thread( void *file );
+//static void skaterift_read_main_save( savedata_file *sav );
+static int skaterift_autosave(int async);
-static void skaterift_read_main_savedata( struct savedata *sav );
-static void skaterift_write_main_savedata(void);
#endif /* SAVE_H */
}
static void skaterift_restore_state(void){
- struct savedata sav;
- skaterift_read_main_savedata( &sav );
+ savedata_file sav;
+ strcpy( sav.path, str_skaterift_main_save );
+ savedata_file_read( &sav );
+
vg_msg kvsav = {0};
kvsav.buf = sav.buf;
kvsav.len = sav.len;
/* and now */
skaterift_restore_state();
- vg_loader_step( NULL, skaterift_write_main_savedata );
+ vg_loader_step( NULL, skaterift_autosave_synchronous );
board_processview_thread(NULL);
vg_async_call( async_call_ready, NULL, 0 );
audio_unlock();
vehicle_update_post();
+
+ if( vg.time - skaterift.last_autosave > 20.0 ){
+ if( skaterift_autosave(1) ){
+ skaterift.last_autosave = vg.time;
+ }
+ }
}
/*
k_skaterift_menu = 0x04
}
activity;
+
+ f64 last_autosave;
}
static skaterift = { .op = k_async_op_clientloading, .time_rate = 1.0f };
VG_MEMORY_SYSTEM );
}
+static void skaterift_world_get_save_path( enum world_purpose which,
+ char buf[128] ){
+ addon_reg *reg;
+
+ if( which == k_world_purpose_hub ) reg = world_static.addon_hub;
+ else reg = world_static.addon_client;
+
+ assert( reg );
+
+ char id[76];
+ addon_alias_uid( ®->alias, id );
+ snprintf( buf, 128, "savedata/%s.bkv", id );
+}
+
+
#include "world_entity.c"
#include "world_gate.c"
#include "world_gen.c"
k_world_geo_type_water = 2
};
+enum world_purpose{
+ k_world_purpose_hub,
+ k_world_purpose_client
+}
+purpose;
+
typedef struct world_instance world_instance;
+static void skaterift_world_get_save_path( enum world_purpose which,
+ char buf[128] );
+
/* submodule headers */
#include "world_entity.h"
#include "world_gate.h"
#include "world.h"
#include "world_load.h"
#include "save.h"
+#include "vg/vg_msg.h"
VG_STATIC void world_gen_entities_init( world_instance *world ){
/* lights */
}
}
-VG_STATIC void world_entity_start( world_instance *world,
- struct savedata *sav ){
+VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav ){
vg_info( "Start instance %p\n", world );
world->probabilities[ k_probability_curve_constant ] = 1.0f;
entity_call( world, &call );
}
}
+
+ /* read savedata
+ * ----------------------------------------------------------------------- */
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
+ ent_unlock *unlock = mdl_arritm( &world->ent_unlock, i );
+ const char *alias = mdl_pstr( &world->meta, unlock->pstr_alias );
+
+ if( vg_msg_seekkvu32( sav, alias, k_vg_msg_first ) ){
+ ent_call call;
+ call.data = NULL;
+ call.function = 0;
+ call.id = mdl_entity_id( k_ent_unlock, i );
+ entity_call( world, &call );
+ }
+ }
}
-VG_STATIC void async_world_entity_start( void *payload, u32 size ){
+VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav ){
+ for( u32 i=0; i<mdl_arrcount(&world->ent_unlock); i++ ){
+ ent_unlock *unlock = mdl_arritm(&world->ent_unlock,i);
+ const char *alias = mdl_pstr(&world->meta,unlock->pstr_alias);
+ vg_msg_wkvu32( sav, alias, unlock->status );
+ }
}
#endif /* WORLD_ENTITY_C */
#include "entity.h"
#include "bvh.h"
#include "save.h"
+#include "vg/vg_msg.h"
VG_STATIC void world_gen_entities_init( world_instance *world );
VG_STATIC ent_spawn *world_find_spawn_by_name( world_instance *world,
const char *name );
VG_STATIC ent_spawn *world_find_closest_spawn( world_instance *world,
v3f position );
-VG_STATIC void world_entity_start( world_instance *world,
- struct savedata *sav );
+VG_STATIC void world_entity_start( world_instance *world, vg_msg *sav );
+VG_STATIC void world_entity_serialize( world_instance *world, vg_msg *sav );
VG_STATIC void ent_volume_call( world_instance *world, ent_call *call );
VG_STATIC void ent_audio_call( world_instance *world, ent_call *call );
#include "ent_skateshop.h"
#include "addon.h"
#include "save.h"
+#include "vg/vg_msg.h"
/*
* load the .mdl file located in path as a world instance
struct world_load_complete_data{
- struct savedata save;
+ savedata_file save;
u32 instance_start, instance_count;
};
static void skaterift_world_load_done( void *payload, u32 size ){
struct world_load_complete_data *data = payload;
+ vg_msg sav = {0};
+ sav.buf = data->save.buf;
+ sav.len = data->save.len;
+ sav.max = data->save.len;
+
for( u32 i=0; i<data->instance_count; i++ ){
world_instance *world = &world_static.instances[ data->instance_start+i ];
- world_entity_start( world, &data->save );
+ world_entity_start( world, &sav );
}
world_static.load_state = k_world_loader_none;
}
struct world_load_args {
- enum world_purpose{
- k_world_purpose_hub,
- k_world_purpose_client
- }
- purpose;
+ enum world_purpose purpose;
addon_reg *reg;
};
struct world_load_complete_data *data = final_call->payload;
data->instance_start = instance_start;
data->instance_count = instance_count;
- strcpy( data->save.path, "temp_fuckyou.bkv" );
- savedata_read( &data->save );
+ skaterift_world_get_save_path( purpose, data->save.path );
+ savedata_file_read( &data->save );
vg_async_dispatch( final_call, skaterift_world_load_done );
vg_async_stall();
char buf[76];
addon_alias_uid( ®->alias, buf );
vg_info( "switching to: %s\n", buf );
+ skaterift_autosave(1);
world_static.load_state = k_world_loader_preload;
if( inst->status == k_world_status_loaded ){
inst->status = k_world_status_unloading;
world_fadeout_audio( inst );
-
- /* TODO: THIS IS WHERE A SAVE SHOULD BE DONE */
}
}
return 0;
}
-#if 0
-static world_instance *world_loading_instance(void){
- return &world_static.instances[ world_loader.world_index ];
-}
-#endif
-
/*
* checks:
* 1. to see if all audios owned by the world have been stopped