diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm
index a8a4a457c7c..268ecb73ab3 100644
--- a/code/__DEFINES/logs.dm
+++ b/code/__DEFINES/logs.dm
@@ -3,4 +3,6 @@
#define CONVERSION_LOG "Conversion"
#define SAY_LOG "Say"
#define EMOTE_LOG "Emote"
-#define MISC_LOG "Misc"
\ No newline at end of file
+#define MISC_LOG "Misc"
+
+#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 300d3a41fc0..ac3ac7d982e 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -47,7 +47,7 @@
#define INIT_ORDER_GARBAGE 19
#define INIT_ORDER_DBCORE 18
-#define INIT_ORDER_BLACKBOX 17
+#define INIT_ORDER_LOGGING 17
#define INIT_ORDER_SERVER_MAINT 16
#define INIT_ORDER_INPUT 15
#define INIT_ORDER_RESEARCH 14
diff --git a/code/controllers/subsystem/logging.dm b/code/controllers/subsystem/logging.dm
new file mode 100644
index 00000000000..ab2c22d424f
--- /dev/null
+++ b/code/controllers/subsystem/logging.dm
@@ -0,0 +1,57 @@
+SUBSYSTEM_DEF(logging)
+ name = "Logging"
+ priority = INIT_ORDER_LOGGING
+ flags = SS_NO_FIRE
+ var/list/datum/log_record/logs // Assoc list of assoc lists (ckey, (log_type, list/logs))
+
+/datum/controller/subsystem/logging/Initialize()
+ LAZYINITLIST(logs)
+ return ..()
+
+/datum/controller/subsystem/logging/proc/add_log(ckey, datum/log_record/log)
+ if(!ckey)
+ log_debug("SSLogging.add_log called with an invalid ckey")
+ return
+
+ LAZYINITLIST(logs)
+
+ if(!logs[ckey])
+ logs[ckey] = list()
+
+ var/list/log_types_list = logs[ckey]
+
+ if(!log_types_list[log.log_type])
+ log_types_list[log.log_type] = list()
+
+ var/list/datum/log_record/log_records = log_types_list[log.log_type]
+ log_records.Add(log)
+
+/datum/controller/subsystem/logging/proc/get_ckeys_logged()
+ var/list/ckeys = list()
+ for(var/ckey in logs)
+ ckeys.Add(ckey)
+ return ckeys
+
+/* Returns the logs of a given ckey and log_type
+ * If no logs exist it will return an empty list
+*/
+/datum/controller/subsystem/logging/proc/get_logs_by_type(ckey, log_type)
+ if(!ckey)
+ log_debug("SSLogging.get_logs_by_type called with an invalid ckey")
+ return
+ if(!log_type || !(log_type in ALL_LOGS))
+ log_debug("SSLogging.get_logs_by_type called with an invalid log_type '[log_type]'")
+ return
+
+ LAZYINITLIST(logs)
+ var/list/log_types_list = logs[ckey]
+ // Check if logs exist for the ckey
+ if(!log_types_list?.len)
+ return list()
+
+ var/list/datum/log_record/log_records = log_types_list[log_type]
+
+ // Check if logs exist for this type
+ if(!log_records)
+ return list()
+ return log_records
diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm
index 460cd47100f..c9bad8a6736 100644
--- a/code/datums/log_viewer.dm
+++ b/code/datums/log_viewer.dm
@@ -1,11 +1,9 @@
-#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
-
/datum/log_viewer
var/time_from = 0
var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up
- var/list/selected_mobs = list() // The mobs in question
+ var/list/selected_mobs = list() // The mobs in question.
+ var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to
var/list/selected_log_types = ALL_LOGS // The log types being searched for
-
var/list/log_records = list() // Found and sorted records
/datum/log_viewer/proc/clear_all()
@@ -19,13 +17,17 @@
/datum/log_viewer/proc/search()
log_records.Cut() // Empty the old results
var/list/invalid_mobs = list()
+ var/list/ckeys = selected_ckeys.Copy()
for(var/i in selected_mobs)
var/mob/M = i
- if(!M || QDELETED(M))
+ if(!M || QDELETED(M) || !M.last_known_ckey)
invalid_mobs |= M
continue
+ ckeys |= M.last_known_ckey
+
+ for(var/ckey in ckeys)
for(var/log_type in selected_log_types)
- var/list/logs = M.logs[log_type]
+ var/list/logs = SSlogging.get_logs_by_type(ckey, log_type)
var/len_logs = length(logs)
if(len_logs)
var/start_index = get_earliest_log_index(logs)
@@ -95,13 +97,22 @@
if(!mobs?.len)
return
for(var/i in mobs)
- var/mob/M = i
- if(istype(M))
- selected_mobs |= M
+ add_mob(usr, i, FALSE)
-/datum/log_viewer/proc/add_mob(mob/user, mob/M)
+/datum/log_viewer/proc/add_ckey(mob/user, ckey)
+ if(!user || !user)
+ return
+ selected_ckeys |= ckey
+ show_ui(user)
+
+/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE)
if(!M || !user)
return
+ if(!M.ckey)
+ if(alert(user, "No ckey found on the mob [M]. Use the last known ckey? This will add the ckey to the list of ckeys.", "No ckey bound to mob", "Yes", "No") == "Yes")
+ add_ckey(user, M.last_known_ckey)
+ return
+
selected_mobs |= M
show_ui(user)
@@ -112,7 +123,7 @@
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
var/dat
dat += "
"
- dat += ""
+ dat += "
"
dat += "
Time Search Range: [gameTimestamp(wtime = time_from)]"
dat += " To:
[gameTimestamp(wtime = time_to)]"
dat += "
"
@@ -126,6 +137,13 @@
dat += "
Clear All Mobs"
dat += "
"
+ dat += "
Ckeys being used:"
+ for(var/ckey in selected_ckeys)
+ dat += "
[ckey]"
+ dat += "
Add ckey"
+ dat += "
Clear All ckeys"
+ dat += "
"
+
dat += "
Log Types:"
for(var/i in all_log_types)
var/log_type = i
@@ -150,7 +168,7 @@
var/tdStyleType = "width:80px; text-align:center;"
var/tdStyleWho = "width:400px; text-align:center;"
var/tdStyleWhere = "width:150px; text-align:center;"
- dat += "
"
+ dat += "
"
dat += "
"
dat += "| When | Type | Who | What | Target | Where |
"
for(var/i in log_records)
@@ -204,6 +222,10 @@
selected_mobs.Cut()
show_ui(usr)
return
+ if(href_list["clear_ckeys"])
+ selected_ckeys.Cut()
+ show_ui(usr)
+ return
if(href_list["add_mob"])
var/list/mobs = getpois(TRUE, TRUE)
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
@@ -214,12 +236,21 @@
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", mobs)
A.on_close(CALLBACK(src, .proc/add_mob, usr))
return
+ if(href_list["add_ckey"])
+ var/list/ckeys = SSlogging.get_ckeys_logged()
+ var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys)
+ A.on_close(CALLBACK(src, .proc/add_ckey, usr))
+ return
if(href_list["remove_mob"])
var/mob/M = locate(href_list["remove_mob"])
if(M)
selected_mobs -= M
show_ui(usr)
return
+ if(href_list["remove_ckey"])
+ selected_ckeys -= href_list["remove_ckey"]
+ show_ui(usr)
+ return
if(href_list["toggle_log_type"])
var/log_type = href_list["toggle_log_type"]
if(log_type in selected_log_types)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 135114c11f9..4f176970a92 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -105,14 +105,6 @@
current.mind = null
leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it
- for(var/log_type in current.logs) // Copy the old logs
- var/list/logs = current.logs[log_type]
- if(new_character.logs[log_type])
- new_character.logs[log_type] += logs.Copy() // Append the old ones
- new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time
- else
- new_character.logs[log_type] = logs.Copy() // Just copy them
-
SSnanoui.user_transferred(current, new_character)
if(new_character.mind) //remove any mind currently in our new body's mind variable
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 548d22a3fa3..1239d56d5c0 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -54,7 +54,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
if(ismob(body))
T = get_turf(body) //Where is the body located?
attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost
- logs = body.logs.Copy()
var/mutable_appearance/MA = copy_appearance(body)
if(body.mind && body.mind.name)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 5345bda5b94..c222f76eadb 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -28,6 +28,7 @@
/mob/Login()
GLOB.player_list |= src
+ last_known_ckey = ckey
update_Login_details()
world.update_status()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d93b2723024..386f395363e 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -22,7 +22,6 @@
for(var/datum/alternate_appearance/AA in viewing_alternate_appearances)
AA.viewers -= src
viewing_alternate_appearances = null
- logs.Cut()
..()
return QDEL_HINT_HARDDEL
@@ -1267,10 +1266,10 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
create_log_in_list(debug_log, text, collapse, world.timeofday)
/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src))
- LAZYINITLIST(logs[log_type])
- var/list/log_list = logs[log_type]
+ if(!ckey)
+ return
var/datum/log_record/record = new(log_type, src, what, target, where, world.time)
- log_list.Add(record)
+ SSlogging.add_log(ckey, record)
/proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc
//this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar."
@@ -1413,4 +1412,4 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
///Force set the mob nutrition
/mob/proc/set_nutrition(change)
- nutrition = max(0, change)
\ No newline at end of file
+ nutrition = max(0, change)
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 2f074aeaa9f..cea1e59ceb9 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -36,7 +36,7 @@
var/list/attack_log_old = list( )
var/list/debug_log = null
- var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm
+ var/last_known_ckey = null // Used in logging
var/last_log = 0
var/obj/machinery/machine = null
@@ -211,4 +211,4 @@
var/list/tkgrabbed_objects = list() // Assoc list of items to TK grabs
var/forced_look = null // This can either be a numerical direction or a soft object reference (UID). It makes the mob always face towards the selected thing.
- var/registered_z
\ No newline at end of file
+ var/registered_z
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 6c464a9dca1..7ccb3759dcc 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -270,7 +270,6 @@
M.mind.transfer_to(new_mob)
else
new_mob.attack_log_old = M.attack_log_old.Copy()
- new_mob.logs = M.logs.Copy()
new_mob.key = M.key
to_chat(new_mob, "Your form morphs into that of a [randomize].")
diff --git a/paradise.dme b/paradise.dme
index 5ef4e7db728..9ce3187a1d9 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -226,6 +226,7 @@
#include "code\controllers\subsystem\ipintel.dm"
#include "code\controllers\subsystem\jobs.dm"
#include "code\controllers\subsystem\lighting.dm"
+#include "code\controllers\subsystem\logging.dm"
#include "code\controllers\subsystem\machinery.dm"
#include "code\controllers\subsystem\mapping.dm"
#include "code\controllers\subsystem\medals.dm"