Files
FabianK3 9f59d9dec5 Bugfix persistence of items in inventories and persistent charge card location (#22426)
# Summary

This PR fixes two bugs with persistent items, trash and the persistent
charge card.

## Changes

- Fixed the prevention of trash in inventories becoming persistent also
preventing any items on characters becoming persistent.
- Fixed persistent charge card location on init. If the card is not in
an command area, it will be moved into the XO office, if it is in a
command area, it will stay there.
2026-05-07 20:31:08 +00:00

133 lines
6.3 KiB
Plaintext

/**
* Initializes persistent objects.
* This includes cleaning up expired objects from the database and instanciating all active tracks.
*/
/datum/controller/subsystem/persistence/proc/objectsInitialize()
PRIVATE_PROC(TRUE)
GLOB.persistence_object_track_register = list()
if(SSatlas.current_map.path != "sccv_horizon") // The persistence system only supports objects from the main map levels for multiple reasons, e.g. Z level value, mapping support
log_subsystem_persistence_info("Persistent objects: Current map did not match SCCV Horizon, skipping persistent object initialization.")
return
// Delete all persistent objects in the database that have expired and have passed the cleanup grace period (PERSISTENT_EXPIRATION_CLEANUP_DELAY_DAYS)
objectsDatabaseCleanEntries()
// Retrieve all persistent data that is not expired
var/list/persistent_data = objectsDatabaseGetActiveEntries()
log_subsystem_persistence_info("Persistent objects: Retrieved [length(persistent_data)] entries for instancing this round.")
// Instantiate all remaining entries based of their type
// Assign persistence related vars found in /obj, apply content and add to live tracking list.
for (var/data in persistent_data)
CHECK_TICK
var/typepath = text2path(data["type"])
if (!ispath(typepath)) // Type checking
continue
// Note that the object here is instantiated without init args.
// Objects that require init args should fall back to INITALIZE_HINT_LATELOAD during Init,
// as this will give the subsystem the chance to apply the content and
// the object to continue with init logic after the subsystem is done in LateInitialize.
var/obj/instance = new typepath()
instance.persistent_objects_track_id = data["id"]
objectsApplyTrackContent(instance, data["content"], data["x"], data["y"], data["z"])
objectsRegisterTrack(instance, data["author_ckey"])
/**
* Finalize persistent object tracking.
* Adds new persistent objects, removes no longer existing persistent objects and updates changed persistent objects in the database.
*/
/datum/controller/subsystem/persistence/proc/objectsFinalize()
PRIVATE_PROC(TRUE)
if(SSatlas.current_map.path != "sccv_horizon") // The persistence system only supports objects from the main map levels for multiple reasons, e.g. Z level value, mapping support
log_subsystem_persistence_info("Persistent objects: Current map did not match SCCV Horizon, skipping persistent object finalization.")
if(length(GLOB.persistence_object_track_register) > 0)
log_subsystem_persistence_warning("Persistent objects: There are [length(GLOB.persistence_object_track_register)] tracked objects at finalization, while the map is not supported! These track will not be saved! Verify that SSatlas.current_map.path has not changed during the round!")
return
// Subsystem shutdown:
// Create new persistent records for objects that have been created in the round
// Update tracked objects that have an ID (already existing from previous rounds)
// Delete persistent records that no longer exist in the registry (removed during the round)
// Run checks on each track that might prevent further persistence
for (var/obj/track as anything in GLOB.persistence_object_track_register)
CHECK_TICK
var/turf/T = get_turf(track)
if(!T || !is_station_level(T.z)) // The persistence system only supports objects from the main map levels for multiple reasons, e.g. Z level value, mapping support
objectsDeregisterTrack(track)
var/created = 0
var/updated = 0
var/expired = 0
// Get already stored data before saving new tracks so we can compare what has been updated or removed during the round.
var/list/existing_data = objectsDatabaseGetActiveEntries()
for (var/obj/track as anything in GLOB.persistence_object_track_register)
CHECK_TICK
if (track.persistent_objects_track_id == 0)
// Tracked object has no ID meaning it is new, create a new persistent record for it
objectsDatabaseAddEntry(track)
created++
// Find tracks that have been removed during the round by trying to find the track by database ID
// If we find the track, we need to check if it requires an update instead
for (var/record in existing_data)
var/found = FALSE
for (var/obj/track as anything in GLOB.persistence_object_track_register)
CHECK_TICK
if (record["id"] == track.persistent_objects_track_id)
// A track with the same ID has been found in the register, it still exists, check if we need to update it instead
found = TRUE // Prevent expiration of track
var/changed = FALSE
var/turf/T = get_turf(track)
if (T && T.x != record["x"])
changed = TRUE
else if (T && T.y != record["y"])
changed = TRUE
else if (T && T.z != record["z"])
changed = TRUE
else if (objectsGetTrackContent(track) != record["content"])
changed = TRUE
if (changed)
objectsDatabaseUpdateEntry(track)
updated++
break // Track found (and perhaps updated), break off loop search as it won't need to be deleted anyways
if (!found)
// No track with the same ID has been found in the register, remove it from the database (expire)
objectsDatabaseExpireEntry(record["id"])
expired++
log_subsystem_persistence_info("Persistent objects: Created [created], updated [updated] and expired [expired] tracks.")
/**
* Safely get JSON persistent content of track.
* RETURN: JSON formatted content of track or null if an exception occured.
*/
/datum/controller/subsystem/persistence/proc/objectsGetTrackContent(obj/track)
PRIVATE_PROC(TRUE)
var/result = json_encode(list())
try
var/list/content = track.persistent_objects_get_content()
if(length(content))
result = json_encode(content)
catch(var/exception/e)
log_subsystem_persistence_error("Error during json serialization for persistent object. Failed to get/encode track content: [e]")
return result
/**
* Safely apply persistent content to track.
* PARAMS:
* track = Object to apply content to.
* json = Custom persistent content JSON to be applied.
* x,y,z = x-y-z coordinates of object, can be null.
*/
/datum/controller/subsystem/persistence/proc/objectsApplyTrackContent(obj/track, json, x, y, z)
PRIVATE_PROC(TRUE)
try
track.persistent_objects_apply_content(json_decode(json), x, y, z)
catch(var/exception/e)
log_subsystem_persistence_error("Error during json deserialization for persistent object. Failed to apply/decode track content: [e]")