Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] Fixes AI lag by re-adding idle mode to all AI that was lost with the simple mob to basic mob conversion. #2854

Merged
merged 1 commit into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions code/__DEFINES/ai/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
#define AI_STATUS_ON "ai_on"
///The AI is currently offline for any reason.
#define AI_STATUS_OFF "ai_off"
///The AI is currently in idle mode.
#define AI_STATUS_IDLE "ai_idle"

///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on
#define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles
#define AI_BOT_PATH_LENGTH 150

// How far should we, by default, be looking for interesting things to de-idle?
#define AI_DEFAULT_INTERESTING_DIST 14

///Cooldown on planning if planning failed last time

#define AI_FAILED_PLANNING_COOLDOWN (1.5 SECONDS)
Expand Down
27 changes: 26 additions & 1 deletion code/controllers/subsystem/ai_controllers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ SUBSYSTEM_DEF(ai_controllers)
var/list/ai_controllers_by_status = list(
AI_STATUS_ON = list(),
AI_STATUS_OFF = list(),
AI_STATUS_IDLE = list(),
)
///Assoc List of all AI controllers and the Z level they are on, which we check when someone enters/leaves a Z level to turn them on/off.
var/list/ai_controllers_by_zlevel = list()
/// The tick cost of all active AI, calculated on fire.
var/cost_on
/// The tick cost of all idle AI, calculated on fire.
var/cost_idle


/datum/controller/subsystem/ai_controllers/Initialize()
setup_subtrees()
Expand All @@ -24,10 +30,20 @@ SUBSYSTEM_DEF(ai_controllers)
/datum/controller/subsystem/ai_controllers/stat_entry(msg)
var/list/active_list = ai_controllers_by_status[AI_STATUS_ON]
var/list/inactive_list = ai_controllers_by_status[AI_STATUS_OFF]
msg = "Active AIs:[length(active_list)]|Inactive:[length(inactive_list)]"
var/list/idle_list = ai_controllers_by_status[AI_STATUS_IDLE]
msg = "Active AIs:[length(active_list)]/[round(cost_on,1)]%|Inactive:[length(inactive_list)]|Idle:[length(idle_list)]/[round(cost_idle,1)]%"
return ..()

/datum/controller/subsystem/ai_controllers/fire(resumed)
var/timer = TICK_USAGE_REAL
for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_IDLE])
for(var/client/client_found in GLOB.clients)
if(get_dist(get_turf(client_found.mob), get_turf(ai_controller.pawn)) <= ai_controller.interesting_dist)
ai_controller.set_ai_status(AI_STATUS_ON)
break
cost_idle = MC_AVERAGE(cost_idle, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))

timer = TICK_USAGE_REAL
for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_ON])
if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown))
continue
Expand All @@ -37,6 +53,15 @@ SUBSYSTEM_DEF(ai_controllers)
ai_controller.SelectBehaviors(wait * 0.1)
if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan
COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN)
if(ai_controller.can_idle)
var/found_interesting = FALSE
for(var/client/client_found in GLOB.clients)
if(get_dist(get_turf(client_found.mob), get_turf(ai_controller.pawn)) <= ai_controller.interesting_dist)
found_interesting = TRUE
break
if(!found_interesting)
ai_controller.set_ai_status(AI_STATUS_IDLE)
cost_on = MC_AVERAGE(cost_on, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))

///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list.
/datum/controller/subsystem/ai_controllers/proc/setup_subtrees()
Expand Down
11 changes: 9 additions & 2 deletions code/datums/ai/_ai_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ multiple modular subtrees with behaviors
// The variables below are fucking stupid and should be put into the blackboard at some point.
///AI paused time
var/paused_until = 0
///Can this AI idle?
var/can_idle = TRUE
///What distance should we be checking for interesting things when considering idling/deidling? Defaults to AI_DEFAULT_INTERESTING_DIST
var/interesting_dist = AI_DEFAULT_INTERESTING_DIST

/datum/ai_controller/New(atom/new_pawn)
change_ai_movement_type(ai_movement)
Expand Down Expand Up @@ -175,7 +179,7 @@ multiple modular subtrees with behaviors
SSai_controllers.ai_controllers_by_zlevel[new_turf.z] += src
var/new_level_clients = SSmobs.clients_by_zlevel[new_turf.z].len
if(new_level_clients)
set_ai_status(AI_STATUS_ON)
set_ai_status(AI_STATUS_IDLE)
else
set_ai_status(AI_STATUS_OFF)

Expand Down Expand Up @@ -319,6 +323,9 @@ multiple modular subtrees with behaviors
if(AI_STATUS_OFF)
STOP_PROCESSING(SSai_behaviors, src)
CancelActions()
if(AI_STATUS_IDLE)
STOP_PROCESSING(SSai_behaviors, src)
CancelActions()

/datum/ai_controller/proc/PauseAi(time)
paused_until = world.time + time
Expand Down Expand Up @@ -378,7 +385,7 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/on_sentience_lost()
SIGNAL_HANDLER
UnregisterSignal(pawn, COMSIG_MOB_LOGOUT)
set_ai_status(AI_STATUS_ON) //Can't do anything while player is connected
set_ai_status(AI_STATUS_IDLE) //Can't do anything while player is connected
RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained))

// Turn the controller off if the pawn has been qdeleted
Expand Down
2 changes: 1 addition & 1 deletion code/datums/brain_damage/special.dm
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@
owner.grant_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT)
owner.ai_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, prob(75))
if(owner.ai_controller.ai_status == AI_STATUS_OFF)
owner.ai_controller.set_ai_status(AI_STATUS_ON)
owner.ai_controller.set_ai_status(AI_STATUS_IDLE)
owner.log_message("became controlled by monkey instincts ([owner.ai_controller.blackboard[BB_MONKEY_AGGRESSIVE] ? "aggressive" : "docile"])", LOG_ATTACK, color = "orange")
to_chat(owner, span_warning("You feel the urge to act on your primal instincts..."))
// extend original timer if we roll the effect while it's already ongoing
Expand Down
1 change: 1 addition & 0 deletions code/modules/mob/living/basic/bots/bot_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
var/current_pathing_attempts = 0
///if we cant reach it after this many attempts, add it to our ignore list
var/max_pathing_attempts = 25
can_idle = FALSE // we want these to be running always

/datum/ai_controller/basic_controller/bot/TryPossessPawn(atom/new_pawn)
. = ..()
Expand Down
1 change: 1 addition & 0 deletions code/modules/mob/living/basic/lavaland/mook/mook_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
/datum/ai_planning_subtree/mine_walls/mook,
/datum/ai_planning_subtree/wander_away_from_village,
)
can_idle = FALSE // these guys are intended to operate even if nobody's around

///check for faction if not a ash walker, otherwise just attack
/datum/targeting_strategy/basic/mook/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/basic/ruin_defender/flesh.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
/mob/living/basic/living_limb_flesh/proc/wake_up(atom/limb)
visible_message(span_warning("[src] begins flailing around!"))
Shake(6, 6, 0.5 SECONDS)
ai_controller.set_ai_status(AI_STATUS_ON)
ai_controller.set_ai_status(AI_STATUS_IDLE)
forceMove(limb.drop_location())
qdel(limb)

1 change: 1 addition & 0 deletions code/modules/unit_tests/mouse_bite_cable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
var/fake_dt = SSai_controllers.wait * 0.1
// Set AI - AIs by default are off in z-levels with no client, we have to force it on.
biter.ai_controller.set_ai_status(AI_STATUS_ON)
biter.ai_controller.can_idle = FALSE
// Select behavior - this will queue finding the cable
biter.ai_controller.SelectBehaviors(fake_dt)
// Process behavior - this will execute the "locate the cable" behavior
Expand Down
Loading