From 988c2b0a00c799f2f4aea4d22a98a542a2c07309 Mon Sep 17 00:00:00 2001 From: Leonid Pospelov Date: Fri, 5 Apr 2024 19:52:16 +0500 Subject: [PATCH] feat: add NetImmerse.SetNodeScale Papyrus native (#1903) --- .../services/messages/createActorMessage.ts | 1 + .../src/services/services/remoteServer.ts | 15 ++++++++-- .../cpp/server_guest_lib/MpChangeForms.cpp | 23 +++++++++++++- .../cpp/server_guest_lib/MpChangeForms.h | 5 +++- .../server_guest_lib/MpObjectReference.cpp | 26 ++++++++++++++++ .../cpp/server_guest_lib/MpObjectReference.h | 1 + .../script_classes/PapyrusNetImmerse.cpp | 30 ++++++++++++++++++- 7 files changed, 96 insertions(+), 5 deletions(-) diff --git a/skymp5-client/src/services/messages/createActorMessage.ts b/skymp5-client/src/services/messages/createActorMessage.ts index 07f639c5ac..2e5e51d272 100644 --- a/skymp5-client/src/services/messages/createActorMessage.ts +++ b/skymp5-client/src/services/messages/createActorMessage.ts @@ -28,6 +28,7 @@ export interface CreateActorMessageAdditionalProps { isOpen?: boolean; isHarvested?: boolean; setNodeTextureSet?: Record; + setNodeScale?: Record; disabled?: boolean; lastAnimation?: string; displayName?: string; diff --git a/skymp5-client/src/services/services/remoteServer.ts b/skymp5-client/src/services/services/remoteServer.ts index e2da461254..e4d86030d4 100644 --- a/skymp5-client/src/services/services/remoteServer.ts +++ b/skymp5-client/src/services/services/remoteServer.ts @@ -257,6 +257,17 @@ export class RemoteServer extends ClientListener { !!msg.props['isHarvested'], ); + // TODO: move to a separate module + if (msg.props.setNodeScale) { + const setNodeScale = msg.props.setNodeScale; + for (const key in setNodeScale) { + const scale = setNodeScale[key]; + const firstPerson = false; + this.sp.NetImmerse.setNodeScale(refr, key, scale, firstPerson); + logTrace(this, refr.getFormID().toString(16), `Applied node scale`, scale, `to`, key); + } + } + // TODO: move to a separate module if (msg.props.setNodeTextureSet) { const setNodeTextureSet = msg.props.setNodeTextureSet; @@ -267,9 +278,9 @@ export class RemoteServer extends ClientListener { const textureSet = this.sp.TextureSet.from(Game.getFormEx(textureSetId)); if (textureSet !== null) { this.sp.NetImmerse.setNodeTextureSet(refr, key, textureSet, firstPerson); - logTrace(this, `Applied texture set`, textureSetId.toString(16), `to`, key); + logTrace(this, refr.getFormID().toString(16), `Applied texture set`, textureSetId.toString(16), `to`, key); } else { - logError(this, `Failed to apply texture set`, textureSetId.toString(16), `to`, key); + logError(this, refr.getFormID().toString(16), `Failed to apply texture set`, textureSetId.toString(16), `to`, key); } } } diff --git a/skymp5-server/cpp/server_guest_lib/MpChangeForms.cpp b/skymp5-server/cpp/server_guest_lib/MpChangeForms.cpp index 53fd43e132..b741ad3702 100644 --- a/skymp5-server/cpp/server_guest_lib/MpChangeForms.cpp +++ b/skymp5-server/cpp/server_guest_lib/MpChangeForms.cpp @@ -88,6 +88,10 @@ nlohmann::json MpChangeForm::ToJson(const MpChangeForm& changeForm) res["setNodeTextureSet"] = *changeForm.setNodeTextureSet; } + if (changeForm.setNodeScale.has_value()) { + res["setNodeScale"] = *changeForm.setNodeScale; + } + if (changeForm.displayName.has_value()) { res["displayName"] = *changeForm.displayName; } @@ -113,7 +117,8 @@ MpChangeForm MpChangeForm::JsonToChangeForm(simdjson::dom::element& element) spawnPointCellOrWorldDesc("spawnPoint_cellOrWorldDesc"), spawnDelay("spawnDelay"), effects("effects"), templateChain("templateChain"), lastAnimation("lastAnimation"), - setNodeTextureSet("setNodeTextureSet"), displayName("displayName"); + setNodeTextureSet("setNodeTextureSet"), setNodeScale("setNodeScale"), + displayName("displayName"); MpChangeForm res; ReadEx(element, recType, &res.recType); @@ -253,6 +258,22 @@ MpChangeForm MpChangeForm::JsonToChangeForm(simdjson::dom::element& element) } } + if (element.at_pointer(setNodeScale.GetData()).error() == + simdjson::error_code::SUCCESS) { + simdjson::dom::element data; + ReadEx(element, setNodeScale, &data); + + if (res.setNodeScale == std::nullopt) { + res.setNodeScale = std::map(); + } + + for (auto [key, value] : data.get_object()) { + std::string keyStr = key.data(); + float valueFloat = static_cast(value.get_double().value()); + res.setNodeScale->emplace(keyStr, valueFloat); + } + } + if (element.at_pointer(displayName.GetData()).error() == simdjson::error_code::SUCCESS) { const char* tmp; diff --git a/skymp5-server/cpp/server_guest_lib/MpChangeForms.h b/skymp5-server/cpp/server_guest_lib/MpChangeForms.h index 96cb34015c..9c5f810b33 100644 --- a/skymp5-server/cpp/server_guest_lib/MpChangeForms.h +++ b/skymp5-server/cpp/server_guest_lib/MpChangeForms.h @@ -107,6 +107,9 @@ class MpChangeFormREFR // Used for SetNodeTextureSet (node, texture set desc) std::optional> setNodeTextureSet; + // Used for SetNodeScale (node, scale value) + std::optional> setNodeScale; + // Used for SetDisplayName (object reference) std::optional displayName; @@ -126,7 +129,7 @@ class MpChangeFormREFR count, isRaceMenuOpen, isDead, consoleCommandsAllowed, appearanceDump, equipmentDump, actorValues.ToTuple(), spawnPoint, dynamicFields, spawnDelay, learnedSpells, templateChain, lastAnimation, - setNodeTextureSet, displayName); + setNodeTextureSet, setNodeScale, displayName); } static nlohmann::json ToJson(const MpChangeFormREFR& changeForm); diff --git a/skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp b/skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp index e0a7a2ce6d..22701f51a7 100644 --- a/skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp +++ b/skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp @@ -307,8 +307,19 @@ void MpObjectReference::VisitProperties(const PropertiesVisitor& visitor, visitor("lastAnimation", lastAnimationAsJson.data()); } + if (ChangeForm().setNodeScale.has_value()) { + // worse performance than building json string manually but proper escaping + // TODO: consider switching to a faster JSON builder + nlohmann::json setNodeScaleAsJson; + for (auto& [key, value] : *ChangeForm().setNodeScale) { + setNodeScaleAsJson[key] = value; + } + visitor("setNodeScale", setNodeScaleAsJson.dump().data()); + } + if (ChangeForm().setNodeTextureSet.has_value()) { // worse performance than building json string manually but proper escaping + // TODO: consider switching to a faster JSON builder nlohmann::json setNodeTextureSetAsJson; for (auto& [key, value] : *ChangeForm().setNodeTextureSet) { setNodeTextureSetAsJson[key] = @@ -938,6 +949,8 @@ void MpObjectReference::SetNodeTextureSet(const std::string& node, const espm::LookupResult& textureSet, bool firstPerson) { + // This only changes var in database, SpSnippet is being sent in + // PapyrusNetImmerse.cpp EditChangeForm([&](MpChangeForm& changeForm) { if (changeForm.setNodeTextureSet == std::nullopt) { changeForm.setNodeTextureSet = std::map(); @@ -953,6 +966,19 @@ void MpObjectReference::SetNodeTextureSet(const std::string& node, }); } +void MpObjectReference::SetNodeScale(const std::string& node, float scale, + bool firstPerson) +{ + // This only changes var in database, SpSnippet is being sent in + // PapyrusNetImmerse.cpp + EditChangeForm([&](MpChangeForm& changeForm) { + if (changeForm.setNodeScale == std::nullopt) { + changeForm.setNodeScale = std::map(); + } + changeForm.setNodeScale->insert_or_assign(node, scale); + }); +} + void MpObjectReference::SetDisplayName(const std::string& newName) { EditChangeForm( diff --git a/skymp5-server/cpp/server_guest_lib/MpObjectReference.h b/skymp5-server/cpp/server_guest_lib/MpObjectReference.h index dc0863eb1b..f5c36ec9ba 100644 --- a/skymp5-server/cpp/server_guest_lib/MpObjectReference.h +++ b/skymp5-server/cpp/server_guest_lib/MpObjectReference.h @@ -148,6 +148,7 @@ class MpObjectReference void SetNodeTextureSet(const std::string& node, const espm::LookupResult& textureSet, bool firstPerson); + void SetNodeScale(const std::string& node, float scale, bool firstPerson); void SetDisplayName(const std::string& newName); const std::set& GetListeners() const; diff --git a/skymp5-server/cpp/server_guest_lib/script_classes/PapyrusNetImmerse.cpp b/skymp5-server/cpp/server_guest_lib/script_classes/PapyrusNetImmerse.cpp index f31ac02cc8..9a0730e0f9 100644 --- a/skymp5-server/cpp/server_guest_lib/script_classes/PapyrusNetImmerse.cpp +++ b/skymp5-server/cpp/server_guest_lib/script_classes/PapyrusNetImmerse.cpp @@ -45,7 +45,35 @@ VarValue PapyrusNetImmerse::SetNodeTextureSet( VarValue PapyrusNetImmerse::SetNodeScale( VarValue, const std::vector& arguments) { - // TODO + if (arguments.size() != 4) { + throw std::runtime_error( + "PapyrusNetImmerse::SetNodeScale - expected 4 arguments"); + } + + MpObjectReference* ref = GetFormPtr(arguments[0]); + const char* node = static_cast(arguments[1]); + float scale = static_cast(static_cast(arguments[2])); + bool firstPerson = static_cast(arguments[3]); + + std::ignore = firstPerson; + + if (!ref) { + throw std::runtime_error( + "PapyrusNetImmerse::SetNodeScale - ref is nullptr"); + } + + ref->SetNodeScale(node, scale, firstPerson); + + auto funcName = "SetNodeScale"; + auto serializedArgs = SpSnippetFunctionGen::SerializeArguments(arguments); + for (auto listener : ref->GetListeners()) { + auto targetRefr = dynamic_cast(listener); + if (targetRefr) { + SpSnippet(GetName(), funcName, serializedArgs.data()) + .Execute(targetRefr); + } + } + return VarValue::None(); }