#include "SObject.h" #include "GarbageCollection.h" #include "Util.h" #include "AssetResolver.h" #include "Logger.h" #include namespace sh::core { template<> SH_CORE_API auto DeserializeProperty(const core::Json& json, const std::string& key, SObject*& value) -> bool { if (json.contains(key)) { const std::string& uuidStr = json[key].get_ref(); value = SObject::GetSObjectUsingResolver(core::UUID{ uuidStr }); if (value == nullptr) return false; return true; } return false; } SH_CORE_API SObject::SObject() : bPendingKill(false), uuid(UUID::Generate()), name("Unknown") { } SH_CORE_API SObject::SObject(const SObject& other) : uuid(UUID::Generate()), name(other.name) { } SH_CORE_API SObject::SObject(SObject&& other) noexcept : bPendingKill(other.bPendingKill), uuid(std::move(other.uuid)), name(std::move(other.name)), onDestroy(std::move(other.onDestroy)) { other.bPendingKill = true; if (other.bMark.test_and_set(std::memory_order::memory_order_acquire)) bMark.test_and_set(std::memory_order::memory_order_relaxed); } SH_CORE_API SObject::~SObject() { SObjectManager::GetInstance()->UnRegisterSObject(this); } SH_CORE_API auto SObject::operator=(SObject&& other) noexcept -> SObject& { uuid = std::move(other.uuid); name = std::move(other.name); onDestroy = std::move(other.onDestroy); bPendingKill = other.bPendingKill; other.SetUUID(core::UUID::Generate()); return *this; } SH_CORE_API void SObject::RegisterToManager(SObject* ptr) { SObjectManager::GetInstance()->RegisterSObject(ptr); } auto SObject::operator new(std::size_t size) -> void* { return ::operator new(size); } auto SObject::operator new(std::size_t size, void* ptr) -> void* { return ::operator new(size, ptr); } void SObject::operator delete(void* ptr) { ::operator delete(ptr); } void SObject::operator delete(void* ptr, std::size_t size) { ::operator delete(ptr, size); } SH_CORE_API void SObject::OnPropertyChanged(const reflection::Property& prop) { } SH_CORE_API void SObject::Destroy() { static GarbageCollection& gc = *GarbageCollection::GetInstance(); if (bPendingKill) return; bPendingKill = true; gc.RemoveRootSet(this); OnDestroy(); } SH_CORE_API void SObject::OnDestroy() { static GarbageCollection& gc = *GarbageCollection::GetInstance(); gc.AddToPendingKillList(this); onDestroy.Notify(this); } SH_CORE_API void SObject::SetName(std::string_view name) { this->name = Name{ name }; } SH_CORE_API void SObject::SetName(const core::Name& name) { this->name = name; } SH_CORE_API void SObject::SetName(core::Name&& name) { this->name = std::move(name); } SH_CORE_API auto SObject::SetUUID(const UUID& uuid) -> bool { SObjectManager& objManager = *SObjectManager::GetInstance(); if (objManager.GetSObject(uuid) != nullptr) { //SH_ERROR_FORMAT("UUID({}) already exists!", uuid.ToString()); return false; } objManager.UnRegisterSObject(this); this->uuid = uuid; RegisterToManager(this); return true; } SH_CORE_API auto SObject::Serialize() const -> core::Json { const reflection::STypeInfo* stypeInfo = &GetType(); core::Json mainJson; mainJson["version"] = 1; mainJson["type"] = stypeInfo->name.ToString(); mainJson["uuid"] = uuid.ToString(); mainJson["name"] = name.ToString(); while (stypeInfo) { core::Json json{}; for (auto& prop :stypeInfo->GetProperties()) { if (prop->bNoSaveProperty) continue; const reflection::TypeInfo& propType = prop->type; const core::Name& name = prop->GetName(); if (propType == core::reflection::GetType() || prop->isEnum) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (propType == core::reflection::GetType()) core::SerializeProperty(json, name, *prop->Get(*this)); else if (prop->isSObjectPointer) { auto ptr = prop->Get(*this); if (core::IsValid(*ptr)) core::SerializeProperty(json, name, *ptr); } else if (prop->isSObjectPointerContainer) { json[name] = core::Json::array(); for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) { assert(!it.IsPair()); if (!it.IsPair()) // pair인 경우는 map, unordered_map { auto ptr = it.Get(); if (core::IsValid(*ptr)) json[name].push_back((*ptr)->GetUUID().ToString()); } } } else if (prop->isContainer) { json[name] = core::Json::array(); if (*prop->containerElementType == core::reflection::GetType() || *prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType() || *prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } else if (*prop->containerElementType == core::reflection::GetType()) { for (auto it = prop->Begin(*this); it != prop->End(*this); ++it) json[name].push_back(*it.Get()); } } } if (!json.empty()) mainJson[stypeInfo->name] = std::move(json); stypeInfo = stypeInfo->super; } return mainJson; } SH_CORE_API void SObject::Deserialize(const Json& json) { const reflection::STypeInfo* stypeInfo = &GetType(); if (stypeInfo->name != json["type"].get_ref()) return; if (json.contains("uuid")) { UUID newUUID{ json["uuid"].get_ref() }; if (uuid != newUUID) { auto objPtr = core::SObjectManager::GetInstance()->GetSObject(newUUID); if (objPtr != nullptr && objPtr->IsPendingKill()) objPtr->SetUUID(core::UUID::Generate()); bool success = SetUUID(std::move(newUUID)); // assert(success); } } if (json.contains("name")) SetName(json["name"].get_ref()); while (stypeInfo) { if (!json.contains(stypeInfo->name)) { stypeInfo = stypeInfo->super; continue; } const core::Json& subJson = json[stypeInfo->name]; for (auto& prop : stypeInfo->GetProperties()) { if (prop->bNoSaveProperty) continue; const reflection::TypeInfo& propType = prop->type; const core::Name& name = prop->GetName(); // 해당 타입들에 대해 if문으로 검사 후 역직렬화 // 코드에서 똥 냄새 난다 using Types = std::tuple; bool bDone = false; std::apply( [&](auto&&... args) { ( [&](auto t) { using Type = std::decay_t; if (!bDone && propType == core::reflection::GetType()) { if (core::DeserializeProperty(subJson, name, *prop->Get(*this))) OnPropertyChanged(*prop.get()); bDone = true; } }(std::forward(args)), ... ); }, Types{} ); if (bDone) continue; if (prop->isEnum) { if (core::DeserializeProperty(subJson, name, *prop->Get(*this))) OnPropertyChanged(*prop.get()); } else if (prop->isSObjectPointer) { if (core::DeserializeProperty(subJson, name, *prop->Get(*this))) OnPropertyChanged(*prop.get()); } else if (prop->isSObjectPointerContainer) { if (subJson.contains(name) && subJson[name].is_array()) { prop->ClearContainer(*this); for (auto& uuidStr : subJson[name]) prop->InsertToContainer(*this, GetSObjectUsingResolver(core::UUID{ uuidStr.get_ref() })); OnPropertyChanged(*prop.get()); } } else if (prop->isContainer) { if (!subJson.contains(name) || !subJson[name].is_array()) continue; prop->ClearContainer(*this); // c++20이면 람다식에도 템플릿이 된다던데.. auto insertFn = [&](auto type) { for (auto& arr : subJson[name]) prop->InsertToContainer(*this, arr.get()); }; if (*prop->containerElementType == core::reflection::GetType() || *prop->containerElementType == core::reflection::GetType()) insertFn(0); else if (*prop->containerElementType == core::reflection::GetType() || *prop->containerElementType == core::reflection::GetType()) insertFn((int16_t)0); else if (*prop->containerElementType == core::reflection::GetType()) insertFn(0.f); else if (*prop->containerElementType == core::reflection::GetType()) insertFn(0.0); else if (*prop->containerElementType == core::reflection::GetType()) insertFn(false); else if (*prop->containerElementType == core::reflection::GetType()) insertFn('\0'); else if (*prop->containerElementType == core::reflection::GetType()) insertFn(std::string{}); OnPropertyChanged(*prop.get()); } }//for stypeInfo = stypeInfo->super; } } SH_CORE_API auto SObject::GetSObjectUsingResolver(const core::UUID& uuid) -> core::SObject* { static SObjectManager* objectManager = SObjectManager::GetInstance(); SObject* objPtr = objectManager->GetSObject(uuid); if (objPtr == nullptr) { static auto& resolver = AssetResolverRegistry::GetResolver(); if (resolver) objPtr = resolver(uuid); else return nullptr; } return objPtr; } }//namespace