From 5b8cb7f34bd95efc107e68fd32eafacb22a218e4 Mon Sep 17 00:00:00 2001 From: trompetin17 Date: Wed, 18 Jun 2025 12:53:49 -0500 Subject: [PATCH] Support std::optional in FromJSVal This commit introduces support for std::optional 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 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 --- source/scriptinterface/ScriptConversions.h | 17 +++++++++++++++++ source/simulation2/components/ICmpIdentity.cpp | 6 ++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/source/scriptinterface/ScriptConversions.h b/source/scriptinterface/ScriptConversions.h index 5fd917268d..a0bd267760 100644 --- a/source/scriptinterface/ScriptConversions.h +++ b/source/scriptinterface/ScriptConversions.h @@ -23,6 +23,8 @@ #include "ScriptExtraHeaders.h" // for typed arrays #include +#include +#include #include namespace Script @@ -32,6 +34,21 @@ namespace Script */ template bool FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, T& ret); +template +bool FromJSVal(const ScriptRequest& rq, JS::HandleValue v, std::optional& 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.) diff --git a/source/simulation2/components/ICmpIdentity.cpp b/source/simulation2/components/ICmpIdentity.cpp index bdb318e97c..19fd7f7939 100644 --- a/source/simulation2/components/ICmpIdentity.cpp +++ b/source/simulation2/components/ICmpIdentity.cpp @@ -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 BEGIN_INTERFACE_WRAPPER(Identity) @@ -43,7 +44,8 @@ public: std::wstring GetCiv() override { - return m_Script.Call("GetCiv"); + std::optional civ{m_Script.Call>("GetCiv")}; + return !civ || !civ.has_value() ? L"" : *civ; } };