Bevor wir zum fix kommen sollten wir uns erst mal anschauen, wieso der Fehler überhaupt auftritt...
1) Ihr startet in eurer Quest einen timer.
2) Dies erstellt ein neues timer event. (questevent.cpp//quest_timer_event), welches eurem charakter zugeordnet wird. q.GetCurrentPC()->AddTimer
3) Wenn der timer "fällig" wird, wird der "when [...].timer" block in der quest ausgeführt.
4) In unserer quest versuchen wir, einen timer mit dem selben namen neu zu erstellen
5) Dies führt wieder q.GetCurrentPC()->AddTimer([...], quest_create_timer_event(...), [...]) aus
6) In AddTimer, wird bevor ein neuer timer hinzugefügt wird, der alte gecancelt (falls einer existent sein sollte)
7) Im CancelTimerEvent wird zum einen das event gecancelt, als auch quest_event_info::name gelöscht.
8) Der neue Timer, der in unserer quest im timer block erstellt wurde ist aktiv. (siehe sylog: QUEST add timer 0x3581e140 1)
9) Da unser "when [...].timer" block erfolgreich ausgeführt wurde, wird versucht, den timer in der EVENTFUNC aufzuräumen.
10) Timer versucht, auf quest_event_info::name zuzugreifen, um sich damit zu löschen. (RemoveTimerNotCancel(info->name))
11) Check, ob quest_event_info::name NULL ist, fehlt -> Server crasht.
Fix:
Code:
EVENTFUNC(quest_timer_event) { quest_event_info * info = dynamic_cast<quest_event_info *>( event->info ); if (info == NULL) { sys_err( "quest_timer_event> <Factor> Null pointer" ); return 0; } CQuestManager & q = CQuestManager::instance(); if (CHARACTER_MANAGER::instance().FindByPID(info->player_id)) { if (!CQuestManager::instance().Timer(info->player_id, info->npc_id)) return (passes_per_sec / 2 + 1); if (info->time_cycle != 0) return info->time_cycle; } if (info->name != NULL) { PC * pPC = q.GetPC(info->player_id); if (pPC) pPC->RemoveTimerNotCancel(info->name); else sys_err("quest::PC pointer null. player_id: %u", info->player_id); M2_DELETE_ARRAY(info->name); info->name = NULL; } return 0; }
Code:
quest crash_test begin state start begin when login begin timer("test", 2) syschat("timer läuft.") end when test.timer begin syschat("timer ausgeführt.") cleartimer("test") timer("test", 2) syschat("timer läuft wieder.") end end end