diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm
index 93ac8ed70d..20eb648fe6 100644
--- a/code/__DEFINES/_tick.dm
+++ b/code/__DEFINES/_tick.dm
@@ -1,5 +1,5 @@
#define TICK_LIMIT_RUNNING 80
-#define TICK_LIMIT_TO_RUN 78
+#define TICK_LIMIT_TO_RUN 70
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm
index 28017de85c..987d52e0cd 100644
--- a/code/__DEFINES/time.dm
+++ b/code/__DEFINES/time.dm
@@ -20,4 +20,8 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using
#define HOURS MINUTES*60
-#define TICKS *world.tick_lag
\ No newline at end of file
+#define TICKS *world.tick_lag
+
+#define DS2TICKS(DS) (DS/world.tick_lag)
+
+#define TICKS2DS(T) (T TICKS)
\ No newline at end of file
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index de606b137e..4800f35b65 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -321,7 +321,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time
. = 1
while (world.time < endtime)
- stoplag()
+ stoplag(1)
if (progress)
progbar.update(world.time - starttime)
if(QDELETED(user) || QDELETED(target))
@@ -382,7 +382,7 @@ Proc for attack log creation, because really why not
var/starttime = world.time
. = 1
while (world.time < endtime)
- stoplag()
+ stoplag(1)
if (progress)
progbar.update(world.time - starttime)
@@ -437,7 +437,7 @@ Proc for attack log creation, because really why not
. = 1
mainloop:
while(world.time < endtime)
- sleep(1)
+ stoplag(1)
if(progress)
progbar.update(world.time - starttime)
if(QDELETED(user) || !targets)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 6c7bb0cda6..473817e12c 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -504,10 +504,10 @@ Turf and target are separate in case you want to teleport some distance from a t
/atom/proc/GetAllContents(list/output=list())
. = output
- output += src
- for(var/i in 1 to contents.len)
- var/atom/thing = contents[i]
- thing.GetAllContents(output)
+ output += src
+ for(var/i in 1 to contents.len)
+ var/atom/thing = contents[i]
+ thing.GetAllContents(output)
//Step-towards method of determining whether one atom can see another. Similar to viewers()
/proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate.
@@ -1225,16 +1225,19 @@ proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
//Increases delay as the server gets more overloaded,
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
-#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta,1)), 1)
+#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1)
-/proc/stoplag()
+//returns the number of ticks slept
+/proc/stoplag(initial_delay)
if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
sleep(world.tick_lag)
return 1
+ if (!initial_delay)
+ initial_delay = world.tick_lag
. = 0
- var/i = 1
+ var/i = DS2TICKS(initial_delay)
do
- . += round(i*DELTA_CALC)
+ . += Ceiling(i*DELTA_CALC)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index b92853a2c4..5607dde491 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -33,7 +33,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/init_time
var/tickdrift = 0
- var/sleep_delta
+ var/sleep_delta = 1
var/make_runtime = 0
@@ -53,7 +53,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/static/restart_clear = 0
var/static/restart_timeout = 0
var/static/restart_count = 0
-
+
//current tick limit, assigned before running a subsystem.
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
@@ -276,35 +276,45 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
iteration = 1
var/error_level = 0
- var/sleep_delta = 0
+ var/sleep_delta = 1
var/list/subsystems_to_check
//the actual loop.
+
while (1)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
+ var/starting_tick_usage = TICK_USAGE
if (processing <= 0)
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
- //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later
- // because sleeps are processed in the order received, so longer sleeps are more likely to run first
- if (TICK_USAGE > TICK_LIMIT_MC)
- sleep_delta += 2
+ //Anti-tick-contention heuristics:
+ //if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
+ // (because sleeps are processed in the order received, longer sleeps are more likely to run first)
+ if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
+ sleep_delta *= 2
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
- sleep(world.tick_lag * (processing + sleep_delta))
+ sleep(world.tick_lag * (processing * sleep_delta))
continue
- sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0)
- if (last_run + (world.tick_lag * processing) > world.time)
- sleep_delta += 1
- if (TICK_USAGE > (TICK_LIMIT_MC*0.5))
+ //Byond resumed us late. assume it might have to do the same next tick
+ if (last_run + Ceiling(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
sleep_delta += 1
+ sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
+
+ if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) //we ran 3/4 of the way into the tick
+ sleep_delta += 1
+
+ //debug
if (make_runtime)
var/datum/controller/subsystem/SS
SS.can_fire = 0
+
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
+
+ //now do the actual stuff
if (!queue_head || !(iteration % 3))
var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel)
@@ -321,6 +331,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
subsystems_to_check = current_runlevel_subsystems
else
subsystems_to_check = tickersubsystems
+
if (CheckQueue(subsystems_to_check) <= 0)
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
@@ -351,8 +362,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
- current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
- sleep(world.tick_lag * (processing + sleep_delta))
+ current_ticklimit = TICK_LIMIT_RUNNING
+ if (processing * sleep_delta <= world.tick_lag)
+ current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
+ sleep(world.tick_lag * (processing * sleep_delta))
diff --git a/code/datums/browser.dm b/code/datums/browser.dm
index cae6e04e9b..48fb688e53 100644
--- a/code/datums/browser.dm
+++ b/code/datums/browser.dm
@@ -177,7 +177,7 @@
/datum/browser/alert/proc/wait()
while (opentime && selectedbutton <= 0 && (!timeout || opentime+timeout > world.time))
- stoplag()
+ stoplag(1)
/datum/browser/alert/Topic(href,href_list)
if (href_list["close"] || !user || !user.client)
diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm
index 9869b43d8f..b9ea00da22 100644
--- a/code/datums/explosion.dm
+++ b/code/datums/explosion.dm
@@ -61,14 +61,14 @@ GLOBAL_LIST_EMPTY(explosions)
flash_range = min(GLOB.MAX_EX_FLASH_RANGE, flash_range)
flame_range = min(GLOB.MAX_EX_FLAME_RANGE, flame_range)
- //DO NOT REMOVE THIS SLEEP, IT BREAKS THINGS
+ //DO NOT REMOVE THIS STOPLAG, IT BREAKS THINGS
//not sleeping causes us to ex_act() the thing that triggered the explosion
//doing that might cause it to trigger another explosion
//this is bad
//I would make this not ex_act the thing that triggered the explosion,
//but everything that explodes gives us their loc or a get_turf()
//and somethings expect us to ex_act them so they can qdel()
- sleep(1) //tldr, let the calling proc call qdel(src) before we explode
+ stoplag() //tldr, let the calling proc call qdel(src) before we explode
EX_PREPROCESS_EXIT_CHECK
diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm
index 02e2d9614d..f489f99564 100644
--- a/code/datums/martial/wrestling.dm
+++ b/code/datums/martial/wrestling.dm
@@ -199,7 +199,7 @@
set waitfor = FALSE
if (D)
animate(D, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0)
- sleep (15)
+ sleep(15)
if (D)
animate(D, transform = null, time = 1, loop = 0)
@@ -258,7 +258,7 @@
D.pixel_y = 0
return 0
- sleep (1)
+ sleep(1)
if (A && D)
A.pixel_x = 0
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 8f33465a57..1b050f9aa8 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -1300,7 +1300,7 @@
if (istype(M))
for(var/datum/disease/transformation/jungle_fever/JF in M.viruses)
JF.cure(0)
- sleep(0) //because deleting of virus is doing throught spawn(0) //What
+ stoplag() //because deleting of virus is doing throught spawn(0) //What
log_admin("[key_name(usr)] attempting to humanize [key_name(current)]")
message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]")
H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
index d5b9ce4401..3091d6071a 100644
--- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
+++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm
@@ -123,10 +123,9 @@
if(glow_type)
glow = new glow_type(get_turf(src))
animate(glow, alpha = 255, time = convert_time)
- var/I = 0
- while(I < convert_time && get_turf(L) == get_turf(src))
- I++
- sleep(1)
+ var/end_time = world.time+convert_time
+ while(world.time < end_time && get_turf(L) == get_turf(src))
+ stoplag(1)
if(get_turf(L) != get_turf(src))
if(glow)
qdel(glow)
diff --git a/code/game/gamemodes/clock_cult/clock_scripture.dm b/code/game/gamemodes/clock_cult/clock_scripture.dm
index 8e8cd912e7..049a49f5f4 100644
--- a/code/game/gamemodes/clock_cult/clock_scripture.dm
+++ b/code/game/gamemodes/clock_cult/clock_scripture.dm
@@ -319,7 +319,7 @@ Applications: 8 servants, 3 caches, and 100 CV
qdel(progbar)
else
progbar.update(end_time - world.time)
- sleep(1)
+ stoplag(1)
if(slab)
if(slab.slab_ability)
successful = slab.slab_ability.successful
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index f919bdbab8..4c90f3ae4e 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -381,7 +381,7 @@
initial_matrix = matrix(M.transform)
initial_matrix.Translate(-3,0)
animate(M, transform = initial_matrix, time = 1, loop = 0)
- sleep (1)
+ sleep(1)
M.lying_fix()
@@ -428,7 +428,7 @@
initial_matrix = matrix(M.transform)
initial_matrix.Translate(-3,0)
animate(M, transform = initial_matrix, time = 1, loop = 0)
- sleep (1)
+ sleep(1)
M.lying_fix()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 76e1468751..e158c1f90a 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -344,7 +344,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/datum/progressbar/progress = new(user, len, loc)
while (do_after(user, 10, TRUE, S, FALSE, CALLBACK(src, .proc/handle_mass_pickup, S, things, loc, rejections, progress)))
- sleep(1)
+ stoplag(1)
qdel(progress)
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index 309479dd86..cb9e6e2d63 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -88,7 +88,7 @@
var/list/things = src_object.contents.Copy()
var/datum/progressbar/progress = new(user, things.len, src)
while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(src, .proc/handle_mass_item_insertion, things, src_object, user, progress)))
- sleep(1)
+ stoplag(1)
qdel(progress)
orient2hud(user)
src_object.orient2hud(user)
@@ -489,7 +489,7 @@
var/list/things = contents.Copy()
var/datum/progressbar/progress = new(usr, things.len, T)
while (do_after(usr, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
- sleep(1)
+ stoplag(1)
qdel(progress)
/obj/item/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress)
diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 6845a1c9f0..a6cc3d0464 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -120,7 +120,7 @@
return
emagged = TRUE
to_chat(user, "You emag the barsign. Takeover in progress...")
- sleep(100) //10 seconds
+ sleep(10 SECONDS)
set_sign(new /datum/barsign/hiddensigns/syndibarsign)
req_access = list(ACCESS_SYNDICATE)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 66406b88be..77ef696714 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -330,7 +330,7 @@
var/list/things = src_object.contents.Copy()
var/datum/progressbar/progress = new(user, things.len, src)
while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /obj/item/storage.proc/mass_remove_from_storage, src, things, progress)))
- sleep(1)
+ stoplag(1)
qdel(progress)
return TRUE
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index d5cf3aaf2c..7ea6c60000 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -61,7 +61,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
var/t = 0
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
- sleep(1) // Lock up the caller until this is received.
+ stoplag(1) // Lock up the caller until this is received.
t++
if(client)
@@ -112,7 +112,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
var/t = 0
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
- sleep(1) // Lock up the caller until this is received.
+ stoplag(1) // Lock up the caller until this is received.
t++
if(client)
@@ -131,7 +131,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
if (register_asset)
register_asset(file,files[file])
send_asset(client,file)
- sleep(0) //queuing calls like this too quickly can cause issues in some client versions
+ stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//if it's an icon or something be careful, you'll have to copy it before further use.
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index e755ff28ac..9b694d58b7 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -631,10 +631,9 @@ GLOBAL_LIST(external_rsc_urls)
/client/Stat()
. = ..()
if (holder)
- sleep(1)
+ stoplag(1)
else
- sleep(5)
- stoplag()
+ stoplag(5)
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
/client/proc/send_resources()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
index 815b20c0c5..1980e57dbe 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
@@ -161,7 +161,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
if(next_move <= world.time)
- sleep(1)
+ stoplag(1)
.() //retry
return
sleep((next_move - world.time) * 1.5)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index db8a93d540..5a6116bf66 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -228,7 +228,7 @@
while(loc)
if(paused)
next_run = world.time
- sleep(1)
+ stoplag(1)
continue
if((!( current ) || loc == current))
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index 06a699e54f..46563da53d 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -21,7 +21,7 @@
return 0
while(attached_hand) //hibernate untill the spell is actually used
charge_counter = 0
- sleep(1)
+ stoplag(1)
/obj/effect/proc_holder/spell/targeted/touch/proc/ChargeHand(mob/living/carbon/user)
attached_hand = new hand_path(src)