Support std::optional in FromJSVal

This commit introduces support for std::optional<T> in
Script::FromJSVal. When a JavaScript value is undefined or null, the
resulting optional is set to std::nullopt; otherwise, the value is
converted and wrapped.

This change allows components to cleanly handle optional script values
without needing manual null checks or exception handling in C++.

As a direct application of this feature, the Identity component now uses
std::optional<std::wstring> for the return value of GetCiv(). This
resolves a bug where formation templates (e.g., those inheriting from
template_formation.xml) do not explicitly define a civ. After commit
03f7903fec, the code assumed GetCiv() would always return a valid
string, leading to undefined behavior when it was missing.

With this update:
- GetCiv() returning undefined results in an empty optional.
- The Identity component defaults to an empty civilization string ("")
  when the civ is not defined.
- This avoids crashes or actor parsing errors for civ-less templates and
  improves robustness in script-C++ interaction.

Closes: #8107
Fixes: #8091
This commit is contained in:
trompetin17
2025-06-18 12:53:49 -05:00
parent 007b4f5db7
commit 5b8cb7f34b
2 changed files with 21 additions and 2 deletions
@@ -23,6 +23,8 @@
#include "ScriptExtraHeaders.h" // for typed arrays
#include <limits>
#include <optional>
#include <type_traits>
#include <vector>
namespace Script
@@ -32,6 +34,21 @@ namespace Script
*/
template<typename T> bool FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, T& ret);
template<typename T>
bool FromJSVal(const ScriptRequest& rq, JS::HandleValue v, std::optional<T>& out)
{
if (v.isNullOrUndefined())
{
out = std::nullopt;
return true;
}
T value;
if (!FromJSVal(rq, v, value))
return false;
out = std::move(value);
return true;
}
/**
* Convert a C++ type to a JS::Value. (This might trigger GC. The return
* value must be rooted if you don't want it to be collected.)