LCOV - code coverage report
Current view: top level - source/simulation2/scripting - EngineScriptConversions.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 69 164 42.1 %
Date: 2023-01-19 00:18:29 Functions: 9 17 52.9 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "scriptinterface/Object.h"
      21             : #include "scriptinterface/ScriptConversions.h"
      22             : #include "scriptinterface/ScriptInterface.h"
      23             : 
      24             : #include "graphics/Color.h"
      25             : #include "maths/Fixed.h"
      26             : #include "maths/FixedVector2D.h"
      27             : #include "maths/FixedVector3D.h"
      28             : #include "maths/Rect.h"
      29             : #include "ps/CLogger.h"
      30             : #include "simulation2/helpers/CinemaPath.h"
      31             : #include "simulation2/helpers/Grid.h"
      32             : #include "simulation2/system/IComponent.h"
      33             : #include "simulation2/system/ParamNode.h"
      34             : 
      35             : #define FAIL(msg) STMT(LOGERROR(msg); return false)
      36             : #define FAIL_VOID(msg) STMT(ScriptException::Raise(rq, msg); return)
      37             : 
      38           4 : template<> void Script::ToJSVal<IComponent*>(const ScriptRequest& rq,  JS::MutableHandleValue ret, IComponent* const& val)
      39             : {
      40           4 :     if (val == NULL)
      41             :     {
      42           1 :         ret.setNull();
      43           4 :         return;
      44             :     }
      45             : 
      46             :     // If this is a scripted component, just return the JS object directly
      47           4 :     JS::RootedValue instance(rq.cx, val->GetJSInstance());
      48           3 :     if (!instance.isNull())
      49             :     {
      50           2 :         ret.set(instance);
      51           2 :         return;
      52             :     }
      53             : 
      54             :     // Otherwise we need to construct a wrapper object
      55             :     // (TODO: cache wrapper objects?)
      56           2 :     JS::RootedObject obj(rq.cx);
      57           1 :     if (!val->NewJSObject(rq.GetScriptInterface(), &obj))
      58             :     {
      59             :         // Report as an error, since scripts really shouldn't try to use unscriptable interfaces
      60           0 :         LOGERROR("IComponent does not have a scriptable interface");
      61           0 :         ret.setUndefined();
      62           0 :         return;
      63             :     }
      64             : 
      65           1 :     JS::SetPrivate(obj, static_cast<void*>(val));
      66           1 :     ret.setObject(*obj);
      67             : }
      68             : 
      69          57 : template<> void Script::ToJSVal<CParamNode>(const ScriptRequest& rq,  JS::MutableHandleValue ret, CParamNode const& val)
      70             : {
      71          57 :     val.ToJSVal(rq, true, ret);
      72             : 
      73             :     // Prevent modifications to the object, so that it's safe to share between
      74             :     // components and to reconstruct on deserialization
      75          57 :     if (ret.isObject())
      76          32 :         Script::FreezeObject(rq, ret, true);
      77          57 : }
      78             : 
      79           4 : template<> void Script::ToJSVal<const CParamNode*>(const ScriptRequest& rq,  JS::MutableHandleValue ret, const CParamNode* const& val)
      80             : {
      81           4 :     if (val)
      82           4 :         ToJSVal(rq, ret, *val);
      83             :     else
      84           0 :         ret.setUndefined();
      85           4 : }
      86             : 
      87           0 : template<> bool Script::FromJSVal<CColor>(const ScriptRequest& rq,  JS::HandleValue v, CColor& out)
      88             : {
      89           0 :     if (!v.isObject())
      90           0 :         FAIL("CColor has to be an object");
      91             : 
      92           0 :     JS::RootedObject obj(rq.cx, &v.toObject());
      93             : 
      94           0 :     JS::RootedValue r(rq.cx);
      95           0 :     JS::RootedValue g(rq.cx);
      96           0 :     JS::RootedValue b(rq.cx);
      97           0 :     JS::RootedValue a(rq.cx);
      98           0 :     if (!JS_GetProperty(rq.cx, obj, "r", &r) || !FromJSVal(rq, r, out.r))
      99           0 :         FAIL("Failed to get property CColor.r");
     100           0 :     if (!JS_GetProperty(rq.cx, obj, "g", &g) || !FromJSVal(rq, g, out.g))
     101           0 :         FAIL("Failed to get property CColor.g");
     102           0 :     if (!JS_GetProperty(rq.cx, obj, "b", &b) || !FromJSVal(rq, b, out.b))
     103           0 :         FAIL("Failed to get property CColor.b");
     104           0 :     if (!JS_GetProperty(rq.cx, obj, "a", &a) || !FromJSVal(rq, a, out.a))
     105           0 :         FAIL("Failed to get property CColor.a");
     106             : 
     107           0 :     return true;
     108             : }
     109             : 
     110           0 : template<> void Script::ToJSVal<CColor>(const ScriptRequest& rq,  JS::MutableHandleValue ret, CColor const& val)
     111             : {
     112           0 :     Script::CreateObject(
     113             :         rq,
     114             :         ret,
     115             :         "r", val.r,
     116             :         "g", val.g,
     117             :         "b", val.b,
     118             :         "a", val.a);
     119           0 : }
     120             : 
     121          21 : template<> bool Script::FromJSVal<fixed>(const ScriptRequest& rq,  JS::HandleValue v, fixed& out)
     122             : {
     123             :     double ret;
     124          21 :     if (!JS::ToNumber(rq.cx, v, &ret))
     125           0 :         return false;
     126          21 :     out = fixed::FromDouble(ret);
     127             :     // double can precisely represent the full range of fixed, so this is a non-lossy conversion
     128             : 
     129          21 :     return true;
     130             : }
     131             : 
     132          12 : template<> void Script::ToJSVal<fixed>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, const fixed& val)
     133             : {
     134          12 :     ret.set(JS::NumberValue(val.ToDouble()));
     135          12 : }
     136             : 
     137           2 : template<> bool Script::FromJSVal<CFixedVector3D>(const ScriptRequest& rq,  JS::HandleValue v, CFixedVector3D& out)
     138             : {
     139           2 :     if (!v.isObject())
     140           0 :         return false; // TODO: report type error
     141             : 
     142           4 :     JS::RootedObject obj(rq.cx, &v.toObject());
     143           4 :     JS::RootedValue p(rq.cx);
     144             : 
     145           2 :     if (!JS_GetProperty(rq.cx, obj, "x", &p)) return false; // TODO: report type errors
     146           2 :     if (!FromJSVal(rq, p, out.X)) return false;
     147             : 
     148           2 :     if (!JS_GetProperty(rq.cx, obj, "y", &p)) return false;
     149           2 :     if (!FromJSVal(rq, p, out.Y)) return false;
     150             : 
     151           2 :     if (!JS_GetProperty(rq.cx, obj, "z", &p)) return false;
     152           2 :     if (!FromJSVal(rq, p, out.Z)) return false;
     153             : 
     154           2 :     return true;
     155             : }
     156             : 
     157           4 : template<> void Script::ToJSVal<CFixedVector3D>(const ScriptRequest& rq,  JS::MutableHandleValue ret, const CFixedVector3D& val)
     158             : {
     159           8 :     JS::RootedObject global(rq.cx, rq.glob);
     160           8 :     JS::RootedValue valueVector3D(rq.cx);
     161           4 :     if (!ScriptInterface::GetGlobalProperty(rq, "Vector3D", &valueVector3D))
     162           0 :         FAIL_VOID("Failed to get Vector3D constructor");
     163             : 
     164           8 :     JS::RootedValueArray<3> args(rq.cx);
     165           4 :     args[0].setNumber(val.X.ToDouble());
     166           4 :     args[1].setNumber(val.Y.ToDouble());
     167           4 :     args[2].setNumber(val.Z.ToDouble());
     168             : 
     169           8 :     JS::RootedObject objVec(rq.cx);
     170           4 :     if (!JS::Construct(rq.cx, valueVector3D, args, &objVec))
     171           0 :         FAIL_VOID("Failed to construct Vector3D object");
     172             : 
     173           4 :     ret.setObject(*objVec);
     174             : }
     175             : 
     176           2 : template<> bool Script::FromJSVal<CFixedVector2D>(const ScriptRequest& rq,  JS::HandleValue v, CFixedVector2D& out)
     177             : {
     178           2 :     if (!v.isObject())
     179           0 :         return false; // TODO: report type error
     180           4 :     JS::RootedObject obj(rq.cx, &v.toObject());
     181             : 
     182           4 :     JS::RootedValue p(rq.cx);
     183             : 
     184           2 :     if (!JS_GetProperty(rq.cx, obj, "x", &p)) return false; // TODO: report type errors
     185           2 :     if (!FromJSVal(rq, p, out.X)) return false;
     186             : 
     187           2 :     if (!JS_GetProperty(rq.cx, obj, "y", &p)) return false;
     188           2 :     if (!FromJSVal(rq, p, out.Y)) return false;
     189             : 
     190           2 :     return true;
     191             : }
     192             : 
     193           4 : template<> void Script::ToJSVal<CFixedVector2D>(const ScriptRequest& rq,  JS::MutableHandleValue ret, const CFixedVector2D& val)
     194             : {
     195           8 :     JS::RootedObject global(rq.cx, rq.glob);
     196           8 :     JS::RootedValue valueVector2D(rq.cx);
     197           4 :     if (!ScriptInterface::GetGlobalProperty(rq, "Vector2D", &valueVector2D))
     198           0 :         FAIL_VOID("Failed to get Vector2D constructor");
     199             : 
     200           8 :     JS::RootedValueArray<2> args(rq.cx);
     201           4 :     args[0].setNumber(val.X.ToDouble());
     202           4 :     args[1].setNumber(val.Y.ToDouble());
     203             : 
     204           8 :     JS::RootedObject objVec(rq.cx);
     205           4 :     if (!JS::Construct(rq.cx, valueVector2D, args, &objVec))
     206           0 :         FAIL_VOID("Failed to construct Vector2D object");
     207             : 
     208           4 :     ret.setObject(*objVec);
     209             : }
     210             : 
     211           0 : template<> void Script::ToJSVal<Grid<u8> >(const ScriptRequest& rq,  JS::MutableHandleValue ret, const Grid<u8>& val)
     212             : {
     213           0 :     u32 length = (u32)(val.m_W * val.m_H);
     214           0 :     u32 nbytes = (u32)(length * sizeof(u8));
     215           0 :     JS::RootedObject objArr(rq.cx, JS_NewUint8Array(rq.cx, length));
     216             :     // Copy the array data and then remove the no-GC check to allow further changes to the JS data
     217             :     {
     218           0 :         JS::AutoCheckCannotGC nogc;
     219             :         bool sharedMemory;
     220           0 :         memcpy((void*)JS_GetUint8ArrayData(objArr, &sharedMemory, nogc), val.m_Data, nbytes);
     221             :     }
     222             : 
     223           0 :     JS::RootedValue data(rq.cx, JS::ObjectValue(*objArr));
     224           0 :     Script::CreateObject(
     225             :         rq,
     226             :         ret,
     227             :         "width", val.m_W,
     228             :         "height", val.m_H,
     229             :         "data", data);
     230           0 : }
     231             : 
     232           0 : template<> void Script::ToJSVal<Grid<u16> >(const ScriptRequest& rq,  JS::MutableHandleValue ret, const Grid<u16>& val)
     233             :  {
     234           0 :     u32 length = (u32)(val.m_W * val.m_H);
     235           0 :     u32 nbytes = (u32)(length * sizeof(u16));
     236           0 :     JS::RootedObject objArr(rq.cx, JS_NewUint16Array(rq.cx, length));
     237             :     // Copy the array data and then remove the no-GC check to allow further changes to the JS data
     238             :     {
     239           0 :         JS::AutoCheckCannotGC nogc;
     240             :         bool sharedMemory;
     241           0 :         memcpy((void*)JS_GetUint16ArrayData(objArr, &sharedMemory, nogc), val.m_Data, nbytes);
     242             :     }
     243             : 
     244           0 :     JS::RootedValue data(rq.cx, JS::ObjectValue(*objArr));
     245           0 :     Script::CreateObject(
     246             :         rq,
     247             :         ret,
     248             :         "width", val.m_W,
     249             :         "height", val.m_H,
     250             :         "data", data);
     251           0 : }
     252             : 
     253           0 : template<> bool Script::FromJSVal<TNSpline>(const ScriptRequest& rq,  JS::HandleValue v, TNSpline& out)
     254             : {
     255           0 :     if (!v.isObject())
     256           0 :         FAIL("Argument must be an object");
     257             : 
     258           0 :     JS::RootedObject obj(rq.cx, &v.toObject());
     259             :     bool isArray;
     260           0 :     if (!JS::IsArrayObject(rq.cx, obj, &isArray) || !isArray)
     261           0 :         FAIL("Argument must be an array");
     262             : 
     263           0 :     u32 numberOfNodes = 0;
     264           0 :     if (!JS::GetArrayLength(rq.cx, obj, &numberOfNodes))
     265           0 :         FAIL("Failed to get array length");
     266             : 
     267           0 :     for (u32 i = 0; i < numberOfNodes; ++i)
     268             :     {
     269           0 :         JS::RootedValue node(rq.cx);
     270           0 :         if (!JS_GetElement(rq.cx, obj, i, &node))
     271           0 :             FAIL("Failed to read array element");
     272             : 
     273           0 :         fixed deltaTime;
     274           0 :         if (!FromJSProperty(rq, node, "deltaTime", deltaTime))
     275           0 :             FAIL("Failed to read Spline.deltaTime property");
     276             : 
     277           0 :         CFixedVector3D position;
     278           0 :         if (!FromJSProperty(rq, node, "position", position))
     279           0 :             FAIL("Failed to read Spline.position property");
     280             : 
     281           0 :         out.AddNode(position, CFixedVector3D(), deltaTime);
     282             :     }
     283             : 
     284           0 :     if (out.GetAllNodes().empty())
     285           0 :         FAIL("Spline must contain at least one node");
     286             : 
     287           0 :     return true;
     288             : }
     289             : 
     290           0 : template<> bool Script::FromJSVal<CCinemaPath>(const ScriptRequest& rq,  JS::HandleValue v, CCinemaPath& out)
     291             : {
     292           0 :     if (!v.isObject())
     293           0 :         FAIL("Argument must be an object");
     294             : 
     295           0 :     JS::RootedObject obj(rq.cx, &v.toObject());
     296             : 
     297           0 :     CCinemaData pathData;
     298           0 :     TNSpline positionSpline, targetSpline;
     299             : 
     300           0 :     if (!FromJSProperty(rq, v, "name", pathData.m_Name))
     301           0 :         FAIL("Failed to get CCinemaPath.name property");
     302             : 
     303           0 :     if (!FromJSProperty(rq, v, "orientation", pathData.m_Orientation))
     304           0 :         FAIL("Failed to get CCinemaPath.orientation property");
     305             : 
     306           0 :     if (!FromJSProperty(rq, v, "positionNodes", positionSpline))
     307           0 :         FAIL("Failed to get CCinemaPath.positionNodes property");
     308             : 
     309           0 :     if (pathData.m_Orientation == L"target" && !FromJSProperty(rq, v, "targetNodes", targetSpline))
     310           0 :         FAIL("Failed to get CCinemaPath.targetNodes property");
     311             : 
     312             :     // Other properties are not necessary to be defined
     313           0 :     if (!FromJSProperty(rq, v, "timescale", pathData.m_Timescale))
     314           0 :         pathData.m_Timescale = fixed::FromInt(1);
     315             : 
     316           0 :     if (!FromJSProperty(rq, v, "mode", pathData.m_Mode))
     317           0 :         pathData.m_Mode = L"ease_inout";
     318             : 
     319           0 :     if (!FromJSProperty(rq, v, "style", pathData.m_Style))
     320           0 :         pathData.m_Style = L"default";
     321             : 
     322           0 :     out = CCinemaPath(pathData, positionSpline, targetSpline);
     323             : 
     324           0 :     return true;
     325             : }
     326             : 
     327             : // define vectors
     328           0 : JSVAL_VECTOR(CFixedVector2D)
     329             : 
     330             : #undef FAIL
     331             : #undef FAIL_VOID

Generated by: LCOV version 1.13