mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-05-14 02:41:03 +01:00
5926589c16
* removes var/ inside all procs * . * ugh
115 lines
5.0 KiB
Plaintext
115 lines
5.0 KiB
Plaintext
/**
|
|
* Admin verb: Convert Player Savefile
|
|
*
|
|
* Converts a player's save data between BYOND .sav (binary) and JSON formats.
|
|
* Used for redundancy, import/export processing, and save file recovery.
|
|
*
|
|
* IMPORTANT: The target player must be logged off before conversion.
|
|
* The verb refuses to run if the player is currently connected, and
|
|
* instructs the admin to tell them to log off first.
|
|
*
|
|
* Files inside the player's vore/ subfolder are excluded from this process.
|
|
* Those files are already JSON and are managed separately.
|
|
*/
|
|
|
|
/// Returns the base save directory for a given ckey.
|
|
/proc/get_player_save_dir(ckey)
|
|
return "data/player_saves/[copytext(ckey, 1, 2)]/[ckey]"
|
|
|
|
ADMIN_VERB(admin_convert_savefile, R_ADMIN, "Convert Player Savefile", "Convert a player's preferences.sav to preferences.json or vice versa. Player must be logged off.", ADMIN_CATEGORY_SERVER_ADMIN)
|
|
// Pick your target.
|
|
var/target_ckey = tgui_input_text(user, "Enter the ckey of the player whose save file you want to convert.", "Convert Player Savefile")
|
|
if(!target_ckey)
|
|
return
|
|
target_ckey = lowertext(target_ckey)
|
|
|
|
// Refuse outright if the player is currently connected.
|
|
// Modifying save files while the client is online risks data loss or corruption
|
|
// because the server may overwrite the converted file on the next auto-save.
|
|
for(var/client/C as anything in GLOB.clients)
|
|
if(C.ckey == target_ckey)
|
|
to_chat(user, span_danger("[target_ckey] is currently logged in. Tell them to log off before you try again."))
|
|
message_admins("[key_name_admin(user)] attempted to convert [target_ckey]'s save file, but that player is online.")
|
|
return
|
|
|
|
// Also block if they connected at any point this round, even if currently offline.
|
|
if(GLOB.persistent_clients_by_ckey[target_ckey])
|
|
to_chat(user, span_danger("[target_ckey] has connected this round. Their save may have been written by the server since then. Wait until next round to convert."))
|
|
message_admins("[key_name_admin(user)] attempted to convert [target_ckey]'s save file, but that player connected this round.")
|
|
return
|
|
|
|
// Make sure save files actually exist for this ckey.
|
|
var/save_dir = get_player_save_dir(target_ckey)
|
|
var/has_sav = fexists("[save_dir]/preferences.sav")
|
|
var/has_json = fexists("[save_dir]/preferences.json")
|
|
|
|
if(!has_sav && !has_json)
|
|
to_chat(user, span_danger("No save files found for '[target_ckey]'. Check that the ckey is correct."))
|
|
return
|
|
|
|
// Let the admin pick the conversion direction.
|
|
var/list/options = list()
|
|
if(has_sav)
|
|
options += "preferences.sav -> preferences.json"
|
|
if(has_json)
|
|
options += "preferences.json -> preferences.sav"
|
|
|
|
var/direction = tgui_input_list(user, "Select the conversion to perform for '[target_ckey]'.", "Convert Player Savefile", options)
|
|
if(!direction)
|
|
return
|
|
|
|
// Warn the admin in case the player has logged in since we checked.
|
|
var/confirm = tgui_alert(
|
|
user,
|
|
"WARNING: '[target_ckey]' should be logged off before this runs. Proceeding while they are online can corrupt their save.\n\nAre you sure [target_ckey] is logged off?",
|
|
"Convert Player Savefile",
|
|
list("Cancel", "Yes, they are logged off"))
|
|
if(confirm != "Yes, they are logged off")
|
|
return
|
|
|
|
// Re-verify the player is still offline immediately before touching any files.
|
|
for(var/client/C as anything in GLOB.clients)
|
|
if(C.ckey == target_ckey)
|
|
to_chat(user, span_danger("[target_ckey] is now online. Conversion aborted. Tell them to log off and try again."))
|
|
message_admins("[key_name_admin(user)] attempted to convert [target_ckey]'s save file, but that player came online during the confirmation. Blocked.")
|
|
return
|
|
|
|
if(direction == "preferences.sav -> preferences.json")
|
|
var/sav_path = "[save_dir]/preferences.sav"
|
|
var/json_path = "[save_dir]/preferences.json"
|
|
|
|
// Back up the existing JSON file before overwriting it.
|
|
if(fexists(json_path))
|
|
var/bak = "[json_path].convbak"
|
|
if(fexists(bak))
|
|
fdel(bak)
|
|
fcopy(json_path, bak)
|
|
|
|
var/datum/json_savefile/result = new(json_path)
|
|
result.import_byond_savefile(new /savefile(sav_path))
|
|
result.save()
|
|
|
|
log_and_message_admins("converted [target_ckey]'s preferences.sav to preferences.json", user)
|
|
to_chat(user, span_filter_adminlog("Done. [target_ckey]'s preferences.sav has been converted to preferences.json."))
|
|
|
|
else if(direction == "preferences.json -> preferences.sav")
|
|
var/json_path = "[save_dir]/preferences.json"
|
|
var/sav_path = "[save_dir]/preferences.sav"
|
|
|
|
// Back up the existing .sav file before overwriting it.
|
|
if(fexists(sav_path))
|
|
var/bak = "[sav_path].convbak"
|
|
if(fexists(bak))
|
|
fdel(bak)
|
|
fcopy(sav_path, bak)
|
|
// Delete the original so new() creates a blank savefile.
|
|
// Without this, old entries not present in the JSON would persist in the file.
|
|
fdel(sav_path)
|
|
|
|
var/datum/json_savefile/source = new(json_path)
|
|
var/savefile/result = new(sav_path)
|
|
source.export_to_byond_savefile(result)
|
|
|
|
log_and_message_admins("converted [target_ckey]'s preferences.json to preferences.sav", user)
|
|
to_chat(user, span_filter_adminlog("Done. [target_ckey]'s preferences.json has been converted to preferences.sav."))
|