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.)
@@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#include "simulation2/system/InterfaceScripted.h"
#include "simulation2/scripting/ScriptComponent.h"
#include <optional>
BEGIN_INTERFACE_WRAPPER(Identity)
@@ -43,7 +44,8 @@ public:
std::wstring GetCiv() override
{
return m_Script.Call<std::wstring>("GetCiv");
std::optional<std::wstring> civ{m_Script.Call<std::optional<std::wstring>>("GetCiv")};
return !civ || !civ.has_value() ? L"" : *civ;
}
};