mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-26 22:42:26 +01:00
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" />
This commit is contained in:
@@ -68,36 +68,6 @@ SUBSYSTEM_DEF(persistence)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/admins/proc/toggle_persistence()
|
||||
set name = "Toggle Persistence"
|
||||
set category = "Special Verbs"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/message = ""
|
||||
var/options = list()
|
||||
if(SSpersistence.prevent_saving)
|
||||
message = "The persistence subsystem will NOT save at the end of the round. Do you want to re-enable it?"
|
||||
options = list("Re-enable saving", "Cancel")
|
||||
else
|
||||
message = "The persistence subsystem will save at the end of the round. Do you want to prevent this? This can be un-done before the round ends."
|
||||
options = list("Prevent saving", "Cancel")
|
||||
|
||||
var/confirm = tgui_alert(usr, message, "Toggle Persistence Saving", options)
|
||||
if(confirm == "Prevent saving")
|
||||
SSpersistence.prevent_saving = TRUE
|
||||
to_world(FONT_LARGE(EXAMINE_BLOCK_RED("Persistence saving at the end of the round has been [SPAN_BOLD(SPAN_WARNING("disabled"))] by an administrator.")))
|
||||
log_and_message_admins("has toggled persistence saving at round end, it is now disabled", usr)
|
||||
else if (confirm == "Re-enable saving")
|
||||
SSpersistence.prevent_saving = FALSE
|
||||
to_world(FONT_LARGE(EXAMINE_BLOCK_RED("Persistence saving at the end of the round has been [SPAN_BOLD(SPAN_GOOD("re-enabled"))] by an administrator.")))
|
||||
log_and_message_admins("has toggled persistence saving at round end, it is now re-enabled", usr)
|
||||
else
|
||||
return
|
||||
|
||||
feedback_add_details("admin_verb","TPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/**
|
||||
* Initialization of the persistence subsystem.
|
||||
* Includes generic startup checks and init of the different persistent data types.
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
// 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"]
|
||||
instance.persistent_objects_created_at = data["created_at"]
|
||||
instance.persistent_objects_expires_at = data["expires_at"]
|
||||
objectsApplyTrackContent(instance, data["content"], data["x"], data["y"], data["z"])
|
||||
objectsRegisterTrack(instance, data["author_ckey"])
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
qdel(cleanup_query)
|
||||
|
||||
/**
|
||||
* Retrieve persistent data entries that haven't expired.
|
||||
* RETURN: List of JSON, with ID, author_ckey, type, content, x, y, z
|
||||
* Retrieve active persistent object entries that have not expired.
|
||||
* RETURN: List of associative lists, each containing ID, author_ckey, type, content, x, y, z, created_at, and expires_at.
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/objectsDatabaseGetActiveEntries()
|
||||
PRIVATE_PROC(TRUE)
|
||||
@@ -30,7 +30,7 @@
|
||||
return
|
||||
|
||||
var/datum/db_query/get_query = SSdbcore.NewQuery(
|
||||
"SELECT id, author_ckey, type, content, x, y, z FROM ss13_persistent_objects WHERE NOW() < expires_at"
|
||||
"SELECT id, author_ckey, type, content, x, y, z, created_at, expires_at FROM ss13_persistent_objects WHERE NOW() < expires_at"
|
||||
)
|
||||
get_query.Execute()
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
"content" = get_query.item[4],
|
||||
"x" = get_query.item[5],
|
||||
"y" = get_query.item[6],
|
||||
"z" = get_query.item[7]
|
||||
"z" = get_query.item[7],
|
||||
"created_at" = get_query.item[8],
|
||||
"expires_at" = get_query.item[9]
|
||||
)
|
||||
results += list(entry)
|
||||
qdel(get_query)
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/typesHistoryCacheSelectTopK(k, datum/persistent_record_container/container)
|
||||
PRIVATE_PROC(TRUE)
|
||||
if(length(container.records))
|
||||
if(!length(container.records))
|
||||
return list()
|
||||
|
||||
var/list/datum/persistent_record/top = list()
|
||||
|
||||
@@ -41,10 +41,11 @@
|
||||
* PARAMS:
|
||||
* target_type = Singleton persistent type definition. See /singleton/persistent_type/generic and subtypes.
|
||||
* attribute = Custom attribute of the generic, can be null if the type definition doesn't require it. Defaults to null.
|
||||
* skip_caching = If set to TRUE, database results won't be added to the generic cache. Defaults to FALSE.
|
||||
* RETURN:
|
||||
* /persistent_generic or null if not available.
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/genericLoad(var/singleton/persistent_type/generic/target_type, attribute = null)
|
||||
/datum/controller/subsystem/persistence/proc/genericLoad(var/singleton/persistent_type/generic/target_type, attribute = null, skip_caching = FALSE)
|
||||
if(!target_type)
|
||||
log_subsystem_persistence_warning("Attempted to load generic with null target type.")
|
||||
return
|
||||
@@ -67,6 +68,40 @@
|
||||
new_generic.type_define = target_type
|
||||
new_generic.attribute = attribute
|
||||
new_generic.content = json_decode(result["content"])
|
||||
new_generic.created_at = result["created_at"]
|
||||
new_generic.expires_at = result["expires_at"]
|
||||
new_generic.expires_in_days = 0
|
||||
generic_cache[typesGetCacheName(target_type, attribute)] = new_generic
|
||||
if(!skip_caching)
|
||||
generic_cache[typesGetCacheName(target_type, attribute)] = new_generic
|
||||
return new_generic
|
||||
|
||||
/**
|
||||
* Retrieves all known attributes of a generic type.
|
||||
* PARAMS:
|
||||
* target_type = Singleton persistent type definition. See /singleton/persistent_type/generic and subtypes.
|
||||
* RETURN:
|
||||
* Distinct list of attributes or empty list.
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/genericGetAllAttributesForType(var/singleton/persistent_type/generic/target_type)
|
||||
if(!target_type)
|
||||
log_subsystem_persistence_warning("Attempted to load all generic attributes with null target type.")
|
||||
return list()
|
||||
|
||||
var/singleton/persistent_type/type_instance = GET_SINGLETON(target_type)
|
||||
if(!type_instance)
|
||||
log_subsystem_persistence_warning("Attempted to load all generic attributes of type [target_type], but no singleton instance was found.")
|
||||
return list()
|
||||
|
||||
if(!type_instance.requires_attribute)
|
||||
log_subsystem_persistence_warning("Attempted to load all generic attributes of type [target_type], but this type does not support attributes.")
|
||||
return list()
|
||||
|
||||
var/list/attributes = genericDatabaseGetAllAttributes(type_instance.database_id) // Retrieve all attributes in the database
|
||||
|
||||
for(var/datum/persistent_generic/cache_entry in generic_cache) // Get attributes that aren't in the database yet
|
||||
if(cache_entry.type_define == target_type && cache_entry.attribute && !(cache_entry.attribute in attributes))
|
||||
attributes += cache_entry.attribute
|
||||
|
||||
if(!attributes)
|
||||
return list()
|
||||
return attributes
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
* type_id = Type of ID.
|
||||
* attribute = Custom attribute of the record, can be null.
|
||||
* RETURN:
|
||||
* Associative list of keys "id", "content" (JSON).
|
||||
* Associative list of keys "id", "content" (JSON), "created_at", "expires_at".
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/genericDatabaseLoad(type_id, attribute)
|
||||
PRIVATE_PROC(TRUE)
|
||||
@@ -117,7 +117,7 @@
|
||||
return 0
|
||||
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(
|
||||
"SELECT id, content FROM ss13_persistent_generics \
|
||||
"SELECT id, content, created_at, expires_at FROM ss13_persistent_generics \
|
||||
WHERE type = :type_id AND attribute <=> :attribute",
|
||||
list(
|
||||
"type_id" = type_id,
|
||||
@@ -132,6 +132,32 @@
|
||||
|
||||
var/result = null
|
||||
while(query.NextRow())
|
||||
result = list("id" = query.item[1], "content" = query.item[2])
|
||||
result = list("id" = query.item[1], "content" = query.item[2], "created_at" = query.item[3], "expires_at" = query.item[4])
|
||||
qdel(query)
|
||||
return result
|
||||
|
||||
/**
|
||||
* Returns all attributes from persistent generics for a specified type.
|
||||
* RETURN:
|
||||
* Distinct list of attributes or empty list.
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/genericDatabaseGetAllAttributes(type_id)
|
||||
PRIVATE_PROC(TRUE)
|
||||
if(!databaseCheckConnection("genericDatabaseGetAllAttributes"))
|
||||
return
|
||||
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(
|
||||
"SELECT DISTINCT attribute FROM ss13_persistent_generics WHERE type = :type_id",
|
||||
list("type_id" = type_id)
|
||||
)
|
||||
query.Execute()
|
||||
|
||||
if(!databaseCheckQueryResult(query, "genericDatabaseGetAllAttributes"))
|
||||
qdel(query)
|
||||
return null
|
||||
|
||||
var/result = list()
|
||||
while(query.NextRow())
|
||||
result += query.item[1]
|
||||
qdel(query)
|
||||
return result
|
||||
|
||||
@@ -105,11 +105,12 @@
|
||||
* target_type = Singleton persistent type definition. See /singleton/persistent_type and subtypes.
|
||||
* If the type definition is a character record type, the attribute must be a valid character ID or the record will be rejected.
|
||||
* attribute = Custom attribute of the record, can be null if the type definition doesn't require it.
|
||||
* skip_caching = If set to TRUE, the results won't be added to the types cache, defaults to FALSE.
|
||||
* RETURN:
|
||||
* Single /persistent_record or null.
|
||||
*/
|
||||
/datum/controller/subsystem/persistence/proc/historyGetLastRecord(var/singleton/persistent_type/history/target_type, attribute)
|
||||
var/result = historyGetLastRecords(target_type, attribute, 1)
|
||||
/datum/controller/subsystem/persistence/proc/historyGetLastRecord(var/singleton/persistent_type/history/target_type, attribute, skip_caching = FALSE)
|
||||
var/result = historyGetLastRecords(target_type, attribute, 1, skip_caching)
|
||||
if(length(result) == 0)
|
||||
return null
|
||||
else
|
||||
@@ -233,14 +234,20 @@
|
||||
log_subsystem_persistence_warning("Attempted to draw more records then allowed for target type [target_type].")
|
||||
|
||||
var/singleton/persistent_type/type_instance = GET_SINGLETON(target_type)
|
||||
var/list/result = list()
|
||||
|
||||
var/attributes = historyDatabaseGetAllAttributes(type_instance.database_id)
|
||||
var/attributes = list()
|
||||
if(type_instance.requires_attribute)
|
||||
attributes = historyDatabaseGetAllAttributes(type_instance.database_id) // Retrieve all attributes in the database
|
||||
for(var/datum/persistent_record_container/cache_entry in history_cache) // Get attributes that aren't in the database yet
|
||||
if(cache_entry.type_define == target_type && cache_entry.attribute && !(cache_entry.attribute in attributes))
|
||||
attributes += cache_entry.attribute
|
||||
|
||||
var/list/result = list()
|
||||
if(attributes && length(attributes) > 0)
|
||||
for(var/attribute in attributes)
|
||||
result += list(alist("attribute" = attribute, "records" = historyGetAllRecords(target_type, attribute, skip_caching)))
|
||||
result += list(alist("attribute" = attribute, "records" = historyGetLastRecords(target_type, attribute, limit, skip_caching)))
|
||||
else
|
||||
var/no_attribute_records = historyGetAllRecords(target_type, null, skip_caching)
|
||||
var/no_attribute_records = historyGetLastRecords(target_type, null, limit, skip_caching)
|
||||
if(no_attribute_records && length(no_attribute_records) > 0)
|
||||
result = list(alist("attribute" = null, "records" = no_attribute_records))
|
||||
return result
|
||||
|
||||
@@ -230,6 +230,11 @@
|
||||
|
||||
var/records = list()
|
||||
while(query.NextRow())
|
||||
records += list(alist("id" = query.item[1], "created_at" = query.item[2], "value" = query.item[3]))
|
||||
records += list(alist(
|
||||
"id" = query.item[1],
|
||||
"created_at" = query.item[2],
|
||||
"value" = query.item[3],
|
||||
"game_id" = query.item[4],
|
||||
))
|
||||
qdel(query)
|
||||
return records
|
||||
|
||||
Reference in New Issue
Block a user