diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/map/map.hpp | 6 | ||||
-rw-r--r-- | src/map/npc-parse.cpp | 8 | ||||
-rw-r--r-- | src/map/npc.cpp | 19 | ||||
-rw-r--r-- | src/map/script-fun.cpp | 15 |
4 files changed, 36 insertions, 12 deletions
diff --git a/src/map/map.hpp b/src/map/map.hpp index 7cf43d5..56d07a5 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -360,7 +360,11 @@ struct npc_data : block_list Opt0 option; short flag; - bool deletion_pending; + enum { + NOT_DELETING = 0, + DELETION_QUEUED = 1, + DELETION_ACTIVE = 2 + } deletion_pending; Array<Timer, MAX_EVENTTIMER> eventtimer; diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp index 47b851c..df1a09a 100644 --- a/src/map/npc-parse.cpp +++ b/src/map/npc-parse.cpp @@ -164,7 +164,7 @@ bool npc_load_warp(ast::npc::Warp& warp) nd->warp.xs = xs; nd->warp.ys = ys; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_warp++; nd->bl_type = BL::NPC; @@ -228,7 +228,7 @@ bool npc_load_shop(ast::npc::Shop& shop) nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_shop++; nd->bl_type = BL::NPC; @@ -458,7 +458,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_script++; nd->bl_type = BL::NPC; @@ -568,7 +568,7 @@ bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& scr nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_script++; nd->bl_type = BL::NPC; diff --git a/src/map/npc.cpp b/src/map/npc.cpp index e8d6d4b..8a6bead 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -357,7 +357,7 @@ void npc_eventtimer(TimerData *, tick_t, BlockId, NpcEvent data) data); return; }); - if ((nd = ev->nd) == nullptr || nd->deletion_pending == true) + if ((nd = ev->nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING) { if (battle_config.error_log) PRINTF("npc_event: event not found [%s]\n"_fmt, @@ -591,7 +591,7 @@ int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname, ev.pos = ev2->pos; } - if ((nd = ev.nd) == nullptr || nd->deletion_pending == true) + if ((nd = ev.nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING) { if (!mob_kill && battle_config.error_log) PRINTF("npc_event: event not found [%s]\n"_fmt, @@ -774,6 +774,14 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id) nd = map_id_is_npc(id); + // If someone clicked on an NPC that is about to no longer exist, then + // release them + if (nd->deletion_pending != npc_data::NOT_DELETING) + { + clif_scriptclose(sd, id); + return 1; + } + if (nd->flag & 1) // 無効化されている return 1; @@ -818,7 +826,8 @@ int npc_scriptcont(dumb_ptr<map_session_data> sd, BlockId id) nd = map_id_is_npc(id); - if (!nd /* NPC was disposed? */) + // If the NPC is about to be deleted, release the PC + if (nd->deletion_pending != npc_data::NOT_DELETING) { clif_scriptclose(sd, id); npc_event_dequeue(sd); @@ -1091,10 +1100,10 @@ void npc_propagate_update(dumb_ptr<npc_data> nd) void npc_free(dumb_ptr<npc_data> nd) { - if (nd == nullptr || nd->deletion_pending == true) + if (nd == nullptr || nd->deletion_pending == npc_data::DELETION_ACTIVE) return; - nd->deletion_pending = true; + nd->deletion_pending = npc_data::DELETION_ACTIVE; nd->flag |= 1; clif_clearchar(nd, BeingRemoveWhy::GONE); npc_propagate_update(nd); diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 0014805..13b4bc8 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -1283,8 +1283,19 @@ void builtin_destroy(ScriptState *st) npc_event_dequeue(sd); } + // Cancel all existing timers on the NPC. + // They "would" never fire, and we don't want race conditions here. + for (int i = 0; i < MAX_EVENTTIMER; i++) + { + nd->eventtimer[i].cancel(); + } + // Schedule the NPC to be freed on the next available tick. + // Scripts can be invoked under iteration of the ev_db global event + // database, and we don't want to invalidate active iterators. + nd->deletion_pending = npc_data::DELETION_QUEUED; + nd->eventtimer[0] = Timer(gettick(), std::bind(npc_free, nd)); + nd = nd->is_script(); - npc_free(nd); st->oid = BlockId(); if (!HARG(0)) @@ -1350,7 +1361,7 @@ void builtin_puppet(ScriptState *st) nd->npc_subtype = NpcSubtype::SCRIPT; npc_script++; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; nd->n = map_addnpc(nd->bl_m, nd); |