From c1398363c5a6e4304f9cee10351cd9cd59bb6b0c Mon Sep 17 00:00:00 2001 From: jmd Date: Sun, 13 Oct 2024 16:56:38 -0700 Subject: [PATCH] [objects] add: destroying child objects when parent is destroyed I.e. inventories --- lib/nw/kernel/Kernel.cpp | 1 + lib/nw/kernel/Objects.cpp | 1 + lib/nw/objects/Area.cpp | 18 ++++++++++++++++++ lib/nw/objects/Area.hpp | 1 + lib/nw/objects/Creature.cpp | 6 ++++++ lib/nw/objects/Creature.hpp | 1 + lib/nw/objects/Equips.cpp | 9 +++++++++ lib/nw/objects/Equips.hpp | 1 + lib/nw/objects/Inventory.cpp | 36 ++++++++++++++++++++++------------- lib/nw/objects/Inventory.hpp | 5 +++-- lib/nw/objects/Item.cpp | 5 +++++ lib/nw/objects/Item.hpp | 1 + lib/nw/objects/Module.cpp | 35 +++++++++++++++++++++------------- lib/nw/objects/Module.hpp | 4 +++- lib/nw/objects/ObjectBase.cpp | 4 ++++ lib/nw/objects/ObjectBase.hpp | 5 +++-- lib/nw/objects/Placeable.cpp | 5 +++++ lib/nw/objects/Placeable.hpp | 1 + rollnw-py/wrapper_objects.cpp | 2 +- tests/kernel_load_module.cpp | 1 + tests/objects_store.cpp | 4 ++-- 21 files changed, 112 insertions(+), 34 deletions(-) diff --git a/lib/nw/kernel/Kernel.cpp b/lib/nw/kernel/Kernel.cpp index 8beabf30c..80a8a8692 100644 --- a/lib/nw/kernel/Kernel.cpp +++ b/lib/nw/kernel/Kernel.cpp @@ -166,6 +166,7 @@ void set_game_profile(GameProfile* profile) void unload_module() { + // Since everything is getting nuked, it's not necessary to call Module::destroy services().shutdown(); services().module_loaded_ = false; services().start(); diff --git a/lib/nw/kernel/Objects.cpp b/lib/nw/kernel/Objects.cpp index 22afbe519..ac91d0f68 100644 --- a/lib/nw/kernel/Objects.cpp +++ b/lib/nw/kernel/Objects.cpp @@ -89,6 +89,7 @@ void ObjectSystem::destroy(ObjectHandle obj) } objects_[idx] = new_handle; + o->destroy(); switch (new_handle.type) { default: break; diff --git a/lib/nw/objects/Area.cpp b/lib/nw/objects/Area.cpp index 6451f8724..d6771d9d0 100644 --- a/lib/nw/objects/Area.cpp +++ b/lib/nw/objects/Area.cpp @@ -145,6 +145,24 @@ Area::Area() set_handle(ObjectHandle{object_invalid, ObjectType::area, 0}); } +void Area::destroy() +{ +#define DESTROY_OBJECTS(thing) \ + for (auto it : thing) { \ + nw::kernel::objects().destroy(it->handle()); \ + } + DESTROY_OBJECTS(creatures); + DESTROY_OBJECTS(doors); + DESTROY_OBJECTS(encounters); + DESTROY_OBJECTS(items); + DESTROY_OBJECTS(placeables); + DESTROY_OBJECTS(sounds); + DESTROY_OBJECTS(stores); + DESTROY_OBJECTS(triggers); + DESTROY_OBJECTS(waypoints); +#undef DESTROY_OBJECTS +} + bool Area::instantiate() { tileset = nw::kernel::tilesets().get(tileset_resref.view()); diff --git a/lib/nw/objects/Area.hpp b/lib/nw/objects/Area.hpp index dd5c5ef83..9738e8c8b 100644 --- a/lib/nw/objects/Area.hpp +++ b/lib/nw/objects/Area.hpp @@ -87,6 +87,7 @@ struct Area : public ObjectBase { virtual const Common* as_common() const override { return &common; } virtual Area* as_area() override { return this; } virtual const Area* as_area() const override { return this; } + virtual void destroy() override; virtual bool instantiate() override; /// Deserialize from JSON diff --git a/lib/nw/objects/Creature.cpp b/lib/nw/objects/Creature.cpp index acc8aa90b..3bd586a28 100644 --- a/lib/nw/objects/Creature.cpp +++ b/lib/nw/objects/Creature.cpp @@ -100,6 +100,12 @@ Creature::Creature() inventory.owner = this; } +void Creature::destroy() +{ + equipment.destroy(); + inventory.destroy(); +} + bool Creature::instantiate() { if (instantiated_) return true; diff --git a/lib/nw/objects/Creature.hpp b/lib/nw/objects/Creature.hpp index 1f9ee0cbd..4b1a1d88b 100644 --- a/lib/nw/objects/Creature.hpp +++ b/lib/nw/objects/Creature.hpp @@ -50,6 +50,7 @@ struct Creature : public ObjectBase { virtual const Common* as_common() const override { return &common; } virtual Creature* as_creature() override { return this; } virtual const Creature* as_creature() const override { return this; } + virtual void destroy() override; virtual bool instantiate() override; virtual InternedString tag() const override { return common.tag; } virtual Versus versus_me() const override; diff --git a/lib/nw/objects/Equips.cpp b/lib/nw/objects/Equips.cpp index 047166b8a..4804950b5 100644 --- a/lib/nw/objects/Equips.cpp +++ b/lib/nw/objects/Equips.cpp @@ -16,6 +16,15 @@ Equips::Equips(Creature* owner) { } +void Equips::destroy() +{ + for (auto& e : equips) { + if (e.is()) { + nw::kernel::objects().destroy(e.as()->handle()); + } + } +} + bool Equips::instantiate() { int i = 0; diff --git a/lib/nw/objects/Equips.hpp b/lib/nw/objects/Equips.hpp index 4946d416a..e2c357970 100644 --- a/lib/nw/objects/Equips.hpp +++ b/lib/nw/objects/Equips.hpp @@ -153,6 +153,7 @@ struct Equips { Equips& operator=(Equips&&) = default; ~Equips() = default; + void destroy(); bool instantiate(); bool from_json(const nlohmann::json& archive, SerializationProfile profile); nlohmann::json to_json(SerializationProfile profile) const; diff --git a/lib/nw/objects/Inventory.cpp b/lib/nw/objects/Inventory.cpp index 2c88d3fe3..ea396eced 100644 --- a/lib/nw/objects/Inventory.cpp +++ b/lib/nw/objects/Inventory.cpp @@ -9,16 +9,26 @@ namespace nw { +void Inventory::destroy() +{ + for (auto& it : items) { + if (!it.item.is()) { continue; } + auto item = it.item.as(); + item->inventory.destroy(); + nw::kernel::objects().destroy(item->handle()); + } +} + bool Inventory::instantiate() { - for (auto& ii : items) { - if (std::holds_alternative(ii.item)) { - auto temp = kernel::objects().load(std::get(ii.item).view()); + for (auto& it : items) { + if (it.item.is()) { + auto temp = kernel::objects().load(it.item.as().view()); if (temp) { - ii.item = temp; + it.item = temp; } else { LOG_F(WARNING, "failed to instantiate item, perhaps you're missing '{}.uti'?", - std::get(ii.item)); + it.item.as()); } } } @@ -77,15 +87,15 @@ nlohmann::json Inventory::to_json(SerializationProfile profile) const } payload["position"] = {it.pos_x, it.pos_y}; - if (std::holds_alternative(it.item)) { + if (it.item.is()) { if (SerializationProfile::blueprint == profile) { - payload["item"] = std::get(it.item)->common.resref; + payload["item"] = it.item.as()->common.resref; } else { - Item::serialize(std::get(it.item), payload["item"], profile); + Item::serialize(it.item.as(), payload["item"], profile); } } else { if (SerializationProfile::blueprint == profile) { - payload["item"] = std::get(it.item); + payload["item"] = it.item.as(); } } } @@ -148,13 +158,13 @@ bool serialize(const Inventory& self, GffBuilderStruct& archive, SerializationPr } if (SerializationProfile::blueprint == profile) { - if (std::holds_alternative(it.item)) { - str.add_field("InventoryRes", std::get(it.item)); + if (it.item.is()) { + str.add_field("InventoryRes", it.item.as()); } else { - str.add_field("InventoryRes", std::get(it.item)->common.resref); + str.add_field("InventoryRes", it.item.as()->common.resref); } } else { - serialize(std::get(it.item), str, profile); + serialize(it.item.as(), str, profile); } } return true; diff --git a/lib/nw/objects/Inventory.hpp b/lib/nw/objects/Inventory.hpp index 3a503f2eb..af3380e1a 100644 --- a/lib/nw/objects/Inventory.hpp +++ b/lib/nw/objects/Inventory.hpp @@ -1,10 +1,10 @@ #pragma once #include "../serialization/Serialization.hpp" +#include "../util/Variant.hpp" #include "ObjectBase.hpp" #include -#include namespace nw { @@ -14,7 +14,7 @@ struct InventoryItem { bool infinite = false; uint16_t pos_x; uint16_t pos_y; - std::variant item; + Variant item; }; struct Inventory { @@ -29,6 +29,7 @@ struct Inventory { Inventory& operator=(Inventory&&) = default; ~Inventory() = default; + void destroy(); bool instantiate(); bool from_json(const nlohmann::json& archive, SerializationProfile profile); diff --git a/lib/nw/objects/Item.cpp b/lib/nw/objects/Item.cpp index f04852b95..6e493a7ec 100644 --- a/lib/nw/objects/Item.cpp +++ b/lib/nw/objects/Item.cpp @@ -17,6 +17,11 @@ Item::Item() inventory.owner = this; } +void Item::destroy() +{ + inventory.destroy(); +} + bool Item::instantiate() { if (instantiated_) { return true; } diff --git a/lib/nw/objects/Item.hpp b/lib/nw/objects/Item.hpp index 74ee7b285..6a355bca4 100644 --- a/lib/nw/objects/Item.hpp +++ b/lib/nw/objects/Item.hpp @@ -24,6 +24,7 @@ struct Item : public ObjectBase { virtual const Common* as_common() const override { return &common; } virtual Item* as_item() override { return this; } virtual const Item* as_item() const override { return this; } + virtual void destroy() override; virtual bool instantiate() override; virtual InternedString tag() const override { return common.tag; } diff --git a/lib/nw/objects/Module.cpp b/lib/nw/objects/Module.cpp index 23c5fa951..4c8e34ccb 100644 --- a/lib/nw/objects/Module.cpp +++ b/lib/nw/objects/Module.cpp @@ -64,16 +64,16 @@ nlohmann::json ModuleScripts::to_json() const size_t Module::area_count() const noexcept { - if (std::holds_alternative>(areas)) { - return std::get>(areas).size(); + if (areas.is>()) { + return areas.as>().size(); } return 0; } Area* Module::get_area(size_t index) { - if (std::holds_alternative>(areas) && index < area_count()) { - return std::get>(areas)[index]; + if (areas.is>() && index < area_count()) { + return areas.as>()[index]; } return nullptr; @@ -81,19 +81,28 @@ Area* Module::get_area(size_t index) const Area* Module::get_area(size_t index) const { - if (std::holds_alternative>(areas) && index < area_count()) { - return std::get>(areas)[index]; + if (areas.is>() && index < area_count()) { + return areas.as>()[index]; } return nullptr; } +void Module::destroy() +{ + if (areas.is>()) { + for (auto it : areas.as>()) { + nw::kernel::objects().destroy(it->handle()); + } + } +} + bool Module::instantiate() { if (instantiated_) return true; LOG_F(INFO, "instantiating module"); - auto& area_list = std::get>(areas); + auto& area_list = areas.as>(); Vector area_objects; area_objects.reserve(area_list.size()); for (auto& area : area_list) { @@ -263,11 +272,11 @@ bool Module::serialize(const Module* obj, nlohmann::json& archive) archive["locals"] = obj->locals.to_json(SerializationProfile::any); archive["scripts"] = obj->scripts.to_json(); - if (std::holds_alternative>(obj->areas)) { - archive["areas"] = std::get>(obj->areas); + if (obj->areas.is>()) { + archive["areas"] = obj->areas.as>(); } else { auto& area_list = archive["areas"] = nlohmann::json::array(); - for (const auto area : std::get>(obj->areas)) { + for (const auto area : obj->areas.as>()) { area_list.push_back(area->common.resref); } } @@ -324,12 +333,12 @@ bool serialize(const Module* obj, GffBuilderStruct& archive) serialize(obj->scripts, archive); auto& area_list = archive.add_list("Mod_Area_list"); - if (std::holds_alternative>(obj->areas)) { - for (const auto& area : std::get>(obj->areas)) { + if (obj->areas.is>()) { + for (const auto& area : obj->areas.as>()) { area_list.push_back(6).add_field("Area_Name", area); } } else { - for (const auto& area : std::get>(obj->areas)) { + for (const auto& area : obj->areas.as>()) { area_list.push_back(6).add_field("Area_Name", area->common.resref); } } diff --git a/lib/nw/objects/Module.hpp b/lib/nw/objects/Module.hpp index 040cb8fde..0023b8930 100644 --- a/lib/nw/objects/Module.hpp +++ b/lib/nw/objects/Module.hpp @@ -3,6 +3,7 @@ #include "../i18n/LocString.hpp" #include "../serialization/Serialization.hpp" #include "../util/ByteArray.hpp" +#include "../util/Variant.hpp" #include "LocalData.hpp" #include "ObjectBase.hpp" @@ -42,7 +43,7 @@ struct ModuleScripts { }; struct Module : public ObjectBase { - using AreaVariant = std::variant, Vector>; + using AreaVariant = Variant, Vector>; static constexpr int json_archive_version = 1; static constexpr ObjectType object_type = ObjectType::module; @@ -50,6 +51,7 @@ struct Module : public ObjectBase { virtual Module* as_module() override { return this; } virtual const Module* as_module() const override { return this; } + virtual void destroy() override; virtual bool instantiate() override; size_t area_count() const noexcept; diff --git a/lib/nw/objects/ObjectBase.cpp b/lib/nw/objects/ObjectBase.cpp index edbfb2df8..3dafd2f99 100644 --- a/lib/nw/objects/ObjectBase.cpp +++ b/lib/nw/objects/ObjectBase.cpp @@ -7,6 +7,10 @@ namespace nw { // == ObjectBase ============================================================== // ============================================================================ +void ObjectBase::destroy() +{ +} + EffectArray& ObjectBase::effects() { return effects_; } const EffectArray& ObjectBase::effects() const { return effects_; } diff --git a/lib/nw/objects/ObjectBase.hpp b/lib/nw/objects/ObjectBase.hpp index 1716ab513..022b2404f 100644 --- a/lib/nw/objects/ObjectBase.hpp +++ b/lib/nw/objects/ObjectBase.hpp @@ -29,9 +29,11 @@ struct ObjectBase { ObjectHandle handle() const noexcept { return handle_; } void set_handle(ObjectHandle handle) { handle_ = handle; } const EffectArray& effects() const; - virtual Versus versus_me() const { return Versus{}; } EffectArray& effects(); + virtual void destroy(); + virtual bool instantiate() = 0; virtual InternedString tag() const; + virtual Versus versus_me() const { return Versus{}; } virtual Area* as_area() { return nullptr; } virtual const Area* as_area() const { return nullptr; } @@ -59,7 +61,6 @@ struct ObjectBase { virtual const Trigger* as_trigger() const { return nullptr; } virtual Waypoint* as_waypoint() { return nullptr; } virtual const Waypoint* as_waypoint() const { return nullptr; } - virtual bool instantiate() = 0; private: ObjectHandle handle_; diff --git a/lib/nw/objects/Placeable.cpp b/lib/nw/objects/Placeable.cpp index 94e42e467..d38146ba8 100644 --- a/lib/nw/objects/Placeable.cpp +++ b/lib/nw/objects/Placeable.cpp @@ -69,6 +69,11 @@ Placeable::Placeable() inventory.owner = this; } +void Placeable::destroy() +{ + inventory.destroy(); +} + bool Placeable::instantiate() { if (instantiated_) return true; diff --git a/lib/nw/objects/Placeable.hpp b/lib/nw/objects/Placeable.hpp index 4bdf761d3..d6b6679b1 100644 --- a/lib/nw/objects/Placeable.hpp +++ b/lib/nw/objects/Placeable.hpp @@ -74,6 +74,7 @@ struct Placeable : public ObjectBase { virtual const Common* as_common() const override { return &common; } virtual Placeable* as_placeable() override { return this; } virtual const Placeable* as_placeable() const override { return this; } + virtual void destroy() override; virtual bool instantiate() override; virtual InternedString tag() const override { return common.tag; } diff --git a/rollnw-py/wrapper_objects.cpp b/rollnw-py/wrapper_objects.cpp index 91e2b00fd..ff6cae735 100644 --- a/rollnw-py/wrapper_objects.cpp +++ b/rollnw-py/wrapper_objects.cpp @@ -535,7 +535,7 @@ void init_objects_module(py::module& nw) .def("get_area", [](nw::Module& self, size_t index) { return self.get_area(index); }, py::return_value_policy::reference_internal) .def("__len__", [](const nw::Module& self) { return self.area_count(); }) .def("__iter__", [](nw::Module& self) { - auto& areas = std::get>(self.areas); + auto& areas = self.areas.as>(); return py::make_iterator(areas.begin(), areas.end()); }, py::keep_alive<0, 1>()) .def_readwrite("description", &nw::Module::description) diff --git a/tests/kernel_load_module.cpp b/tests/kernel_load_module.cpp index 5f97d45ce..8ef686392 100644 --- a/tests/kernel_load_module.cpp +++ b/tests/kernel_load_module.cpp @@ -18,6 +18,7 @@ TEST(Kernel, LoadModuleErf) auto area = mod->get_area(0); EXPECT_TRUE(area); EXPECT_TRUE(area->common.resref == "start"); + mod->destroy(); // Make sure nothing crashes, not called on unload.. nw::kernel::unload_module(); } diff --git a/tests/objects_store.cpp b/tests/objects_store.cpp index 22bfcbcd9..bb4fab0f1 100644 --- a/tests/objects_store.cpp +++ b/tests/objects_store.cpp @@ -44,7 +44,7 @@ TEST(Store, JsonDeserialize) EXPECT_TRUE(ent->blackmarket); EXPECT_EQ(ent->blackmarket_markdown, 25); EXPECT_GT(ent->inventory.weapons.items.size(), 0); - EXPECT_EQ(std::get(ent->inventory.weapons.items[0].item), "nw_wswdg001"); + EXPECT_EQ(ent->inventory.weapons.items[0].item.as(), "nw_wswdg001"); } TEST(Store, JsonRoundTrip) @@ -74,7 +74,7 @@ TEST(Store, GffDeserialize) EXPECT_TRUE(ent->blackmarket); EXPECT_EQ(ent->blackmarket_markdown, 25); EXPECT_TRUE(ent->inventory.weapons.items.size() > 0); - EXPECT_EQ(std::get(ent->inventory.weapons.items[0].item)->common.resref, "nw_wswdg001"); + EXPECT_EQ(ent->inventory.weapons.items[0].item.as()->common.resref, "nw_wswdg001"); } TEST(Store, GffRoundTrip)