Skip to content

Commit

Permalink
[Editor] Add support for editing entity properties through the server
Browse files Browse the repository at this point in the history
  • Loading branch information
OrfeasZ committed Apr 27, 2024
1 parent 8240d58 commit 0c982d4
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 32 deletions.
4 changes: 2 additions & 2 deletions Mods/DebugMod/Src/DebugMod.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ class DebugMod : public IPluginInterface
ZActor* m_NPCTracked = nullptr;
bool m_TrackCamActive = false;
ZEntityRef m_PlayerCam = nullptr;
TEntityRef<ZCameraEntity> m_TrackCam = TEntityRef<ZCameraEntity>(nullptr);
TEntityRef<IRenderDestinationEntity> m_RenderDest = TEntityRef<IRenderDestinationEntity>(nullptr);
TEntityRef<ZCameraEntity> m_TrackCam {};
TEntityRef<IRenderDestinationEntity> m_RenderDest {};

private:
void EnableTrackCam();
Expand Down
65 changes: 43 additions & 22 deletions Mods/Editor/Src/Components/EditorAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ZEntityImpl> 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<ZEntityImpl> 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.");
}
Expand Down
14 changes: 13 additions & 1 deletion Mods/Editor/Src/Components/EntityTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,20 @@ void Editor::UpdateEntities() {
s_NodeQueue.emplace(s_BpFactory, s_BrickEnt);
}

auto s_SceneFactory = reinterpret_cast<ZTemplateEntityBlueprintFactory*>(s_SceneEnt.GetBlueprintFactory());

if (s_SceneEnt.GetOwningEntity()) {
s_SceneFactory = reinterpret_cast<ZTemplateEntityBlueprintFactory*>(s_SceneEnt.GetOwningEntity().GetBlueprintFactory());
}

// Create the root scene node.
auto s_SceneNode = std::make_shared<EntityTreeNode>("Scene Root", 0, 0, s_SceneEnt);
auto s_SceneNode = std::make_shared<EntityTreeNode>(
"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);

Expand Down
58 changes: 51 additions & 7 deletions Mods/Editor/Src/EditorServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void*>(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<TEntityRef<ZEntityImpl>*>(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<ZTemplateEntityBlueprintFactory*>(s_EntityData->m_ref.GetBlueprintFactory());

if (s_EntityData->m_ref.GetOwningEntity()) {
s_Factory = reinterpret_cast<ZTemplateEntityBlueprintFactory*>(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) {
Expand Down
2 changes: 2 additions & 0 deletions Mods/Editor/Src/EditorServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ 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);
static SMatrix ReadTransform(simdjson::ondemand::value p_Transform);
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<std::string> p_IgnoreClient);

private:
Expand Down
28 changes: 28 additions & 0 deletions Mods/Editor/editor-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions Mods/Editor/editor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
7 changes: 7 additions & 0 deletions ZHMModSDK/Include/Glacier/ZEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,13 @@ template <typename T>
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;

Expand Down

0 comments on commit 0c982d4

Please sign in to comment.