From cfe182a1f76e9ddac6fc0af917b586facc823850 Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sat, 28 Apr 2018 20:26:04 -0400 Subject: [PATCH 1/5] Ports over Timer Subsystem --- code/__DEFINES/math.dm | 2 + code/__HELPERS/cmp.dm | 3 + code/__HELPERS/qdel.dm | 7 +- code/controllers/Processes/nano_mob_hunter.dm | 2 +- code/controllers/Processes/timer.dm | 100 ---- code/controllers/Processes/weather.dm | 2 +- code/controllers/subsystem/timer.dm | 518 ++++++++++++++++++ code/controllers/verbs.dm | 2 +- code/datums/datum.dm | 9 + code/datums/mind.dm | 3 +- code/datums/spells/ethereal_jaunt.dm | 2 +- code/datums/weather/weather.dm | 8 +- code/game/atoms.dm | 2 +- code/game/atoms_movable.dm | 6 +- .../gamemodes/changeling/powers/biodegrade.dm | 8 +- .../gamemodes/changeling/powers/fakedeath.dm | 2 +- .../gamemodes/changeling/powers/fleshmend.dm | 2 +- .../gamemodes/malfunction/Malf_Modules.dm | 5 +- code/game/gamemodes/miniantags/borer/borer.dm | 7 +- code/game/machinery/doors/airlock.dm | 18 +- code/game/machinery/doors/airlock_types.dm | 6 +- code/game/machinery/doors/door.dm | 4 +- code/game/machinery/recycler.dm | 2 +- code/game/machinery/syndicatebomb.dm | 2 +- code/game/mecha/equipment/mecha_equipment.dm | 2 +- .../effects/decals/Cleanable/humans.dm | 2 +- .../effects/effect_system/effect_system.dm | 4 +- .../effect_system/effects_explosion.dm | 2 +- .../effects/effect_system/effects_smoke.dm | 4 +- code/game/objects/effects/glowshroom.dm | 4 +- code/game/objects/items/devices/flash.dm | 4 +- .../game/objects/items/devices/radio/radio.dm | 4 +- code/game/objects/items/toys.dm | 2 +- code/game/objects/items/weapons/RCD.dm | 2 +- code/game/objects/items/weapons/explosives.dm | 2 +- .../items/weapons/grenades/chem_grenade.dm | 2 +- .../items/weapons/grenades/ghettobomb.dm | 2 +- code/game/objects/items/weapons/legcuffs.dm | 2 +- code/game/objects/obj_defense.dm | 2 +- code/game/objects/structures/barsign.dm | 2 +- code/game/objects/structures/tables_racks.dm | 2 +- code/game/objects/structures/watercloset.dm | 4 +- code/modules/arcade/mob_hunt/mob_avatar.dm | 2 +- .../awaymissions/mission_code/beach.dm | 4 +- .../awaymissions/mission_code/spacehotel.dm | 4 +- code/modules/flufftext/Dreaming.dm | 4 +- code/modules/flufftext/Hallucination.dm | 10 +- code/modules/hydroponics/grown/citrus.dm | 2 +- code/modules/mining/lavaland/ash_flora.dm | 2 +- code/modules/mob/living/living.dm | 2 +- .../mob/living/simple_animal/bot/bot.dm | 2 +- .../simple_animal/hostile/mining/hivelord.dm | 4 +- .../hostile/terror_spiders/terror_spiders.dm | 4 +- .../simple_animal/hostile/venus_human_trap.dm | 2 +- code/modules/power/apc.dm | 3 +- code/modules/power/tesla/coil.dm | 2 +- code/modules/power/tesla/energy_ball.dm | 2 +- code/modules/research/rdconsole.dm | 2 +- code/modules/shuttle/on_move.dm | 22 +- code/modules/surgery/organs/augments_arms.dm | 2 +- code/modules/surgery/organs/blood.dm | 2 +- code/modules/telesci/gps.dm | 2 +- paradise.dme | 2 +- 63 files changed, 642 insertions(+), 209 deletions(-) delete mode 100644 code/controllers/Processes/timer.dm create mode 100644 code/controllers/subsystem/timer.dm diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index c6ffc61495e..3f360d5bad7 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -32,6 +32,8 @@ #define ToDegrees(radians) ((radians) * 57.2957795) // 180 / Pi #define ToRadians(degrees) ((degrees) * 0.0174532925) // Pi / 180 +#define SHORT_REAL_LIMIT 16777216 + //"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks //percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio) //collapsed to percent_of_tick_used * tick_lag diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 4b97b67e27e..0db57b27eae 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -31,6 +31,9 @@ /proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) return a.priority - b.priority +/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b) + return a.timeToRun - b.timeToRun + /proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B) . = B.hard_delete_time - A.hard_delete_time if(!.) diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 8171dd66427..136e573a25e 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -1,5 +1,10 @@ -#define QDEL_IN(item, time) addtimer(GLOBAL_PROC, "qdel", time, FALSE, item) +#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) +#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) if(item) { qdel(item); item = null } #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } +#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) #define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } #define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } + +/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. + QDEL_LIST(L) \ No newline at end of file diff --git a/code/controllers/Processes/nano_mob_hunter.dm b/code/controllers/Processes/nano_mob_hunter.dm index fb57b6e0bbf..dc5dcba805b 100644 --- a/code/controllers/Processes/nano_mob_hunter.dm +++ b/code/controllers/Processes/nano_mob_hunter.dm @@ -44,7 +44,7 @@ DECLARE_GLOBAL_CONTROLLER(mob_hunt, mob_hunt_server) recover_time = 3000 if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still //set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too) - addtimer(src, "auto_recover", recover_time, TRUE) + addtimer(CALLBACK(src, .proc/auto_recover), recover_time, TIMER_UNIQUE) /datum/controller/process/mob_hunt/proc/client_mob_update() var/list/ex_players = list() diff --git a/code/controllers/Processes/timer.dm b/code/controllers/Processes/timer.dm deleted file mode 100644 index 4b42d69221d..00000000000 --- a/code/controllers/Processes/timer.dm +++ /dev/null @@ -1,100 +0,0 @@ -var/global/datum/controller/process/timer/timer_master - -/datum/controller/process/timer - var/list/processing_timers = list() - var/list/hashes = list() - -/datum/controller/process/timer/setup() - name = "timer" - schedule_interval = 1 //every 0.1 seconds--2 server ticks - log_startup_progress("Timer process starting up.") - -/datum/controller/process/timer/statProcess() - ..() - stat(null, "[processing_timers.len] timers") - -/datum/controller/process/timer/doWork() - if(!processing_timers.len) - disabled = 1 //nothing to do, lets stop firing. - return - for(last_object in processing_timers) - var/datum/timedevent/event = last_object - if(!event.thingToCall || check_datum_qdeleted(event.thingToCall)) - qdel(event) - if(event.timeToRun <= world.time) - runevent(event) - qdel(event) - SCHECK - -DECLARE_GLOBAL_CONTROLLER(timer, timer_master) - -/datum/controller/process/timer/proc/runevent(datum/timedevent/event) - set waitfor = 0 - if(event.thingToCall) - if(event.thingToCall == GLOBAL_PROC && istext(event.procToCall)) - call("/proc/[event.procToCall]")(arglist(event.argList)) - else - call(event.thingToCall, event.procToCall)(arglist(event.argList)) - -/datum/timedevent - var/thingToCall - var/procToCall - var/timeToRun - var/argList - var/id - var/hash - var/static/nextid = 1 - -/datum/timedevent/New() - id = nextid++ - -/datum/timedevent/Destroy() - timer_master.processing_timers -= src - timer_master.hashes -= hash - return QDEL_HINT_IWILLGC - -/proc/addtimer(thingToCall, procToCall, wait, unique = FALSE, ...) - if(!timer_master) //can't run timers before the mc has been created - return - if(!thingToCall || !procToCall) - return - if(timer_master.disabled) - timer_master.disabled = 0 - - var/datum/timedevent/event = new() - event.thingToCall = thingToCall - event.procToCall = procToCall - event.timeToRun = world.time + wait - var/hashlist = args.Copy() - - hashlist[1] = "[thingToCall](\ref[thingToCall])" - event.hash = jointext(hashlist, null) - if(args.len > 4) - event.argList = args.Copy(5) - - // Check for dupes if unique = 1. - if(unique) - var/datum/timedevent/hash_event = timer_master.hashes[event.hash] - if(hash_event) - return hash_event.id - timer_master.hashes[event.hash] = event - if(wait <= 0) - timer_master.runevent(event) - timer_master.hashes -= event.hash - return - // If we are unique (or we're not checking that), add the timer and return the id. - timer_master.processing_timers += event - - return event.id - -/proc/deltimer(id) - if(id == 0) - // No event will correspond to an id of 0 - the timer does not exist - // Save us a possibly expensive iteration through the timer list - // This would probably be more efficient in general if we used an associative list instead - return 0 - for(var/datum/timedevent/event in timer_master.processing_timers) - if(event.id == id) - qdel(event) - return 1 - return 0 diff --git a/code/controllers/Processes/weather.dm b/code/controllers/Processes/weather.dm index 1706b2232c0..919352313c6 100644 --- a/code/controllers/Processes/weather.dm +++ b/code/controllers/Processes/weather.dm @@ -37,7 +37,7 @@ var/global/datum/controller/process/weather/weather_master var/datum/weather/W = pickweight(possible_weather_for_this_z) run_weather(W.name) eligible_zlevels -= Z - addtimer(src, "make_z_eligible", rand(3000, 6000) + W.weather_duration_upper, TRUE, Z) //Around 5-10 minutes between weathers + addtimer(CALLBACK(src, .proc/make_z_eligible, Z), rand(3000, 6000) + W.weather_duration_upper, TIMER_UNIQUE) //Around 5-10 minutes between weathers DECLARE_GLOBAL_CONTROLLER(weather, weather_master) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm new file mode 100644 index 00000000000..2cb4c46eb67 --- /dev/null +++ b/code/controllers/subsystem/timer.dm @@ -0,0 +1,518 @@ +#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) % 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) + name = "Timer" + wait = 1 //SS_TICKER subsystem, so wait is in ticks + init_order = INIT_ORDER_TIMER + + flags = SS_TICKER|SS_NO_INIT + + 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 = 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 + + var/list/bucket_list = list() //list of buckets, each bucket holds every timer that has to run that byond tick. + + var/list/timer_id_dict = list() //list of all active timers assoicated to their timer id (for easy lookup) + + var/list/clienttime_timers = list() //special snowflake timers that run on fancy pansy "client time" + + var/last_invoke_tick = 0 + 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(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 + + if(lit && lit < last_check && last_invoke_warning < last_check) + last_invoke_warning = world.time + var/msg = "No regular timers processed in the last [TIMER_NO_INVOKE_WARNING] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!" + message_admins(msg) + WARNING(msg) + if(bucket_auto_reset) + bucket_resolution = 0 + + log_world("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + for(var/i in 1 to length(bucket_list)) + var/datum/timedevent/bucket_head = bucket_list[i] + if(!bucket_head) + continue + + log_world("Active timers at index [i]:") + + var/datum/timedevent/bucket_node = bucket_head + var/anti_loop_check = 1000 + do + log_world(get_timer_debug_string(bucket_node)) + bucket_node = bucket_node.next + anti_loop_check-- + while(bucket_node && bucket_node != bucket_head && anti_loop_check) + log_world("Active timers in the second_queue queue:") + for(var/I in second_queue) + log_world(get_timer_debug_string(I)) + + var/next_clienttime_timer_index = 0 + var/len = length(clienttime_timers) + + for(next_clienttime_timer_index in 1 to len) + if(MC_TICK_CHECK) + 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 + if(practical_offset > BUCKET_LEN) + head_offset += TICKS2DS(BUCKET_LEN) + practical_offset = 1 + resumed = FALSE + + if((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution)) + reset_buckets() + bucket_list = src.bucket_list + resumed = FALSE + + + if(!resumed) + timer = null + + 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] + timer = head + while(timer) + var/datum/callback/callBack = timer.callBack + if(!callBack) + 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]") + + if(!timer.spent) + spent += timer + timer.spent = world.time + callBack.InvokeAsync() + last_invoke_tick = world.time + + if(MC_TICK_CHECK) + return + + timer = timer.next + if(timer == head) + break + + + bucket_list[practical_offset++] = null + + //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) + + for(var/spent_timer in spent) + qdel(spent_timer) + + spent.len = 0 + +//formated this way to be runtime resistant +/datum/controller/subsystem/timer/proc/get_timer_debug_string(datum/timedevent/TE) + . = "Timer: [TE]" + . += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]" + if(TE.spent) + . += ", SPENT([TE.spent])" + if(QDELETED(TE)) + . += ", QDELETED" + if(!TE.callBack) + . += ", NO CALLBACK" + +/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 + for(var/bucket_head in bucket_list) + if(!bucket_head) + continue + var/datum/timedevent/bucket_node = bucket_head + do + alltimers += bucket_node + bucket_node = bucket_node.next + while(bucket_node && bucket_node != bucket_head) + + bucket_list.len = 0 + bucket_list.len = BUCKET_LEN + + practical_offset = 1 + bucket_count = 0 + head_offset = world.time + bucket_resolution = world.tick_lag + + alltimers += second_queue + if(!length(alltimers)) + return + + sortTim(alltimers, .proc/cmp_timer) + + var/datum/timedevent/head = alltimers[1] + + if(head.timeToRun < head_offset) + head_offset = head.timeToRun + + var/new_bucket_count + var/i = 1 + for(i in 1 to length(alltimers)) + var/datum/timedevent/timer = alltimers[1] + if(!timer) + continue + + var/bucket_pos = BUCKET_POS(timer) + if(timer.timeToRun >= TIMER_MAX) + i-- + break + + + 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 + + new_bucket_count++ + 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) + alltimers.Cut(1, i+1) + second_queue = alltimers + bucket_count = new_bucket_count + + +/datum/controller/subsystem/timer/Recover() + second_queue |= SStimer.second_queue + hashes |= SStimer.hashes + timer_id_dict |= SStimer.timer_id_dict + bucket_list |= SStimer.bucket_list + +/datum/timedevent + var/id + var/datum/callback/callBack + var/timeToRun + var/hash + var/list/flags + var/spent = 0 //time we ran the timer. + var/name //for easy debugging. + //cicular doublely linked list + var/datum/timedevent/next + var/datum/timedevent/prev + +/datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash) + var/static/nextid = 1 + id = TIMER_ID_NULL + src.callBack = callBack + src.timeToRun = timeToRun + src.flags = flags + src.hash = hash + + if(flags & TIMER_UNIQUE) + SStimer.hashes[hash] = src + + if(flags & TIMER_STOPPABLE) + id = num2text(nextid, 100) + if(nextid >= SHORT_REAL_LIMIT) + nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT)) + else + nextid++ + SStimer.timer_id_dict[id] = src + + 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((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) + 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/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 + 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) + + //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 + return + //other wise, lets do a simplified linked list add. + if(!bucket_head.prev) + bucket_head.prev = bucket_head + next = bucket_head + prev = bucket_head.prev + next.prev = src + prev.next = src + +/datum/timedevent/Destroy() + ..() + 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) + + callBack = null + + if(flags & TIMER_STOPPABLE) + SStimer.timer_id_dict -= id + + if(flags & TIMER_CLIENT_TIME) + 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 + else + if(prev) + prev.next = next + if(next) + next.prev = prev + else + if(prev && prev.next == src) + prev.next = next + if(next && next.prev == src) + next.prev = prev + next = null + prev = null + return QDEL_HINT_IWILLGC + +/datum/timedevent/proc/getcallingtype() + . = "ERROR" + if(callBack.object == GLOBAL_PROC) + . = "GLOBAL_PROC" + else + . = "[callBack.object.type]" + +/proc/addtimer(datum/callback/callback, wait = 0, flags = 0) + if(!callback) + CRASH("addtimer called without a callback") + + if(wait < 0) + stack_trace("addtimer called with a negative 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("addtimer called with a callback assigned to a qdeleted object") + + wait = max(wait, 0) + + if(wait >= INFINITY) + CRASH("Attempted to create timer with INFINITY delay") + + var/hash + + if(flags & TIMER_UNIQUE) + var/list/hashlist + if(flags & TIMER_NO_HASH_WAIT) + hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, flags & TIMER_CLIENT_TIME) + else + hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, wait, flags & TIMER_CLIENT_TIME) + hashlist += callback.arguments + hash = hashlist.Join("|||||||") + + 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 //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) + . = hash_timer.id + return + + + var/timeToRun = world.time + wait + if(flags & TIMER_CLIENT_TIME) + timeToRun = REALTIMEOFDAY + wait + + var/datum/timedevent/timer = new(callback, timeToRun, flags, hash) + return timer.id + +/proc/deltimer(id) + if(!id) + return FALSE + if(id == TIMER_ID_NULL) + CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag") + if(!istext(id)) + if(istype(id, /datum/timedevent)) + qdel(id) + return TRUE + //id is string + var/datum/timedevent/timer = SStimer.timer_id_dict[id] + if(timer && !timer.spent) + qdel(timer) + return TRUE + return FALSE + + +#undef BUCKET_LEN +#undef BUCKET_POS +#undef TIMER_MAX +#undef TIMER_ID_MAX diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 73104f212ff..d5426b3d673 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -91,7 +91,7 @@ debug_variables(shuttle_master) feedback_add_details("admin_verb","DShuttle") if("Timer") - debug_variables(timer_master) + debug_variables(SStimer) feedback_add_details("admin_verb","DTimer") if("Weather") debug_variables(weather_master) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 3e8c11cc1e8..7d7679b93e8 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -1,5 +1,6 @@ /datum var/gc_destroyed //Time when this object was destroyed. + var/list/active_timers //for SStimer var/list/datum_components //for /datum/components var/var_edited = FALSE //Warranty void if seal is broken @@ -16,6 +17,14 @@ /datum/proc/Destroy(force = FALSE, ...) tag = null + var/list/timers = active_timers + active_timers = null + for(var/thing in timers) + var/datum/timedevent/timer = thing + if (timer.spent) + continue + qdel(timer) + var/list/dc = datum_components if(dc) var/all_components = dc[/datum/component] diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 5fee7994a83..be35cf8cca8 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1538,8 +1538,7 @@ H.update_inv_w_uniform(0,0) add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") - addtimer(src, "remove_zealot", convert_duration, FALSE, jumpsuit) //deconverts after the timer expires - + addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires return 1 /datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 43c3d3a61cd..6e3ead3849e 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -26,7 +26,7 @@ if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains to_chat(target, "You are somehow too bound to your current location to abandon it.") continue - addtimer(src, "do_jaunt", 0, FALSE, target) + INVOKE_ASYNC(src, .proc/do_jaunt, target) /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) target.notransform = 1 diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm index c87b2b455e7..320cc137806 100644 --- a/code/datums/weather/weather.dm +++ b/code/datums/weather/weather.dm @@ -31,7 +31,7 @@ var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins var/target_z = MAIN_STATION //The z-level to affect var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas. - + var/overlay_layer = 10 //Since it's above everything else, this is the layer used by default. 2 is below mobs and walls if you need to use that. var/aesthetic = FALSE //If the weather has no purpose other than looks var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather @@ -70,7 +70,7 @@ to_chat(M, telegraph_message) if(telegraph_sound) M << sound(telegraph_sound) - addtimer(src, "start", telegraph_duration) + addtimer(CALLBACK(src, .proc/start), telegraph_duration) /datum/weather/proc/start() if(stage >= MAIN_STAGE) @@ -85,7 +85,7 @@ if(weather_sound) M << sound(weather_sound) weather_master.processing_weather |= src - addtimer(src, "wind_down", weather_duration) + addtimer(CALLBACK(src, .proc/wind_down), weather_duration) /datum/weather/proc/wind_down() if(stage >= WIND_DOWN_STAGE) @@ -100,7 +100,7 @@ if(end_sound) M << sound(end_sound) weather_master.processing_weather -= src - addtimer(src, "end", end_duration) + addtimer(CALLBACK(src, .proc/end), end_duration) /datum/weather/proc/end() if(stage == END_STAGE) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d57199d5f91..1e931690333 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -242,7 +242,7 @@ /atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked) if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(src, "hitby_react", 2, TRUE, AM) + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) /atom/proc/hitby_react(atom/movable/AM) if(AM && isturf(AM.loc)) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 3fdc402bb8a..55af5fae17b 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -176,11 +176,11 @@ /mob/living/forceMove(atom/destination) if(buckled) - addtimer(src, "check_buckled", 1, TRUE) + addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) if(buckled_mob) - addtimer(buckled_mob, "check_buckled", 1, TRUE) + addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) if(pulling) - addtimer(src, "check_pull", 1, TRUE) + addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) . = ..() if(client) reset_perspective(destination) diff --git a/code/game/gamemodes/changeling/powers/biodegrade.dm b/code/game/gamemodes/changeling/powers/biodegrade.dm index 5e4b0e7fa12..3103416b9e4 100644 --- a/code/game/gamemodes/changeling/powers/biodegrade.dm +++ b/code/game/gamemodes/changeling/powers/biodegrade.dm @@ -18,7 +18,7 @@ return FALSE user.visible_message("[user] vomits a glob of acid on \his [O]!", \ "We vomit acidic ooze onto our restraints!") - addtimer(src, "dissolve_handcuffs", 30, FALSE, user, O) + addtimer(CALLBACK(src, .proc/dissolve_handcuffs, user, O), 30) used = TRUE if(user.wear_suit && user.wear_suit.breakouttime && !used) @@ -27,7 +27,7 @@ return FALSE user.visible_message("[user] vomits a glob of acid across the front of \his [S]!", \ "We vomit acidic ooze onto our straight jacket!") - addtimer(src, "dissolve_straightjacket", 30, FALSE, user, S) + addtimer(CALLBACK(src, .proc/dissolve_straightjacket, user, S), 30) used = TRUE @@ -37,7 +37,7 @@ return FALSE C.visible_message("[C]'s hinges suddenly begin to melt and run!") to_chat(user, "We vomit acidic goop onto the interior of [C]!") - addtimer(src, "open_closet", 70, FALSE, user, C) + addtimer(CALLBACK(src, .proc/open_closet, user, C), 70) used = TRUE if(istype(user.loc, /obj/structure/spider/cocoon) && !used) @@ -46,7 +46,7 @@ return FALSE C.visible_message("[src] shifts and starts to fall apart!") to_chat(user, "We secrete acidic enzymes from our skin and begin melting our cocoon...") - addtimer(src, "dissolve_cocoon", 25, FALSE, user, C) //Very short because it's just webs + addtimer(CALLBACK(src, .proc/dissolve_cocoon, user, C), 25) //Very short because it's just webs used = TRUE if(used) diff --git a/code/game/gamemodes/changeling/powers/fakedeath.dm b/code/game/gamemodes/changeling/powers/fakedeath.dm index 156e8ffb896..a166fd74502 100644 --- a/code/game/gamemodes/changeling/powers/fakedeath.dm +++ b/code/game/gamemodes/changeling/powers/fakedeath.dm @@ -17,7 +17,7 @@ user.status_flags |= FAKEDEATH //play dead user.update_canmove() - addtimer(src, "ready_to_regenerate", LING_FAKEDEATH_TIME, FALSE, user) + addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME) feedback_add_details("changeling_powers","FD") return 1 diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm index 08786b6d6db..56417ac4335 100644 --- a/code/game/gamemodes/changeling/powers/fleshmend.dm +++ b/code/game/gamemodes/changeling/powers/fleshmend.dm @@ -31,7 +31,7 @@ by quick repeated use!") recent_uses++ - addtimer(src, "fleshmend", 0, FALSE, user) + INVOKE_ASYNC(src, .proc/fleshmend, user) feedback_add_details("changeling_powers","RR") return TRUE diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 57ce00c92af..8f4b5dab95e 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -159,9 +159,8 @@ for(var/obj/machinery/door/D in airlocks) if(!is_station_level(D.z)) continue - spawn(0) - D.hostile_lockdown(src) - addtimer(D, "disable_lockdown", 900) + INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, src) + addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) post_status("alert", "lockdown") diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index e9a830e7ed6..09d8a84539a 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -43,7 +43,8 @@ to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") var/delay = (rand(350,450) + B.host.brainloss) - addtimer(src, "return_control", delay, FALSE, B) + addtimer(CALLBACK(src, .proc/return_control, B), delay) + /mob/living/captive_brain/proc/return_control(mob/living/simple_animal/borer/B) if(!B || !B.controlling) @@ -534,7 +535,7 @@ leaving = TRUE - addtimer(src, "let_go", 200) + addtimer(CALLBACK(src, .proc/let_go), 200) /mob/living/simple_animal/borer/proc/let_go() @@ -611,7 +612,7 @@ bonding = TRUE var/delay = 300+(host.getBrainLoss()*5) - addtimer(src, "assume_control", delay) + addtimer(CALLBACK(src, .proc/assume_control), delay) /mob/living/simple_animal/borer/proc/assume_control() if(!host || !src || controlling) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index baded609280..ce07e7bb920 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -118,7 +118,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/initialize() . = ..() if(closeOtherId != null) - addtimer(src, "update_other_id", 5) + addtimer(CALLBACK(src, .proc/update_other_id), 5) if(glass) airlock_material = "glass" if(security_level > AIRLOCK_SECURITY_METAL) @@ -226,13 +226,11 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/loseMainPower() main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) if(main_power_lost_until > 0) - main_power_timer = addtimer(src, "regainMainPower", SecondsToTicks(60), 1) - + main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE) // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running if(backup_power_lost_until == -1 && !backupPowerCablesCut()) backup_power_lost_until = world.time + SecondsToTicks(10) - backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(10), 1) - + backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(10), TIMER_UNIQUE | TIMER_STOPPABLE) // Disable electricity if required if(electrified_until && isAllPowerLoss()) electrify(0) @@ -240,7 +238,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/loseBackupPower() backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) if(backup_power_lost_until > 0) - backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(60), 1) + backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE) // Disable electricity if required if(electrified_until && isAllPowerLoss()) @@ -288,8 +286,7 @@ About the new airlock wires panel: message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) if(duration != -1) - electrified_timer = addtimer(src, "electrify", SecondsToTicks(duration), 1, 0) - + electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), SecondsToTicks(duration), TIMER_UNIQUE | TIMER_STOPPABLE) if(feedback && message) to_chat(usr, message) @@ -1080,8 +1077,7 @@ About the new airlock wires panel: // The `addtimer` system has the advantage of being cancelable if(autoclose) - autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1) - + autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE) return TRUE /obj/machinery/door/airlock/close(forced=0, override = 0) @@ -1098,7 +1094,7 @@ About the new airlock wires panel: for(var/turf/turf in locs) for(var/atom/movable/M in turf) if(M.density && M != src) //something is blocking the door - addtimer(src, "autoclose", 60) + addtimer(CALLBACK(src, .proc/autoclose), 60) use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people if(forced) diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index bff4bda660d..db78e8c82ef 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -150,13 +150,15 @@ /obj/machinery/door/airlock/uranium/New() ..() - addtimer(src, "radiate", event_step) + addtimer(CALLBACK(src, .proc/radiate), event_step) + /obj/machinery/door/airlock/uranium/proc/radiate() if(prob(50)) for(var/mob/living/L in range (3,src)) L.apply_effect(15,IRRADIATE,0) - addtimer(src, "radiate", event_step) + addtimer(CALLBACK(src, .proc/radiate), event_step) + /obj/machinery/door/airlock/uranium/glass opacity = 0 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 61a528ed93d..59297e04b30 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -279,7 +279,7 @@ // The `addtimer` system has the advantage of being cancelable if(autoclose) - autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1) + autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE) return TRUE @@ -292,7 +292,7 @@ for(var/atom/movable/M in get_turf(src)) if(M.density && M != src) //something is blocking the door if(autoclose) - addtimer(src, "autoclose", 60) + addtimer(CALLBACK(src, .proc/autoclose), 60) return operating = TRUE diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 13a2ba49ddd..d80a628927b 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -143,7 +143,7 @@ var/const/SAFETY_COOLDOWN = 100 safety_mode = 1 update_icon() L.loc = loc - addtimer(src, "reboot", SAFETY_COOLDOWN) + addtimer(CALLBACK(src, .proc/reboot), SAFETY_COOLDOWN) /obj/machinery/recycler/proc/reboot() playsound(loc, 'sound/machines/ping.ogg', 50, 0) diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 48637f9e67f..a4c73cc8fac 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -438,7 +438,7 @@ chem_splash(get_turf(src), spread_range, list(reactants), temp_boost) // Detonate it again in one second, until it's out of juice. - addtimer(src, "detonate", 10) + addtimer(CALLBACK(src, .proc/detonate), 10) // If it's not a time release bomb, do normal explosion diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 98e7b6f342d..b990029423c 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -90,7 +90,7 @@ /obj/item/mecha_parts/mecha_equipment/proc/start_cooldown() set_ready_state(0) chassis.use_power(energy_drain) - addtimer(src, "set_ready_state", equip_cooldown, FALSE, 1) + addtimer(CALLBACK(src, .proc/set_ready_state, 1), equip_cooldown) /obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target) if(!chassis) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 02f89f6f0f6..f832b446cd5 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -44,7 +44,7 @@ var/global/list/image/splatter_cache=list() if(B.blood_DNA) blood_DNA |= B.blood_DNA.Copy() qdel(B) - dry_timer = addtimer(src, "dry", DRYING_TIME * (amount+1)) + dry_timer = addtimer(CALLBACK(src, .proc/dry), DRYING_TIME * (amount+1), TIMER_STOPPABLE) /obj/effect/decal/cleanable/blood/Destroy() if(GAMEMODE_IS_CULT) diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index fa65008d307..7fb397297d7 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -51,7 +51,7 @@ would spawn and follow the beaker, even if it is carried or thrown. for(var/i in 1 to number) if(total_effects > 20) return - addtimer(src, "generate_effect", 0) + INVOKE_ASYNC(src, .proc/generate_effect) /datum/effect_system/proc/generate_effect() if(holder) @@ -67,7 +67,7 @@ would spawn and follow the beaker, even if it is carried or thrown. for(var/j in 1 to steps_amt) sleep(5) step(E,direction) - addtimer(src, "decrement_total_effect", 20) + addtimer(CALLBACK(src, .proc/decrement_total_effect), 20) /datum/effect_system/proc/decrement_total_effect() total_effects-- diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index aa3ecd8376b..5ad00157a02 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -58,4 +58,4 @@ /datum/effect_system/explosion/smoke/start() ..() - addtimer(src, "create_smoke", 5) + addtimer(CALLBACK(src, .proc/create_smoke), 5) diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index 64421b35fe9..385067e0611 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -39,7 +39,7 @@ /obj/effect/particle_effect/smoke/proc/kill_smoke() processing_objects.Remove(src) - addtimer(src, "fade_out", 0) + INVOKE_ASYNC(src, .proc/fade_out) QDEL_IN(src, 10) /obj/effect/particle_effect/smoke/process() @@ -67,7 +67,7 @@ if(C.smoke_delay) return FALSE C.smoke_delay++ - addtimer(src, "remove_smoke_delay", 10, FALSE, C) + addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10) return TRUE /obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C) diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index 9ecafee37f0..4cf5e79cc77 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -72,7 +72,7 @@ else //if on the floor, glowshroom on-floor sprite icon_state = "[base_icon_state]f" - addtimer(src, "Spread", delay, FALSE) + addtimer(CALLBACK(src, .proc/Spread), delay) /obj/structure/glowshroom/proc/Spread() var/turf/ownturf = get_turf(src) @@ -117,7 +117,7 @@ shrooms_planted++ //if we failed due to generation, don't try to plant one later if(shrooms_planted < myseed.yield) //if we didn't get all possible shrooms planted, try again later myseed.yield -= shrooms_planted - addtimer(src, "Spread", delay, FALSE) + addtimer(CALLBACK(src, .proc/Spread), delay) /obj/structure/glowshroom/proc/CalcDir(turf/location = loc) var/direction = 16 diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 3f56932371d..0e2e3cc3785 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -208,7 +208,7 @@ to_chat(I.owner, "Your photon projector implant overheats and deactivates!") I.Retract() overheat = FALSE - addtimer(src, "cooldown", flashcd * 2) + addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) /obj/item/flash/armimplant/try_use_flash(mob/user = null) if(overheat) @@ -216,7 +216,7 @@ to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") return FALSE overheat = TRUE - addtimer(src, "cooldown", flashcd) + addtimer(CALLBACK(src, .proc/cooldown), flashcd) playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) update_icon(1) return TRUE diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index d61aea153d7..e1ee7029439 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -270,7 +270,7 @@ var/global/list/default_medbay_channels = list( universal_speak = 1 /mob/living/automatedannouncer/New() - lifetime_timer = addtimer(src, "autocleanup", SecondsToTicks(10)) + lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE) ..() /mob/living/automatedannouncer/Destroy() @@ -619,7 +619,7 @@ var/global/list/default_medbay_channels = list( /obj/item/radio/emp_act(severity) on = 0 disable_timer++ - addtimer(src, "enable_radio", rand(100, 200)) + addtimer(CALLBACK(src, .proc/enable_radio), rand(100, 200)) if(listening) visible_message("[src] buzzes violently!") diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 3382292bc3a..b321721b639 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -303,7 +303,7 @@ /obj/effect/decal/cleanable/ash/snappop_phoenix/New() . = ..() - addtimer(src, "respawn", respawn_time) + addtimer(CALLBACK(src, .proc/respawn), respawn_time) /obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn() new /obj/item/toy/snappop/phoenix(get_turf(src)) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index b52a92112eb..fe132c107b0 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -385,7 +385,7 @@ RCD buzz loudly!","[src] begins \ vibrating violently!") // 5 seconds to get rid of it - addtimer(src, "detonate_pulse_explode", 50) + addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50) /obj/item/rcd/proc/detonate_pulse_explode() explosion(src, 0, 0, 3, 1, flame_range = 1) diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 381e6177bb2..6164ed01047 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -85,7 +85,7 @@ target.overlays += image_overlay if(!nadeassembly) to_chat(user, "You plant the bomb. Timer counting down from [det_time].") - addtimer(src, "prime", det_time*10) + addtimer(CALLBACK(src, .proc/prime), det_time*10) /obj/item/grenade/plastic/suicide_act(mob/user) message_admins("[key_name_admin(user)](?) (FLW) suicided with [src.name] at ([user.x],[user.y],[user.z] - JMP)",0,1) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index c6899d71c22..de85ec64a58 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -422,7 +422,7 @@ message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).") log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])") else - addtimer(src, "prime", det_time) + addtimer(CALLBACK(src, .proc/prime), det_time) var/turf/DT = get_turf(src) var/area/DA = get_area(DT) log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])") diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm index d24f93736e5..d0dc2c8fe06 100644 --- a/code/game/objects/items/weapons/grenades/ghettobomb.dm +++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm @@ -53,7 +53,7 @@ if(iscarbon(user)) var/mob/living/carbon/C = user C.throw_mode_on() - addtimer(src, "prime", det_time) + addtimer(CALLBACK(src, .proc/prime), det_time) /obj/item/grenade/iedcasing/prime() //Blowing that can up update_mob() diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm index b238899676b..e6ba57ff8da 100644 --- a/code/game/objects/items/weapons/legcuffs.dm +++ b/code/game/objects/items/weapons/legcuffs.dm @@ -132,7 +132,7 @@ /obj/item/restraints/legcuffs/beartrap/energy/New() ..() - addtimer(src, "dissipate", 100) + addtimer(CALLBACK(src, .proc/dissipate), 100) /obj/item/restraints/legcuffs/beartrap/energy/proc/dissipate() if(!ismob(loc)) diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 0738d57e8fb..fcd233318d5 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -162,7 +162,7 @@ being_shocked = TRUE var/power_bounced = power * 0.5 tesla_zap(src, 3, power_bounced) - addtimer(src, "reset_shocked", 10) + addtimer(CALLBACK(src, .proc/reset_shocked), 10) /obj/proc/reset_shocked() being_shocked = FALSE diff --git a/code/game/objects/structures/barsign.dm b/code/game/objects/structures/barsign.dm index 6c627bb40ce..d392deeec81 100644 --- a/code/game/objects/structures/barsign.dm +++ b/code/game/objects/structures/barsign.dm @@ -105,7 +105,7 @@ to_chat(user, "Nothing interesting happens!") return to_chat(user, "You emag the barsign. Takeover in progress...") - addtimer(src, "post_emag", 100) + addtimer(CALLBACK(src, .proc/post_emag), 100) /obj/structure/sign/barsign/proc/post_emag() if(broken || emagged) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 002bbb566d6..367be27b6dc 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -376,7 +376,7 @@ return // Don't break if they're just flying past if(AM.throwing) - addtimer(src, "throw_check", 5, FALSE, AM) + addtimer(CALLBACK(src, .proc/throw_check, AM), 5) else check_break(AM) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 7cc9c97dfdd..d1940259554 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -338,9 +338,9 @@ mist_time = 70 //7 seconds on freezing temperature to disperse existing mist if(watertemp == "boiling") mist_time = 20 //2 seconds on boiling temperature to build up mist - addtimer(src, "update_mist", mist_time) + addtimer(CALLBACK(src, .proc/update_mist), mist_time) else - addtimer(src, "update_mist", 250) //25 seconds for mist to disperse after being turned off + addtimer(CALLBACK(src, .proc/update_mist), 250) //25 seconds for mist to disperse after being turned off /obj/machinery/shower/proc/update_mist() if(on) diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 42342e0693b..e4ea98a44a6 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -21,7 +21,7 @@ update_self() forceMove(mob_info.spawn_point) if(!mob_info.is_trap) - addtimer(src, "despawn", mob_info.lifetime) + addtimer(CALLBACK(src, .proc/despawn), mob_info.lifetime) /obj/effect/nanomob/proc/update_self() if(!mob_info) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index f285d4434da..4d2384ceb67 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -12,7 +12,7 @@ var/water_timer = 0 /obj/effect/waterfall/New() - water_timer = addtimer(src, "drip", water_frequency) + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) /obj/effect/waterfall/Destroy() if(water_timer) @@ -25,4 +25,4 @@ W.dir = dir spawn(1) W.loc = get_step(W, dir) - water_timer = addtimer(src, "drip", water_frequency) \ No newline at end of file + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) \ No newline at end of file diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm index d9299dfe061..e86123abc2e 100644 --- a/code/modules/awaymissions/mission_code/spacehotel.dm +++ b/code/modules/awaymissions/mission_code/spacehotel.dm @@ -238,7 +238,7 @@ return null D.occupant = occupant - D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid) + D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE) vacant_rooms -= D guests[occupant] = roomid @@ -252,7 +252,7 @@ return if(D.account.charge(100, transaction_purpose = "10 minutes", dest_name = name)) - D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid) + D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE) else force_checkout(roomid) diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index d01b5595d92..6a1d28eaf87 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -11,7 +11,7 @@ dream_images += pick_n_take(dreams) dreaming++ for(var/i in 1 to dream_images.len) - addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, dream_images[i], FALSE) + addtimer(CALLBACK(src, .proc/experience_dream, dream_images[i], FALSE), ((i - 1) * rand(30,60))) return TRUE @@ -33,7 +33,7 @@ dream_images += pick_n_take(nightmares) nightmare++ for(var/i in 1 to dream_images.len) - addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, nightmares[i], TRUE) + addtimer(CALLBACK(src, .proc/experience_dream, nightmares[i], TRUE), ((i - 1) * rand(30,60))) return TRUE /mob/living/carbon/proc/handle_dreams() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 621b8ec37ea..021db59f8a4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -339,7 +339,7 @@ Gunshots/explosions/opening doors/less rare audio (done) s.loc = get_step(get_turf(s), get_dir(s, target)) s.Show() s.Eat() - addtimer(src, "wake_and_restore", rand(50, 100)) + addtimer(CALLBACK(src, .proc/wake_and_restore), rand(50, 100)) qdel(s) /obj/effect/hallucination/simple/singularity @@ -365,9 +365,9 @@ Gunshots/explosions/opening doors/less rare audio (done) for(var/i=0,iPriority waypoint set by [calling_ai] [caller]. Proceed to [end_area.name].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.") if(message) to_chat(calling_ai, "[bicon(src)] [name] called to [end_area.name]. [path.len-1] meters to destination.") diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index e38ed7624ba..a026d33ed8d 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -62,7 +62,7 @@ /obj/item/organ/internal/hivelord_core/New() ..() - addtimer(src, "inert_check", 2400) + addtimer(CALLBACK(src, .proc/inert_check), 2400) /obj/item/organ/internal/hivelord_core/proc/inert_check() if(!owner && !preserved) @@ -151,7 +151,7 @@ /mob/living/simple_animal/hostile/asteroid/hivelordbrood/New() ..() - addtimer(src, "death", 100) + addtimer(CALLBACK(src, .proc/death), 100) /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood name = "blood brood" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 2a1e0f17ef4..a8856d191e4 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -270,8 +270,8 @@ var/global/list/ts_spiderling_list = list() else ts_count_alive_station++ // after 30 seconds, assuming nobody took control of it yet, offer it to ghosts. - addtimer(src, "CheckFaction", 150) - addtimer(src, "announcetoghosts", 300) + addtimer(CALLBACK(src, .proc/CheckFaction), 100) + addtimer(CALLBACK(src, .proc/announcetoghosts), 300) var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 9e6396dbde0..57412eb5a0d 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -22,7 +22,7 @@ for(var/turf/T in anchors) var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine) B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow) - addtimer(src, "bear_fruit", growth_time) + addtimer(CALLBACK(src, .proc/bear_fruit), growth_time) /obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit() visible_message("the plant has borne fruit!") diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 23c6e2a1f15..9c3d64ef4fc 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1004,8 +1004,7 @@ return to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") malf.malfhack = src - malf.malfhacking = addtimer(malf, "malfhacked", 600, FALSE, src) - + malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) var/obj/screen/alert/hackingapc/A A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) A.target = src diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 8bca07ccd45..aa25105cba6 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -66,7 +66,7 @@ flick("coilhit", src) playsound(src.loc, 'sound/magic/LightningShock.ogg', 100, 1, extrarange = 5) tesla_zap(src, 5, power_produced) - addtimer(src, "reset_shocked", 10) + addtimer(CALLBACK(src, .proc/reset_shocked), 10) else ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index f2ffbb9b857..9cfd526585e 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -83,7 +83,7 @@ energy_to_raise = energy_to_raise * 1.25 playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, 1, extrarange = 30) - addtimer(src, "new_mini_ball", 100) + addtimer(CALLBACK(src, .proc/new_mini_ball), 100) else if(energy < energy_to_lower && orbiting_balls.len) energy_to_raise = energy_to_raise / 1.25 diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 05038017aec..50c8e1a4cf8 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -887,7 +887,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, //helper proc that guarantees the wait message will not freeze the UI /obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) wait_message = message - wait_message_timer = addtimer(src, "clear_wait_message", delay, TRUE) + wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) // This is here to guarantee that we never lock the console, so long as the timer // process is running diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 342931a065c..d48007eda99 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -17,18 +17,18 @@ return 0 /obj/machinery/door/airlock/onShuttleMove() - . = ..() - if(!.) - return - addtimer(src, "close", 0, TRUE, 0, 1) - // Close any nearby airlocks as well - for(var/obj/machinery/door/airlock/D in orange(1, src)) - addtimer(D, "close", 0, TRUE, 0, 1) + . = ..() + if(!.) + return + INVOKE_ASYNC(src, .proc/close, 0, 1) + // Close any nearby airlocks as well + for(var/obj/machinery/door/airlock/D in orange(1, src)) + INVOKE_ASYNC(src, .proc/close, 0, 1) /obj/machinery/door/airlock/onShuttleMove() - . = ..() - if(id_tag == "s_docking_airlock") - addtimer(src, "lock", 0, TRUE) + . = ..() + if(id_tag == "s_docking_airlock") + INVOKE_ASYNC(src, .proc/lock) /mob/onShuttleMove() if(!move_on_shuttle) @@ -57,7 +57,7 @@ /obj/machinery/door/airlock/postDock(obj/docking_port/stationary/S1) . = ..() if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock") - addtimer(src, "unlock", 0, TRUE) + INVOKE_ASYNC(src, .proc/unlock) // Shuttle Rotation // /atom/proc/shuttleRotate(rotation) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 1290212a249..d98e40700df 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -290,7 +290,7 @@ if(H.nutrition >= NUTRITION_LEVEL_WELL_FED) to_chat(user, "You are already fully charged!") else - addtimer(src, "powerdraw_loop", 0, TRUE, A, H) + INVOKE_ASYNC(src, .proc/powerdraw_loop, A, H) else to_chat(user, "There is no charge to draw from that APC.") else diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 88e8341d37c..9f732fdef4d 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -7,7 +7,7 @@ return else bleedsuppress = TRUE - addtimer(src, "resume_bleeding", amount) + addtimer(CALLBACK(src, .proc/resume_bleeding), amount) /mob/living/carbon/human/proc/resume_bleeding() bleedsuppress = FALSE diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index c8ed52ec787..467f92d095e 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -26,7 +26,7 @@ var/list/GPS_list = list() emped = 1 overlays -= "working" overlays += "emp" - addtimer(src, "reboot", 300) + addtimer(CALLBACK(src, .proc/reboot), 300) /obj/item/gps/proc/reboot() emped = FALSE diff --git a/paradise.dme b/paradise.dme index 438cb2acaae..471ced090fd 100644 --- a/paradise.dme +++ b/paradise.dme @@ -200,7 +200,6 @@ #include "code\controllers\Processes\obj.dm" #include "code\controllers\Processes\shuttles.dm" #include "code\controllers\Processes\ticker.dm" -#include "code\controllers\Processes\timer.dm" #include "code\controllers\Processes\weather.dm" #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" @@ -212,6 +211,7 @@ #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\sun.dm" #include "code\controllers\subsystem\throwing.dm" +#include "code\controllers\subsystem\timer.dm" #include "code\datums\action.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" From 510c5b4268ef9a29681004fc3dc714d0303ae46b Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sun, 29 Apr 2018 00:53:13 -0400 Subject: [PATCH 2/5] fixes --- code/datums/datum.dm | 2 +- .../simple_animal/hostile/terror_spiders/terror_spiders.dm | 2 +- code/modules/shuttle/on_move.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 7d7679b93e8..ae9a6df5b4f 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -21,7 +21,7 @@ active_timers = null for(var/thing in timers) var/datum/timedevent/timer = thing - if (timer.spent) + if(timer.spent) continue qdel(timer) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index a8856d191e4..cd01006546c 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -270,7 +270,7 @@ var/global/list/ts_spiderling_list = list() else ts_count_alive_station++ // after 30 seconds, assuming nobody took control of it yet, offer it to ghosts. - addtimer(CALLBACK(src, .proc/CheckFaction), 100) + addtimer(CALLBACK(src, .proc/CheckFaction), 150) addtimer(CALLBACK(src, .proc/announcetoghosts), 300) var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index d48007eda99..88551f49096 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -23,7 +23,7 @@ INVOKE_ASYNC(src, .proc/close, 0, 1) // Close any nearby airlocks as well for(var/obj/machinery/door/airlock/D in orange(1, src)) - INVOKE_ASYNC(src, .proc/close, 0, 1) + INVOKE_ASYNC(D, .proc/close, 0, 1) /obj/machinery/door/airlock/onShuttleMove() . = ..() From 278e6a7c25460491626e1e95853be2bcc035b35f Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sun, 29 Apr 2018 01:12:57 -0400 Subject: [PATCH 3/5] the last of the TIMER_STOPPABLE --- code/modules/mob/living/silicon/ai/ai.dm | 3 +++ code/modules/mob/living/silicon/ai/life.dm | 6 ++++-- code/modules/mob/living/simple_animal/bot/bot.dm | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index c84c7bdbbed..09b62f1c89c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -268,6 +268,9 @@ var/list/ai_verbs_default = list( shuttle_caller_list -= src shuttle_master.autoEvac() QDEL_NULL(eyeobj) // No AI, no Eye + if(malfhacking) + deltimer(malfhacking) + malfhacking = null malfhack = null return ..() diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 13b5308bc86..a6a043a8945 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -29,8 +29,10 @@ if(malfhack && malfhack.aidisabled) to_chat(src, "ERROR: APC access disabled, hack attempt canceled.") - malfhacking = 0 - malfhack = null + deltimer(malfhacking) + // This proc handles cleanup of screen notifications and + // messenging the client + malfhacked(malfhack) if(aiRestorePowerRoutine) adjustOxyLoss(1) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 786314d1ed6..986795ccd1e 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -193,6 +193,9 @@ bots_list -= src QDEL_NULL(Radio) QDEL_NULL(access_card) + if(reset_access_timer_id) + deltimer(reset_access_timer_id) + reset_access_timer_id = null if(radio_controller && bot_filter) radio_controller.remove_object(bot_core, control_freq) QDEL_NULL(bot_core) From 0c39cb11e4766894570535b17d1921b358f9b68a Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sun, 29 Apr 2018 20:15:46 -0400 Subject: [PATCH 4/5] tweak --- code/controllers/subsystem/timer.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 2cb4c46eb67..21fea2ee419 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -10,7 +10,7 @@ SUBSYSTEM_DEF(timer) flags = SS_TICKER|SS_NO_INIT - var/list/datum/timedevent/second_queue = list() //awe, yes, you've had first queue, but what about second queue? + var/list/second_queue = list() //awe, yes, you've had first queue, but what about second queue? Contains: /datum/timedevent var/list/hashes = list() var/head_offset = 0 //world.time of the first entry in the the bucket. From 6ab3ec48edce6466389ca8321b9cacf0b5660f4d Mon Sep 17 00:00:00 2001 From: Fox McCloud Date: Sun, 29 Apr 2018 21:15:42 -0400 Subject: [PATCH 5/5] change these timers --- code/datums/looping_sounds/looping_sound.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm index 4a5af0010e9..2cf79ad318b 100644 --- a/code/datums/looping_sounds/looping_sound.dm +++ b/code/datums/looping_sounds/looping_sound.dm @@ -65,7 +65,7 @@ return if(!chance || prob(chance)) play(get_sound(looped)) - addtimer(src, "sound_loop", mid_length, FALSE, ++looped) + addtimer(CALLBACK(src, .proc/sound_loop, ++looped), mid_length) /datum/looping_sound/proc/play(soundfile) var/list/atoms_cache = output_atoms @@ -93,7 +93,7 @@ if(start_sound) play(start_sound) start_wait = start_length - addtimer(src, "sound_loop", start_wait) + addtimer(CALLBACK(src, .proc/sound_loop), start_wait) /datum/looping_sound/proc/on_stop(looped) if(end_sound)