Skip to content

Commit

Permalink
feat: add NetImmerse.SetNodeScale Papyrus native (skyrim-multiplayer#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Pospelove authored Apr 5, 2024
1 parent 043781f commit 988c2b0
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 5 deletions.
1 change: 1 addition & 0 deletions skymp5-client/src/services/messages/createActorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface CreateActorMessageAdditionalProps {
isOpen?: boolean;
isHarvested?: boolean;
setNodeTextureSet?: Record<string, number>;
setNodeScale?: Record<string, number>;
disabled?: boolean;
lastAnimation?: string;
displayName?: string;
Expand Down
15 changes: 13 additions & 2 deletions skymp5-client/src/services/services/remoteServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion skymp5-server/cpp/server_guest_lib/MpChangeForms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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<std::string, float>();
}

for (auto [key, value] : data.get_object()) {
std::string keyStr = key.data();
float valueFloat = static_cast<float>(value.get_double().value());
res.setNodeScale->emplace(keyStr, valueFloat);
}
}

if (element.at_pointer(displayName.GetData()).error() ==
simdjson::error_code::SUCCESS) {
const char* tmp;
Expand Down
5 changes: 4 additions & 1 deletion skymp5-server/cpp/server_guest_lib/MpChangeForms.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class MpChangeFormREFR
// Used for SetNodeTextureSet (node, texture set desc)
std::optional<std::map<std::string, std::string>> setNodeTextureSet;

// Used for SetNodeScale (node, scale value)
std::optional<std::map<std::string, float>> setNodeScale;

// Used for SetDisplayName (object reference)
std::optional<std::string> displayName;

Expand All @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand Down Expand Up @@ -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<std::string, std::string>();
Expand All @@ -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<std::string, float>();
}
changeForm.setNodeScale->insert_or_assign(node, scale);
});
}

void MpObjectReference::SetDisplayName(const std::string& newName)
{
EditChangeForm(
Expand Down
1 change: 1 addition & 0 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MpObjectReference*>& GetListeners() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,35 @@ VarValue PapyrusNetImmerse::SetNodeTextureSet(
VarValue PapyrusNetImmerse::SetNodeScale(
VarValue, const std::vector<VarValue>& arguments)
{
// TODO
if (arguments.size() != 4) {
throw std::runtime_error(
"PapyrusNetImmerse::SetNodeScale - expected 4 arguments");
}

MpObjectReference* ref = GetFormPtr<MpObjectReference>(arguments[0]);
const char* node = static_cast<const char*>(arguments[1]);
float scale = static_cast<float>(static_cast<double>(arguments[2]));
bool firstPerson = static_cast<bool>(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<MpActor*>(listener);
if (targetRefr) {
SpSnippet(GetName(), funcName, serializedArgs.data())
.Execute(targetRefr);
}
}

return VarValue::None();
}

Expand Down

0 comments on commit 988c2b0

Please sign in to comment.