Life changes (#19560)

Refactored Life() to receive seconds per tick and times fired as
parameters.
Life() now cannot be slept in, turned various sleepings into async calls
procs.
Optimized mob AI subsystems, gave them new priorities levels and flags.
Grab upgrades are now elaborated asynchronously, tweaked them to avoid
stacking multiple upgrades.
Fixed plains tyrants keeping sending messages about stomping even if
dead.
This commit is contained in:
Fluffy
2024-07-08 12:48:18 +00:00
committed by GitHub
parent 78348238a3
commit b3a4aa501f
55 changed files with 350 additions and 222 deletions
+13 -19
View File
@@ -1,10 +1,9 @@
SUBSYSTEM_DEF(mobs)
name = "Mobs - Life"
init_order = INIT_ORDER_MISC // doesn't really matter when we init
priority = SS_PRIORITY_MOB
runlevels = RUNLEVELS_PLAYING
var/list/slept = list()
priority = FIRE_PRIORITY_MOBS
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 2 SECONDS
var/list/currentrun = list()
var/list/processing = list()
@@ -70,7 +69,7 @@ SUBSYSTEM_DEF(mobs)
return SS_INIT_SUCCESS
/datum/controller/subsystem/mobs/stat_entry(msg)
msg = "P:[GLOB.mob_list.len]"
msg = "P:[length(GLOB.mob_list)]"
return ..()
/datum/controller/subsystem/mobs/fire(resumed = 0)
@@ -82,10 +81,12 @@ SUBSYSTEM_DEF(mobs)
//the mobs that we didn't in the previous run, hence we have to pay the price of a list subtraction
//with &= we say to remove any item in the first list that is not in the second one
//of course, if we haven't resumed, this comparison would be useless, hence we skip it
var/list/currentrun = resumed ? (src.currentrun &= GLOB.mob_list) : src.currentrun
var/list/currentrun = resumed ? (src.currentrun &= (GLOB.mob_list + processing)) : src.currentrun
while (currentrun.len)
var/datum/thing = currentrun[currentrun.len]
var/seconds_per_tick = wait / (1 SECONDS)
while(length(currentrun))
var/datum/thing = currentrun[length(currentrun)]
currentrun.len--
if(!ismob(thing))
if(!QDELETED(thing))
@@ -93,13 +94,13 @@ SUBSYSTEM_DEF(mobs)
stop_processing(thing)
else
processing -= thing
if (MC_TICK_CHECK)
if(MC_TICK_CHECK)
return
continue
var/mob/M = thing
if (QDELETED(M))
if(QDELETED(M))
LOG_DEBUG("SSmobs: QDELETED mob [DEBUG_REF(M)] left in processing list!")
// We can just go ahead and remove them from all the mob lists.
GLOB.mob_list -= M
@@ -110,15 +111,8 @@ SUBSYSTEM_DEF(mobs)
return
continue
var/time = world.time
if (!M.frozen)
M.Life()
if (time != world.time && !slept[M.type])
slept[M.type] = TRUE
var/diff = world.time - time
LOG_DEBUG("SSmobs: Type '[M.type]' slept for [diff] ds in Life()! Suppressing further warnings.")
M.Life(seconds_per_tick, times_fired)
if (MC_TICK_CHECK)
return
+13 -12
View File
@@ -1,17 +1,18 @@
SUBSYSTEM_DEF(mob_ai)
name = "Mobs - AI"
flags = SS_NO_INIT
priority = SS_PRIORITY_MOB
runlevels = RUNLEVELS_PLAYING
flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT
priority = FIRE_PRIORITY_NPC_MOVEMENT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_AI_CONTROLLERS
var/list/processing = list()
var/list/currentrun = list()
///A list of mutex locks, to avoid reentry and starting new processings on mobs that were already processing from the previous run
var/list/datum/weakref/mutexes = list() //Yes i know they're opinionably not mutexes as there's only one process shut up
VAR_PRIVATE/list/datum/weakref/mutexes = list() //Yes i know they're opinionably not mutexes as there's only one process shut up
/datum/controller/subsystem/mob_ai/stat_entry(msg)
msg = "P:[processing.len]"
msg = "P:[length(processing)]"
return ..()
/datum/controller/subsystem/mob_ai/fire(resumed = FALSE)
@@ -21,7 +22,7 @@ SUBSYSTEM_DEF(mob_ai)
var/list/currentrun = src.currentrun
//Remove the mobs that have a mutex from being reprocessed again, this run
for(var/datum/weakref/locked_mob in mutexes)
for(var/datum/weakref/locked_mob as anything in mutexes)
var/mob/possible_locked_mob = locked_mob.resolve()
//The mob got QDEL'd, or otherwise somehow disappeared
@@ -31,24 +32,24 @@ SUBSYSTEM_DEF(mob_ai)
currentrun -= possible_locked_mob
while (currentrun.len)
var/mob/M = currentrun[currentrun.len]
while(length(currentrun))
var/mob/M = currentrun[length(currentrun)]
currentrun.len--
if (QDELETED(M))
if(QDELETED(M))
processing -= M
continue
if (M.ckey)
if(M.ckey)
// cliented mobs are not allowed to think
LOG_DEBUG("SSmob_ai: Type '[M.type]' was still thinking despite having a client!")
MOB_STOP_THINKING(M)
continue
if (!M.frozen && !M.stat)
if(!M.frozen && !M.stat)
INVOKE_ASYNC(src, PROC_REF(async_think), M)
if (MC_TICK_CHECK)
if(MC_TICK_CHECK)
return
/**
@@ -1,3 +1,10 @@
/**
* This is the fast AI subsystem, used for mobs that are doing something visible and thus have priority over other mobs processing
*
* You should promote mobs to fast thinking if eg. they are fighting players or similar, and de-promote them once they resume their normal routine
*/
MOB_AI_SUBSYSTEM_DEF(mob_fast_ai)
name = "Mobs - Fast AI"
flags = SS_KEEP_TIMING | SS_BACKGROUND | SS_NO_INIT
priority = FIRE_PRIORITY_NPC_ACTIONS
wait = 5