From 99afe463dc57aea90ec579b9cf400a91fadf8d08 Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:03:53 -0700 Subject: [PATCH] [MIRROR] reimplements attack and player logs (#12553) Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- SQL/feedback_schema.sql | 37 ++-- code/__defines/logging.dm | 27 +++ code/_helpers/logging/attack.dm | 4 +- code/_helpers/logging/log_to_db.dm | 13 ++ code/_helpers/logging/mob.dm | 52 ++---- code/_helpers/logging/talk.dm | 4 +- code/_helpers/mobs.dm | 5 - .../configuration/entries/general.dm | 7 +- code/controllers/subsystems/dbcore.dm | 5 + code/controllers/subsystems/mobs.dm | 1 - code/controllers/subsystems/persist_vr.dm | 1 - code/defines/procs/statistics.dm | 2 - .../game/mecha/equipment/tools/armor_melee.dm | 2 +- code/game/mecha/mecha.dm | 4 +- code/game/mecha/mecha_actions.dm | 6 +- code/game/world.dm | 71 ------- code/modules/admin/DB ban/functions.dm | 4 - code/modules/admin/IsBanned.dm | 2 +- code/modules/admin/admin_attack_log.dm | 29 --- code/modules/admin/admin_tools.dm | 123 ++++++------ code/modules/admin/admin_verb_lists_vr.dm | 6 - code/modules/admin/admin_verbs.dm | 1 - code/modules/admin/banjob.dm | 2 +- code/modules/admin/verbs/antag-ooc.dm | 2 +- .../admin/verbs/check_customitem_activity.dm | 1 - code/modules/admin/verbs/dbcon_fix.dm | 1 - code/modules/client/client procs.dm | 2 - code/modules/client/verbs/ooc.dm | 4 +- .../food/kitchen/cooking_machines/fryer.dm | 3 +- code/modules/library/lib_machines.dm | 6 - code/modules/mob/dead/observer/observer.dm | 1 - code/modules/mob/dead/observer/say.dm | 4 +- code/modules/mob/living/say.dm | 4 +- code/modules/mob/new_player/new_player.dm | 1 - code/modules/mob/new_player/poll.dm | 6 - code/modules/mob/say_vr.dm | 4 +- .../nifsoft/software/13_soulcatcher.dm | 4 +- code/modules/pda/messenger.dm | 2 +- code/modules/recycling/packagewrap.dm | 2 +- code/modules/research/message_server.dm | 1 - code/modules/shieldgen/directional_shield.dm | 4 +- code/modules/spells/spell_code.dm | 2 +- code/modules/spells/spellbook.dm | 2 +- code/modules/vehicles/vehicle.dm | 2 +- code/modules/vore/eating/soulcatcher.dm | 4 +- config/example/config.txt | 5 + .../interfaces/PlayerLogViewer/function.ts | 11 ++ .../tgui/interfaces/PlayerLogViewer/index.tsx | 176 ++++++++++++++++++ .../tgui/interfaces/PlayerLogViewer/types.ts | 18 ++ vorestation.dme | 1 + 50 files changed, 411 insertions(+), 270 deletions(-) create mode 100644 code/_helpers/logging/log_to_db.dm create mode 100644 tgui/packages/tgui/interfaces/PlayerLogViewer/function.ts create mode 100644 tgui/packages/tgui/interfaces/PlayerLogViewer/index.tsx create mode 100644 tgui/packages/tgui/interfaces/PlayerLogViewer/types.ts diff --git a/SQL/feedback_schema.sql b/SQL/feedback_schema.sql index 62db2ea212..2e7bbb799a 100644 --- a/SQL/feedback_schema.sql +++ b/SQL/feedback_schema.sql @@ -76,7 +76,7 @@ CREATE TABLE IF NOT EXISTS `erro_feedback` ( `var_value` int(16) DEFAULT NULL, `details` text, PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_player` CREATE TABLE IF NOT EXISTS `erro_player` ( @@ -93,7 +93,7 @@ CREATE TABLE IF NOT EXISTS `erro_player` ( KEY `discord_id` (`discord_id`), KEY `computerid` (`computerid`), KEY `ip` (`ip`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_poll_option` CREATE TABLE IF NOT EXISTS `erro_poll_option` ( @@ -107,7 +107,7 @@ CREATE TABLE IF NOT EXISTS `erro_poll_option` ( `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_poll_question` CREATE TABLE IF NOT EXISTS `erro_poll_question` ( @@ -118,7 +118,7 @@ CREATE TABLE IF NOT EXISTS `erro_poll_question` ( `question` varchar(255) NOT NULL, `adminonly` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_poll_textreply` CREATE TABLE IF NOT EXISTS `erro_poll_textreply` ( @@ -130,7 +130,7 @@ CREATE TABLE IF NOT EXISTS `erro_poll_textreply` ( `replytext` text NOT NULL, `adminrank` varchar(32) NOT NULL DEFAULT 'Player', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_poll_vote` CREATE TABLE IF NOT EXISTS `erro_poll_vote` ( @@ -143,7 +143,7 @@ CREATE TABLE IF NOT EXISTS `erro_poll_vote` ( `adminrank` varchar(32) NOT NULL, `rating` int(2) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `erro_privacy` CREATE TABLE IF NOT EXISTS `erro_privacy` ( @@ -152,7 +152,20 @@ CREATE TABLE IF NOT EXISTS `erro_privacy` ( `ckey` varchar(32) NOT NULL, `option` varchar(128) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- Dumping structure for table ss13.erro_dialog +CREATE TABLE IF NOT EXISTS `erro_dialog` ( + `mid` int(11) NOT NULL AUTO_INCREMENT, + `time` datetime NOT NULL, + `ckey` varchar(32) NOT NULL, + `color` varchar(16) DEFAULT NULL, + `mob` varchar(128) DEFAULT NULL, + `area` varchar(128) DEFAULT NULL, + `type` varchar(32) NOT NULL, + `message` text NOT NULL, + PRIMARY KEY (`mid`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `death` CREATE TABLE IF NOT EXISTS `death` ( @@ -172,7 +185,7 @@ CREATE TABLE IF NOT EXISTS `death` ( `fireloss` INT(11) NOT NULL, `oxyloss` INT(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `karma` @@ -188,7 +201,7 @@ CREATE TABLE IF NOT EXISTS `karma` ( `spenderip` TEXT NOT NULL, `time` DATETIME NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `karmatotals` CREATE TABLE IF NOT EXISTS `karmatotals` ( @@ -196,7 +209,7 @@ CREATE TABLE IF NOT EXISTS `karmatotals` ( `byondkey` TEXT NOT NULL, `karma` INT(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `library` CREATE TABLE IF NOT EXISTS `library` ( @@ -206,7 +219,7 @@ CREATE TABLE IF NOT EXISTS `library` ( `content` TEXT NOT NULL, `category` TEXT NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; -- Table structure for table `population` CREATE TABLE IF NOT EXISTS `population` ( @@ -215,7 +228,7 @@ CREATE TABLE IF NOT EXISTS `population` ( `admincount` INT(11) NULL DEFAULT NULL, `time` DATETIME NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- Table structure for table `vr_player_hours` CREATE TABLE IF NOT EXISTS `vr_player_hours` ( diff --git a/code/__defines/logging.dm b/code/__defines/logging.dm index f87ab3f428..4ebd600bcc 100644 --- a/code/__defines/logging.dm +++ b/code/__defines/logging.dm @@ -38,6 +38,33 @@ #define LOG_SPEECH_INDICATORS (1 << 22) #define LOG_TRANSPORT (1 << 23) +GLOBAL_ALIST_INIT(logtype_to_string, alist( + LOG_ATTACK = "ATTACK", + LOG_SAY = "SAY", + LOG_WHISPER = "WHISPER", + LOG_EMOTE = "EMOTE", + LOG_DSAY = "DEADSAY", + LOG_PDA = "PDA", + LOG_CHAT = "CHAT", + LOG_COMMENT = "COMMENT", + LOG_TELECOMMS = "RADIO", + LOG_OOC = "OOC", + LOG_LOOC = "LOOC", + LOG_ADMIN = "ADMIN", + LOG_OWNERSHIP = "OWNERSHIP", + LOG_GAME = "GAME", + LOG_ADMIN_PRIVATE = "ADMIN_PRIV", + LOG_ASAY = "ASAY", + LOG_MECHA = "MECH", + LOG_VIRUS = "VIRUS", + LOG_SHUTTLE = "SHUTTLE", + LOG_ECON = "ECON", + LOG_VICTIM = "VICTIM", + LOG_RADIO_EMOTE = "RADIO_EMOTE", + LOG_SPEECH_INDICATORS = "SPEECH_INDICATOR", + LOG_TRANSPORT = "TRANSPORT" +)) + //Individual logging panel pages #define INDIVIDUAL_GAME_LOG (LOG_GAME) #define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK | LOG_VICTIM) diff --git a/code/_helpers/logging/attack.dm b/code/_helpers/logging/attack.dm index 69a843023b..60099bcebc 100644 --- a/code/_helpers/logging/attack.dm +++ b/code/_helpers/logging/attack.dm @@ -29,11 +29,11 @@ var/postfix = "[sobject][saddition][hp]" var/message = "[what_done] [starget][postfix]" - user.log_message(message, LOG_ATTACK, color="red") + user.log_message(message, LOG_ATTACK, color="#ff0000") if(user != target) var/reverse_message = "was [what_done] by [ssource][postfix]" - target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE) + target.log_message(reverse_message, LOG_VICTIM, color="#ffb300", log_globally=FALSE) /// Log for vore interactions /proc/log_vore(text, list/data) diff --git a/code/_helpers/logging/log_to_db.dm b/code/_helpers/logging/log_to_db.dm new file mode 100644 index 0000000000..0499f420cf --- /dev/null +++ b/code/_helpers/logging/log_to_db.dm @@ -0,0 +1,13 @@ +/proc/db_log_insert(mob/log_target, message, type, color) + set waitfor = FALSE + if(!ismob(log_target)) + return + if(!SSdbcore.IsConnected()) + return + var/datum/db_query/query_insert = SSdbcore.NewQuery("INSERT INTO erro_dialog (mid, time, ckey, mob, area, type, color, message) VALUES (null, NOW(), :sender_ckey, :sender_mob, :message_area, :message_type, :message_color, :message_content)", + list("sender_ckey" = log_target.ckey, "sender_mob" = log_target.real_name, "message_area" = "[loc_name(log_target)]", "message_type" = "[type]", "message_color" = color, "message_content" = message)) + if(!query_insert.Execute()) + log_sql("Error during logging: "+query_insert.ErrorMsg()) + qdel(query_insert) + return + qdel(query_insert) diff --git a/code/_helpers/logging/mob.dm b/code/_helpers/logging/mob.dm index 9db2f0f6db..37e94ddbb6 100644 --- a/code/_helpers/logging/mob.dm +++ b/code/_helpers/logging/mob.dm @@ -13,42 +13,30 @@ return // Cannot use the list as a map if the key is a number, so we stringify it (thank you BYOND) - var/smessage_type = num2text(message_type, MAX_BITFLAG_DIGITS) - - if(HAS_CONNECTED_PLAYER(src)) - if(!islist(persistent_client.logging[smessage_type])) - persistent_client.logging[smessage_type] = list() - - if(!islist(logging[smessage_type])) - logging[smessage_type] = list() - - var/colored_message = message - if(color) - if(color[1] == "#") - colored_message = "[message]" - else - colored_message = "[message]" + var/smessage_type = GLOB.logtype_to_string[message_type] //This makes readability a bit better for admins. - switch(message_type) - if(LOG_WHISPER) - colored_message = "(WHISPER) [colored_message]" - if(LOG_OOC) - colored_message = "(OOC) [colored_message]" - if(LOG_LOOC) - colored_message = "(LOOC) [colored_message]" - if(LOG_ASAY) - colored_message = "(ASAY) [colored_message]" - if(LOG_EMOTE) - colored_message = "(EMOTE) [colored_message]" - if(LOG_RADIO_EMOTE) - colored_message = "(RADIOEMOTE) [colored_message]" + var/list/timestamped_message = list( + "time" = world.timeofday, + "name" = name, + "ckey" = ckey, + "loc" = loc_name(src), + "event_id" = LAZYLEN(logging[smessage_type]), + "message" = message, + "color" = color + ) - var/list/timestamped_message = list("\[[time_stamp(format = "YYYY-MM-DD hh:mm:ss")]\] [key_name_and_tag(src)] [loc_name(src)] (Event #[LAZYLEN(logging[smessage_type])])" = colored_message) - - logging[smessage_type] += timestamped_message + if(!CONFIG_GET(flag/database_logging)) + if(!islist(logging[smessage_type])) + logging[smessage_type] = list() + UNTYPED_LIST_ADD(logging[smessage_type], timestamped_message) if(HAS_CONNECTED_PLAYER(src)) - persistent_client.logging[smessage_type] += timestamped_message + if(CONFIG_GET(flag/database_logging)) + db_log_insert(src, message, smessage_type, color) + else + if(!islist(persistent_client.logging[smessage_type])) + persistent_client.logging[smessage_type] = list() + UNTYPED_LIST_ADD(persistent_client.logging[smessage_type], timestamped_message) ..() diff --git a/code/_helpers/logging/talk.dm b/code/_helpers/logging/talk.dm index 36e8dfb3d8..d90e69978c 100644 --- a/code/_helpers/logging/talk.dm +++ b/code/_helpers/logging/talk.dm @@ -11,10 +11,10 @@ * * log_globally - boolean checking whether or not we write this log to the log file * * forced_by - source that forced the dialogue if any */ -/atom/proc/log_talk(message, message_type, tag = null, log_globally = TRUE, forced_by = null, custom_say_emote = null) +/atom/proc/log_talk(message, message_type, tag = null, log_globally = TRUE, forced_by = null, custom_say_emote = null, color) var/prefix = tag ? "([tag]) " : "" var/suffix = forced_by ? " FORCED by [forced_by]" : "" - log_message("[prefix][custom_say_emote ? "*[custom_say_emote]*, " : ""]\"[message]\"[suffix]", message_type, log_globally = log_globally) + log_message("[prefix][custom_say_emote ? "*[custom_say_emote]*, " : ""]\"[message]\"[suffix]", message_type, color, log_globally) /// Logging for generic spoken messages /proc/log_say(text, list/data) diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index d1d4808b6e..de3c98807a 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -151,13 +151,8 @@ Proc for attack log creation, because really why not add_attack_logs(user,M,what_done,admin_notify) return - var/user_str = key_name(user) var/target_str = key_name(target) - if(ismob(user)) - user.attack_log += text("\[[time_stamp()]\] [span_red("Attacked [target_str]: [what_done]")]") - if(ismob(target)) - target.attack_log += text("\[[time_stamp()]\] [span_orange("Attacked by [user_str]: [what_done]")]") log_combat(user, target, what_done) if(admin_notify) msg_admin_attack("[key_name_admin(user)] vs [target_str]: [what_done]") diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index cefd6a5f67..6138c23edf 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -729,7 +729,7 @@ /datum/config_entry/flag/allow_simple_mob_recolor /// Chatlogs are now saved by calling the chatlogging library instead of letting the clients handle it -/// REQUIRES an database +/// REQUIRES a database /datum/config_entry/flag/chatlog_database_backend default = FALSE @@ -737,6 +737,11 @@ /// REQUIRES chatlog_database_backend to be enabled /datum/config_entry/string/chatlog_database_api_endpoint +/// Enables logging dialogue and attack logs to the database +/// REQUIRES a database +/datum/config_entry/flag/database_logging + default = FALSE + /datum/config_entry/flag/forbid_admin_profiling /datum/config_entry/flag/pixel_size_limit diff --git a/code/controllers/subsystems/dbcore.dm b/code/controllers/subsystems/dbcore.dm index ed9dd2d82a..80381b6f86 100644 --- a/code/controllers/subsystems/dbcore.dm +++ b/code/controllers/subsystems/dbcore.dm @@ -44,6 +44,11 @@ SUBSYSTEM_DEF(dbcore) /datum/controller/subsystem/dbcore/Initialize() Connect() + if(IsConnected() && CONFIG_GET(flag/database_logging)) + var/datum/db_query/query_truncate = NewQuery("TRUNCATE erro_dialog") + if(!query_truncate.Execute()) + log_sql("ERROR TRYING TO CLEAR erro_dialog: "+query_truncate.ErrorMsg()) + qdel(query_truncate) return SS_INIT_SUCCESS /datum/controller/subsystem/dbcore/stat_entry(msg) diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm index 6883a69c1a..63e3d38f52 100644 --- a/code/controllers/subsystems/mobs.dm +++ b/code/controllers/subsystems/mobs.dm @@ -41,7 +41,6 @@ SUBSYSTEM_DEF(mobs) // Lets handle all of these while we have time, should always remain extremely small... if(length(death_list)) // Don't contact DB if this list is empty if(CONFIG_GET(flag/sql_enabled)) - establish_db_connection() if(!SSdbcore.IsConnected()) log_game("SQL ERROR during death reporting. Failed to connect.") else diff --git a/code/controllers/subsystems/persist_vr.dm b/code/controllers/subsystems/persist_vr.dm index 273bc0ea68..0637e217af 100644 --- a/code/controllers/subsystems/persist_vr.dm +++ b/code/controllers/subsystems/persist_vr.dm @@ -20,7 +20,6 @@ SUBSYSTEM_DEF(persist) if(!CONFIG_GET(flag/time_off)) return - establish_db_connection() if(!SSdbcore.IsConnected()) src.currentrun.Cut() return diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 9ac4c420cc..8e55f853de 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -6,7 +6,6 @@ for(var/mob/M in GLOB.player_list) if(M.client) playercount += 1 - establish_db_connection() if(!SSdbcore.IsConnected()) log_game("SQL ERROR during population polling. Failed to connect.") else @@ -47,7 +46,6 @@ log_game("Round ended without any feedback being generated. No feedback was sent to the database.") return - establish_db_connection() if(!SSdbcore.IsConnected()) log_game("SQL ERROR during feedback reporting. Failed to connect.") else diff --git a/code/game/mecha/equipment/tools/armor_melee.dm b/code/game/mecha/equipment/tools/armor_melee.dm index 6232dcc0cd..33677ce9bd 100644 --- a/code/game/mecha/equipment/tools/armor_melee.dm +++ b/code/game/mecha/equipment/tools/armor_melee.dm @@ -20,7 +20,7 @@ /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/handle_melee_contact(var/obj/item/W, var/mob/living/user, var/inc_damage = null) if(!action_checks(user)) return inc_damage - chassis.log_message("Attacked by [W]. Attacker - [user]") + chassis.log_message("Attacked by [W]. Attacker - [user]", LOG_GAME) if(prob(chassis.deflect_chance*deflect_coeff)) to_chat(user, span_danger("\The [W] bounces off \the [chassis]'s armor.")) chassis.log_append_to_last("Armor saved.") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index dcb7660de3..2aa7b2ce40 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -2803,7 +2803,7 @@ src.log_append_to_last("Armor saved.") src.occupant_message(span_notice("\The [user]'s attack is stopped by the armor.")) visible_message(span_infoplain(span_bold("\The [user]") + " rebounds off [src.name]'s armor!")) - user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + add_attack_logs(user, src, "attacked") playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) else if(damage < damage_minimum)//Pathetic damage levels just don't harm MECH. //CHOMPedit temp_damage_minimum -> damage_minimum @@ -2818,7 +2818,7 @@ if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) visible_message(span_danger("[user] [attack_message] [src]!")) - user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + add_attack_logs(user, src, "attacked") return 1 diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index b9e026df10..249896b322 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -285,7 +285,7 @@ else deflect_chance = initial(deflect_chance) src.occupant_message(span_red("You disable [src] defence mode.")) - src.log_message("Toggled defence mode.") + src.log_message("Toggled defence mode.", LOG_GAME) return @@ -363,7 +363,7 @@ return if(src.occupant.client) src.zoom = !src.zoom - src.log_message("Toggled zoom mode.") + src.log_message("Toggled zoom mode.", LOG_GAME) if(src.zoom) src.occupant_message(span_blue("Zoom mode enabled.")) else @@ -390,7 +390,7 @@ if(src.occupant) if(get_charge() > 0) thrusters = !thrusters - src.log_message("Toggled thrusters.") + src.log_message("Toggled thrusters.", LOG_GAME) if(src.thrusters) src.occupant_message(span_blue("Thrusters enabled.")) else diff --git a/code/game/world.dm b/code/game/world.dm index 3942d3525f..daeb4e8139 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -645,75 +645,6 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) if (src.status != s) src.status = s -#define FAILED_DB_CONNECTION_CUTOFF 5 -GLOBAL_VAR_INIT(failed_db_connections, 0) - -/hook/startup/proc/connectDB() - if(!CONFIG_GET(flag/sql_enabled)) - log_sql("SQL connection disabled in config.") - else if(!setup_database_connection()) - log_sql("Your server failed to establish a connection with the feedback database.") - else - log_sql("Feedback database connection established.") - return 1 - -/proc/setup_database_connection() - if(!CONFIG_GET(flag/sql_enabled)) - return 0 - if(GLOB.failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore. - return 0 - - if(!SSdbcore) - SSdbcore = new() - - var/user = CONFIG_GET(string/feedback_login) - var/pass = CONFIG_GET(string/feedback_password) - var/db = CONFIG_GET(string/feedback_database) - var/address = CONFIG_GET(string/address) - var/port = CONFIG_GET(number/port) - - SSdbcore.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") - . = SSdbcore.IsConnected() - if ( . ) - GLOB.failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter. - return - GLOB.failed_db_connections++ //If it failed, increase the failed connections counter. - log_sql(SSdbcore.ErrorMsg()) - return - -//This proc ensures that the connection to the feedback database (global variable dbcon) is established -/proc/establish_db_connection() - if(GLOB.failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) - return 0 - - if(!SSdbcore || !SSdbcore.IsConnected()) - return setup_database_connection() - else - return 1 - -// Cleans up DB connections and recreates them -/proc/reset_database_connections() - var/list/results = list("-- Resetting DB connections --") - GLOB.failed_db_connections = 0 - - if(SSdbcore?.IsConnected()) - SSdbcore.Disconnect() - results += "SSdbcore was connected and asked to disconnect" - else - results += "SSdbcore was not connected" - - if(!CONFIG_GET(flag/sql_enabled)) - results += "stopping because config.sql_enabled = false" - else - . = setup_database_connection() - if(.) - results += "SUCCESS: set up a connection successfully with setup_database_connection()" - else - results += "FAIL: failed to connect to the database with setup_database_connection()" - - results += "-- DB Reset End --" - log_sql(results.Join("\n")) - // Things to do when a new z-level was just made. /world/proc/max_z_changed() if(!istype(GLOB.players_by_zlevel, /list)) @@ -784,8 +715,6 @@ GLOBAL_VAR_INIT(failed_db_connections, 0) /world/proc/on_tickrate_change() SStimer?.reset_buckets() -#undef FAILED_DB_CONNECTION_CUTOFF - /proc/auxtools_stack_trace(msg) CRASH(msg) diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 5ee62875e6..1a17c96d0a 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -4,7 +4,6 @@ if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return - establish_db_connection() if(!SSdbcore.IsConnected()) return @@ -126,7 +125,6 @@ if(job) sql += " AND job = '[job]'" - establish_db_connection() if(!SSdbcore.IsConnected()) return @@ -224,7 +222,6 @@ var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" - establish_db_connection() if(!SSdbcore.IsConnected()) return @@ -276,7 +273,6 @@ if(!check_rights_for(user, R_BAN)) return - establish_db_connection() if(!SSdbcore.IsConnected()) to_chat(usr, span_filter_adminlog("[span_red("Failed to establish database connection")]")) return diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 0d4cc98d02..a4800c875b 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -40,7 +40,7 @@ var/ckeytext = ckey(key) - if(!establish_db_connection()) + if(!SSdbcore.IsConnected()) var/msg = "Ban database connection failure. Key [ckeytext] not checked" log_world(msg) message_admins(msg) diff --git a/code/modules/admin/admin_attack_log.dm b/code/modules/admin/admin_attack_log.dm index c85616f301..6189a59480 100644 --- a/code/modules/admin/admin_attack_log.dm +++ b/code/modules/admin/admin_attack_log.dm @@ -1,7 +1,5 @@ /atom/var/lastattacker = null /mob/var/lastattacked = null -/mob/var/attack_log = list( ) -/mob/var/dialogue_log = list( ) /proc/log_and_message_admins(var/message as text, var/mob/user = usr) log_admin(user ? "[key_name(user)] [message]" : "EVENT [message]") @@ -17,30 +15,3 @@ log_admin("[english_list(user_keys)] [message]") message_admins("[english_list(user_keys)] [message]") -/* Old procs -/proc/admin_attack_log(var/mob/attacker, var/mob/victim, var/attacker_message, var/victim_message, var/admin_message) - if(victim) - victim.attack_log += text("\[[time_stamp()]\]" + span_orange(" [key_name(attacker)] - [victim_message]")) - if(attacker) - attacker.attack_log += text("\[[time_stamp()]\]" + span_red(" [key_name(victim)] - [attacker_message]")) - - msg_admin_attack("[key_name(attacker)] [admin_message] [key_name(victim)] (INTENT: [attacker? uppertext(attacker.a_intent) : "N/A"]) (JMP)") - -/proc/admin_attacker_log_many_victims(var/mob/attacker, var/list/mob/victims, var/attacker_message, var/victim_message, var/admin_message) - if(!victims || !victims.len) - return - - for(var/mob/victim in victims) - admin_attack_log(attacker, victim, attacker_message, victim_message, admin_message) - -/proc/admin_inject_log(mob/attacker, mob/victim, obj/item, reagents, amount_transferred, violent=0) - if(violent) - violent = "violently " - else - violent = "" - admin_attack_log(attacker, - victim, - "used \the [weapon] to [violent]inject - [reagents] - [amount_transferred]u transferred", - "was [violent]injected with \the [weapon] - [reagents] - [amount_transferred]u transferred", - "used \the [weapon] to [violent]inject [reagents] ([amount_transferred]u transferred) into") -*/ diff --git a/code/modules/admin/admin_tools.dm b/code/modules/admin/admin_tools.dm index 61aa8dbaba..6284e6bd52 100644 --- a/code/modules/admin/admin_tools.dm +++ b/code/modules/admin/admin_tools.dm @@ -1,65 +1,84 @@ -/client/proc/cmd_admin_check_player_logs(mob/living/M as mob in GLOB.mob_list) - set category = "Admin.Logs" - set name = "Check Player Attack Logs" - set desc = "Check a player's attack logs." - - show_cmd_admin_check_player_logs(M) +ADMIN_VERB_ONLY_CONTEXT_MENU(cmd_admin_check_player_logs, R_ADMIN|R_MOD, "Check Mob Logs", mob/living/player in GLOB.mob_list) + user.show_cmd_admin_check_player_logs(player.logging, player.name, player.ckey, player.mind?.special_role) //Views specific attack logs belonging to one player. -/client/proc/show_cmd_admin_check_player_logs(mob/living/M) - var/dat = span_bold("[M]'s Attack Log:
") - dat += span_bold("Viewing attack logs of [M]") + " - (Played by ([key_name(M)]).
" - if(M.mind) - dat += span_bold("Current Antag?:") + " [(M.mind.special_role)?"Yes":"No"]
" - dat += "
" + span_bold("Note:") + " This is arranged from earliest to latest.

" +/client/proc/show_cmd_admin_check_player_logs(list/mob_data, target_name, target_ckey, target_special) + if(CONFIG_GET(flag/database_logging)) + var/datum/db_query/query = SSdbcore.NewQuery("SELECT mid,time,ckey,mob,area,color,type,message from erro_dialog WHERE ckey = :t_ckey", list("t_ckey" = ckey)) + if(!query.Execute()) + to_chat(src, span_admin("Database query error")) + qdel(query) + return + var/list/data = list() + while(query.NextRow()) + var/list/timestamped_message = list( + "event_id" = query.item[1], + "time" = query.item[2], + "ckey" = query.item[3], + "name" = query.item[4], + "loc" = query.item[5], + "color" = query.item[6], + "message" = query.item[8] + ) + var/entry_type = query.item[7] + if(!islist(data[entry_type])) + data[entry_type] = list() + UNTYPED_LIST_ADD(data[entry_type], timestamped_message) + qdel(query) + if(!length(data)) + to_chat(src, span_admin("No data found.")) + return + admin_log_view_target(data, target_name, target_ckey, target_special) + return + if(!length(mob_data)) + to_chat(src, span_admin("No data found.")) + return + admin_log_view_target(mob_data, target_name, target_ckey, target_special) - if(!isemptylist(M.attack_log)) - dat += "
" - for(var/l in M.attack_log) - dat += "[l]
" +ADMIN_VERB(persistent_client_logs, R_ADMIN|R_MOD, "Check Player Logs", "Displays the client logs of the selected ckey.", ADMIN_CATEGORY_LOGS) + var/mob/living/selected_key = tgui_input_list(user, "Select a ckey to check their logs", "Ckey", GLOB.persistent_clients_by_ckey) + if(!selected_key) + return - dat += "
" + var/datum/persistent_client/selected = GLOB.persistent_clients_by_ckey[selected_key] - else - dat += span_italics("No attack logs found for [M].") + user.show_cmd_admin_check_player_logs(selected.logging, selected.mob?.name, selected_key, selected.mob?.mind?.special_role) - var/datum/browser/popup = new(usr, "admin_attack_log", "[src]", 650, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() +/client/proc/admin_log_view_target(list/log_data, name, ckey, special, use_db) + var/datum/player_log_viwer/log_view = new(log_data, name, ckey, special) + log_view.tgui_interact(mob) - onclose(usr, "admin_attack_log") +/datum/player_log_viwer + var/list/log_data + var/target_name + var/target_ckey + var/special_role - feedback_add_details("admin_verb","PL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/player_log_viwer/tgui_close(mob/user) + . = ..() + log_data = null -/client/proc/cmd_admin_check_dialogue_logs(mob/living/M as mob in GLOB.mob_list) - set category = "Admin.Logs" - set name = "Check Player Dialogue Logs" - set desc = "Check a player's dialogue logs." - show_cmd_admin_check_dialogue_logs(M) +/datum/player_log_viwer/New(list/data, name, ckey, special) + . = ..() + log_data = data + target_name = name + target_ckey = ckey + special_role = special -//Views specific dialogue logs belonging to one player. -/client/proc/show_cmd_admin_check_dialogue_logs(mob/living/M) - var/dat = span_bold("[M]'s Dialogue Log:
") - dat += span_bold("Viewing say and emote logs of [M]") + " - (Played by ([key_name(M)]).
" - if(M.mind) - dat += span_bold("Current Antag?:") + " [(M.mind.special_role)?"Yes":"No"]
" - dat += "
" + span_bold("Note:") + " This is arranged from earliest to latest.

" +/datum/player_log_viwer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PlayerLogViewer") + ui.open() - if(!isemptylist(M.dialogue_log)) - dat += "
" +/datum/player_log_viwer/tgui_state(mob/user) + return ADMIN_STATE(R_ADMIN|R_MOD) - for(var/d in M.dialogue_log) - dat += "[d]
" - - dat += "
" - else - dat += span_italics("No dialogue logs found for [M].") - var/datum/browser/popup = new(usr, "admin_dialogue_log", "[src]", 650, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() - - onclose(usr, "admin_dialogue_log") - - - feedback_add_details("admin_verb","PDL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/player_log_viwer/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + return list( + "entries" = log_data, + "name" = target_name, + "ckey" = target_ckey, + "special" = special_role + ) diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index de6c1d79c2..98a4fb766f 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -20,8 +20,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/cmd_admin_pm_context, //right-click adminPM interface, /client/proc/cmd_admin_pm_panel, //admin-pm list, /client/proc/cmd_admin_subtle_message, //send an message to somebody as a 'voice in their head', - /client/proc/cmd_admin_check_player_logs, //checks a player's attack logs, - /client/proc/cmd_admin_check_dialogue_logs, //checks a player's dialogue logs, /datum/admins/proc/access_news_network, //allows access of newscasters, /client/proc/jumptocoord, //we ghost and jump to a coordinate, /client/proc/Getmob, //teleports a mob to our location, @@ -174,8 +172,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /datum/admins/proc/toggleguests, /datum/admins/proc/announce, /client/proc/cmd_admin_subtle_message, - /client/proc/cmd_admin_check_player_logs, - /client/proc/cmd_admin_check_dialogue_logs, /datum/admins/proc/access_news_network, /client/proc/cmd_admin_direct_narrate, /client/proc/cmd_admin_world_narrate, @@ -277,8 +273,6 @@ GLOBAL_LIST_INIT(admin_verbs_event_manager, list( /client/proc/cmd_admin_pm_context, //right-click adminPM interface, /client/proc/cmd_admin_pm_panel, //admin-pm list, /client/proc/cmd_admin_subtle_message, //send an message to somebody as a 'voice in their head', - /client/proc/cmd_admin_check_player_logs, //checks a player's attack logs, - /client/proc/cmd_admin_check_dialogue_logs, //checks a player's dialogue logs, /datum/admins/proc/access_news_network, //allows access of newscasters, /client/proc/jumptocoord, //we ghost and jump to a coordinate, /client/proc/Getmob, //teleports a mob to our location, diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 124d722323..384156edd0 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -683,7 +683,6 @@ ADMIN_VERB(removetickets, R_ADMIN, "Security Tickets", "Allows one to remove tic var/dat = "Book Inventory Management\n" dat += "

ADMINISTRATIVE MANAGEMENT

" - establish_db_connection() if(!SSdbcore.IsConnected()) dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.") diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 28b6536332..b64a66cd6d 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -69,7 +69,7 @@ DEBUG GLOB.jobban_keylist=list() log_admin("jobban_keylist was empty") else - if(!establish_db_connection()) + if(!SSdbcore.IsConnected()) log_sql("Database connection failed. Reverting to the legacy ban system.") CONFIG_SET(flag/ban_legacy_system, TRUE) jobban_loadbanfile() diff --git a/code/modules/admin/verbs/antag-ooc.dm b/code/modules/admin/verbs/antag-ooc.dm index 2405d5e6d8..a2b50be813 100644 --- a/code/modules/admin/verbs/antag-ooc.dm +++ b/code/modules/admin/verbs/antag-ooc.dm @@ -39,4 +39,4 @@ if((M.mind && M.mind.special_role && A && A.can_hear_aooc) || isobserver(M)) // Antags must have their type be allowed to AOOC to see AOOC. This prevents, say, ERT from seeing AOOC. to_chat(M, span_ooc(span_aooc("[create_text_tag("aooc", "Antag-OOC:", M.client)] [player_display]: " + span_message("[msg]")))) - src.mob.log_talk("(AOOC) [msg]", LOG_OOC) + src.mob.log_talk("(AOOC) [msg]", LOG_OOC, color="#ff0000") diff --git a/code/modules/admin/verbs/check_customitem_activity.dm b/code/modules/admin/verbs/check_customitem_activity.dm index 0bc0e8dcd0..cadd8d5ae3 100644 --- a/code/modules/admin/verbs/check_customitem_activity.dm +++ b/code/modules/admin/verbs/check_customitem_activity.dm @@ -31,7 +31,6 @@ GLOBAL_VAR_INIT(inactive_keys, "None
") if(GLOB.checked_for_inactives) return - establish_db_connection() if(!SSdbcore.IsConnected()) return diff --git a/code/modules/admin/verbs/dbcon_fix.dm b/code/modules/admin/verbs/dbcon_fix.dm index 99deb034a7..cfc9233aff 100644 --- a/code/modules/admin/verbs/dbcon_fix.dm +++ b/code/modules/admin/verbs/dbcon_fix.dm @@ -15,7 +15,6 @@ else log_admin("Database already disconnected") - establish_db_connection() var/errno = SSdbcore.ErrorMsg() if(errno) log_admin("Database connection returned error message `[errno]`. Aborting.") diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index af1daab44d..0f2dda9aef 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -437,7 +437,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // Returns null if no DB connection can be established, or -1 if the requested key was not found in the database /proc/get_player_age(key) - establish_db_connection() if(!SSdbcore.IsConnected()) return null @@ -458,7 +457,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if ( IsGuestKey(src.key) ) return - establish_db_connection() if(!SSdbcore.IsConnected()) return diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index a8be59f005..b65bb906cf 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -42,7 +42,7 @@ message_admins("[key_name_admin(src)] has attempted to post a link in OOC: [msg]") return - src.mob.log_talk(msg, LOG_OOC) + src.mob.log_talk(msg, LOG_OOC, color="#0051ff") if(msg) handle_spam_prevention(MUTE_OOC) @@ -125,7 +125,7 @@ message_admins("[key_name_admin(src)] has attempted to post a link in OOC: [msg]") return - src.mob.log_message(msg, LOG_LOOC) + src.mob.log_message(msg, LOG_LOOC, color="#53cbff") if(msg) handle_spam_prevention(MUTE_LOOC) diff --git a/code/modules/food/kitchen/cooking_machines/fryer.dm b/code/modules/food/kitchen/cooking_machines/fryer.dm index 0682468818..dc3f731535 100644 --- a/code/modules/food/kitchen/cooking_machines/fryer.dm +++ b/code/modules/food/kitchen/cooking_machines/fryer.dm @@ -235,8 +235,7 @@ else to_chat(victim, span_danger("Searing hot oil scorches your [E ? E.name : "flesh"]!")) - user.attack_log += text("\[[time_stamp()]\] [span_red("Has [cook_type] \the [victim] ([victim.ckey]) in \a [src]")]") - victim.attack_log += text("\[[time_stamp()]\] [span_orange("Has been [cook_type] in \a [src] by [user.name] ([user.ckey])")]") + add_attack_logs(user, victim, "has been [cook_type] with [src]") msg_admin_attack("[key_name_admin(user)] [cook_type] \the [victim] ([victim.ckey]) in \a [src]. (JMP)") //Coat the victim in some oil diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 5540d0b6b0..debe48a02f 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -43,7 +43,6 @@ Filter by Author: [author]
\[Start Search\]
"} if(1) - establish_db_connection() if(!SSdbcore.IsConnected()) dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.") + "
" else if(!SQLquery) @@ -263,7 +262,6 @@ No.
"} if(8) dat += "

External Archive

" //VOREStation Edit - establish_db_connection() //dat += "

" + span_red("arning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.") + "

" //VOREStation Removal @@ -302,7 +300,6 @@ var/dat = "Book Inventory Management\n" // dat += "

ADMINISTRATIVE MANAGEMENT

" - establish_db_connection() if(!SSdbcore.IsConnected()) dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.") @@ -423,7 +420,6 @@ if(scanner.cache.unique) tgui_alert_async(usr, "This book has been rejected from the database. Aborting!") else - establish_db_connection() if(!SSdbcore.IsConnected()) tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") else @@ -448,7 +444,6 @@ if(href_list["targetid"]) var/sqlid = sanitizeSQL(href_list["targetid"]) - establish_db_connection() if(!SSdbcore.IsConnected()) tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") if(bibledelay) @@ -480,7 +475,6 @@ if(!check_rights(R_ADMIN)) return var/sqlid = sanitizeSQL(href_list["delid"]) - establish_db_connection() if(!SSdbcore.IsConnected()) tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") else diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 5b68a079bf..109d07121f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -65,7 +65,6 @@ if(ismob(loc)) var/mob/M = loc T = get_turf(M) //Where is the body located? - attack_log = M.attack_log //preserve our attack logs by copying them to our ghost gender = M.gender if(M.mind && M.mind.name) name = M.mind.name diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index b6459fe641..25075d5a92 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -4,7 +4,7 @@ if(!message) return - log_talk("(GHOST SAY) [message]", LOG_SAY) + log_talk("(GHOST SAY) [message]", LOG_SAY, color="#9c00bc") if (client) if(message) @@ -20,7 +20,7 @@ if(!message) return - log_message("(GHOST EMOTE) [message]", LOG_EMOTE) + log_message("(GHOST EMOTE) [message]", LOG_EMOTE, color="#9c00bc") if(client) if(message) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 02f55e9f8a..e19c18c2e0 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -423,12 +423,12 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) blooploop(message, extrarange = -6, volume = 25, sound_preference = /datum/preference/toggle/whisper_sounds) // playsound(T, pick(voice_sounds_list), 25, TRUE, extrarange = -6, falloff = 1 , is_global = TRUE, frequency = voice_freq, ignore_walls = TRUE, preference = /datum/preference/toggle/whisper_sounds) //CHOMPEdit - ignore_walls = TRUE - log_talk(message, LOG_WHISPER) + log_talk(message, LOG_WHISPER, color="#aeaeae") else if(do_sound && message) blooploop(message, volume = 75) // playsound(T, pick(voice_sounds_list), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = voice_freq, ignore_walls = TRUE, preference = /datum/preference/toggle/say_sounds) //CHOMPEdit - ignore_walls = TRUE - log_talk(message, LOG_SAY) + log_talk(message, LOG_SAY, color="#c0c0c0") return 1 /*************************************************************************/ diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 7be834317b..e06a39ea1f 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -67,7 +67,6 @@ if(!client) return 0 if(href_list["privacy_poll"]) - establish_db_connection() if(!SSdbcore.IsConnected()) return var/voted = 0 diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm index ba7bf5a09c..0a9027e8d2 100644 --- a/code/modules/mob/new_player/poll.dm +++ b/code/modules/mob/new_player/poll.dm @@ -1,6 +1,5 @@ /mob/new_player/proc/handle_privacy_poll() - establish_db_connection() if(!SSdbcore.IsConnected()) return var/voted = 0 @@ -47,7 +46,6 @@ var/optiontext /mob/new_player/proc/handle_player_polling() - establish_db_connection() if(SSdbcore.IsConnected()) var/isadmin = 0 if(src.client && check_rights_for(src.client, R_HOLDER)) @@ -81,7 +79,6 @@ /mob/new_player/proc/poll_player(var/pollid = -1) if(pollid == -1) return - establish_db_connection() if(SSdbcore.IsConnected()) var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid]") @@ -343,7 +340,6 @@ if(!isnum(pollid) || !isnum(optionid)) return - establish_db_connection() if(SSdbcore.IsConnected()) var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") @@ -415,7 +411,6 @@ if(!isnum(pollid) || !istext(replytext)) return - establish_db_connection() if(SSdbcore.IsConnected()) var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") @@ -474,7 +469,6 @@ if(!isnum(pollid) || !isnum(optionid)) return - establish_db_connection() if(SSdbcore.IsConnected()) var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime") diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm index 89b674706b..a8f6799ee5 100644 --- a/code/modules/mob/say_vr.dm +++ b/code/modules/mob/say_vr.dm @@ -366,7 +366,7 @@ G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle)) if(client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) || check_rights_for(G.client, R_HOLDER)) to_chat(G, span_psay("[formatted_name] thinks, \"[message]\"")) - M.log_talk("(PSAY) [message]", LOG_SAY) + M.log_talk("(PSAY) [message]", LOG_SAY, color="#d900ff") else //There wasn't anyone to send the message to, pred or prey, so let's just say it instead and correct our psay just in case. M.forced_psay = FALSE M.say(message) @@ -480,7 +480,7 @@ G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle)) if(client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) || check_rights_for(G.client, R_HOLDER)) to_chat(G, span_pemote("[formatted_name] [message]")) - M.log_talk(message, LOG_SAY) + M.log_talk(message, LOG_SAY, color="#d900ff") else //There wasn't anyone to send the message to, pred or prey, so let's just emote it instead and correct our psay just in case. M.forced_psay = FALSE M.me_verb(message) diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 21e1620d58..2649bf87ad 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -107,7 +107,7 @@ type = MESSAGE_TYPE_NIF, html = span_nif(span_bold("\[[icon2html(nif.big_icon, CS.client)]NIF\]") + message)) - sender.log_talk("NSAY (NIF:[nif.human.real_name]): [message]", LOG_SAY) + sender.log_talk("NSAY (NIF:[nif.human.real_name]): [message]", LOG_SAY, color="#ff00c8") /datum/nifsoft/soulcatcher/proc/emote_into(var/message, var/mob/living/sender, var/mob/eyeobj, var/whisper) var/sender_name = eyeobj ? eyeobj.name : sender.name @@ -135,7 +135,7 @@ type = MESSAGE_TYPE_NIF, html = span_nif(span_bold("\[[icon2html(nif.big_icon,CS.client)]NIF\]") + message)) - sender.log_message("NME (NIF:[nif.human.real_name]): [message]", LOG_EMOTE) + sender.log_message("NME (NIF:[nif.human.real_name]): [message]", LOG_EMOTE, color="#ff00c8") /datum/nifsoft/soulcatcher/proc/show_settings(var/mob/living/carbon/human/H) set waitfor = FALSE diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm index fe821da3c1..ed47ec8384 100644 --- a/code/modules/pda/messenger.dm +++ b/code/modules/pda/messenger.dm @@ -179,7 +179,7 @@ PM.receive_message(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "\ref[pda]"), "\ref[pda]") SStgui.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message - U.log_message("(PDA: [src.name] | [U.real_name]) sent \"[t]\" to [P.name]", LOG_PDA) + U.log_message("(PDA: [src.name] | [U.real_name]) sent \"[t]\" to [P.name]", LOG_PDA, color="#00ff00") to_chat(U, "[icon2html(pda,U.client)] Sent message to [P.owner] ([P.ownjob]), \"[t]\"") else to_chat(U, span_notice("ERROR: Messaging server is not responding.\n\n\ diff --git a/code/modules/recycling/packagewrap.dm b/code/modules/recycling/packagewrap.dm index 04b3d7bffc..e7d0f546f2 100644 --- a/code/modules/recycling/packagewrap.dm +++ b/code/modules/recycling/packagewrap.dm @@ -21,7 +21,7 @@ if(user in target) //no wrapping closets that you are inside - it's not physically possible return - user.attack_log += text("\[[time_stamp()]\] [span_blue("Has used [src.name] on \ref[target]")]") + add_attack_logs(user, target, "has been wrapped with [src]") if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box))) var/obj/item/O = target diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index b22ff03e8c..20c75c90bc 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -363,7 +363,6 @@ GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder) if(!feedback) return round_end_data_gathering() //round_end time logging and some other data processing - establish_db_connection() if(!SSdbcore.IsConnected()) return var/round_id diff --git a/code/modules/shieldgen/directional_shield.dm b/code/modules/shieldgen/directional_shield.dm index 3b7350b956..b964677205 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/modules/shieldgen/directional_shield.dm @@ -383,7 +383,7 @@ else destroy_shields() my_tool.set_ready_state(TRUE) - my_tool.log_message("Power lost.") + my_tool.log_message("Power lost.", LOG_GAME) else my_tool.set_ready_state(TRUE) @@ -409,5 +409,5 @@ ..() my_mecha.use_power(my_tool.energy_drain) if(!active && shield_health < shield_regen_amount) - my_tool.log_message("Shield overloaded.") + my_tool.log_message("Shield overloaded.", LOG_GAME) my_mecha.use_power(my_tool.energy_drain * 4) diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm index 14e0c453b5..5fc7abfb9d 100644 --- a/code/modules/spells/spell_code.dm +++ b/code/modules/spells/spell_code.dm @@ -88,7 +88,7 @@ take_charge(user, skipcharge) before_cast(targets) //applies any overlays and effects - user.attack_log += text("\[[time_stamp()]\] [span_red("[user.real_name] ([user.ckey]) cast the spell [name].")]") + add_attack_logs(user, targets, "casted the spell [name]") if(prob(critfailchance)) critfail(targets, user) else diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 8e9b1edbb1..0bba9b19c1 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -283,7 +283,7 @@ else user.add_spell(S) to_chat(user, span_notice("you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!")) - user.attack_log += text("\[[time_stamp()]\] [span_orange("[user.real_name] ([user.ckey]) learned the spell [spellname] ([S]).")]") + add_attack_logs(user, src, "learned the spell [spellname] ([S]).") onlearned(user) /obj/item/spellbook/oneuse/proc/recoil(mob/user as mob) diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index ff75df11a1..ca2c60804d 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -418,7 +418,7 @@ if(!damage) return visible_message(span_danger("[user] [attack_message] the [src]!")) - user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + add_attack_logs(user, src, "attacked") user.do_attack_animation(src) src.health -= damage if(mechanical && prob(10)) diff --git a/code/modules/vore/eating/soulcatcher.dm b/code/modules/vore/eating/soulcatcher.dm index 6104e8304e..60354b3cd0 100644 --- a/code/modules/vore/eating/soulcatcher.dm +++ b/code/modules/vore/eating/soulcatcher.dm @@ -104,7 +104,7 @@ for(var/mob/living/carbon/brain/caught_soul/CS as anything in brainmobs) to_chat(CS, message) - sender.log_talk("NSAY (NIF:[owner.real_name]): [message]", LOG_SAY) + sender.log_talk("NSAY (SC:[owner.real_name]): [message]", LOG_SAY, color="#ff006f") // Forwards the emotes of captured souls /obj/soulgem/proc/use_emote(var/message, var/mob/living/sender, var/mob/eyeobj, var/whisper) @@ -127,7 +127,7 @@ for(var/mob/living/carbon/brain/caught_soul/CS as anything in brainmobs) to_chat(CS, message) - sender.log_message("NME (NIF:[owner.real_name]): [message]", LOG_EMOTE) + sender.log_message("NME (SC:[owner.real_name]): [message]", LOG_EMOTE, color="#ff006f") // The capture function which transfers the given mob's mind into the soulcatcher /obj/soulgem/proc/catch_mob(var/mob/M, var/custom_name) diff --git a/config/example/config.txt b/config/example/config.txt index f887e74445..36a6c47e56 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -590,6 +590,11 @@ JUKEBOX_TRACK_FILES config/jukebox.json # REQUIRES chatlog_database_backend to be enabled #CHATLOG_DATABASE_API_ENDPOINT https://example.com +# Logs player logs inside the Database. Reduces memory usage, but increases cpu load +# REQUIRES an database +# Uncomment to enable +#DATABASE_LOGGING + ## Uncomment to block granting profiling privileges to users with R_DEBUG, for performance purposes #FORBID_ADMIN_PROFILING diff --git a/tgui/packages/tgui/interfaces/PlayerLogViewer/function.ts b/tgui/packages/tgui/interfaces/PlayerLogViewer/function.ts new file mode 100644 index 0000000000..2eb883cafa --- /dev/null +++ b/tgui/packages/tgui/interfaces/PlayerLogViewer/function.ts @@ -0,0 +1,11 @@ +import { formatTime } from 'tgui-core/format'; + +export function stripHtml(str: string) { + return str.replace(/<[^>]+>/g, ''); +} + +export function displayTime(time: string | number): string { + if (typeof time === 'number') return formatTime(time); + const d = new Date(time); + return d.toLocaleTimeString(); +} diff --git a/tgui/packages/tgui/interfaces/PlayerLogViewer/index.tsx b/tgui/packages/tgui/interfaces/PlayerLogViewer/index.tsx new file mode 100644 index 0000000000..d5446ab021 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PlayerLogViewer/index.tsx @@ -0,0 +1,176 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Box, Button, Input, Section, Stack, Tabs } from 'tgui-core/components'; +import { displayTime, stripHtml } from './function'; +import type { Data, ExtendedLogEntry } from './types'; + +export const PlayerLogViewer = (props) => { + const { data } = useBackend(); + + const [activeTab, setActiveTab] = useState(''); + const [search, setSearch] = useState(''); + const [searchRegex, setSearchRegex] = useState(false); + const [caseSensitive, setCaseSensitive] = useState(false); + if (!search && searchRegex) { + setSearchRegex(false); + } + + const { entries, name, ckey, special } = data; + + const allEntries = Object.entries(entries) + .flatMap(([category, logs]) => + logs.map((entry) => ({ ...entry, category: category })), + ) + .sort((a, b) => { + if (a.time !== b.time) + return displayTime(a.time).localeCompare(displayTime(b.time)); + return a.event_id - b.event_id; + }); + + const entriesWithAll: Record = { + ALL: allEntries, + ...entries, + }; + + if (!activeTab && Object.keys(entriesWithAll).length > 0) { + setActiveTab('ALL'); + } + + return ( + + + + +
+ + + {`Viewing logs of `} + + {name} + + {`, played by `} + + {ckey ?? ''} + + + + {`Characrer special role: `} + + {special ? special : ''} + + + +
+
+ + + {Object.keys(entriesWithAll).map((entry) => ( + setActiveTab(entry)} + > + {entry} + + ))} + + + +
+ + + + +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PlayerLogViewer/types.ts b/tgui/packages/tgui/interfaces/PlayerLogViewer/types.ts new file mode 100644 index 0000000000..fb5bf37cb0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PlayerLogViewer/types.ts @@ -0,0 +1,18 @@ +export type Data = { + entries: Record; + name: string | null; + ckey: string; + special: string | null; +}; + +type LogEntry = { + event_id: number; + time: number | string; + ckey: string | null; + name: string; + loc: string | null; + color: string | null; + message: string; +}; + +export type ExtendedLogEntry = { category: string } & Required; diff --git a/vorestation.dme b/vorestation.dme index dbe329bc1c..fe829217b7 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -440,6 +440,7 @@ #include "code\_helpers\logging\attack.dm" #include "code\_helpers\logging\debug.dm" #include "code\_helpers\logging\game.dm" +#include "code\_helpers\logging\log_to_db.dm" #include "code\_helpers\logging\mob.dm" #include "code\_helpers\logging\pda.dm" #include "code\_helpers\logging\research.dm"