Fix Alchemist Stone procs for healing potion events
This commit is contained in:
1 parent
aba5aeba2c
commit
2403edcf18
3 files changed
+133
-10
No files matched your search
@@ -0,0 +1,38 @@
|
||||
# Alchemistenstein: Heilungsereignis ohne DamageInfo
|
||||
|
||||
Der gesicherte Core worldserver-20261105-002730.core zeigt SIGSEGV in
|
||||
DamageInfo::GetSpellInfo(this=nullptr), aufgerufen aus dem CheckProc des
|
||||
Alchemistensteins in spell_generic.cpp. Akteur: Drakzug; Trankzauber 43185;
|
||||
HealInfo enthält 3728 Heilung, DamageInfo ist null. Der native Zauberpfad
|
||||
übergibt bei reinen Heiltreffern absichtlich HealInfo ohne DamageInfo.
|
||||
|
||||
CheckProc verwendet jetzt die vorhandene native Ereignisabfrage
|
||||
ProcEventInfo::GetSpellInfo und prüft fehlende Metadaten. HandleProc prüft diese
|
||||
Daten ebenfalls und verwendet für Heiltränke HealInfo::GetHeal. So bleiben
|
||||
40 Prozent der ursprünglichen Heilung einschließlich Überheilung als
|
||||
zusätzliche Heilung erhalten (3728 -> 1491 nach bisheriger Rundung).
|
||||
|
||||
Der bisherige Energize-Zweig verwendet weiterhin einen vorhandenen
|
||||
DamageInfo-Betrag. Fehlende oder leere Beträge erzeugen keinen Bonuszauber.
|
||||
Eine allgemeine Neuerfassung von Manawiederherstellungsbeträgen in der Engine
|
||||
ist nicht Teil dieses Crashfixes. Nicht-Trank-Ereignisse erzeugen keinen Bonus.
|
||||
|
||||
Der Regressionstest test_alchemist_stone_proc führt beide Produktionshandler
|
||||
sowie die native Metadatenabfrage mit ASan und UBSan aus: der konkrete Dumpfall,
|
||||
HealInfo ohne DamageInfo, ursprüngliche statt effektive Heilung, Rundung,
|
||||
bisherige Mana-Payloads, fehlende Metadaten und Payloads, Nullbeträge und
|
||||
Nicht-Trank-Ereignisse. Test und Build mit -j10 bestanden.
|
||||
|
||||
Der Server war bei Buildbeginn bereits beendet; Loaderkonfiguration wurde auf
|
||||
0 gesetzt, es gab keine laufenden Bots für einen Logout. Vor Installation und
|
||||
Start wurden Prozess- und tmux-Abwesenheit geprüft. Weitere Ergebnisse in
|
||||
SESSION-RESUME.md und checkpoints/2026-11-05-alchemist-stone/.
|
||||
|
||||
Das ursprüngliche passende Crashbinary liegt zur weiteren Diagnose unter
|
||||
checkpoints/2026-11-05-edwin-crash/worldserver-crashed; Core und Backtrace bleiben
|
||||
im Crashverzeichnis erhalten. Keine Dumps wurden gelöscht.
|
||||
|
||||
Installation und Neustart erfolgreich: PID 4477, frisches Ready und Ports
|
||||
8085/8087 geprüft. Regulärer Loader aktiv, 118 Bots online, kein ausstehender
|
||||
sicherer Logout. Ein zusätzlicher Ingame-Heiltranktest wurde nicht durchgeführt;
|
||||
der konkrete Corefall wird durch den Produktionstest reproduziert.
|
||||
@@ -199,24 +199,43 @@ class spell_gen_alchemist_stone : public SpellScriptLoader
|
||||
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
return eventInfo.GetDamageInfo()->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_POTION;
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
return spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_POTION;
|
||||
}
|
||||
|
||||
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
{
|
||||
PreventDefaultAction();
|
||||
|
||||
uint32 spellId = 0;
|
||||
int32 bp = int32(eventInfo.GetDamageInfo()->GetDamage() * 0.4f);
|
||||
|
||||
if (eventInfo.GetDamageInfo()->GetSpellInfo()->HasEffect(SPELL_EFFECT_HEAL))
|
||||
spellId = ALCHEMIST_STONE_HEAL;
|
||||
else if (eventInfo.GetDamageInfo()->GetSpellInfo()->HasEffect(SPELL_EFFECT_ENERGIZE))
|
||||
spellId = ALCHEMIST_STONE_MANA;
|
||||
|
||||
if (!spellId)
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
if (!spellInfo || spellInfo->SpellFamilyName != SPELLFAMILY_POTION)
|
||||
return;
|
||||
|
||||
uint32 spellId = 0;
|
||||
uint32 amount = 0;
|
||||
if (spellInfo->HasEffect(SPELL_EFFECT_HEAL))
|
||||
{
|
||||
// Healing hits carry HealInfo, not DamageInfo. Use the
|
||||
// original heal, retaining the potion bonus with overheal.
|
||||
if (HealInfo const* healInfo = eventInfo.GetHealInfo())
|
||||
{
|
||||
spellId = ALCHEMIST_STONE_HEAL;
|
||||
amount = healInfo->GetHeal();
|
||||
}
|
||||
}
|
||||
else if (spellInfo->HasEffect(SPELL_EFFECT_ENERGIZE))
|
||||
{
|
||||
if (DamageInfo const* damageInfo = eventInfo.GetDamageInfo())
|
||||
{
|
||||
spellId = ALCHEMIST_STONE_MANA;
|
||||
amount = damageInfo->GetDamage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!spellId || !amount)
|
||||
return;
|
||||
|
||||
int32 bp = int32(amount * 0.4f);
|
||||
GetTarget()->CastSpell(GetTarget(), spellId, CastSpellExtraArgs(aurEff).AddSpellBP0(bp));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""Execute production potion proc handlers and native event metadata fallback."""
|
||||
from pathlib import Path
|
||||
import subprocess,tempfile
|
||||
root=Path(__file__).resolve().parents[2]
|
||||
s=(root/'src/server/scripts/Spells/spell_generic.cpp').read_text();s=s[s.index('class spell_gen_alchemist_stone :'):]
|
||||
def method(src,start):
|
||||
a=src.index(start);o=src.index('{',a);depth=1;b=o+1
|
||||
while depth:
|
||||
depth+=(src[b]=='{')-(src[b]=='}');b+=1
|
||||
return src[a:b]
|
||||
unit=(root/'src/server/game/Entities/Unit/Unit.cpp').read_text()
|
||||
code=r'''
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
using uint32=uint32_t;using int32=int32_t;
|
||||
constexpr int SPELLFAMILY_POTION=13,SPELL_EFFECT_HEAL=10,SPELL_EFFECT_ENERGIZE=30;
|
||||
constexpr unsigned ALCHEMIST_STONE_HEAL=21399,ALCHEMIST_STONE_MANA=21400;
|
||||
struct SpellInfo{int SpellFamilyName=SPELLFAMILY_POTION,effect=SPELL_EFFECT_HEAL;bool HasEffect(int v)const{return effect==v;}};
|
||||
struct DamageInfo{uint32 amount;SpellInfo const*info;uint32 GetDamage()const{return amount;}SpellInfo const*GetSpellInfo()const{return info;}};
|
||||
struct HealInfo{uint32 heal,effective;SpellInfo const*info;uint32 GetHeal()const{return heal;}SpellInfo const*GetSpellInfo()const{return info;}};
|
||||
struct Spell{SpellInfo const*info;SpellInfo const*GetSpellInfo()const{return info;}};
|
||||
struct ProcEventInfo{
|
||||
Spell*_spell=nullptr;DamageInfo*_damageInfo=nullptr;HealInfo*_healInfo=nullptr;
|
||||
SpellInfo const*GetSpellInfo()const;
|
||||
DamageInfo*GetDamageInfo(){return _damageInfo;}HealInfo*GetHealInfo(){return _healInfo;}
|
||||
};
|
||||
'''+method(unit,'SpellInfo const* ProcEventInfo::GetSpellInfo() const')+r'''
|
||||
struct AuraEffect{};
|
||||
struct CastSpellExtraArgs{AuraEffect const*aura;int32 bp=0;CastSpellExtraArgs(AuraEffect const*a):aura(a){}CastSpellExtraArgs&AddSpellBP0(int32 n){bp=n;return *this;}};
|
||||
struct Target{struct Cast{unsigned spell;int32 bp;AuraEffect const*aura;};std::vector<Cast>casts;void CastSpell(Target*t,unsigned id,CastSpellExtraArgs args){assert(t==this);casts.push_back({id,args.bp,args.aura});}};
|
||||
struct Script{Target target;unsigned suppressed=0;void PreventDefaultAction(){++suppressed;}Target*GetTarget(){return ⌖}
|
||||
'''+method(s,'bool CheckProc(')+method(s,'void HandleProc(')+r'''
|
||||
};
|
||||
int main(){
|
||||
Script script;AuraEffect aura;SpellInfo healing;HealInfo heal{1000,200,&healing};
|
||||
ProcEventInfo event;event._healInfo=&heal;
|
||||
// Exact crash shape: a healing potion has no DamageInfo.
|
||||
assert(!event.GetDamageInfo()&&script.CheckProc(event));script.HandleProc(&aura,event);
|
||||
assert(script.target.casts.size()==1&&script.target.casts.back().spell==21399&&script.target.casts.back().bp==400&&script.target.casts.back().aura==&aura);
|
||||
assert(script.suppressed==1); // preserve native default-action suppression
|
||||
// Spell metadata can come from the cast even without damage metadata.
|
||||
Spell cast{&healing};event._spell=&cast;heal.heal=7;script.HandleProc(&aura,event);assert(script.target.casts.back().bp==2);
|
||||
// Actual saved crash: Drakzug, potion 43185, raw heal 3728, DamageInfo=null.
|
||||
heal.heal=3728;script.HandleProc(&aura,event);assert(script.target.casts.back().bp==1491);
|
||||
heal.heal=0;auto count=script.target.casts.size();script.HandleProc(&aura,event);assert(script.target.casts.size()==count);
|
||||
// Existing energize damage payload is preserved, including metadata fallback.
|
||||
SpellInfo mana{SPELLFAMILY_POTION,SPELL_EFFECT_ENERGIZE};DamageInfo damage{500,&mana};
|
||||
event={};event._damageInfo=&damage;assert(script.CheckProc(event));script.HandleProc(&aura,event);assert(script.target.casts.back().spell==21400&&script.target.casts.back().bp==200);
|
||||
count=script.target.casts.size();damage.amount=0;script.HandleProc(&aura,event);assert(script.target.casts.size()==count);
|
||||
// Missing payloads and missing metadata never dereference null or invent a bonus.
|
||||
event={};assert(!script.CheckProc(event));script.HandleProc(&aura,event);
|
||||
cast.info=&mana;event._spell=&cast;assert(script.CheckProc(event));script.HandleProc(&aura,event);
|
||||
damage={500,nullptr};event={};event._damageInfo=&damage;assert(!script.CheckProc(event));script.HandleProc(&aura,event);
|
||||
cast.info=&healing;event._spell=&cast;assert(script.CheckProc(event));script.HandleProc(&aura,event); // no HealInfo
|
||||
assert(script.target.casts.size()==count);
|
||||
// Generic triggered bonuses and non-potion heals cannot recurse into the stone.
|
||||
SpellInfo other{0,SPELL_EFFECT_HEAL};heal={1000,1000,&other};event={};event._healInfo=&heal;
|
||||
assert(!script.CheckProc(event));script.HandleProc(&aura,event);assert(script.target.casts.size()==count);
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
p=Path(d);(p/'test.cpp').write_text(code)
|
||||
subprocess.run(['c++','-std=c++17','-fsanitize=address,undefined','-fno-omit-frame-pointer','-g',str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
||||
subprocess.run([str(p/'test')],check=True)
|
||||
print('Alchemist Stone production proc: heal-only crash, metadata fallback, 40% raw heal/rounding, existing mana payload, missing/zero data and non-potion recursion guards PASS (ASan/UBSan)')
|
||||
Reference in new issue
Block a user