mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
JSON Savefiles | Player Saves use JSON (#70492)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may not be viewable. --> <!-- You can view Contributing.MD for a detailed description of the pull request process. --> TODO: - [x] DOCUMENT SHIT - [x] UPDATE DOCUMENTATION ## About The Pull Request Adds a new datum, which is intended to be a replacement for the stock savefile type, json_savefile As you can imagine, this is essentially just a wrapper around a json file for reading/writing/manipulation that is intended to be a dropin replacement for savefiles It also have the ability to import stock savefiles and parse them into a json tree <!-- Describe The Pull Request. Please be sure every change is documented or this can delay review and even discourage maintainers from merging your PR! --> ## Why It's Good For The Game Permission obtained from MSO and Mothblocks. <!-- Argue for the merits of your changes and how they benefit the game, especially if they are controversial and/or far reaching. If you can't actually explain WHY what you are doing will improve the game, then it probably isn't good for the game in the first place. --> ## Changelog <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> Not player facing, tested locally exhaustively to ensure it doesnt break shit 🆑 /🆑 <!-- Both 🆑's are required for the changelog to work! You can put your name to the right of the first 🆑 if you want to overwrite your GitHub username as author ingame. --> <!-- You can use multiple of the same prefix (they're only used for the icon ingame) and delete the unneeded ones. Despite some of the tags, changelogs should generally represent how a player might be affected by the changes rather than a summary of the PR's contents. --> Co-authored-by: Kyle Spier-Swenson <kyleshome@gmail.com> Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: san7890 <the@san7890.com>
This commit is contained in:
co-authored by
Kyle Spier-Swenson
Mothblocks
san7890
parent
6b6151e16d
commit
47ba13033f
@@ -66,11 +66,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
/// A list of instantiated middleware
|
||||
var/list/datum/preference_middleware/middleware = list()
|
||||
|
||||
/// The savefile relating to core preferences, PREFERENCE_PLAYER
|
||||
var/savefile/game_savefile
|
||||
/// The json savefile for this datum
|
||||
var/datum/json_savefile/savefile
|
||||
|
||||
/// The savefile relating to character preferences, PREFERENCE_CHARACTER
|
||||
var/savefile/character_savefile
|
||||
var/list/character_data
|
||||
|
||||
/// A list of keys that have been updated since the last save.
|
||||
var/list/recently_updated_keys = list()
|
||||
@@ -88,18 +88,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
value_cache = null
|
||||
return ..()
|
||||
|
||||
/datum/preferences/New(client/C)
|
||||
parent = C
|
||||
/datum/preferences/New(client/parent)
|
||||
src.parent = parent
|
||||
|
||||
for (var/middleware_type in subtypesof(/datum/preference_middleware))
|
||||
middleware += new middleware_type(src)
|
||||
|
||||
if(istype(C))
|
||||
if(!is_guest_key(C.key))
|
||||
load_path(C.ckey)
|
||||
unlock_content = !!C.IsByondMember()
|
||||
if(IS_CLIENT_OR_MOCK(parent))
|
||||
if(!is_guest_key(parent.key))
|
||||
load_path(parent.ckey)
|
||||
if(!fexists(path))
|
||||
try_savefile_type_migration()
|
||||
unlock_content = !!parent.IsByondMember()
|
||||
if(unlock_content)
|
||||
max_save_slots = 8
|
||||
else
|
||||
CRASH("attempted to create a preferences datum without a client or mock!")
|
||||
load_savefile()
|
||||
|
||||
// give them default keybinds and update their movement keys
|
||||
key_bindings = deep_copy_list(GLOB.default_hotkeys)
|
||||
@@ -112,9 +117,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
return
|
||||
//we couldn't load character data so just randomize the character appearance + name
|
||||
randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man.
|
||||
if(C)
|
||||
if(parent)
|
||||
apply_all_client_preferences()
|
||||
C.set_macros()
|
||||
parent.set_macros()
|
||||
|
||||
if(!loaded_preferences_successfully)
|
||||
save_preferences()
|
||||
@@ -370,17 +375,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
/datum/preferences/proc/create_character_profiles()
|
||||
var/list/profiles = list()
|
||||
|
||||
var/savefile/savefile = new(path)
|
||||
for (var/index in 1 to max_save_slots)
|
||||
// It won't be updated in the savefile yet, so just read the name directly
|
||||
if (index == default_slot)
|
||||
profiles += read_preference(/datum/preference/name/real_name)
|
||||
continue
|
||||
|
||||
savefile.cd = "/character[index]"
|
||||
|
||||
var/name
|
||||
READ_FILE(savefile["real_name"], name)
|
||||
var/tree_key = "character[index]"
|
||||
var/save_data = savefile.get_entry(tree_key)
|
||||
var/name = save_data?["real_name"]
|
||||
|
||||
if (isnull(name))
|
||||
profiles += null
|
||||
|
||||
@@ -156,13 +156,13 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|
||||
|
||||
/// Given a savefile, return either the saved data or an acceptable default.
|
||||
/// This will write to the savefile if a value was not found with the new value.
|
||||
/datum/preference/proc/read(savefile/savefile, datum/preferences/preferences)
|
||||
/datum/preference/proc/read(list/save_data, datum/preferences/preferences)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
|
||||
var/value
|
||||
|
||||
if (!isnull(savefile))
|
||||
READ_FILE(savefile[savefile_key], value)
|
||||
if (!isnull(save_data))
|
||||
value = save_data[savefile_key]
|
||||
|
||||
if (isnull(value))
|
||||
return null
|
||||
@@ -172,14 +172,14 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|
||||
/// Given a savefile, writes the inputted value.
|
||||
/// Returns TRUE for a successful application.
|
||||
/// Return FALSE if it is invalid.
|
||||
/datum/preference/proc/write(savefile/savefile, value)
|
||||
/datum/preference/proc/write(list/save_data, value)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
|
||||
if (!is_valid(value))
|
||||
return FALSE
|
||||
|
||||
if (!isnull(savefile))
|
||||
WRITE_FILE(savefile[savefile_key], serialize(value))
|
||||
if (!isnull(save_data))
|
||||
save_data[savefile_key] = serialize(value)
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -205,31 +205,22 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|
||||
CRASH("`apply_to_human()` was not implemented for [type]!")
|
||||
|
||||
/// Returns which savefile to use for a given savefile identifier
|
||||
/datum/preferences/proc/get_savefile_for_savefile_identifier(savefile_identifier)
|
||||
RETURN_TYPE(/savefile)
|
||||
/datum/preferences/proc/get_save_data_for_savefile_identifier(savefile_identifier)
|
||||
RETURN_TYPE(/list)
|
||||
|
||||
if (!parent)
|
||||
return null
|
||||
if(!savefile)
|
||||
CRASH("Attempted to get the savedata for [savefile_identifier] of [parent] without a savefile. This should have been handled by load_preferences()")
|
||||
|
||||
// Both of these will cache savefiles, but only for a tick.
|
||||
// This is because storing a savefile will lock it, causing later issues down the line.
|
||||
// Do not change them to addtimer, since the timer SS might not be running at this time.
|
||||
|
||||
switch (savefile_identifier)
|
||||
if (PREFERENCE_CHARACTER)
|
||||
if (!character_savefile)
|
||||
character_savefile = new /savefile(path)
|
||||
character_savefile.cd = "/character[default_slot]"
|
||||
spawn (1)
|
||||
character_savefile = null
|
||||
return character_savefile
|
||||
return savefile.get_entry("character[default_slot]")
|
||||
if (PREFERENCE_PLAYER)
|
||||
if (!game_savefile)
|
||||
game_savefile = new /savefile(path)
|
||||
game_savefile.cd = "/"
|
||||
spawn (1)
|
||||
game_savefile = null
|
||||
return game_savefile
|
||||
return savefile.get_entry()
|
||||
else
|
||||
CRASH("Unknown savefile identifier [savefile_identifier]")
|
||||
|
||||
@@ -252,7 +243,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|
||||
if (preference_type in value_cache)
|
||||
return value_cache[preference_type]
|
||||
|
||||
var/value = preference_entry.read(get_savefile_for_savefile_identifier(preference_entry.savefile_identifier), src)
|
||||
var/value = preference_entry.read(get_save_data_for_savefile_identifier(preference_entry.savefile_identifier), src)
|
||||
if (isnull(value))
|
||||
value = preference_entry.create_informed_default_value(src)
|
||||
if (write_preference(preference_entry, value))
|
||||
@@ -266,9 +257,9 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
|
||||
/// Returns TRUE for a successful preference application.
|
||||
/// Returns FALSE if it is invalid.
|
||||
/datum/preferences/proc/write_preference(datum/preference/preference, preference_value)
|
||||
var/savefile = get_savefile_for_savefile_identifier(preference.savefile_identifier)
|
||||
var/save_data = get_save_data_for_savefile_identifier(preference.savefile_identifier)
|
||||
var/new_value = preference.deserialize(preference_value, src)
|
||||
var/success = preference.write(savefile, new_value)
|
||||
var/success = preference.write(save_data, new_value)
|
||||
if (success)
|
||||
value_cache[preference.type] = new_value
|
||||
return success
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
/// PR #62733 changed this to allow all characters to use body type.
|
||||
/// This migration moves binary-gendered characters over to the "use gender" body type
|
||||
/// so that old characters are preserved.
|
||||
/datum/preferences/proc/migrate_body_types(savefile/savefile)
|
||||
var/current_gender
|
||||
|
||||
READ_FILE(savefile["gender"], current_gender)
|
||||
/datum/preferences/proc/migrate_body_types(list/save_data)
|
||||
var/current_gender = save_data["gender"]
|
||||
if (current_gender == MALE || current_gender == FEMALE)
|
||||
WRITE_FILE(savefile["body_type"], "Use gender")
|
||||
save_data["body_type"] = "Use gender"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/datum/preferences/proc/try_savefile_type_migration()
|
||||
load_path(parent.ckey, "preferences.sav") // old save file
|
||||
var/old_path = path
|
||||
load_path(parent.ckey)
|
||||
if(!fexists(old_path))
|
||||
return
|
||||
var/datum/json_savefile/json_savefile = new(path)
|
||||
json_savefile.import_byond_savefile(new /savefile(old_path))
|
||||
json_savefile.save()
|
||||
return TRUE
|
||||
@@ -23,15 +23,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
Failing all that, the standard sanity checks are performed. They simply check the data is suitable, reverting to
|
||||
initial() values if necessary.
|
||||
*/
|
||||
/datum/preferences/proc/savefile_needs_update(savefile/S)
|
||||
var/savefile_version
|
||||
READ_FILE(S["version"], savefile_version)
|
||||
|
||||
if(savefile_version < SAVEFILE_VERSION_MIN)
|
||||
S.dir.Cut()
|
||||
/datum/preferences/proc/save_data_needs_update(list/save_data)
|
||||
if(!save_data) // empty list, either savefile isnt loaded or its a new char
|
||||
return -1
|
||||
if(save_data["version"] < SAVEFILE_VERSION_MIN)
|
||||
return -2
|
||||
if(savefile_version < SAVEFILE_VERSION_MAX)
|
||||
return savefile_version
|
||||
if(save_data["version"] < SAVEFILE_VERSION_MAX)
|
||||
return save_data["version"]
|
||||
return -1
|
||||
|
||||
//should these procs get fairly long
|
||||
@@ -41,7 +39,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
//This only really meant to avoid annoying frequent players
|
||||
//if your savefile is 3 months out of date, then 'tough shit'.
|
||||
|
||||
/datum/preferences/proc/update_preferences(current_version, savefile/S)
|
||||
/datum/preferences/proc/update_preferences(current_version, datum/json_savefile/S)
|
||||
if(current_version < 34)
|
||||
write_preference(/datum/preference/toggle/auto_fit_viewport, TRUE)
|
||||
|
||||
@@ -93,12 +91,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if (current_version < 41)
|
||||
migrate_preferences_to_tgui_prefs_menu()
|
||||
|
||||
/datum/preferences/proc/update_character(current_version, savefile/savefile)
|
||||
/datum/preferences/proc/update_character(current_version, list/save_data)
|
||||
if (current_version < 41)
|
||||
migrate_character_to_tgui_prefs_menu()
|
||||
|
||||
if (current_version < 42)
|
||||
migrate_body_types(savefile)
|
||||
migrate_body_types(save_data)
|
||||
|
||||
if (current_version < 43)
|
||||
migrate_legacy_sound_toggles(savefile)
|
||||
@@ -141,50 +139,48 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
var/datum/keybinding/conflicted = item
|
||||
to_chat(parent, span_danger("[conflicted.category]: [conflicted.full_name] needs updating"))
|
||||
|
||||
|
||||
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
|
||||
/datum/preferences/proc/load_path(ckey, filename="preferences.json")
|
||||
if(!ckey)
|
||||
return
|
||||
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
|
||||
|
||||
/datum/preferences/proc/load_preferences()
|
||||
/datum/preferences/proc/load_savefile()
|
||||
if(!path)
|
||||
return FALSE
|
||||
if(!fexists(path))
|
||||
return FALSE
|
||||
CRASH("Attempted to load savefile without first loading a path!")
|
||||
savefile = new /datum/json_savefile(path)
|
||||
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return FALSE
|
||||
S.cd = "/"
|
||||
/datum/preferences/proc/load_preferences()
|
||||
if(!savefile)
|
||||
stack_trace("Attempted to load the preferences of [parent] without a savefile; did you forget to call load_savefile?")
|
||||
load_savefile()
|
||||
if(!savefile)
|
||||
stack_trace("Failed to load the savefile for [parent] after manually calling load_savefile; something is very wrong.")
|
||||
return FALSE
|
||||
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
var/needs_update = save_data_needs_update(savefile.get_entry())
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
||||
if (fexists(bacpath))
|
||||
fdel(bacpath) //only keep 1 version of backup
|
||||
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
fcopy(savefile.path, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
return FALSE
|
||||
|
||||
apply_all_client_preferences()
|
||||
|
||||
//general preferences
|
||||
READ_FILE(S["lastchangelog"], lastchangelog)
|
||||
|
||||
READ_FILE(S["be_special"] , be_special)
|
||||
|
||||
|
||||
READ_FILE(S["default_slot"], default_slot)
|
||||
READ_FILE(S["chat_toggles"], chat_toggles)
|
||||
READ_FILE(S["toggles"], toggles)
|
||||
READ_FILE(S["ignoring"], ignoring)
|
||||
lastchangelog = savefile.get_entry("lastchangelog")
|
||||
be_special = savefile.get_entry("be_special")
|
||||
default_slot = savefile.get_entry("default_slot")
|
||||
chat_toggles = savefile.get_entry("chat_toggles")
|
||||
toggles = savefile.get_entry("toggles")
|
||||
ignoring = savefile.get_entry("ignoring")
|
||||
|
||||
// OOC commendations
|
||||
READ_FILE(S["hearted_until"], hearted_until)
|
||||
hearted_until = savefile.get_entry("hearted_until")
|
||||
if(hearted_until > world.realtime)
|
||||
hearted = TRUE
|
||||
//favorite outfits
|
||||
READ_FILE(S["favorite_outfits"], favorite_outfits)
|
||||
favorite_outfits = savefile.get_entry("favorite_outfits")
|
||||
|
||||
var/list/parsed_favs = list()
|
||||
for(var/typetext in favorite_outfits)
|
||||
@@ -194,15 +190,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
favorite_outfits = unique_list(parsed_favs)
|
||||
|
||||
// Custom hotkeys
|
||||
READ_FILE(S["key_bindings"], key_bindings)
|
||||
key_bindings = savefile.get_entry("key_bindings")
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
if(needs_update >= 0)
|
||||
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
||||
if (fexists(bacpath))
|
||||
fdel(bacpath) //only keep 1 version of backup
|
||||
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer)
|
||||
fcopy(savefile.path, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
update_preferences(needs_update, savefile) //needs_update = savefile_version if we need an update (positive integer)
|
||||
|
||||
check_keybindings() // this apparently fails every time and overwrites any unloaded prefs with the default values, so don't load anything after this line or it won't actually save
|
||||
key_bindings_by_key = get_key_bindings_by_key(key_bindings)
|
||||
@@ -219,7 +215,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
var/old_default_slot = default_slot
|
||||
var/old_max_save_slots = max_save_slots
|
||||
|
||||
for (var/slot in S.dir) //but first, update all current character slots.
|
||||
for (var/slot in savefile.get_entry()) //but first, update all current character slots.
|
||||
if (copytext(slot, 1, 10) != "character")
|
||||
continue
|
||||
var/slotnum = text2num(copytext(slot, 10))
|
||||
@@ -236,14 +232,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
return TRUE
|
||||
|
||||
/datum/preferences/proc/save_preferences()
|
||||
if(!path)
|
||||
return FALSE
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return FALSE
|
||||
S.cd = "/"
|
||||
|
||||
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
|
||||
if(!savefile)
|
||||
CRASH("Attempted to save the preferences of [parent] without a savefile. This should have been handled by load_preferences()")
|
||||
savefile.set_entry("version", SAVEFILE_VERSION_MAX) //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
|
||||
|
||||
for (var/preference_type in GLOB.preference_entries)
|
||||
var/datum/preference/preference = GLOB.preference_entries[preference_type]
|
||||
@@ -258,41 +249,31 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if (preference_type in value_cache)
|
||||
write_preference(preference, preference.serialize(value_cache[preference_type]))
|
||||
|
||||
//general preferences
|
||||
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
||||
WRITE_FILE(S["be_special"], be_special)
|
||||
WRITE_FILE(S["default_slot"], default_slot)
|
||||
WRITE_FILE(S["toggles"], toggles)
|
||||
WRITE_FILE(S["chat_toggles"], chat_toggles)
|
||||
WRITE_FILE(S["ignoring"], ignoring)
|
||||
WRITE_FILE(S["key_bindings"], key_bindings)
|
||||
WRITE_FILE(S["hearted_until"], (hearted_until > world.realtime ? hearted_until : null))
|
||||
WRITE_FILE(S["favorite_outfits"], favorite_outfits)
|
||||
savefile.set_entry("lastchangelog", lastchangelog)
|
||||
savefile.set_entry("be_special", be_special)
|
||||
savefile.set_entry("default_slot", default_slot)
|
||||
savefile.set_entry("toggles", toggles)
|
||||
savefile.set_entry("chat_toggles", chat_toggles)
|
||||
savefile.set_entry("ignoring", ignoring)
|
||||
savefile.set_entry("key_bindings", key_bindings)
|
||||
savefile.set_entry("hearted_until", (hearted_until > world.realtime ? hearted_until : null))
|
||||
savefile.set_entry("favorite_outfits", favorite_outfits)
|
||||
savefile.save()
|
||||
return TRUE
|
||||
|
||||
/datum/preferences/proc/load_character(slot)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
if(!path)
|
||||
return FALSE
|
||||
if(!fexists(path))
|
||||
return FALSE
|
||||
|
||||
character_savefile = null
|
||||
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return FALSE
|
||||
S.cd = "/"
|
||||
if(!slot)
|
||||
slot = default_slot
|
||||
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
|
||||
if(slot != default_slot)
|
||||
default_slot = slot
|
||||
WRITE_FILE(S["default_slot"] , slot)
|
||||
savefile.set_entry("default_slot", slot)
|
||||
|
||||
S.cd = "/character[slot]"
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
var/tree_key = "character[slot]"
|
||||
var/list/save_data = savefile.get_entry(tree_key)
|
||||
var/needs_update = save_data_needs_update(save_data)
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
return FALSE
|
||||
|
||||
@@ -306,22 +287,23 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
read_preference(preference_type)
|
||||
|
||||
//Character
|
||||
READ_FILE(S["randomise"], randomise)
|
||||
randomise = save_data?["randomise"]
|
||||
|
||||
//Load prefs
|
||||
READ_FILE(S["job_preferences"], job_preferences)
|
||||
job_preferences = save_data?["job_preferences"]
|
||||
|
||||
//Quirks
|
||||
READ_FILE(S["all_quirks"], all_quirks)
|
||||
all_quirks = save_data?["all_quirks"]
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
//preference updating will handle saving the updated data for us.
|
||||
if(needs_update >= 0)
|
||||
update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer)
|
||||
update_character(needs_update, save_data) //needs_update == savefile_version if we need an update (positive integer)
|
||||
|
||||
//Sanitize
|
||||
randomise = SANITIZE_LIST(randomise)
|
||||
|
||||
job_preferences = SANITIZE_LIST(job_preferences)
|
||||
all_quirks = SANITIZE_LIST(all_quirks)
|
||||
|
||||
//Validate job prefs
|
||||
for(var/j in job_preferences)
|
||||
@@ -338,10 +320,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
if(!path)
|
||||
return FALSE
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return FALSE
|
||||
S.cd = "/character[default_slot]"
|
||||
var/tree_key = "character[default_slot]"
|
||||
if(!(tree_key in savefile.get_entry()))
|
||||
savefile.set_entry(tree_key, list())
|
||||
var/save_data = savefile.get_entry(tree_key)
|
||||
|
||||
for (var/datum/preference/preference as anything in get_preferences_in_priority_order())
|
||||
if (preference.savefile_identifier != PREFERENCE_CHARACTER)
|
||||
@@ -355,7 +337,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if (preference.type in value_cache)
|
||||
write_preference(preference, preference.serialize(value_cache[preference.type]))
|
||||
|
||||
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //load_character will sanitize any bad data, so assume up-to-date.)
|
||||
save_data["version"] = SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date.
|
||||
|
||||
// This is the version when the random security department was removed.
|
||||
// When the minimum is higher than that version, it's impossible for someone to have the "Random" department.
|
||||
@@ -364,13 +346,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
#endif
|
||||
|
||||
//Character
|
||||
WRITE_FILE(S["randomise"] , randomise)
|
||||
save_data["randomise"] = randomise
|
||||
|
||||
//Write prefs
|
||||
WRITE_FILE(S["job_preferences"] , job_preferences)
|
||||
save_data["job_preferences"] = job_preferences
|
||||
|
||||
//Quirks
|
||||
WRITE_FILE(S["all_quirks"] , all_quirks)
|
||||
save_data["all_quirks"] = all_quirks
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -392,17 +374,3 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
#undef SAVEFILE_VERSION_MAX
|
||||
#undef SAVEFILE_VERSION_MIN
|
||||
|
||||
#ifdef TESTING
|
||||
//DEBUG
|
||||
//Some crude tools for testing savefiles
|
||||
//path is the savefile path
|
||||
/client/verb/savefile_export(path as text)
|
||||
var/savefile/S = new /savefile(path)
|
||||
S.ExportText("/",file("[path].txt"))
|
||||
//path is the savefile path
|
||||
/client/verb/savefile_import(path as text)
|
||||
var/savefile/S = new /savefile(path)
|
||||
S.ImportText("/",file("[path].txt"))
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user