Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip #2134

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

wip #2134

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions skymp5-server/cpp/addon/ScampServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,13 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)

emitter = Napi::Persistent(res.As<Napi::Object>());
emit = Napi::Persistent(emitter.Value().Get("emit").As<Napi::Function>());

auto structuredCloneValue = env.Gloval().Get("structuredClone");
if (structuredCloneValue.IsUndefined()) {
throw std::runtime_error("structuredClone is not defined");
}
structuredClone =
Napi::Persistent(structuredCloneValue.As<Napi::Function>());
} catch (std::exception& e) {
throw Napi::Error::New(info.Env(), (std::string)e.what());
}
Expand Down
2 changes: 1 addition & 1 deletion skymp5-server/cpp/addon/ScampServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class ScampServer : public Napi::ObjectWrap<ScampServer>
Napi::Env tickEnv;
Napi::ObjectReference emitter;
Napi::ObjectReference self;
Napi::FunctionReference emit;
Napi::FunctionReference emit, structuredClone;
std::shared_ptr<spdlog::logger> logger;
nlohmann::json serverSettings;
GamemodeApi::State gamemodeApiState;
Expand Down
145 changes: 137 additions & 8 deletions skymp5-server/cpp/addon/property_bindings/CustomPropertyBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,41 @@ Napi::Value CustomPropertyBinding::Get(Napi::Env env, ScampServer& scampServer,

auto& refr = partOne->worldState.GetFormAt<MpObjectReference>(formId);

if (isPrivate) {
return NapiHelper::ParseJson(env,
refr.GetDynamicFields().Get(propertyName));
} else {
if (!isPrivate) {
EnsurePropertyExists(scampServer.GetGamemodeApiState(), propertyName);
return NapiHelper::ParseJson(env,
refr.GetDynamicFields().Get(propertyName));
}

auto& fynamicFields = refr.GetDynamicFields();
auto& entry = dynamicFields.Get(propertyName);

if (entry.cache != std::nullopt) {
switch (entry.cache->index()) {
case static_cast<size_t>(DynamicFieldsEntryCacheIndex::kVoidPtr):

// TODO: !!!!!!!!!!!!!!!!!! Bad type in std::get<DynamicFieldsValueObject>(*entry.cache)

if (auto ptr = std::get<void*>(*entry.cache)) {
auto napiValue = static_cast<napi_value>(ptr);
auto napiValueWrapped = Napi::Value(env, napiValue);
return structuredClone.Value().Call(
{ env.Null(), napiValueWrapped });
} else {
return env.Null();
}
case static_cast<size_t>(DynamicFieldsEntryCacheIndex::kDouble):
return Napi::Number::New(env, std::get<double>(*entry.cache));
case static_cast<size_t>(DynamicFieldsEntryCacheIndex::kBool):
return Napi::Boolean::New(env, std::get<bool>(*entry.cache));
case static_cast<size_t>(DynamicFieldsEntryCacheIndex::kString):
return Napi::String::New(env, std::get<std::string>(*entry.cache));
default:
spdlog::error(
"CustomPropertyBinding::Get {:x} {} - unknown cache type {}", formId,
propertyName, entry.cache->index());
}
}

return NapiHelper::ParseJson(env, entry.value);
}

void CustomPropertyBinding::Set(Napi::Env env, ScampServer& scampServer,
Expand All @@ -64,14 +91,116 @@ void CustomPropertyBinding::Set(Napi::Env env, ScampServer& scampServer,
auto newValueDump = NapiHelper::Stringify(env, newValue);
auto newValueJson = nlohmann::json::parse(newValueDump);

auto& fynamicFields = refr.GetDynamicFields();
auto& entry = dynamicFields.Get(propertyName);

// If there was an object in the cache, we need to free it before setting a
// new value, otherwise we'll have a memory leak
if (entry.cache != std::nullopt) {
switch (entry.cache->index()) {
case static_cast<size_t>(DynamicFieldsEntryCacheIndex::kVoidPtr):

// TODO: napi_delete_reference instead!!!!!!!!!!!!

if (auto ptr = std::get<DynamicFieldsValueObject>(*entry.cache)) {
uint32_t unrefResult;
auto res = napi_reference_unref(env, static_cast<napi_ref>(ptr),
&unrefResult);
if (res != napi_ok) {
spdlog::error("CustomPropertyBinding::Set {:x} {} - failed to "
"unref napi reference, res={}",
formId, propertyName, res);
} else {
spdlog::info("unref result: {}", unrefResult);
}
}
break;
default:
break;
}
}

std::optional<DynamicFieldsEntryCache> newValueCache;

switch (newValue.Type()) {
case napi_undefined:
newValueCache = static_cast<void*>(nullptr);
break;
case napi_null:
newValueCache = static_cast<void*>(nullptr);
break;
case napi_boolean:
newValueCache = newValue.As<Napi::Boolean>().Value();
break;
case napi_number:
newValueCache = newValue.As<Napi::Number>().DoubleValue();
break;
case napi_string:
newValueCache = newValue.As<Napi::String>().Utf8Value();
break;
case napi_symbol:
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(nullptr);
spdlog::error(
"CustomPropertyBinding::Set {:x} {} - symbol type is not supported",
formId, propertyName);
break;
case napi_object: {
auto obj = newValue.As<Napi::Object>();

auto napiValue = static_cast<napi_value>(newValue);
napi_ref ref;
auto res = napi_create_reference(env, napiValue, 1, &ref);
if (res != napi_ok) {
spdlog::error("CustomPropertyBinding::Set {:x} {} - failed to "
"create napi reference, res={}",
formId, propertyName, res);
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(nullptr);
} else {
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(ref);
}

} break;
case napi_function:
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(nullptr);
spdlog::error("CustomPropertyBinding::Set {:x} {} - function type "
"is not supported",
formId, propertyName);
break;
case napi_external:
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(nullptr);
spdlog::error("CustomPropertyBinding::Set {:x} {} - external type "
"is not supported",
formId, propertyName);
break;
case napi_bigint:
newValueJson = nlohmann::json{};
newValueDump = "null";
newValueCache = static_cast<void*>(nullptr);
spdlog::error("CustomPropertyBinding::Set {:x} {} - bigint type is "
"not supported",
formId, propertyName);
break;
}

if (isPrivate) {
refr.SetProperty(propertyName, newValueJson, false, false);
refr.SetProperty(propertyName, newValueJson, newValueCache, false, false);
if (isPrivateIndexed) {
refr.RegisterPrivateIndexedProperty(propertyName, newValueDump);
}
return;
}
auto it = EnsurePropertyExists(state, propertyName);
refr.SetProperty(propertyName, newValueJson, it->second.isVisibleByOwner,
refr.SetProperty(propertyName, newValueJson, newValueCache,
it->second.isVisibleByOwner,
it->second.isVisibleByNeighbors);
}
21 changes: 13 additions & 8 deletions skymp5-server/cpp/server_guest_lib/DynamicFields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
#include <vector>

void DynamicFields::Set(const std::string& propName,
const nlohmann::json& value)
const DynamicFieldsEntryValue& value)
{
jsonCache.reset();
props[propName] = value;
}

const nlohmann::json& DynamicFields::Get(const std::string& propName) const
const DynamicFieldsEntryValue& DynamicFields::Get(
const std::string& propName) const
{
static const auto kNull = nlohmann::json();
static const DynamicFieldsEntryValue kNullEntry = DynamicFieldsValueObject();

auto it = props.find(propName);
if (it == props.end()) {
return kNull;
return kNullEntry;
}
return it->second;
}
Expand All @@ -27,7 +28,7 @@ const nlohmann::json& DynamicFields::GetAsJson() const
auto obj = nlohmann::json::object();

for (auto& [key, v] : props) {
obj[key] = v;
obj[key] = DynamicFields::ToJson(v);
}

jsonCache = std::move(obj);
Expand All @@ -38,9 +39,9 @@ const nlohmann::json& DynamicFields::GetAsJson() const
DynamicFields DynamicFields::FromJson(const nlohmann::json& j)
{
DynamicFields res;
for (auto it = j.begin(); it != j.end(); ++it) {
res.props[it.key()] = it.value();
}
// for (auto it = j.begin(); it != j.end(); ++it) {
// res.props[it.key()] = it.value();
// }
return res;
}

Expand All @@ -58,3 +59,7 @@ bool operator!=(const DynamicFields& r, const DynamicFields& l)
{
return !(r == l);
}

nlohmann::json DynamicFields::ToJson(const DynamicFieldsEntryValue& value)
{
}
27 changes: 24 additions & 3 deletions skymp5-server/cpp/server_guest_lib/DynamicFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,31 @@
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
#include <variant>

enum class DynamicFieldsEntryCacheIndex
{
kObject,
kDouble,
kBool,
kString
};

struct DynamicFieldsValueObject
{
void* napiValue = nullptr;
void *napiRef = nullptr;
std::string jsonDump;
};

using DynamicFieldsEntryValue =
std::variant<DynamicFieldsValueObject, double, bool, std::string> value;

class DynamicFields
{
public:
void Set(const std::string& propName, const nlohmann::json& value);
const nlohmann::json& Get(const std::string& propName) const;
void Set(const std::string& propName, const DynamicFieldsEntryValue& value);
const DynamicFieldsEntryValue& Get(const std::string& propName) const;

const nlohmann::json& GetAsJson() const;
static DynamicFields FromJson(const nlohmann::json& j);
Expand All @@ -27,6 +46,8 @@ class DynamicFields
friend bool operator!=(const DynamicFields& r, const DynamicFields& l);

private:
std::unordered_map<std::string, nlohmann::json> props;
static nlohmann::json ToJson(const DynamicFieldsEntryValue& value);

std::unordered_map<std::string, DynamicFieldsEntryValue> props;
mutable std::optional<nlohmann::json> jsonCache;
};
10 changes: 5 additions & 5 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,13 +684,13 @@ void MpObjectReference::UpdateHoster(uint32_t newHosterId)
}
}

void MpObjectReference::SetProperty(const std::string& propertyName,
const nlohmann::json& newValue,
bool isVisibleByOwner,
bool isVisibleByNeighbor)
void MpObjectReference::SetProperty(
const std::string& propertyName, const nlohmann::json& newValue,
std::optional<DynamicFieldsEntryCache> cache, bool isVisibleByOwner,
bool isVisibleByNeighbor)
{
EditChangeForm([&](MpChangeFormREFR& changeForm) {
changeForm.dynamicFields.Set(propertyName, newValue);
changeForm.dynamicFields.Set(propertyName, newValue, cache);
});
if (isVisibleByNeighbor) {
SendPropertyToListeners(propertyName.data(), newValue);
Expand Down
5 changes: 3 additions & 2 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ class MpObjectReference
void SetPrimitive(const NiPoint3& boundsDiv2);
void UpdateHoster(uint32_t newHosterId);
void SetProperty(const std::string& propertyName,
const nlohmann::json& newValue, bool isVisibleByOwner,
bool isVisibleByNeighbor);
const nlohmann::json& newValue,
std::optional<DynamicFieldsEntryCache> cache,
bool isVisibleByOwner, bool isVisibleByNeighbor);
void SetTeleportFlag(bool value);
void SetPosAndAngleSilent(const NiPoint3& pos, const NiPoint3& rot);
void Delete();
Expand Down
Loading