From d23c808e477007d03bf112727ea094e018e9c111 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Fri, 6 Dec 2024 17:20:12 -0500 Subject: [PATCH] Fixes several attack chain call sites (#27553) --- .../objects/items/weapons/melee/melee_misc.dm | 2 +- .../objects/items/weapons/storage/firstaid.dm | 4 +- .../mob/living/simple_animal/bot/ed209bot.dm | 2 +- .../mob/living/simple_animal/bot/secbot.dm | 2 +- code/modules/surgery/organs/organ_internal.dm | 2 +- tools/ci/check_legacy_attack_chain.py | 58 +++++++++++++++---- 6 files changed, 52 insertions(+), 18 deletions(-) diff --git a/code/game/objects/items/weapons/melee/melee_misc.dm b/code/game/objects/items/weapons/melee/melee_misc.dm index 8c8f6851c40c..cebeffae3c42 100644 --- a/code/game/objects/items/weapons/melee/melee_misc.dm +++ b/code/game/objects/items/weapons/melee/melee_misc.dm @@ -61,7 +61,7 @@ AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = NON_PROJECTILE_ATTACKS) RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(alert_admins_on_destroy)) -/obj/item/melee/saber/attack(mob/living/target, mob/living/user) +/obj/item/melee/saber/attack__legacy__attackchain(mob/living/target, mob/living/user) if(user.a_intent != INTENT_HELP || !ishuman(target)) return ..() if(!COOLDOWN_FINISHED(src, slap_cooldown)) diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 9fcdfeb58398..c6d394ba222e 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -308,7 +308,7 @@ return applying_meds = TRUE for(var/obj/item/reagent_containers/P in contents) - if(P.attack(M, user)) + if(P.attack__legacy__attackchain(M, user)) applying_meds = FALSE else applying_meds = FALSE @@ -357,7 +357,7 @@ C.visible_message("[C] [rapid_intake_message]") if(do_mob(C, C, 100)) // 10 seconds for(var/obj/item/reagent_containers/pill/P in contents) - P.attack(C, C) + P.attack__legacy__attackchain(C, C) C.visible_message("[C] [rapid_post_instake_message]") return diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index ec63bdb27f42..7c3d29e71bb9 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -560,7 +560,7 @@ var/threat = C.assess_threat(src) var/prev_intent = a_intent a_intent = INTENT_HELP - baton.attack(C, src) + baton.attack__legacy__attackchain(C, src) a_intent = prev_intent baton_delayed = TRUE addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index e0c34eb5c94f..510874a95a51 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -263,7 +263,7 @@ var/threat = C.assess_threat(src) var/prev_intent = a_intent a_intent = harmbaton ? INTENT_HARM : INTENT_HELP - baton.attack(C, src) + baton.attack__legacy__attackchain(C, src) a_intent = prev_intent baton_delayed = TRUE addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 2175c825bfea..22e2af8356a8 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -211,7 +211,7 @@ if(S) H.drop_item() H.put_in_active_hand(S) - S.attack(H, H) + S.attack__legacy__attackchain(H, H) qdel(src) else ..() diff --git a/tools/ci/check_legacy_attack_chain.py b/tools/ci/check_legacy_attack_chain.py index 5804a0235a05..b56a1027affb 100644 --- a/tools/ci/check_legacy_attack_chain.py +++ b/tools/ci/check_legacy_attack_chain.py @@ -22,6 +22,7 @@ class AttackChainCall: var_type: p | None call_name: str source_info: any + legacy: bool def make_error_message(self): return f"{self.proc_decl.type_path}/{self.proc_decl.name}(...) calls {self.call_name}(...) on var {self.var_type}/{self.var_name}" @@ -66,7 +67,7 @@ def get_var_type(self, name): return None - def add_attack_call(self, var_name, chain_call, source_info): + def add_attack_call(self, var_name, chain_call, source_info, legacy): var_type = self.get_var_type(var_name) CALLS[var_type].add( AttackChainCall( @@ -75,6 +76,7 @@ def add_attack_call(self, var_name, chain_call, source_info): self.get_var_type(var_name), chain_call, source_info, + legacy=legacy, ) ) @@ -83,15 +85,22 @@ def visit_Var(self, node, source_info): def visit_Expr(self, node, source_info): if node.kind == NodeKind.CALL: + legacy = False + record = False if "__legacy__attackchain" in node.name.name: - if node.expr: - if node.expr.kind == NodeKind.IDENTIFIER: - self.add_attack_call( - str(node.expr), node.name.name, source_info - ) - elif node.expr.kind == NodeKind.CONSTANT: - if not node.expr.constant.val: - self.add_attack_call("src", node.name.name, source_info) + legacy = True + record = True + elif node.name.name in NEW_PROCS: + legacy = False + record = True + if record and node.expr: + if node.expr.kind == NodeKind.IDENTIFIER: + self.add_attack_call( + str(node.expr), node.name.name, source_info, legacy + ) + elif node.expr.kind == NodeKind.CONSTANT: + if not node.expr.constant.val: + self.add_attack_call("src", node.name.name, source_info, legacy) # Ignored types will never be part of the attack chain. @@ -117,10 +126,21 @@ def visit_Expr(self, node, source_info): ASSISTED_TYPES = [ p("/atom"), p("/mob"), + p("/mob/living"), p("/obj"), p("/obj/item"), ] +NEW_PROCS = [ + "activate_self", + "after_attack", + "attack_by", + "attack", + "attacked", + "interact_with_atom", + "item_interaction", +] + if __name__ == "__main__": print("check_legacy_attack_chain started") @@ -131,6 +151,7 @@ def visit_Expr(self, node, source_info): CALLS = defaultdict(set) SETTING_CACHE = dict() LEGACY_PROCS = dict() + MODERN_PROCS = dict() BAD_TREES = dict() PROCS = dict() @@ -153,6 +174,7 @@ def visit_Expr(self, node, source_info): LEGACY_PROCS[pth] = { x for x in td.proc_names(modified=True) if "__legacy__attackchain" in x } + MODERN_PROCS[pth] = {x for x in td.proc_names(modified=True) if x in NEW_PROCS} for proc_decl in td.proc_decls(): walker = AttackChainCallWalker(td, proc_decl) proc_decl.walk(walker) @@ -170,11 +192,23 @@ def visit_Expr(self, node, source_info): exit_code = 1 print(f"new_attack_chain on {pth} but related type {cursor} is not") cursor = cursor.parent - if pth in CALLS: + if pth in CALLS and any([x.legacy for x in CALLS[pth]]): + print("Legacy sites requiring migration:") + for call in CALLS[pth]: + if call.legacy: + print(call.format_error()) + elif pth not in ASSISTED_TYPES: + if MODERN_PROCS[pth]: + exit_code = 1 + print(f"new_attack_chain not on {pth} using new procs:") + for proc in sorted(MODERN_PROCS[pth]): + print(f"\t{proc}") + if pth in CALLS and any([not x.legacy for x in CALLS[pth]]): exit_code = 1 - print("Call sites requiring migration:") + print("Unexpected new call sites:") for call in CALLS[pth]: - print(call.format_error()) + if not call.legacy: + print(call.format_error()) end = time.time() print(f"check_legacy_attack_chain tests completed in {end - start:.2f}s\n")