Files
Aurora.3/code/controllers/subsystems/persistence/persistence.dm
FabianK3andGitHub 65a9f06765 Persistence Panel (#22979)
# Summary

This PR adds a new admin panel for persistence.

## Contents

- The panel features information about the current round and detailed
tracking of objects, records and generics.
- The previously introduced toggle-persistence verb has been removed and
integrated into the new panel.
- The panel can be seen by admins, mods and devs, but the
toggle-persistence is still locked to admin permissions only.

## Preview

The images are WIP and subject to change.

<img width="918" height="229" alt="image"
src="https://github.com/user-attachments/assets/16d3d73d-32e6-4907-bdff-d96699c27d49"
/>

<img width="919" height="253" alt="image"
src="https://github.com/user-attachments/assets/92e7f4d9-ccd4-4e79-949d-b8a35039da01"
/>

<img width="923" height="285" alt="image"
src="https://github.com/user-attachments/assets/e3796504-4cc6-4fce-b5f1-4df3486bfbee"
/>

<img width="923" height="270" alt="image"
src="https://github.com/user-attachments/assets/9fbb185d-181a-40ec-b89b-58eafcd47e4d"
/>
2026-08-08 20:47:55 +00:00

126 lines
5.2 KiB
Plaintext

/*
* Persistence subsystem
* Subsytem for managing any form of persistent content across rounds.
*
* This subsystem consists of multiple partial files, split into different responsibilities:
* persistence.dm - Subsystem define and related code
* Objects and types (with Generics and History respectively), each containing:
* Base file (no suffix), public procs (_public.dm suffix), SQL code (_sql.dm suffix)
*/
SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE // The order is tied with the init and maploading subsystem.
flags = SS_NO_FIRE // This subsystem has no continues workload, it's init and shutdown only.
/// Sanity check to confirm init was a success before finalizing.
var/init_success = FALSE
/// Global toggle to prevent saving at round end, changed by toggle_persistence proc, used for admin purposes.
var/prevent_saving = FALSE
/// In-memory register of all persistent objects that were loaded or created during the round, used for tracking and finalization purposes.
var/object_track_register = list()
/// Dictionary<"[type](+[attribute])" cache of persistent history records.
var/history_cache = alist()
/// Manual record counter of cache containers.
var/history_cache_count = 0
/// ID of last found history record.
/// Higher found IDs mean the record is not yet found in the database, lower or equal found ID means the are record that are already in the database.
/// Used during history_virtual_id init and read-through cache hits.
var/history_last_database_id = 0
/// ID used for instanciating new history records during the round, used for cache tracking.
/// Their database ID will be set during insert/finalization.
var/history_virtual_id = 0
/// Dictionary<char_id, charname> cache of Character name by ID for history/character helper.
var/char_cache = alist()
/// Dictionary<"[type](+[attribute])", container> cache of persistent generics.
var/generic_cache = alist()
/**
* Subsystem info stub message generation.
*/
/datum/controller/subsystem/persistence/stat_entry(msg)
msg = ("[init_success ? "" : "INIT FAILED!!!|"][prevent_saving ? "SAVING DISABLED!|" : ""]Objects:[length(object_track_register)]|Containers:[length(history_cache)];Records:[history_cache_count]|Generics:[length(generic_cache)]")
return msg
/**
* Helper method to check and log database connection.
* RETURN: True if connection is scuccessful, false if not.
* PARAMS:
* action = Custom string of the action being performed written to log.
*/
/datum/controller/subsystem/persistence/proc/databaseCheckConnection(action = "unlabeled action")
PRIVATE_PROC(TRUE)
if(!SSdbcore.Connect())
log_subsystem_persistence_error("SQL error during [action], connection failed.")
return FALSE
return TRUE
/**
* Helper method to check the SQL query result and log possible errors.
* RETURN: True if no error occured, false if an error was found.
*/
/datum/controller/subsystem/persistence/proc/databaseCheckQueryResult(datum/db_query/query, action = "unlabeled action")
PRIVATE_PROC(TRUE)
if (!query)
log_subsystem_persistence_error("SQL error during [action], in addition query object provided to check was null.")
return FALSE
if (query.ErrorMsg())
log_subsystem_persistence_error("SQL error during [action]. " + query.ErrorMsg())
return FALSE
return TRUE
/**
* Initialization of the persistence subsystem.
* Includes generic startup checks and init of the different persistent data types.
*/
/datum/controller/subsystem/persistence/Initialize()
. = ..()
if(!GLOB.config.sql_enabled)
log_subsystem_persistence_warning("SQL configuration not enabled. Persistence subsystem requires SQL. Skipping init.")
return SS_INIT_SUCCESS
if(!databaseCheckConnection("subsystem init"))
log_subsystem_persistence_error("SQL connection unavailable. Init not possible.")
return SS_INIT_FAILURE
try
objectsInitialize()
catch(var/exception/e_objects)
log_subsystem_persistence_panic("Unhandled exception during persistent objects initialization!", e_objects)
return SS_INIT_FAILURE
try
typesInitialize()
catch(var/exception/e_types)
log_subsystem_persistence_panic("Unhandled exception during persistent type initialization!", e_types)
return SS_INIT_FAILURE
init_success = TRUE
return SS_INIT_SUCCESS
/**
* Shutdown of the persistence subsystem.
* The shutdown consists of finalization steps for each persistent data type.
*/
/datum/controller/subsystem/persistence/Shutdown()
if(!init_success)
log_subsystem_persistence_panic("Init success flag is FALSE. Something went wrong during subsystem init! Aborting finalization to prevent corrupt data!")
return
if(prevent_saving)
log_subsystem_persistence_warning("Persistence subsystem was toggled to not save. Skipping subsystem finalization.")
return
if(!databaseCheckConnection("subsystem shutdown"))
log_subsystem_persistence_panic("SQL error during persistence subsystem shutdown. Cannot finalise persistence of the round.")
return
try
objectsFinalize()
catch(var/exception/e_objects)
log_subsystem_persistence_panic("Unhandled exception during persistent objects finalization!", e_objects)
try
typesFinalize()
catch(var/exception/e_types)
log_subsystem_persistence_panic("Unhandled exception during persistent types finalization!", e_types)