diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 854b10bad48..9dae5569556 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -50,3 +50,15 @@ #define LOGSRC_CKEY "Ckey" #define LOGSRC_MOB "Mob" + +//wrapper macros for easier grepping +#define DIRECT_OUTPUT(A, B) A << B +#define DIRECT_INPUT(A, B) A >> B +#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) +#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) +#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) +#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) +#define READ_FILE(file, text) DIRECT_INPUT(file, text) +//This is an external call, "true" and "false" are how rust parses out booleans +#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") +#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") diff --git a/code/__HELPERS/logging/_logging.dm b/code/__HELPERS/logging/_logging.dm new file mode 100644 index 00000000000..9f0070c7247 --- /dev/null +++ b/code/__HELPERS/logging/_logging.dm @@ -0,0 +1,244 @@ +//print a warning message to world.log +#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") +/proc/warning(msg) + msg = "## WARNING: [msg]" + log_world(msg) + +//not an error or a warning, but worth to mention on the world log, just in case. +#define NOTICE(MSG) notice(MSG) +/proc/notice(msg) + msg = "## NOTICE: [msg]" + log_world(msg) + +//print a testing-mode debug message to world.log and world +#ifdef TESTING +#define testing(msg) log_world("## TESTING: [msg]"); to_chat(world, "## TESTING: [msg]") + +GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global")) +// we don't really check if a word or name is used twice, be aware of that +#define testing_profile_start(NAME, LIST) LIST[NAME] = world.timeofday +#define testing_profile_current(NAME, LIST) round((world.timeofday - LIST[NAME])/10,0.1) +#define testing_profile_output(NAME, LIST) testing("[LIST["_PROFILE_NAME"]] profile of [NAME] is [testing_profile_current(NAME,LIST)]s") +#define testing_profile_output_all(LIST) { for(var/_NAME in LIST) { testing_profile_current(,_NAME,LIST); }; }; +#else +#define testing(msg) +#define testing_profile_start(NAME, LIST) +#define testing_profile_current(NAME, LIST) +#define testing_profile_output(NAME, LIST) +#define testing_profile_output_all(LIST) +#endif + +#define testing_profile_global_start(NAME) testing_profile_start(NAME,GLOB.testing_global_profiler) +#define testing_profile_global_current(NAME) testing_profile_current(NAME, GLOB.testing_global_profiler) +#define testing_profile_global_output(NAME) testing_profile_output(NAME, GLOB.testing_global_profiler) +#define testing_profile_global_output_all testing_profile_output_all(GLOB.testing_global_profiler) + +#define testing_profile_local_init(PROFILE_NAME) var/list/_timer_system = list( "_PROFILE_NAME" = PROFILE_NAME, "_start_of_proc" = world.timeofday ) +#define testing_profile_local_start(NAME) testing_profile_start(NAME, _timer_system) +#define testing_profile_local_current(NAME) testing_profile_current(NAME, _timer_system) +#define testing_profile_local_output(NAME) testing_profile_output(NAME, _timer_system) +#define testing_profile_local_output_all testing_profile_output_all(_timer_system) + +#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM) +/proc/log_test(text) + WRITE_LOG(GLOB.test_log, text) + SEND_TEXT(world.log, text) +#endif + +#if defined(REFERENCE_DOING_IT_LIVE) +#define log_reftracker(msg) log_harddel("## REF SEARCH [msg]") + +/proc/log_harddel(text) + WRITE_LOG(GLOB.harddel_log, text) + +#elif defined(REFERENCE_TRACKING) // Doing it locally +#define log_reftracker(msg) log_world("## REF SEARCH [msg]") + +#else //Not tracking at all +#define log_reftracker(msg) +#endif + +/** + * Generic logging helper + * + * reads the type of the log + * and writes it to the respective log file + * unless log_globally is FALSE + * Arguments: + * * message - The message being logged + * * message_type - the type of log the message is(ATTACK, SAY, etc) + * * color - color of the log text + * * log_globally - boolean checking whether or not we write this log to the log file + */ +/atom/proc/log_message(message, message_type, color = null, log_globally = TRUE) + if(!log_globally) + return + //SKYRAT EDIT ADDITION BEGIN + #ifndef SPACEMAN_DMM + if(CONFIG_GET(flag/sql_game_log) && CONFIG_GET(flag/sql_enabled)) + var/datum/db_query/query_sql_log_messages = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("game_log")] (datetime, round_id, ckey, loc, type, message) + VALUES (:time, :round_id, :ckey, :loc, :type, :message) + "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "ckey" = key_name(src), "loc" = loc_name(src), type = message_type, "message" = message)) + if(!query_sql_log_messages.warn_execute()) + qdel(query_sql_log_messages) + return + qdel(query_sql_log_messages) + if(!CONFIG_GET(flag/file_game_log)) + return + #endif + //SKYRAT EDIT ADDITION END + var/log_text = "[key_name(src)] [message] [loc_name(src)]" + switch(message_type) + if(LOG_ATTACK) + log_attack(log_text) + if(LOG_SAY) + log_say(log_text) + if(LOG_WHISPER) + log_whisper(log_text) + if(LOG_EMOTE) + log_emote(log_text) + //SKYRAT EDIT ADDITION BEGIN + if(LOG_SUBTLER) + log_subtler(log_text) + //SKYRAT EDIT ADDITION END + if(LOG_RADIO_EMOTE) + log_radio_emote(log_text) + if(LOG_DSAY) + log_dsay(log_text) + if(LOG_PDA) + log_pda(log_text) + if(LOG_CHAT) + log_chat(log_text) + if(LOG_COMMENT) + log_comment(log_text) + if(LOG_TELECOMMS) + log_telecomms(log_text) + if(LOG_ECON) + log_econ(log_text) + if(LOG_OOC) + log_ooc(log_text) + if(LOG_ADMIN) + log_admin(log_text) + if(LOG_ADMIN_PRIVATE) + log_admin_private(log_text) + if(LOG_ASAY) + log_adminsay(log_text) + if(LOG_OWNERSHIP) + log_game(log_text) + if(LOG_GAME) + log_game(log_text) + if(LOG_MECHA) + log_mecha(log_text) + if(LOG_SHUTTLE) + log_shuttle(log_text) + else + stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") + log_game(log_text) + +/* For logging round startup. */ +/proc/start_log(log) + WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------") + +/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ +/proc/shutdown_logging() + rustg_log_close_all() + +/* Helper procs for building detailed log lines */ +/proc/key_name(whom, include_link = null, include_name = TRUE) + var/mob/M + var/client/C + var/key + var/ckey + var/fallback_name + + if(!whom) + return "*null*" + if(istype(whom, /client)) + C = whom + M = C.mob + key = C.key + ckey = C.ckey + else if(ismob(whom)) + M = whom + C = M.client + key = M.key + ckey = M.ckey + else if(istext(whom)) + key = whom + ckey = ckey(whom) + C = GLOB.directory[ckey] + if(C) + M = C.mob + else if(istype(whom,/datum/mind)) + var/datum/mind/mind = whom + key = mind.key + ckey = ckey(key) + if(mind.current) + M = mind.current + if(M.client) + C = M.client + else + fallback_name = mind.name + else // Catch-all cases if none of the types above match + var/swhom = null + + if(istype(whom, /atom)) + var/atom/A = whom + swhom = "[A.name]" + else if(istype(whom, /datum)) + swhom = "[whom]" + + if(!swhom) + swhom = "*invalid*" + + return "\[[swhom]\]" + + . = "" + + if(!ckey) + include_link = FALSE + + if(key) + if(C?.holder && C.holder.fakekey && !include_name) + if(include_link) + . += "" + . += "Administrator" + else + if(include_link) + . += "" + . += key + if(!C) + . += "\[DC\]" + + if(include_link) + . += "" + else + . += "*no key*" + + if(include_name) + if(M) + if(M.real_name) + . += "/([M.real_name])" + else if(M.name) + . += "/([M.name])" + else if(fallback_name) + . += "/([fallback_name])" + + return . + +/proc/key_name_admin(whom, include_name = TRUE) + return key_name(whom, TRUE, include_name) + +/proc/loc_name(atom/A) + if(!istype(A)) + return "(INVALID LOCATION)" + + var/turf/T = A + if (!istype(T)) + T = get_turf(A) + + if(istype(T)) + return "([AREACOORD(T)])" + else if(A.loc) + return "(UNKNOWN (?, ?, ?))" diff --git a/code/__HELPERS/logging/admin.dm b/code/__HELPERS/logging/admin.dm new file mode 100644 index 00000000000..5e35d35d09f --- /dev/null +++ b/code/__HELPERS/logging/admin.dm @@ -0,0 +1,43 @@ +/* Items with ADMINPRIVATE prefixed are stripped from public logs. */ + +/// General logging for admin actions +/proc/log_admin(text) + GLOB.admin_log.Add(text) + if (CONFIG_GET(flag/log_admin)) + WRITE_LOG(GLOB.world_game_log, "ADMIN: [text]") + +/// Logging for admin actions on or with circuits +/proc/log_admin_circuit(text) + GLOB.admin_log.Add(text) + if(CONFIG_GET(flag/log_admin)) + WRITE_LOG(GLOB.world_game_log, "ADMIN: CIRCUIT: [text]") + +/// General logging for admin actions +/proc/log_admin_private(text) + GLOB.admin_log.Add(text) + if (CONFIG_GET(flag/log_admin)) + WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: [text]") + +/// Logging for AdminSay (ASAY) messages +/proc/log_adminsay(text) + GLOB.admin_log.Add(text) + if (CONFIG_GET(flag/log_adminchat)) + WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: ASAY: [text]") + +/// Logging for DeachatSay (DSAY) messages +/proc/log_dsay(text) + if (CONFIG_GET(flag/log_adminchat)) + WRITE_LOG(GLOB.world_game_log, "ADMIN: DSAY: [text]") + +/** + * Writes to a special log file if the log_suspicious_login config flag is set, + * which is intended to contain all logins that failed under suspicious circumstances. + * + * Mirrors this log entry to log_access when access_log_mirror is TRUE, so this proc + * doesn't need to be used alongside log_access and can replace it where appropriate. + */ +/proc/log_suspicious_login(text, access_log_mirror = TRUE) + if (CONFIG_GET(flag/log_suspicious_login)) + WRITE_LOG(GLOB.world_suspicious_login_log, "SUSPICIOUS_ACCESS: [text]") + if(access_log_mirror) + log_access(text) diff --git a/code/__HELPERS/logging/antagonists.dm b/code/__HELPERS/logging/antagonists.dm new file mode 100644 index 00000000000..c66fc8437e1 --- /dev/null +++ b/code/__HELPERS/logging/antagonists.dm @@ -0,0 +1,24 @@ +/// Logging for traitor objectives +/proc/log_traitor(text) + if (CONFIG_GET(flag/log_traitor)) + WRITE_LOG(GLOB.world_game_log, "TRAITOR: [text]") + +/// Logging for items purchased from a traitor uplink +/proc/log_uplink(text) + if (CONFIG_GET(flag/log_uplink)) + WRITE_LOG(GLOB.world_uplink_log, "UPLINK: [text]") + +/// Logging for changeling powers purchased +/proc/log_changeling_power(text) + if (CONFIG_GET(flag/log_uplink)) + WRITE_LOG(GLOB.world_uplink_log, "CHANGELING: [text]") + +/// Logging for heretic powers learned +/proc/log_heretic_knowledge(text) + if (CONFIG_GET(flag/log_uplink)) + WRITE_LOG(GLOB.world_uplink_log, "HERETIC RESEARCH: [text]") + +/// Logging for wizard powers learned +/proc/log_spellbook(text) + if (CONFIG_GET(flag/log_uplink)) + WRITE_LOG(GLOB.world_uplink_log, "SPELLBOOK: [text]") diff --git a/code/__HELPERS/logging/atmos.dm b/code/__HELPERS/logging/atmos.dm new file mode 100644 index 00000000000..a5a13e61a71 --- /dev/null +++ b/code/__HELPERS/logging/atmos.dm @@ -0,0 +1,8 @@ +/// Logs the contents of the gasmix to the game log, prefixed by text +/proc/log_atmos(text, datum/gas_mixture/mix) + var/message = text + message += "TEMP=[mix.temperature],MOL=[mix.total_moles()],VOL=[mix.volume]" + for(var/key in mix.gases) + var/list/gaslist = mix.gases[key] + message += "[gaslist[GAS_META][META_GAS_ID]]=[gaslist[MOLES]];" + log_game(message) diff --git a/code/__HELPERS/logging/attack.dm b/code/__HELPERS/logging/attack.dm new file mode 100644 index 00000000000..6b03bc1fdf4 --- /dev/null +++ b/code/__HELPERS/logging/attack.dm @@ -0,0 +1,85 @@ +/// Generic attack logging +/proc/log_attack(text) + if (CONFIG_GET(flag/log_attack)) + WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]") + +/** + * Log a combat message in the attack log + * + * Arguments: + * * atom/user - argument is the actor performing the action + * * atom/target - argument is the target of the action + * * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.) + * * atom/object - is a tool with which the action was made (usually an item) + * * addition - is any additional text, which will be appended to the rest of the log line + */ +/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) + var/ssource = key_name(user) + var/starget = key_name(target) + + var/mob/living/living_target = target + var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" + + var/sobject = "" + if(object) + sobject = " with [object]" + var/saddition = "" + if(addition) + saddition = " [addition]" + + var/postfix = "[sobject][saddition][hp]" + + var/message = "has [what_done] [starget][postfix]" + user.log_message(message, LOG_ATTACK, color="red") + + if(user != target) + var/reverse_message = "has been [what_done] by [ssource][postfix]" + target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE) + +/** + * log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over + * + * Note that this has no info on the attack that dealt the wound: information about where damage came from isn't passed to the bodypart's damaged proc. When in doubt, check the attack log for attacks at that same time + * TODO later: Add logging for healed wounds, though that will require some rewriting of healing code to prevent admin heals from spamming the logs. Not high priority + * + * Arguments: + * * victim- The guy who got wounded + * * suffered_wound- The wound, already applied, that we're logging. It has to already be attached so we can get the limb from it + * * dealt_damage- How much damage is associated with the attack that dealt with this wound. + * * dealt_wound_bonus- The wound_bonus, if one was specified, of the wounding attack + * * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present + * * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods + */ +/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll) + if(QDELETED(victim) || !suffered_wound) + return + var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote? + + if(dealt_damage) + message += " | Damage: [dealt_damage]" + // The base roll is useful since it can show how lucky someone got with the given attack. For example, dealing a cut + if(base_roll) + message += " (rolled [base_roll]/[dealt_damage ** WOUND_DAMAGE_EXPONENT])" + + if(dealt_wound_bonus) + message += " | WB: [dealt_wound_bonus]" + + if(dealt_bare_wound_bonus) + message += " | BWB: [dealt_bare_wound_bonus]" + + victim.log_message(message, LOG_ATTACK, color="blue") + +/// Logging for bombs detonating +/proc/log_bomber(atom/user, details, atom/bomb, additional_details, message_admins = TRUE) + var/bomb_message = "[details][bomb ? " [bomb.name] at [AREACOORD(bomb)]": ""][additional_details ? " [additional_details]" : ""]." + + if(user) + user.log_message(bomb_message, LOG_ATTACK) //let it go to individual logs as well as the game log + bomb_message = "[key_name(user)] at [AREACOORD(user)] [bomb_message]" + else + log_game(bomb_message) + + GLOB.bombers += bomb_message + + if(message_admins) + message_admins("[user ? "[ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(user)] " : ""][details][bomb ? " [bomb.name] at [ADMIN_VERBOSEJMP(bomb)]": ""][additional_details ? " [additional_details]" : ""].") diff --git a/code/__HELPERS/logging/debug.dm b/code/__HELPERS/logging/debug.dm new file mode 100644 index 00000000000..513f9e0471a --- /dev/null +++ b/code/__HELPERS/logging/debug.dm @@ -0,0 +1,57 @@ +/// Logging for loading and caching assets +/proc/log_asset(text) + if(CONFIG_GET(flag/log_asset)) + WRITE_LOG(GLOB.world_asset_log, "ASSET: [text]") + +/// Logging for config errors +/// Rarely gets called; just here in case the config breaks. +/proc/log_config(text) + WRITE_LOG(GLOB.config_error_log, text) + SEND_TEXT(world.log, text) + +/proc/log_filter_raw(text) + WRITE_LOG(GLOB.filter_log, "FILTER: [text]") + +/// Logging for job slot changes +/proc/log_job_debug(text) + if (CONFIG_GET(flag/log_job_debug)) + WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]") + +/// Logging for mapping errors +/proc/log_mapping(text, skip_world_log) + WRITE_LOG(GLOB.world_map_error_log, text) + if(skip_world_log) + return + SEND_TEXT(world.log, text) + +/// Logging for game performance +/proc/log_perf(list/perf_info) + . = "[perf_info.Join(",")]\n" + WRITE_LOG_NO_FORMAT(GLOB.perf_log, .) + +/// Logging for hard deletes +/proc/log_qdel(text) + WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]") + +/// Logging for SQL errors +/proc/log_query_debug(text) + WRITE_LOG(GLOB.query_debug_log, "SQL: [text]") + +/// Logging for DB errors +/proc/log_sql(text) + WRITE_LOG(GLOB.sql_error_log, "SQL: [text]") + +/// Logging for world/Topic +/proc/log_topic(text) + WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]") + +/* Log to the logfile only. */ +/proc/log_runtime(text) + WRITE_LOG(GLOB.world_runtime_log, text) + +/// Log to both DD and the logfile. +/proc/log_world(text) +#ifdef USE_CUSTOM_ERROR_HANDLER + WRITE_LOG(GLOB.world_runtime_log, text) +#endif + SEND_TEXT(world.log, text) diff --git a/code/__HELPERS/logging/economy.dm b/code/__HELPERS/logging/economy.dm new file mode 100644 index 00000000000..8f190cb934c --- /dev/null +++ b/code/__HELPERS/logging/economy.dm @@ -0,0 +1,3 @@ +/proc/log_econ(text) + if (CONFIG_GET(flag/log_econ)) + WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]") diff --git a/code/__HELPERS/logging/game.dm b/code/__HELPERS/logging/game.dm new file mode 100644 index 00000000000..ed3318fe8b4 --- /dev/null +++ b/code/__HELPERS/logging/game.dm @@ -0,0 +1,34 @@ +/// Logging for generic/unsorted game messages +/proc/log_game(text) + if (CONFIG_GET(flag/log_game)) + WRITE_LOG(GLOB.world_game_log, "GAME: [text]") + +/// Logging for emotes +/proc/log_emote(text) + if (CONFIG_GET(flag/log_emote)) + WRITE_LOG(GLOB.world_game_log, "EMOTE: [text]") + +/// Logging for emotes sent over the radio +/proc/log_radio_emote(text) + if (CONFIG_GET(flag/log_emote)) + WRITE_LOG(GLOB.world_game_log, "RADIOEMOTE: [text]") + +/// Logging for messages sent in OOC +/proc/log_ooc(text) + if (CONFIG_GET(flag/log_ooc)) + WRITE_LOG(GLOB.world_game_log, "OOC: [text]") + +/// Logging for prayed messages +/proc/log_prayer(text) + if (CONFIG_GET(flag/log_prayer)) + WRITE_LOG(GLOB.world_game_log, "PRAY: [text]") + +/// Logging for changes to ID card access +/proc/log_access(text) + if (CONFIG_GET(flag/log_access)) + WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]") + +/// Logging for OOC votes +/proc/log_vote(text) + if (CONFIG_GET(flag/log_vote)) + WRITE_LOG(GLOB.world_game_log, "VOTE: [text]") diff --git a/code/__HELPERS/logging/manifest.dm b/code/__HELPERS/logging/manifest.dm new file mode 100644 index 00000000000..7275b1cc73d --- /dev/null +++ b/code/__HELPERS/logging/manifest.dm @@ -0,0 +1,4 @@ +/// Logging for player manifest (ckey, name, job, special role, roundstart/latejoin) +/proc/log_manifest(ckey, datum/mind/mind, mob/body, latejoin = FALSE) + if (CONFIG_GET(flag/log_manifest)) + WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role.title] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]") diff --git a/code/__HELPERS/logging/mecha.dm b/code/__HELPERS/logging/mecha.dm new file mode 100644 index 00000000000..45c6a445dd2 --- /dev/null +++ b/code/__HELPERS/logging/mecha.dm @@ -0,0 +1,10 @@ +/// Logging for mech actions +/proc/log_mecha(text) + if (CONFIG_GET(flag/log_mecha)) + WRITE_LOG(GLOB.world_mecha_log, "MECHA: [text]") + +/// Logging for equipment installed in a mecha +/obj/item/mecha_parts/mecha_equipment/log_message(message, message_type = LOG_MECHA, color = null, log_globally) + if(chassis) + return chassis.log_message("ATTACHMENT: [src] [message]", message_type, color) + return ..() diff --git a/code/__HELPERS/logging/mob.dm b/code/__HELPERS/logging/mob.dm new file mode 100644 index 00000000000..7d752c1eb6b --- /dev/null +++ b/code/__HELPERS/logging/mob.dm @@ -0,0 +1,56 @@ +/proc/log_mob_tag(text) + WRITE_LOG(GLOB.world_mob_tag_log, "TAG: [text]") + +/proc/log_silicon(text) + if (CONFIG_GET(flag/log_silicon)) + WRITE_LOG(GLOB.world_silicon_log, "SILICON: [text]") + + +/// Logs a message in a mob's individual log, and in the global logs as well if log_globally is true +/mob/log_message(message, message_type, color = null, log_globally = TRUE) + if(!LAZYLEN(message)) + stack_trace("Empty message") + 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(client) + if(!islist(client.player_details.logging[smessage_type])) + client.player_details.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]" + + //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_ASAY) + colored_message = "(ASAY) [colored_message]" + if(LOG_EMOTE) + colored_message = "(EMOTE) [colored_message]" + //SKYRAT EDIT ADDITION BEGIN + if(LOG_SUBTLER) + colored_message = "(EMOTE) (SUBTLER) [colored_message]" + //SKYRAT EDIT ADDITION END + if(LOG_RADIO_EMOTE) + colored_message = "(RADIOEMOTE) [colored_message]" + + var/list/timestamped_message = list("\[[time_stamp(format = "YYYY-MM-DD hh:mm:ss")]\] [key_name(src)] [loc_name(src)] (Event #[LAZYLEN(logging[smessage_type])])" = colored_message) + + logging[smessage_type] += timestamped_message + + if(client) + client.player_details.logging[smessage_type] += timestamped_message + + ..() diff --git a/code/__HELPERS/logging/paper.dm b/code/__HELPERS/logging/paper.dm new file mode 100644 index 00000000000..b748faf217e --- /dev/null +++ b/code/__HELPERS/logging/paper.dm @@ -0,0 +1,3 @@ +/// Logging for writing made on paper +/proc/log_paper(text) + WRITE_LOG(GLOB.world_paper_log, "PAPER: [text]") diff --git a/code/__HELPERS/logging/pda.dm b/code/__HELPERS/logging/pda.dm new file mode 100644 index 00000000000..01c8d98d95a --- /dev/null +++ b/code/__HELPERS/logging/pda.dm @@ -0,0 +1,16 @@ +/// Logging for PDA messages sent +/proc/log_pda(text) + if (CONFIG_GET(flag/log_pda)) + WRITE_LOG(GLOB.world_pda_log, "PDA: [text]") + +/// Logging for newscaster comments +/proc/log_comment(text) + //reusing the PDA option because I really don't think news comments are worth a config option + if (CONFIG_GET(flag/log_pda)) + WRITE_LOG(GLOB.world_pda_log, "COMMENT: [text]") + +/// Logging for chatting on modular computer channels +/proc/log_chat(text) + //same thing here + if (CONFIG_GET(flag/log_pda)) + WRITE_LOG(GLOB.world_pda_log, "CHAT: [text]") diff --git a/code/__HELPERS/logging/shuttle.dm b/code/__HELPERS/logging/shuttle.dm new file mode 100644 index 00000000000..62a5fcd3e6e --- /dev/null +++ b/code/__HELPERS/logging/shuttle.dm @@ -0,0 +1,4 @@ +/// Logging for shuttle actions +/proc/log_shuttle(text) + if (CONFIG_GET(flag/log_shuttle)) + WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]") diff --git a/code/__HELPERS/logging/talk.dm b/code/__HELPERS/logging/talk.dm new file mode 100644 index 00000000000..86382f6a339 --- /dev/null +++ b/code/__HELPERS/logging/talk.dm @@ -0,0 +1,42 @@ +/** + * Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) + * + * This proc compiles a log string by prefixing the tag to the message + * and suffixing what it was forced_by if anything + * if the message lacks a tag and suffix then it is logged on its own + * Arguments: + * * message - The message being logged + * * message_type - the type of log the message is(ATTACK, SAY, etc) + * * tag - tag that indicates the type of text(announcement, telepathy, etc) + * * 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) + 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) + +/// Logging for generic spoken messages +/proc/log_say(text) + if (CONFIG_GET(flag/log_say)) + WRITE_LOG(GLOB.world_game_log, "SAY: [text]") + +/// Logging for whispered messages +/proc/log_whisper(text) + if (CONFIG_GET(flag/log_whisper)) + WRITE_LOG(GLOB.world_game_log, "WHISPER: [text]") + +/// Helper for logging of messages with only one sender and receiver (i.e. mind links) +/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) + if(!tag) + stack_trace("Unspecified tag for private message") + tag = "UNKNOWN" + + source.log_talk(message, message_type, tag = "[tag] to [key_name(target)]") + if(source != target) + target.log_talk(message, LOG_VICTIM, tag = "[tag] from [key_name(source)]", log_globally = FALSE) + +/// Logging for speech taking place over comms, as well as tcomms equipment +/proc/log_telecomms(text) + if (CONFIG_GET(flag/log_telecomms)) + WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]") diff --git a/code/__HELPERS/logging/tool.dm b/code/__HELPERS/logging/tool.dm new file mode 100644 index 00000000000..4fd354eb58d --- /dev/null +++ b/code/__HELPERS/logging/tool.dm @@ -0,0 +1,4 @@ +/// Logging for tool usage +/proc/log_tool(text, mob/initiator) + if(CONFIG_GET(flag/log_tools)) + WRITE_LOG(GLOB.world_tool_log, "TOOL: [text]") diff --git a/code/__HELPERS/logging/tools.dm b/code/__HELPERS/logging/tools.dm new file mode 100644 index 00000000000..4c5f2d3e655 --- /dev/null +++ b/code/__HELPERS/logging/tools.dm @@ -0,0 +1,3 @@ +/proc/log_tool(text, mob/initiator) + if(CONFIG_GET(flag/log_tools)) + WRITE_LOG(GLOB.world_tool_log, "TOOL: [text]") diff --git a/code/__HELPERS/logging/ui.dm b/code/__HELPERS/logging/ui.dm new file mode 100644 index 00000000000..3a0432d35f7 --- /dev/null +++ b/code/__HELPERS/logging/ui.dm @@ -0,0 +1,34 @@ +/proc/log_href(text) + WRITE_LOG(GLOB.world_href_log, "HREF: [text]") + +/** + * Appends a tgui-related log entry. All arguments are optional. + */ +/proc/log_tgui(user, message, context, + datum/tgui_window/window, + datum/src_object) + var/entry = "" + // Insert user info + if(!user) + entry += "" + else if(istype(user, /mob)) + var/mob/mob = user + entry += "[mob.ckey] (as [mob] at [mob.x],[mob.y],[mob.z])" + else if(istype(user, /client)) + var/client/client = user + entry += "[client.ckey]" + // Insert context + if(context) + entry += " in [context]" + else if(window) + entry += " in [window.id]" + // Resolve src_object + if(!src_object && window?.locked_by) + src_object = window.locked_by.src_object + // Insert src_object info + if(src_object) + entry += "\nUsing: [src_object.type] [REF(src_object)]" + // Insert message + if(message) + entry += "\n[message]" + WRITE_LOG(GLOB.tgui_log, entry) diff --git a/code/__HELPERS/logging/virus.dm b/code/__HELPERS/logging/virus.dm new file mode 100644 index 00000000000..6ec03af8e17 --- /dev/null +++ b/code/__HELPERS/logging/virus.dm @@ -0,0 +1,15 @@ +/// Logging for the creation and contraction of viruses +/proc/log_virus(text) + if (CONFIG_GET(flag/log_virus)) + WRITE_LOG(GLOB.world_virus_log, "VIRUS: [text]") + +/// Returns a string for admin logging uses, should describe the disease in detail +/datum/disease/proc/admin_details() + return "[src.name] : [src.type]" + +/// Describes this disease to an admin in detail (for logging) +/datum/disease/advance/admin_details() + var/list/name_symptoms = list() + for(var/datum/symptom/S in symptoms) + name_symptoms += S.name + return "[name] sym:[english_list(name_symptoms)] r:[totalResistance()] s:[totalStealth()] ss:[totalStageSpeed()] t:[totalTransmittable()]" diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index f767ef7d747..f85b0a4ca89 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -61,10 +61,6 @@ var/turf/source_turf = get_turf(infectee) log_virus("[key_name(infectee)] was infected by virus: [src.admin_details()] at [loc_name(source_turf)]") -//Return a string for admin logging uses, should describe the disease in detail -/datum/disease/proc/admin_details() - return "[src.name] : [src.type]" - ///Proc to process the disease and decide on whether to advance, cure or make the sympthoms appear. Returns a boolean on whether to continue acting on the symptoms or not. /datum/disease/proc/stage_act(delta_time, times_fired) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 4638cc01fbd..265ed0e5620 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -155,19 +155,6 @@ //this is a new disease starting over at stage 1, so processing is not copied return A -//Describe this disease to an admin in detail (for logging) -/datum/disease/advance/admin_details() - var/list/name_symptoms = list() - for(var/datum/symptom/S in symptoms) - name_symptoms += S.name - return "[name] sym:[english_list(name_symptoms)] r:[totalResistance()] s:[totalStealth()] ss:[totalStageSpeed()] t:[totalTransmittable()]" - -/* - - NEW PROCS - - */ - // Mix the symptoms of two diseases (the src and the argument) /datum/disease/advance/proc/Mix(datum/disease/advance/D) if(!(IsSame(D))) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index b4ddb1d4709..6e235f70096 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1576,178 +1576,6 @@ /atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock) return -/** - * Generic logging helper - * - * reads the type of the log - * and writes it to the respective log file - * unless log_globally is FALSE - * Arguments: - * * message - The message being logged - * * message_type - the type of log the message is(ATTACK, SAY, etc) - * * color - color of the log text - * * log_globally - boolean checking whether or not we write this log to the log file - */ -/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) - if(!log_globally) - return - //SKYRAT EDIT ADDITION BEGIN - #ifndef SPACEMAN_DMM - if(CONFIG_GET(flag/sql_game_log) && CONFIG_GET(flag/sql_enabled)) - var/datum/db_query/query_sql_log_messages = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("game_log")] (datetime, round_id, ckey, loc, type, message) - VALUES (:time, :round_id, :ckey, :loc, :type, :message) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "ckey" = key_name(src), "loc" = loc_name(src), type = message_type, "message" = message)) - if(!query_sql_log_messages.warn_execute()) - qdel(query_sql_log_messages) - return - qdel(query_sql_log_messages) - if(!CONFIG_GET(flag/file_game_log)) - return - #endif - //SKYRAT EDIT ADDITION END - var/log_text = "[key_name(src)] [message] [loc_name(src)]" - switch(message_type) - if(LOG_ATTACK) - log_attack(log_text) - if(LOG_SAY) - log_say(log_text) - if(LOG_WHISPER) - log_whisper(log_text) - if(LOG_EMOTE) - log_emote(log_text) - //SKYRAT EDIT ADDITION BEGIN - if(LOG_SUBTLER) - log_subtler(log_text) - //SKYRAT EDIT ADDITION END - if(LOG_RADIO_EMOTE) - log_radio_emote(log_text) - if(LOG_DSAY) - log_dsay(log_text) - if(LOG_PDA) - log_pda(log_text) - if(LOG_CHAT) - log_chat(log_text) - if(LOG_COMMENT) - log_comment(log_text) - if(LOG_TELECOMMS) - log_telecomms(log_text) - if(LOG_ECON) - log_econ(log_text) - if(LOG_OOC) - log_ooc(log_text) - if(LOG_ADMIN) - log_admin(log_text) - if(LOG_ADMIN_PRIVATE) - log_admin_private(log_text) - if(LOG_ASAY) - log_adminsay(log_text) - if(LOG_OWNERSHIP) - log_game(log_text) - if(LOG_GAME) - log_game(log_text) - if(LOG_MECHA) - log_mecha(log_text) - if(LOG_SHUTTLE) - log_shuttle(log_text) - else - stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") - log_game(log_text) - -/** - * Helper for logging chat messages or other logs with arbitrary inputs(e.g. announcements) - * - * This proc compiles a log string by prefixing the tag to the message - * and suffixing what it was forced_by if anything - * if the message lacks a tag and suffix then it is logged on its own - * Arguments: - * * message - The message being logged - * * message_type - the type of log the message is(ATTACK, SAY, etc) - * * tag - tag that indicates the type of text(announcement, telepathy, etc) - * * 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) - 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) - -/// Helper for logging of messages with only one sender and receiver -/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) - if(!tag) - stack_trace("Unspecified tag for private message") - tag = "UNKNOWN" - - source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") - if(source != target) - target.log_talk(message, LOG_VICTIM, tag="[tag] from [key_name(source)]", log_globally=FALSE) - -/** - * Log a combat message in the attack log - * - * Arguments: - * * atom/user - argument is the actor performing the action - * * atom/target - argument is the target of the action - * * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.) - * * atom/object - is a tool with which the action was made (usually an item) - * * addition - is any additional text, which will be appended to the rest of the log line - */ -/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) - var/ssource = key_name(user) - var/starget = key_name(target) - - var/mob/living/living_target = target - var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" - - var/sobject = "" - if(object) - sobject = " with [object]" - var/saddition = "" - if(addition) - saddition = " [addition]" - - var/postfix = "[sobject][saddition][hp]" - - var/message = "has [what_done] [starget][postfix]" - user.log_message(message, LOG_ATTACK, color="red") - - if(user != target) - var/reverse_message = "has been [what_done] by [ssource][postfix]" - target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE) - -/** - * log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over - * - * Note that this has no info on the attack that dealt the wound: information about where damage came from isn't passed to the bodypart's damaged proc. When in doubt, check the attack log for attacks at that same time - * TODO later: Add logging for healed wounds, though that will require some rewriting of healing code to prevent admin heals from spamming the logs. Not high priority - * - * Arguments: - * * victim- The guy who got wounded - * * suffered_wound- The wound, already applied, that we're logging. It has to already be attached so we can get the limb from it - * * dealt_damage- How much damage is associated with the attack that dealt with this wound. - * * dealt_wound_bonus- The wound_bonus, if one was specified, of the wounding attack - * * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present - * * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods - */ -/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll) - if(QDELETED(victim) || !suffered_wound) - return - var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote? - - if(dealt_damage) - message += " | Damage: [dealt_damage]" - // The base roll is useful since it can show how lucky someone got with the given attack. For example, dealing a cut - if(base_roll) - message += " (rolled [base_roll]/[dealt_damage ** WOUND_DAMAGE_EXPONENT])" - - if(dealt_wound_bonus) - message += " | WB: [dealt_wound_bonus]" - - if(dealt_bare_wound_bonus) - message += " | BWB: [dealt_bare_wound_bonus]" - - victim.log_message(message, LOG_ATTACK, color="blue") - /atom/proc/add_filter(name,priority,list/params) LAZYINITLIST(filter_data) var/list/copied_parameters = params.Copy() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 6d47732c2ad..c9933421b94 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1779,7 +1779,7 @@ var/answer = href_list["slowquery"] if(answer == "yes") log_query_debug("[usr.key] | Reported a server hang") - if(tgui_alert(usr, "Had you just press any admin buttons?", "Query server hang report", list("Yes", "No")) == "Yes") + if(tgui_alert(usr, "Did you just press any admin buttons?", "Query server hang report", list("Yes", "No")) == "Yes") var/response = input(usr,"What were you just doing?","Query server hang report") as null|text if(response) log_query_debug("[usr.key] | [response]") diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 55d81b09007..ee419d70677 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -341,55 +341,6 @@ var/mob/living/T = pick(nearby_mobs) ClickOn(T) -/// Logs a message in a mob's individual log, and in the global logs as well if log_globally is true -/mob/log_message(message, message_type, color=null, log_globally = TRUE) - if(!LAZYLEN(message)) - stack_trace("Empty message") - 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(client) - if(!islist(client.player_details.logging[smessage_type])) - client.player_details.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]" - - //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_ASAY) - colored_message = "(ASAY) [colored_message]" - if(LOG_EMOTE) - colored_message = "(EMOTE) [colored_message]" - //SKYRAT EDIT ADDITION BEGIN - if(LOG_SUBTLER) - colored_message = "(EMOTE) (SUBTLER) [colored_message]" - //SKYRAT EDIT ADDITION END - if(LOG_RADIO_EMOTE) - colored_message = "(RADIOEMOTE) [colored_message]" - - var/list/timestamped_message = list("\[[time_stamp(format = "YYYY-MM-DD hh:mm:ss")]\] [key_name(src)] [loc_name(src)] (Event #[LAZYLEN(logging[smessage_type])])" = colored_message) - - logging[smessage_type] += timestamped_message - - if(client) - client.player_details.logging[smessage_type] += timestamped_message - - ..() - ///Can the mob hear /mob/proc/can_hear() . = TRUE diff --git a/tgstation.dme b/tgstation.dme index 730bd8f710f..326d7ec5792 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -328,7 +328,6 @@ #include "code\__DEFINES\~skyrat_defines\_globalvars\lists\mapping.dm" #include "code\__DEFINES\~skyrat_defines\_HELPERS\lighting.dm" #include "code\__HELPERS\_lists.dm" -#include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\admin.dm" #include "code\__HELPERS\ai.dm" @@ -400,6 +399,24 @@ #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\view.dm" #include "code\__HELPERS\weakref.dm" +#include "code\__HELPERS\logging\_logging.dm" +#include "code\__HELPERS\logging\admin.dm" +#include "code\__HELPERS\logging\antagonists.dm" +#include "code\__HELPERS\logging\atmos.dm" +#include "code\__HELPERS\logging\attack.dm" +#include "code\__HELPERS\logging\debug.dm" +#include "code\__HELPERS\logging\economy.dm" +#include "code\__HELPERS\logging\game.dm" +#include "code\__HELPERS\logging\manifest.dm" +#include "code\__HELPERS\logging\mecha.dm" +#include "code\__HELPERS\logging\mob.dm" +#include "code\__HELPERS\logging\paper.dm" +#include "code\__HELPERS\logging\pda.dm" +#include "code\__HELPERS\logging\shuttle.dm" +#include "code\__HELPERS\logging\talk.dm" +#include "code\__HELPERS\logging\tool.dm" +#include "code\__HELPERS\logging\ui.dm" +#include "code\__HELPERS\logging\virus.dm" #include "code\__HELPERS\sorts\__main.dm" #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm"