Skip to content

Commit

Permalink
[objects] add: destroying child objects when parent is destroyed
Browse files Browse the repository at this point in the history
I.e. inventories
  • Loading branch information
jd28 committed Oct 14, 2024
1 parent 5120aa6 commit c139836
Show file tree
Hide file tree
Showing 21 changed files with 112 additions and 34 deletions.
1 change: 1 addition & 0 deletions lib/nw/kernel/Kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions lib/nw/kernel/Objects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void ObjectSystem::destroy(ObjectHandle obj)
}
objects_[idx] = new_handle;

o->destroy();
switch (new_handle.type) {
default:
break;
Expand Down
18 changes: 18 additions & 0 deletions lib/nw/objects/Area.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions lib/nw/objects/Area.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions lib/nw/objects/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ Creature::Creature()
inventory.owner = this;
}

void Creature::destroy()
{
equipment.destroy();
inventory.destroy();
}

bool Creature::instantiate()
{
if (instantiated_) return true;
Expand Down
1 change: 1 addition & 0 deletions lib/nw/objects/Creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions lib/nw/objects/Equips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Equips::Equips(Creature* owner)
{
}

void Equips::destroy()
{
for (auto& e : equips) {
if (e.is<nw::Item*>()) {
nw::kernel::objects().destroy(e.as<nw::Item*>()->handle());
}
}
}

bool Equips::instantiate()
{
int i = 0;
Expand Down
1 change: 1 addition & 0 deletions lib/nw/objects/Equips.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
36 changes: 23 additions & 13 deletions lib/nw/objects/Inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@

namespace nw {

void Inventory::destroy()
{
for (auto& it : items) {
if (!it.item.is<Item*>()) { continue; }
auto item = it.item.as<Item*>();
item->inventory.destroy();
nw::kernel::objects().destroy(item->handle());
}
}

bool Inventory::instantiate()
{
for (auto& ii : items) {
if (std::holds_alternative<Resref>(ii.item)) {
auto temp = kernel::objects().load<Item>(std::get<Resref>(ii.item).view());
for (auto& it : items) {
if (it.item.is<Resref>()) {
auto temp = kernel::objects().load<Item>(it.item.as<Resref>().view());
if (temp) {
ii.item = temp;
it.item = temp;
} else {
LOG_F(WARNING, "failed to instantiate item, perhaps you're missing '{}.uti'?",
std::get<Resref>(ii.item));
it.item.as<Resref>());
}
}
}
Expand Down Expand Up @@ -77,15 +87,15 @@ nlohmann::json Inventory::to_json(SerializationProfile profile) const
}

payload["position"] = {it.pos_x, it.pos_y};
if (std::holds_alternative<Item*>(it.item)) {
if (it.item.is<Item*>()) {
if (SerializationProfile::blueprint == profile) {
payload["item"] = std::get<Item*>(it.item)->common.resref;
payload["item"] = it.item.as<Item*>()->common.resref;
} else {
Item::serialize(std::get<Item*>(it.item), payload["item"], profile);
Item::serialize(it.item.as<Item*>(), payload["item"], profile);
}
} else {
if (SerializationProfile::blueprint == profile) {
payload["item"] = std::get<Resref>(it.item);
payload["item"] = it.item.as<Resref>();
}
}
}
Expand Down Expand Up @@ -148,13 +158,13 @@ bool serialize(const Inventory& self, GffBuilderStruct& archive, SerializationPr
}

if (SerializationProfile::blueprint == profile) {
if (std::holds_alternative<Resref>(it.item)) {
str.add_field("InventoryRes", std::get<Resref>(it.item));
if (it.item.is<Resref>()) {
str.add_field("InventoryRes", it.item.as<Resref>());
} else {
str.add_field("InventoryRes", std::get<Item*>(it.item)->common.resref);
str.add_field("InventoryRes", it.item.as<Item*>()->common.resref);
}
} else {
serialize(std::get<Item*>(it.item), str, profile);
serialize(it.item.as<Item*>(), str, profile);
}
}
return true;
Expand Down
5 changes: 3 additions & 2 deletions lib/nw/objects/Inventory.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#pragma once

#include "../serialization/Serialization.hpp"
#include "../util/Variant.hpp"
#include "ObjectBase.hpp"

#include <cstdint>
#include <variant>

namespace nw {

Expand All @@ -14,7 +14,7 @@ struct InventoryItem {
bool infinite = false;
uint16_t pos_x;
uint16_t pos_y;
std::variant<Resref, Item*> item;
Variant<Resref, Item*> item;
};

struct Inventory {
Expand All @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions lib/nw/objects/Item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Item::Item()
inventory.owner = this;
}

void Item::destroy()
{
inventory.destroy();
}

bool Item::instantiate()
{
if (instantiated_) { return true; }
Expand Down
1 change: 1 addition & 0 deletions lib/nw/objects/Item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
35 changes: 22 additions & 13 deletions lib/nw/objects/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,36 +64,45 @@ nlohmann::json ModuleScripts::to_json() const

size_t Module::area_count() const noexcept
{
if (std::holds_alternative<Vector<Area*>>(areas)) {
return std::get<Vector<Area*>>(areas).size();
if (areas.is<Vector<Area*>>()) {
return areas.as<Vector<Area*>>().size();
}
return 0;
}

Area* Module::get_area(size_t index)
{
if (std::holds_alternative<Vector<Area*>>(areas) && index < area_count()) {
return std::get<Vector<Area*>>(areas)[index];
if (areas.is<Vector<Area*>>() && index < area_count()) {
return areas.as<Vector<Area*>>()[index];
}

return nullptr;
}

const Area* Module::get_area(size_t index) const
{
if (std::holds_alternative<Vector<Area*>>(areas) && index < area_count()) {
return std::get<Vector<Area*>>(areas)[index];
if (areas.is<Vector<Area*>>() && index < area_count()) {
return areas.as<Vector<Area*>>()[index];
}

return nullptr;
}

void Module::destroy()
{
if (areas.is<Vector<Area*>>()) {
for (auto it : areas.as<Vector<Area*>>()) {
nw::kernel::objects().destroy(it->handle());
}
}
}

bool Module::instantiate()
{
if (instantiated_) return true;
LOG_F(INFO, "instantiating module");

auto& area_list = std::get<Vector<Resref>>(areas);
auto& area_list = areas.as<Vector<Resref>>();
Vector<Area*> area_objects;
area_objects.reserve(area_list.size());
for (auto& area : area_list) {
Expand Down Expand Up @@ -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<Vector<Resref>>(obj->areas)) {
archive["areas"] = std::get<Vector<Resref>>(obj->areas);
if (obj->areas.is<Vector<Resref>>()) {
archive["areas"] = obj->areas.as<Vector<Resref>>();
} else {
auto& area_list = archive["areas"] = nlohmann::json::array();
for (const auto area : std::get<Vector<Area*>>(obj->areas)) {
for (const auto area : obj->areas.as<Vector<Area*>>()) {
area_list.push_back(area->common.resref);
}
}
Expand Down Expand Up @@ -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<Vector<Resref>>(obj->areas)) {
for (const auto& area : std::get<Vector<Resref>>(obj->areas)) {
if (obj->areas.is<Vector<Resref>>()) {
for (const auto& area : obj->areas.as<Vector<Resref>>()) {
area_list.push_back(6).add_field("Area_Name", area);
}
} else {
for (const auto& area : std::get<Vector<Area*>>(obj->areas)) {
for (const auto& area : obj->areas.as<Vector<Area*>>()) {
area_list.push_back(6).add_field("Area_Name", area->common.resref);
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/nw/objects/Module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -42,14 +43,15 @@ struct ModuleScripts {
};

struct Module : public ObjectBase {
using AreaVariant = std::variant<Vector<Resref>, Vector<Area*>>;
using AreaVariant = Variant<Vector<Resref>, Vector<Area*>>;

static constexpr int json_archive_version = 1;
static constexpr ObjectType object_type = ObjectType::module;
static constexpr ResourceType::type restype = ResourceType::ifo;

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;
Expand Down
4 changes: 4 additions & 0 deletions lib/nw/objects/ObjectBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ namespace nw {
// == ObjectBase ==============================================================
// ============================================================================

void ObjectBase::destroy()
{
}

EffectArray& ObjectBase::effects() { return effects_; }
const EffectArray& ObjectBase::effects() const { return effects_; }

Expand Down
5 changes: 3 additions & 2 deletions lib/nw/objects/ObjectBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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_;
Expand Down
5 changes: 5 additions & 0 deletions lib/nw/objects/Placeable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ Placeable::Placeable()
inventory.owner = this;
}

void Placeable::destroy()
{
inventory.destroy();
}

bool Placeable::instantiate()
{
if (instantiated_) return true;
Expand Down
1 change: 1 addition & 0 deletions lib/nw/objects/Placeable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
2 changes: 1 addition & 1 deletion rollnw-py/wrapper_objects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::vector<nw::Area*>>(self.areas);
auto& areas = self.areas.as<std::vector<nw::Area*>>();
return py::make_iterator(areas.begin(), areas.end()); }, py::keep_alive<0, 1>())

.def_readwrite("description", &nw::Module::description)
Expand Down
1 change: 1 addition & 0 deletions tests/kernel_load_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
Loading

0 comments on commit c139836

Please sign in to comment.