diff --git a/Mods/DebugMod/Src/DebugMod.h b/Mods/DebugMod/Src/DebugMod.h index aad22753..9461d22e 100644 --- a/Mods/DebugMod/Src/DebugMod.h +++ b/Mods/DebugMod/Src/DebugMod.h @@ -127,8 +127,8 @@ class DebugMod : public IPluginInterface ZActor* m_NPCTracked = nullptr; bool m_TrackCamActive = false; ZEntityRef m_PlayerCam = nullptr; - TEntityRef m_TrackCam = TEntityRef(nullptr); - TEntityRef m_RenderDest = TEntityRef(nullptr); + TEntityRef m_TrackCam {}; + TEntityRef m_RenderDest {}; private: void EnableTrackCam(); diff --git a/Mods/Editor/Src/Components/EditorAPI.cpp b/Mods/Editor/Src/Components/EditorAPI.cpp index fb54a595..8c8e4bc2 100644 --- a/Mods/Editor/Src/Components/EditorAPI.cpp +++ b/Mods/Editor/Src/Components/EditorAPI.cpp @@ -137,30 +137,51 @@ void Editor::SetEntityProperty(EntitySelector p_Selector, uint32_t p_PropertyId, const auto s_PropertyInfo = s_Property->m_pType->getPropertyInfo(); - const uint16_t s_TypeSize = s_PropertyInfo->m_pType->typeInfo()->m_nTypeSize; - const uint16_t s_TypeAlignment = s_PropertyInfo->m_pType->typeInfo()->m_nTypeAlignment; - const std::string s_TypeName = s_PropertyInfo->m_pType->typeInfo()->m_pTypeName; - - void* s_Data = (*Globals::MemoryManager)->m_pNormalAllocator->AllocateAligned( - s_TypeSize, - s_TypeAlignment - ); - - const bool s_Success = HM3_JsonToGameStruct( - s_TypeName.c_str(), - p_JsonValue.data(), - p_JsonValue.size(), - s_Data, - s_TypeSize - ); - - if (!s_Success) { + if (s_PropertyInfo->m_pType->typeInfo()->isEntity()) { + if (p_JsonValue == "null") { + TEntityRef s_EntityRef; + OnSetPropertyValue(s_Entity, p_PropertyId, ZObjectRef(s_PropertyInfo->m_pType, &s_EntityRef), std::move(p_ClientId)); + } else { + // Parse EntitySelector + simdjson::ondemand::parser s_Parser; + const auto s_EntitySelectorJson = simdjson::padded_string(p_JsonValue); + simdjson::ondemand::document s_EntitySelectorMsg = s_Parser.iterate(s_EntitySelectorJson); + + const auto s_EntitySelector = EditorServer::ReadEntitySelector(s_EntitySelectorMsg); + + if (const auto s_TargetEntity = FindEntity(s_EntitySelector)) { + TEntityRef s_EntityRef(s_TargetEntity); + OnSetPropertyValue(s_Entity, p_PropertyId, ZObjectRef(s_PropertyInfo->m_pType, &s_EntityRef), std::move(p_ClientId)); + } else { + throw std::runtime_error("Could not find entity for the given selector."); + } + } + } else { + const uint16_t s_TypeSize = s_PropertyInfo->m_pType->typeInfo()->m_nTypeSize; + const uint16_t s_TypeAlignment = s_PropertyInfo->m_pType->typeInfo()->m_nTypeAlignment; + const std::string s_TypeName = s_PropertyInfo->m_pType->typeInfo()->m_pTypeName; + + void* s_Data = (*Globals::MemoryManager)->m_pNormalAllocator->AllocateAligned( + s_TypeSize, + s_TypeAlignment + ); + + const bool s_Success = HM3_JsonToGameStruct( + s_TypeName.c_str(), + p_JsonValue.data(), + p_JsonValue.size(), + s_Data, + s_TypeSize + ); + + if (!s_Success) { + (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); + throw std::runtime_error("Unable to convert JSON to game struct."); + } + + OnSetPropertyValue(s_Entity, p_PropertyId, ZObjectRef(s_PropertyInfo->m_pType, s_Data), std::move(p_ClientId)); (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); - throw std::runtime_error("Unable to convert JSON to game struct."); } - - OnSetPropertyValue(s_Entity, p_PropertyId, ZObjectRef(s_PropertyInfo->m_pType, s_Data), std::move(p_ClientId)); - (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); } else { throw std::runtime_error("Could not find entity for the given selector."); } diff --git a/Mods/Editor/Src/Components/EntityTree.cpp b/Mods/Editor/Src/Components/EntityTree.cpp index bd1ec51a..2189fda0 100644 --- a/Mods/Editor/Src/Components/EntityTree.cpp +++ b/Mods/Editor/Src/Components/EntityTree.cpp @@ -52,8 +52,20 @@ void Editor::UpdateEntities() { s_NodeQueue.emplace(s_BpFactory, s_BrickEnt); } + auto s_SceneFactory = reinterpret_cast(s_SceneEnt.GetBlueprintFactory()); + + if (s_SceneEnt.GetOwningEntity()) { + s_SceneFactory = reinterpret_cast(s_SceneEnt.GetOwningEntity().GetBlueprintFactory()); + } + // Create the root scene node. - auto s_SceneNode = std::make_shared("Scene Root", 0, 0, s_SceneEnt); + auto s_SceneNode = std::make_shared( + "Scene Root", + s_SceneEnt->GetType()->m_nEntityId, + s_SceneFactory->m_ridResource, + s_SceneEnt + ); + s_NodeMap.emplace(s_SceneEnt, s_SceneNode); s_NodeMap.emplace(ZEntityRef(), s_SceneNode); diff --git a/Mods/Editor/Src/EditorServer.cpp b/Mods/Editor/Src/EditorServer.cpp index 056dbc49..435f9365 100644 --- a/Mods/Editor/Src/EditorServer.cpp +++ b/Mods/Editor/Src/EditorServer.cpp @@ -819,16 +819,60 @@ void EditorServer::WriteProperty(std::ostream& p_Stream, ZEntityRef p_Entity, ZE else s_PropertyInfo->m_pType->typeInfo()->m_pTypeFunctions->copyConstruct(s_Data, reinterpret_cast(s_PropertyAddress)); - auto s_JsonProperty = HM3_GameStructToJson(s_TypeName.c_str(), s_Data, s_TypeSize); - (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); + if (s_PropertyInfo->m_pType->typeInfo() && s_PropertyInfo->m_pType->typeInfo()->isEntity()) { + auto* s_EntityData = reinterpret_cast*>(s_Data); - if (!s_JsonProperty) { - p_Stream << "null" << "}"; - return; + if (!s_EntityData || !*s_EntityData) { + (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); + p_Stream << "null" << "}"; + return; + } + + p_Stream << "{" << write_json("id") << ":" << write_json(std::format("{:016x}", s_EntityData->m_ref->GetType()->m_nEntityId)) << ","; + + auto s_Factory = reinterpret_cast(s_EntityData->m_ref.GetBlueprintFactory()); + + if (s_EntityData->m_ref.GetOwningEntity()) { + s_Factory = reinterpret_cast(s_EntityData->m_ref.GetOwningEntity().GetBlueprintFactory()); + } + + if (s_Factory) { + // This is also probably wrong. + auto s_Index = s_Factory->GetSubEntityIndex( s_EntityData->m_ref->GetType()->m_nEntityId); + + if (s_Index != -1) { + const auto s_Name = s_Factory->m_pTemplateEntityBlueprint->subEntities[s_Index].entityName; + p_Stream << write_json("name") << ":" << write_json(s_Name) << ","; + } + + p_Stream << write_json("source") << ":" << write_json("game") << ","; + + p_Stream << write_json("tblu") << ":" << write_json(std::format("{:016X}", s_Factory->m_ridResource.GetID())) << ","; + } + else { + // TODO: Name. + p_Stream << write_json("source") << ":" << write_json("editor") << ","; + } + + // Write type and interfaces. + auto s_Interfaces = (*s_EntityData->m_ref->GetType()->m_pInterfaces); + + p_Stream << write_json("type") << ":" << write_json(s_Interfaces[0].m_pTypeId->typeInfo()->m_pTypeName) << "}}"; + + (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); } + else { + auto s_JsonProperty = HM3_GameStructToJson(s_TypeName.c_str(), s_Data, s_TypeSize); + (*Globals::MemoryManager)->m_pNormalAllocator->Free(s_Data); + + if (!s_JsonProperty) { + p_Stream << "null" << "}"; + return; + } - p_Stream << std::string_view(s_JsonProperty->JsonData, s_JsonProperty->StrSize) << "}"; - HM3_FreeJsonString(s_JsonProperty); + p_Stream << std::string_view(s_JsonProperty->JsonData, s_JsonProperty->StrSize) << "}"; + HM3_FreeJsonString(s_JsonProperty); + } } EntitySelector EditorServer::ReadEntitySelector(simdjson::ondemand::value p_Selector) { diff --git a/Mods/Editor/Src/EditorServer.h b/Mods/Editor/Src/EditorServer.h index ffd55ec6..10e3feb4 100644 --- a/Mods/Editor/Src/EditorServer.h +++ b/Mods/Editor/Src/EditorServer.h @@ -55,6 +55,7 @@ class EditorServer { static void WritePropertyName(std::ostream& p_Stream, ZEntityProperty* p_Property); static void WriteProperty(std::ostream& p_Stream, ZEntityRef p_Entity, ZEntityProperty* p_Property); +public: static EntitySelector ReadEntitySelector(simdjson::ondemand::value p_Selector); static SVector3 ReadVector3(simdjson::ondemand::value p_Vector); static EulerAngles ReadRotation(simdjson::ondemand::value p_Rotation); @@ -62,6 +63,7 @@ class EditorServer { static ZRuntimeResourceID ReadResourceId(simdjson::ondemand::value p_ResourceId); static uint64_t ReadEntityId(simdjson::ondemand::value p_EntityId); +private: void PublishEvent(const std::string& p_Event, std::optional p_IgnoreClient); private: diff --git a/Mods/Editor/editor-client.ts b/Mods/Editor/editor-client.ts index 276e07e3..a30ec421 100644 --- a/Mods/Editor/editor-client.ts +++ b/Mods/Editor/editor-client.ts @@ -67,6 +67,34 @@ ws.on("message", (message) => { return; } + + if (msg.type === "entityDetails") { + console.log("Received details for an entity:", msg.entity); + + if (msg.entity.properties.m_eidParent.data !== null) { + console.log("Setting parent property to null and getting details again."); + + sendMessage({ + type: "setEntityProperty", + entity: { + id: msg.entity.id, + tblu: msg.entity.tblu, + }, + property: "m_eidParent", + value: null, + }); + + sendMessage({ + type: "getEntityDetails", + entity: { + id: msg.entity.id, + tblu: msg.entity.tblu, + }, + }); + } + + return; + } } catch (e) { console.error("Encountered error while parsing message from editor:", e, message.data); } diff --git a/Mods/Editor/editor.d.ts b/Mods/Editor/editor.d.ts index 808a2f03..5ad2b6de 100644 --- a/Mods/Editor/editor.d.ts +++ b/Mods/Editor/editor.d.ts @@ -171,6 +171,7 @@ declare namespace EditorRequests { property: string | number; // The new value to give to the property, as it would appear in RT JSON. + // For entity properties, provide an [EntitySelector] object or [null]. value: unknown; } diff --git a/ZHMModSDK/Include/Glacier/ZEntity.h b/ZHMModSDK/Include/Glacier/ZEntity.h index d93f44fc..b0575bea 100644 --- a/ZHMModSDK/Include/Glacier/ZEntity.h +++ b/ZHMModSDK/Include/Glacier/ZEntity.h @@ -524,6 +524,13 @@ template class TEntityRef { public: + TEntityRef() = default; + + explicit TEntityRef(ZEntityRef p_Ref) : m_ref(p_Ref), m_pInterfaceRef(p_Ref.GetEntity()) + { + + } + ZEntityRef m_ref; T* m_pInterfaceRef = nullptr;