diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm index d2db743d91..d1be661f6a 100644 --- a/code/controllers/configuration/config_entry.dm +++ b/code/controllers/configuration/config_entry.dm @@ -53,8 +53,9 @@ . = !(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "[REF(src)]") if(!.) log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]") - + /datum/config_entry/proc/ValidateAndSet(str_val) + VASProcCallGuard(str_val) CRASH("Invalid config entry type!") /datum/config_entry/proc/ValidateKeyedList(str_val, list_mode, splitter) @@ -78,12 +79,12 @@ if(LIST_MODE_TEXT) temp = key_value continue_check = temp - if(continue_check && ValidateKeyName(key_name)) + if(continue_check && ValidateListEntry(key_name, temp)) value[key_name] = temp return TRUE return FALSE -/datum/config_entry/proc/ValidateKeyName(key_name) +/datum/config_entry/proc/ValidateListEntry(key_name, key_value) return TRUE /datum/config_entry/string @@ -95,6 +96,8 @@ return var_name != "auto_trim" && ..() /datum/config_entry/string/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE value = auto_trim ? trim(str_val) : str_val return TRUE @@ -106,6 +109,8 @@ var/min_val = -INFINITY /datum/config_entry/number/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE var/temp = text2num(trim(str_val)) if(!isnull(temp)) value = CLAMP(integer ? round(temp) : temp, min_val, max_val) @@ -123,6 +128,8 @@ abstract_type = /datum/config_entry/flag /datum/config_entry/flag/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE value = text2num(trim(str_val)) != 0 return TRUE @@ -131,6 +138,8 @@ value = list() /datum/config_entry/number_list/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE str_val = trim(str_val) var/list/new_list = list() var/list/values = splittext(str_val," ") @@ -150,6 +159,8 @@ dupes_allowed = TRUE /datum/config_entry/keyed_flag_list/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE return ValidateKeyedList(str_val, LIST_MODE_FLAG, " ") /datum/config_entry/keyed_number_list @@ -162,6 +173,8 @@ return var_name != "splitter" && ..() /datum/config_entry/keyed_number_list/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE return ValidateKeyedList(str_val, LIST_MODE_NUM, splitter) /datum/config_entry/keyed_string_list @@ -174,6 +187,8 @@ return var_name != "splitter" && ..() /datum/config_entry/keyed_string_list/ValidateAndSet(str_val) + if(!VASProcCallGuard(str_val)) + return FALSE return ValidateKeyedList(str_val, LIST_MODE_TEXT, splitter) #undef LIST_MODE_NUM diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index e5e999f003..fa55c9deb4 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -97,19 +97,13 @@ GLOBAL_PROTECT(config_dir) if(!entry) continue - if(entry == "$include") - if(!value) - log_config("Warning: Invalid $include directive: [value]") - else - LoadEntries(value, stack) - ++. - continue if(entry == "$include") if(!value) log_config("Warning: Invalid $include directive: [value]") else LoadEntries(value, stack) + ++. continue var/datum/config_entry/E = _entries[entry] diff --git a/code/controllers/configuration/entries/comms.dm b/code/controllers/configuration/entries/comms.dm index 56cec985f1..e720ed93fd 100644 --- a/code/controllers/configuration/entries/comms.dm +++ b/code/controllers/configuration/entries/comms.dm @@ -2,13 +2,21 @@ protection = CONFIG_ENTRY_HIDDEN /datum/config_entry/string/comms_key/ValidateAndSet(str_val) - return str_val != "default_pwd" && length(str_val) > 6 && ..() + return str_val != "default_pwd" && length(str_val) > 6 && ..() /datum/config_entry/keyed_string_list/cross_server protection = CONFIG_ENTRY_LOCKED -/datum/config_entry/string/cross_server_address/ValidateAndSet(str_val) - return str_val != "byond:\\address:port" && ..() +/datum/config_entry/keyed_string_list/cross_server/ValidateAndSet(str_val) + . = ..() + if(.) + var/list/newv = list() + for(var/I in value) + newv[replacetext(I, "+", " ")] = value[I] + value = newv + +/datum/config_entry/keyed_string_list/cross_server/ValidateListEntry(key_name, key_value) + return key_value != "byond:\\address:port" && ..() /datum/config_entry/string/cross_comms_name @@ -17,4 +25,4 @@ GLOBAL_VAR_INIT(medals_enabled, TRUE) //will be auto set to false if the game fa /datum/config_entry/string/medal_hub_address /datum/config_entry/string/medal_hub_password - protection = CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_HIDDEN \ No newline at end of file diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 2d96e3c64b..5b516f031a 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -2,27 +2,27 @@ /datum/config_entry/keyed_number_list/probability -/datum/config_entry/keyed_number_list/probability/ValidateKeyName(key_name) +/datum/config_entry/keyed_number_list/probability/ValidateListEntry(key_name) return key_name in config.modes /datum/config_entry/keyed_number_list/max_pop -/datum/config_entry/keyed_number_list/max_pop/ValidateKeyName(key_name) +/datum/config_entry/keyed_number_list/max_pop/ValidateListEntry(key_name) return key_name in config.modes /datum/config_entry/keyed_number_list/min_pop -/datum/config_entry/keyed_number_list/min_pop/ValidateKeyName(key_name) +/datum/config_entry/keyed_number_list/min_pop/ValidateListEntry(key_name, key_value) return key_name in config.modes /datum/config_entry/keyed_flag_list/continuous // which roundtypes continue if all antagonists die -/datum/config_entry/keyed_flag_list/continuous/ValidateKeyName(key_name) +/datum/config_entry/keyed_flag_list/continuous/ValidateListEntry(key_name, key_value) return key_name in config.modes /datum/config_entry/keyed_flag_list/midround_antag // which roundtypes use the midround antagonist system -/datum/config_entry/keyed_flag_list/midround_antag/ValidateKeyName(key_name) +/datum/config_entry/keyed_flag_list/midround_antag/ValidateListEntry(key_name, key_value) return key_name in config.modes /datum/config_entry/keyed_string_list/policy diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 1775d470b0..240be1f212 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -394,4 +394,4 @@ SUBSYSTEM_DEF(air) #undef SSAIR_EXCITEDGROUPS #undef SSAIR_HIGHPRESSURE #undef SSAIR_HOTSPOT -#undef SSAIR_SUPERCONDUCTIVITY \ No newline at end of file +#undef SSAIR_SUPERCONDUCTIVITY diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index ca407bb58c..0877ba66c6 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -8,11 +8,10 @@ SUBSYSTEM_DEF(blackbox) var/list/feedback = list() //list of datum/feedback_variable var/triggertime = 0 var/sealed = FALSE //time to stop tracking stats? - var/list/research_levels = list() //list of highest tech levels attained that isn't lost lost by destruction of RD computers - var/list/versions = list("time_dilation_current" = 2, - "science_techweb_unlock" = 2, - "antagonists" = 3) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this - + var/list/versions = list("antagonists" = 3, + "admin_secrets_fun_used" = 2, + "time_dilation_current" = 3, + "science_techweb_unlock" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this /datum/controller/subsystem/blackbox/Initialize() triggertime = world.time @@ -59,13 +58,11 @@ SUBSYSTEM_DEF(blackbox) /datum/controller/subsystem/blackbox/Shutdown() sealed = FALSE record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved") - for (var/obj/machinery/message_server/MS in GLOB.message_servers) + for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list) if (MS.pda_msgs.len) record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA") if (MS.rc_msgs.len) record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console") - if(research_levels.len) - SSblackbox.record_feedback("associative", "high_research_level", 1, research_levels) if (!SSdbcore.Connect()) return @@ -92,39 +89,35 @@ SUBSYSTEM_DEF(blackbox) sealed = TRUE return TRUE -/datum/controller/subsystem/blackbox/proc/log_research(tech, level) - if(!(tech in research_levels) || research_levels[tech] < level) - research_levels[tech] = level - /datum/controller/subsystem/blackbox/proc/LogBroadcast(freq) if(sealed) return switch(freq) - if(1459) + if(FREQ_COMMON) record_feedback("tally", "radio_usage", 1, "common") - if(GLOB.SCI_FREQ) + if(FREQ_SCIENCE) record_feedback("tally", "radio_usage", 1, "science") - if(GLOB.COMM_FREQ) + if(FREQ_COMMAND) record_feedback("tally", "radio_usage", 1, "command") - if(GLOB.MED_FREQ) + if(FREQ_MEDICAL) record_feedback("tally", "radio_usage", 1, "medical") - if(GLOB.ENG_FREQ) + if(FREQ_ENGINEERING) record_feedback("tally", "radio_usage", 1, "engineering") - if(GLOB.SEC_FREQ) + if(FREQ_SECURITY) record_feedback("tally", "radio_usage", 1, "security") - if(GLOB.SYND_FREQ) + if(FREQ_SYNDICATE) record_feedback("tally", "radio_usage", 1, "syndicate") - if(GLOB.SERV_FREQ) + if(FREQ_SERVICE) record_feedback("tally", "radio_usage", 1, "service") - if(GLOB.SUPP_FREQ) + if(FREQ_SUPPLY) record_feedback("tally", "radio_usage", 1, "supply") - if(GLOB.CENTCOM_FREQ) + if(FREQ_CENTCOM) record_feedback("tally", "radio_usage", 1, "centcom") - if(GLOB.AIPRIV_FREQ) + if(FREQ_AI_PRIVATE) record_feedback("tally", "radio_usage", 1, "ai private") - if(GLOB.REDTEAM_FREQ) + if(FREQ_CTF_RED) record_feedback("tally", "radio_usage", 1, "CTF red team") - if(GLOB.BLUETEAM_FREQ) + if(FREQ_CTF_BLUE) record_feedback("tally", "radio_usage", 1, "CTF blue team") else record_feedback("tally", "radio_usage", 1, "other") @@ -194,7 +187,7 @@ Versioning "gun_fired" = 2) */ /datum/controller/subsystem/blackbox/proc/record_feedback(key_type, key, increment, data, overwrite) - if(sealed || !key_type || !istext(key) || !isnum(increment || !data)) + if(sealed || !key_type || !istext(key) || !isnum(increment) || !data) return var/datum/feedback_variable/FV = find_feedback_datum(key, key_type) switch(key_type) diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm index a033512d58..a15237e5a7 100644 --- a/code/controllers/subsystem/idlenpcpool.dm +++ b/code/controllers/subsystem/idlenpcpool.dm @@ -41,4 +41,3 @@ SUBSYSTEM_DEF(idlenpcpool) SA.consider_wakeup() if (MC_TICK_CHECK) return - diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index f553d66307..6ffd12264f 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -1,10 +1,101 @@ SUBSYSTEM_DEF(input) name = "Input" wait = 1 //SS_TICKER means this runs every tick - flags = SS_TICKER | SS_NO_INIT + init_order = INIT_ORDER_INPUT + flags = SS_TICKER priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY + var/list/macro_sets + +/datum/controller/subsystem/input/Initialize() + setup_default_macro_sets() + + initialized = TRUE + + refresh_client_macro_sets() + + return ..() + +// This is for when macro sets are eventualy datumized +/datum/controller/subsystem/input/proc/setup_default_macro_sets() + var/list/static/default_macro_sets + + if(default_macro_sets) + macro_sets = default_macro_sets + return + + default_macro_sets = list( + "default" = list( + "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "O" = "ooc", + "T" = "say", + "M" = "me", + "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + "old_default" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_hotkeys map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\"", + "Ctrl+T" = "say", + "Ctrl+O" = "ooc", + ), + "old_hotkeys" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_default input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "O" = "ooc", + "T" = "say", + "M" = "me", + "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + ) + + // Because i'm lazy and don't want to type all these out twice + var/list/old_default = default_macro_sets["old_default"] + + var/list/static/oldmode_keys = list( + "North", "East", "South", "West", + "Northeast", "Southeast", "Northwest", "Southwest", + "Insert", "Delete", "Ctrl", "Alt", + "F1", "F2", "F5", "F6", "F7", "F8", "F12", + ) + + for(var/i in 1 to oldmode_keys.len) + var/key = oldmode_keys[i] + old_default[key] = "\"KeyDown [key]\"" + old_default["[key]+UP"] = "\"KeyUp [key]\"" + + var/list/static/oldmode_ctrl_override_keys = list( + "W" = "W", "A" = "A", "S" = "S", "D" = "D", // movement + "1" = "1", "2" = "2", "3" = "3", "4" = "4", // intent + "B" = "B", // resist + "E" = "E", // quick equip + "F" = "F", // intent left + "G" = "G", // intent right + "H" = "H", // stop pulling + "Q" = "Q", // drop + "R" = "R", // throw + "X" = "X", // switch hands + "Y" = "Y", // activate item + "Z" = "Z", // activate item + ) + + for(var/i in 1 to oldmode_ctrl_override_keys.len) + var/key = oldmode_ctrl_override_keys[i] + var/override = oldmode_ctrl_override_keys[key] + old_default["Ctrl+[key]"] = "\"KeyDown [override]\"" + old_default["Ctrl+[key]+UP"] = "\"KeyUp [override]\"" + + macro_sets = default_macro_sets + +// Badmins just wanna have fun ♪ +/datum/controller/subsystem/input/proc/refresh_client_macro_sets() + var/list/clients = GLOB.clients + for(var/i in 1 to clients.len) + var/client/user = clients[i] + user.set_macros() + /datum/controller/subsystem/input/fire() var/list/clients = GLOB.clients // Let's sing the list cache song for(var/i in 1 to clients.len) diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 5cfbeb9414..65d5f4befb 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -107,9 +107,6 @@ SUBSYSTEM_DEF(job) if(player.mind && job.title in player.mind.restricted_roles) Debug("FOC incompatible with antagonist role, Player: [player]") continue - if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) - Debug("FOC non-human failed, Player: [player]") - continue if(player.client.prefs.GetJobDepartment(job, level) & job.flag) Debug("FOC pass, Player: [player], Level:[level]") candidates += player @@ -144,11 +141,6 @@ SUBSYSTEM_DEF(job) Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]") continue - if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) - Debug("GRJ non-human failed, Player: [player]") - continue - - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) Debug("GRJ Random job given, Player: [player], Job: [job]") if(AssignRole(player, job.title)) @@ -319,10 +311,6 @@ SUBSYSTEM_DEF(job) Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]") continue - if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features)) - Debug("DO non-human failed, Player: [player], Job:[job.title]") - continue - // If the player wants that job on this level, then try give it to him. if(player.client.prefs.GetJobDepartment(job, level) & job.flag) @@ -424,7 +412,8 @@ SUBSYSTEM_DEF(job) if(job && H) job.after_spawn(H, M) - handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role) + handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role) //CIT CHANGE - makes donators spawn with their items + return H diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 43565846fb..1ad3a8ab15 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -22,7 +22,7 @@ SUBSYSTEM_DEF(lighting) create_all_lighting_objects() initialized = TRUE - + fire(FALSE, TRUE) ..() diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index db6af6d686..e235afaaa4 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -62,4 +62,4 @@ SUBSYSTEM_DEF(machines) if (istype(SSmachines.processing)) processing = SSmachines.processing if (istype(SSmachines.powernets)) - powernets = SSmachines.powernets \ No newline at end of file + powernets = SSmachines.powernets diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index ca050cb9b2..334b44d081 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -135,12 +135,12 @@ SUBSYSTEM_DEF(npcpool) if(facCount == 1 && helpProb) helpProb = 100 - + if(prob(helpProb) && candidate.takeDelegate(check,FALSE)) --canBeUsed.len candidate.eye_color = "yellow" candidate.update_icons() - + if(!currentrun.len || MC_TICK_CHECK) //don't change SS state if it isn't necessary return diff --git a/code/controllers/subsystem/orbit.dm b/code/controllers/subsystem/orbit.dm index 581b7821b2..45d280b601 100644 --- a/code/controllers/subsystem/orbit.dm +++ b/code/controllers/subsystem/orbit.dm @@ -40,3 +40,5 @@ SUBSYSTEM_DEF(orbit) O.Check(targetloc) if (MC_TICK_CHECK) return + + diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index d310f46d2a..b98be937fc 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -250,4 +250,4 @@ SUBSYSTEM_DEF(persistence) var/list/file_data = list() file_data["data"] = saved_modes fdel(json_file) - WRITE_FILE(json_file, json_encode(file_data)) \ No newline at end of file + WRITE_FILE(json_file, json_encode(file_data)) diff --git a/code/controllers/subsystem/radio.dm b/code/controllers/subsystem/radio.dm index 43803aa647..8299709392 100644 --- a/code/controllers/subsystem/radio.dm +++ b/code/controllers/subsystem/radio.dm @@ -14,35 +14,22 @@ SUBSYSTEM_DEF(radio) /datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency as num, filter = null as text|null) var/f_text = num2text(new_frequency) var/datum/radio_frequency/frequency = frequencies[f_text] - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - + frequencies[f_text] = frequency = new(new_frequency) frequency.add_listener(device, filter) return frequency /datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) var/f_text = num2text(old_frequency) var/datum/radio_frequency/frequency = frequencies[f_text] - if(frequency) frequency.remove_listener(device) - - if(frequency.devices.len == 0) - qdel(frequency) - frequencies -= f_text - + // let's don't delete frequencies in case a non-listener keeps a reference return 1 /datum/controller/subsystem/radio/proc/return_frequency(new_frequency as num) var/f_text = num2text(new_frequency) var/datum/radio_frequency/frequency = frequencies[f_text] - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - + frequencies[f_text] = frequency = new(new_frequency) return frequency diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index f10ed9125a..b1468a65c4 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -54,6 +54,7 @@ SUBSYSTEM_DEF(research) bitcoins = single_server_income break //Just need one to work. var/income_time_difference = world.time - last_income + science_tech.last_bitcoins = bitcoins // Doesn't take tick drift into account bitcoins *= income_time_difference / 10 science_tech.research_points += bitcoins last_income = world.time diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 65385c031f..54c686964e 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -54,7 +54,7 @@ SUBSYSTEM_DEF(shuttle) var/lockdown = FALSE //disallow transit after nuke goes off - var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3. + var/auto_call = 99000 //CIT CHANGE - time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3. /datum/controller/subsystem/shuttle/Initialize(timeofday) if(!arrivals) @@ -158,7 +158,7 @@ SUBSYSTEM_DEF(shuttle) break /datum/controller/subsystem/shuttle/proc/CheckAutoEvac() - if(emergencyNoEscape || emergencyNoRecall || !emergency) + if(emergencyNoEscape || emergencyNoRecall || !emergency || !SSticker.HasRoundStarted()) return var/threshold = CONFIG_GET(number/emergency_shuttle_autocall_threshold) @@ -261,7 +261,7 @@ SUBSYSTEM_DEF(shuttle) if(!admiral_message) admiral_message = pick(GLOB.admiral_messages) - var/intercepttext = "NanoTrasen Update: Request For Shuttle.
\ + var/intercepttext = "Nanotrasen Update: Request For Shuttle.
\ To whom it may concern:

\ We have taken note of the situation upon [station_name()] and have come to the \ conclusion that it does not warrant the abandonment of the station.
\ @@ -385,7 +385,7 @@ SUBSYSTEM_DEF(shuttle) emergency.setTimer(emergencyDockTime) priority_announce("Hostile environment resolved. \ You have 3 minutes to board the Emergency Shuttle.", - null, 'sound/AI/shuttledock.ogg', "Priority") + null, 'sound/ai/shuttledock.ogg', "Priority") //try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons /datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed) @@ -429,7 +429,8 @@ SUBSYSTEM_DEF(shuttle) if(!(M in transit_requesters)) transit_requesters += M -/datum/controller/subsystem/shuttle/proc/autoEnd() + +/datum/controller/subsystem/shuttle/proc/autoEnd() //CIT CHANGE - allows shift to end after 3 hours has passed. if(world.time > auto_call && EMERGENCY_IDLE_OR_RECALLED) //3 hours SSshuttle.emergency.request(null, 1.5) priority_announce("The shift has come to an end and the shuttle called.") diff --git a/code/controllers/subsystem/squeak.dm b/code/controllers/subsystem/squeak.dm index 0148011e8b..022b659d8a 100644 --- a/code/controllers/subsystem/squeak.dm +++ b/code/controllers/subsystem/squeak.dm @@ -1,6 +1,6 @@ // The Squeak // because this is about placement of mice mobs, and nothing to do with -// mice - the computer peripheral +// mice - the computer peripheral SUBSYSTEM_DEF(squeak) name = "Squeak" diff --git a/code/controllers/subsystem/stickyban.dm b/code/controllers/subsystem/stickyban.dm index 8251df0039..371cf22b3b 100644 --- a/code/controllers/subsystem/stickyban.dm +++ b/code/controllers/subsystem/stickyban.dm @@ -27,6 +27,6 @@ SUBSYSTEM_DEF(stickyban) ban["existing_user_matches_this_round"] = list() ban["admin_matches_this_round"] = list() cache[ckey] = ban - + for (var/bannedckey in cache) world.SetConfig("ban", bannedckey, list2stickyban(cache[bannedckey])) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index d14404ed97..df6ac28247 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -631,6 +631,7 @@ SUBSYSTEM_DEF(ticker) world.Reboot() /datum/controller/subsystem/ticker/Shutdown() + gather_newscaster() //called here so we ensure the log is created even upon admin reboot if(!round_end_sound) round_end_sound = pick(\ 'sound/roundend/newroundsexy.ogg', diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm index 17cfa6fc06..3b19ae31cd 100644 --- a/code/controllers/subsystem/time_track.dm +++ b/code/controllers/subsystem/time_track.dm @@ -35,4 +35,4 @@ SUBSYSTEM_DEF(time_track) last_tick_realtime = current_realtime last_tick_byond_time = current_byondtime last_tick_tickcount = current_tickcount - SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[time_dilation_current]" = "[SQLtime()]")) + SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[SQLtime()]" = list("current" = "[time_dilation_current]", "avg_fast" = "[time_dilation_avg_fast]", "avg" = "[time_dilation_avg]", "avg_slow" = "[time_dilation_avg_slow]"))) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 3a2ca82bcd..f24068dbff 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -1,5 +1,6 @@ #define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth) -#define BUCKET_POS(timer) (round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) + 1) +#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN) + 1) +#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1))) #define TIMER_ID_MAX (2**24) //max float with integer precision SUBSYSTEM_DEF(timer) @@ -9,11 +10,11 @@ SUBSYSTEM_DEF(timer) flags = SS_TICKER|SS_NO_INIT - var/list/datum/timedevent/processing = list() + var/list/datum/timedevent/second_queue = list() //awe, yes, you've had first queue, but what about second queue? var/list/hashes = list() var/head_offset = 0 //world.time of the first entry in the the bucket. - var/practical_offset = 0 //index of the first non-empty item in the bucket. + var/practical_offset = 1 //index of the first non-empty item in the bucket. var/bucket_resolution = 0 //world.tick_lag the bucket was designed for var/bucket_count = 0 //how many timers are in the buckets @@ -27,13 +28,19 @@ SUBSYSTEM_DEF(timer) var/static/last_invoke_warning = 0 var/static/bucket_auto_reset = TRUE +/datum/controller/subsystem/timer/PreInit() + bucket_list.len = BUCKET_LEN + head_offset = world.time + bucket_resolution = world.tick_lag + /datum/controller/subsystem/timer/stat_entry(msg) - ..("B:[bucket_count] P:[length(processing)] H:[length(hashes)] C:[length(clienttime_timers)]") + ..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]") /datum/controller/subsystem/timer/fire(resumed = FALSE) var/lit = last_invoke_tick var/last_check = world.time - TIMER_NO_INVOKE_WARNING var/list/bucket_list = src.bucket_list + if(!bucket_count) last_invoke_tick = world.time @@ -60,50 +67,62 @@ SUBSYSTEM_DEF(timer) bucket_node = bucket_node.next anti_loop_check-- while(bucket_node && bucket_node != bucket_head && anti_loop_check) - log_world("Active timers in the processing queue:") - for(var/I in processing) + log_world("Active timers in the second_queue queue:") + for(var/I in second_queue) log_world(get_timer_debug_string(I)) - while(length(clienttime_timers)) - var/datum/timedevent/ctime_timer = clienttime_timers[clienttime_timers.len] - if (ctime_timer.timeToRun <= REALTIMEOFDAY) - --clienttime_timers.len - var/datum/callback/callBack = ctime_timer.callBack - ctime_timer.spent = REALTIMEOFDAY - callBack.InvokeAsync() - qdel(ctime_timer) - else - break //None of the rest are ready to run + var/next_clienttime_timer_index = 0 + var/len = length(clienttime_timers) + + for (next_clienttime_timer_index in 1 to len) if (MC_TICK_CHECK) - return + next_clienttime_timer_index-- + break + var/datum/timedevent/ctime_timer = clienttime_timers[next_clienttime_timer_index] + if (ctime_timer.timeToRun > REALTIMEOFDAY) + next_clienttime_timer_index-- + break + + var/datum/callback/callBack = ctime_timer.callBack + if (!callBack) + clienttime_timers.Cut(next_clienttime_timer_index,next_clienttime_timer_index+1) + CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]") + + ctime_timer.spent = REALTIMEOFDAY + callBack.InvokeAsync() + qdel(ctime_timer) + + + if (next_clienttime_timer_index) + clienttime_timers.Cut(1,next_clienttime_timer_index+1) + + if (MC_TICK_CHECK) + return var/static/list/spent = list() var/static/datum/timedevent/timer - var/static/datum/timedevent/head + if (practical_offset > BUCKET_LEN) + head_offset += TICKS2DS(BUCKET_LEN) + practical_offset = 1 + resumed = FALSE - if (practical_offset > BUCKET_LEN || (!resumed && length(bucket_list) != BUCKET_LEN || world.tick_lag != bucket_resolution)) - shift_buckets() + if ((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution)) + reset_buckets() bucket_list = src.bucket_list resumed = FALSE if (!resumed) timer = null - head = null - while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time && !MC_TICK_CHECK) + while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time) + var/datum/timedevent/head = bucket_list[practical_offset] if (!timer || !head || timer == head) head = bucket_list[practical_offset] - if (!head) - practical_offset++ - if (MC_TICK_CHECK) - break - continue timer = head - do + while (timer) var/datum/callback/callBack = timer.callBack if (!callBack) - qdel(timer) bucket_resolution = null //force bucket recreation CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") @@ -113,15 +132,68 @@ SUBSYSTEM_DEF(timer) callBack.InvokeAsync() last_invoke_tick = world.time - timer = timer.next - if (MC_TICK_CHECK) return - while (timer && timer != head) - timer = null + + timer = timer.next + if (timer == head) + break + + bucket_list[practical_offset++] = null - if (MC_TICK_CHECK) - return + + //we freed up a bucket, lets see if anything in second_queue needs to be shifted to that bucket. + var/i = 0 + var/L = length(second_queue) + for (i in 1 to L) + timer = second_queue[i] + if (timer.timeToRun >= TIMER_MAX) + i-- + break + + if (timer.timeToRun < head_offset) + bucket_resolution = null //force bucket recreation + CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + + if (timer.callBack && !timer.spent) + timer.callBack.InvokeAsync() + spent += timer + bucket_count++ + else if(!QDELETED(timer)) + qdel(timer) + continue + + if (timer.timeToRun < head_offset + TICKS2DS(practical_offset)) + bucket_resolution = null //force bucket recreation + CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + if (timer.callBack && !timer.spent) + timer.callBack.InvokeAsync() + spent += timer + bucket_count++ + else if(!QDELETED(timer)) + qdel(timer) + continue + + bucket_count++ + var/bucket_pos = max(1, BUCKET_POS(timer)) + + var/datum/timedevent/bucket_head = bucket_list[bucket_pos] + if (!bucket_head) + bucket_list[bucket_pos] = timer + timer.next = null + timer.prev = null + continue + + if (!bucket_head.prev) + bucket_head.prev = bucket_head + timer.next = bucket_head + timer.prev = bucket_head.prev + timer.next.prev = timer + timer.prev.next = timer + if (i) + second_queue.Cut(1, i+1) + + timer = null bucket_count -= length(spent) @@ -141,7 +213,7 @@ SUBSYSTEM_DEF(timer) if(!TE.callBack) . += ", NO CALLBACK" -/datum/controller/subsystem/timer/proc/shift_buckets() +/datum/controller/subsystem/timer/proc/reset_buckets() var/list/bucket_list = src.bucket_list var/list/alltimers = list() //collect the timers currently in the bucket @@ -162,7 +234,7 @@ SUBSYSTEM_DEF(timer) head_offset = world.time bucket_resolution = world.tick_lag - alltimers += processing + alltimers += second_queue if (!length(alltimers)) return @@ -173,22 +245,26 @@ SUBSYSTEM_DEF(timer) if (head.timeToRun < head_offset) head_offset = head.timeToRun - var/list/timers_to_remove = list() - - for (var/thing in alltimers) - var/datum/timedevent/timer = thing + var/new_bucket_count + var/i = 1 + for (i in 1 to length(alltimers)) + var/datum/timedevent/timer = alltimers[1] if (!timer) - timers_to_remove += timer continue var/bucket_pos = BUCKET_POS(timer) - if (bucket_pos > BUCKET_LEN) + if (timer.timeToRun >= TIMER_MAX) + i-- break - timers_to_remove += timer //remove it from the big list once we are done + if (!timer.callBack || timer.spent) + WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + if (timer.callBack) + qdel(timer) continue - bucket_count++ + + new_bucket_count++ var/datum/timedevent/bucket_head = bucket_list[bucket_pos] if (!bucket_head) bucket_list[bucket_pos] = timer @@ -202,12 +278,14 @@ SUBSYSTEM_DEF(timer) timer.prev = bucket_head.prev timer.next.prev = timer timer.prev.next = timer - - processing = (alltimers - timers_to_remove) + if (i) + alltimers.Cut(1, i+1) + second_queue = alltimers + bucket_count = new_bucket_count /datum/controller/subsystem/timer/Recover() - processing |= SStimer.processing + second_queue |= SStimer.second_queue hashes |= SStimer.hashes timer_id_dict |= SStimer.timer_id_dict bucket_list |= SStimer.bucket_list @@ -224,8 +302,6 @@ SUBSYSTEM_DEF(timer) var/datum/timedevent/next var/datum/timedevent/prev - var/static/nextid = 1 - /datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash) id = TIMER_ID_NULL src.callBack = callBack @@ -235,56 +311,65 @@ SUBSYSTEM_DEF(timer) if (flags & TIMER_UNIQUE) SStimer.hashes[hash] = src + if (flags & TIMER_STOPPABLE) - do - if (nextid >= TIMER_ID_MAX) - nextid = 1 - id = nextid++ - while(SStimer.timer_id_dict["timerid" + num2text(id, 8)]) - SStimer.timer_id_dict["timerid" + num2text(id, 8)] = src + id = GUID() + SStimer.timer_id_dict[id] = src - name = "Timer: " + num2text(id, 8) + ", TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: [REF(callBack)], callBack.object: [callBack.object][REF(callBack.object)]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" + name = "Timer: [id] (\ref[src]), TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" - if (spent) - CRASH("HOLY JESUS. WHAT IS THAT? WHAT THE FUCK IS THAT?") + if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME)) + CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]") if (callBack.object != GLOBAL_PROC) LAZYADD(callBack.object.active_timers, src) + + var/list/L + if (flags & TIMER_CLIENT_TIME) - //sorted insert - var/list/ctts = SStimer.clienttime_timers - var/cttl = length(ctts) + L = SStimer.clienttime_timers + else if (timeToRun >= TIMER_MAX) + L = SStimer.second_queue + + + if (L) + //binary search sorted insert + var/cttl = length(L) if(cttl) - var/datum/timedevent/Last = ctts[cttl] - if(Last.timeToRun >= timeToRun) - ctts += src - else - for(var/i in cttl to 1 step -1) - var/datum/timedevent/E = ctts[i] - if(E.timeToRun <= timeToRun) - ctts.Insert(i, src) - break + var/left = 1 + var/right = cttl + var/mid = (left+right) >> 1 //rounded divide by two for hedgehogs + + var/datum/timedevent/item + while (left < right) + item = L[mid] + if (item.timeToRun <= timeToRun) + left = mid+1 + else + right = mid + mid = (left+right) >> 1 + + item = L[mid] + mid = item.timeToRun > timeToRun ? mid : mid+1 + L.Insert(mid, src) + else - ctts += src + L += src return //get the list of buckets var/list/bucket_list = SStimer.bucket_list + //calculate our place in the bucket list var/bucket_pos = BUCKET_POS(src) - //we are too far aways from needing to run to be in the bucket list, shift_buckets() will handle us. - if (bucket_pos > length(bucket_list)) - SStimer.processing += src - return + //get the bucket for our tick var/datum/timedevent/bucket_head = bucket_list[bucket_pos] SStimer.bucket_count++ //empty bucket, we will just add ourselves if (!bucket_head) bucket_list[bucket_pos] = src - if (bucket_pos < SStimer.practical_offset) - SStimer.practical_offset = bucket_pos return //other wise, lets do a simplified linked list add. if (!bucket_head.prev) @@ -296,10 +381,9 @@ SUBSYSTEM_DEF(timer) /datum/timedevent/Destroy() ..() - if (flags & TIMER_UNIQUE) + if (flags & TIMER_UNIQUE && hash) SStimer.hashes -= hash - if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers) callBack.object.active_timers -= src UNSETEMPTY(callBack.object.active_timers) @@ -307,13 +391,33 @@ SUBSYSTEM_DEF(timer) callBack = null if (flags & TIMER_STOPPABLE) - SStimer.timer_id_dict -= "timerid" + num2text(id, 8) + SStimer.timer_id_dict -= id if (flags & TIMER_CLIENT_TIME) - SStimer.clienttime_timers -= src + if (!spent) + spent = world.time + SStimer.clienttime_timers -= src return QDEL_HINT_IWILLGC if (!spent) + spent = world.time + var/bucketpos = BUCKET_POS(src) + var/datum/timedevent/buckethead + var/list/bucket_list = SStimer.bucket_list + if (bucketpos > 0) + buckethead = bucket_list[bucketpos] + + if (buckethead == src) + bucket_list[bucketpos] = next + SStimer.bucket_count-- + else if (timeToRun < TIMER_MAX || next || prev) + SStimer.bucket_count-- + else + var/l = length(SStimer.second_queue) + SStimer.second_queue -= src + if (l == length(SStimer.second_queue)) + SStimer.bucket_count-- + if (prev == next && next) next.prev = null prev.next = null @@ -322,19 +426,6 @@ SUBSYSTEM_DEF(timer) prev.next = next if (next) next.prev = prev - - var/bucketpos = BUCKET_POS(src) - var/datum/timedevent/buckethead - var/list/bucket_list = SStimer.bucket_list - - if (bucketpos > 0 && bucketpos <= length(bucket_list)) - buckethead = bucket_list[bucketpos] - SStimer.bucket_count-- - else - SStimer.processing -= src - - if (buckethead == src) - bucket_list[bucketpos] = next else if (prev && prev.next == src) prev.next = next @@ -351,9 +442,16 @@ SUBSYSTEM_DEF(timer) else . = "[callBack.object.type]" -/proc/addtimer(datum/callback/callback, wait, flags) +/proc/addtimer(datum/callback/callback, wait = 0, flags = 0) if (!callback) - return + CRASH("addtimer called without a callback") + + if (wait < 0) + stack_trace("Addtimer called with a negitive wait. Converting to 0") + + //alot of things add short timers on themselves in their destroy, we ignore those cases + if (wait >= 1 && callback && callback.object && callback.object != GLOBAL_PROC && QDELETED(callback.object)) + stack_trace("Add timer called with a callback assigned to a qdeleted object") wait = max(wait, 0) @@ -374,11 +472,10 @@ SUBSYSTEM_DEF(timer) var/datum/timedevent/hash_timer = SStimer.hashes[hash] if(hash_timer) if (hash_timer.spent) //it's pending deletion, pretend it doesn't exist. - hash_timer.hash = null - SStimer.hashes -= hash + hash_timer.hash = null //but keep it from accidentally deleting us else - if (flags & TIMER_OVERRIDE) + hash_timer.hash = null //no need having it delete it's hash if we are going to replace it qdel(hash_timer) else if (hash_timer.flags & TIMER_STOPPABLE) @@ -403,7 +500,7 @@ SUBSYSTEM_DEF(timer) qdel(id) return TRUE //id is string - var/datum/timedevent/timer = SStimer.timer_id_dict["timerid[id]"] + var/datum/timedevent/timer = SStimer.timer_id_dict[id] if (timer && !timer.spent) qdel(timer) return TRUE @@ -412,3 +509,5 @@ SUBSYSTEM_DEF(timer) #undef BUCKET_LEN #undef BUCKET_POS +#undef TIMER_MAX +#undef TIMER_ID_MAX \ No newline at end of file diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm index 4f1dbc37c7..a0bcb5feec 100644 --- a/code/controllers/subsystem/title.dm +++ b/code/controllers/subsystem/title.dm @@ -36,7 +36,7 @@ SUBSYSTEM_DEF(title) break file_path = "config/title_screens/images/[pick(title_screens)]" - + icon = new(fcopy_rsc(file_path)) if(splash_turf)