/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "ps/CLogger.h" #include "scriptinterface/ScriptConversions.h" #include "simulation2/MessageTypes.h" #define TOJSVAL_SETUP() \ JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx)); \ if (!obj) \ return JS::UndefinedValue(); #define SET_MSG_PROPERTY(name) \ do { \ JS::RootedValue prop(rq.cx);\ Script::ToJSVal(rq, &prop, this->name); \ if (! JS_SetProperty(rq.cx, obj, #name, prop)) \ return JS::UndefinedValue(); \ } while (0); #define FROMJSVAL_SETUP() \ if (val.isPrimitive()) \ return NULL; \ JS::RootedObject obj(rq.cx, &val.toObject()); \ JS::RootedValue prop(rq.cx); #define GET_MSG_PROPERTY(type, name) \ type name; \ { \ if (! JS_GetProperty(rq.cx, obj, #name, &prop)) \ return NULL; \ if (! Script::FromJSVal(rq, prop, name)) \ return NULL; \ } JS::Value CMessage::ToJSValCached(const ScriptRequest& rq) const { if (!m_Cached) m_Cached.reset(new JS::PersistentRootedValue(rq.cx, ToJSVal(rq))); return m_Cached->get(); } //////////////////////////////// JS::Value CMessageTurnStart::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageTurnStart::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageTurnStart(); } //////////////////////////////// #define MESSAGE_1(name, t0, a0) \ JS::Value CMessage##name::ToJSVal(const ScriptRequest& rq) const \ { \ TOJSVAL_SETUP(); \ SET_MSG_PROPERTY(a0); \ return JS::ObjectValue(*obj); \ } \ CMessage* CMessage##name::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) \ { \ FROMJSVAL_SETUP(); \ GET_MSG_PROPERTY(t0, a0); \ return new CMessage##name(a0); \ } MESSAGE_1(Update, fixed, turnLength) MESSAGE_1(Update_MotionFormation, fixed, turnLength) MESSAGE_1(Update_MotionUnit, fixed, turnLength) MESSAGE_1(Update_Final, fixed, turnLength) //////////////////////////////// JS::Value CMessageInterpolate::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(deltaSimTime); SET_MSG_PROPERTY(offset); SET_MSG_PROPERTY(deltaRealTime); return JS::ObjectValue(*obj); } CMessage* CMessageInterpolate::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(float, deltaSimTime); GET_MSG_PROPERTY(float, offset); GET_MSG_PROPERTY(float, deltaRealTime); return new CMessageInterpolate(deltaSimTime, offset, deltaRealTime); } //////////////////////////////// JS::Value CMessageRenderSubmit::ToJSVal(const ScriptRequest& UNUSED(rq)) const { LOGWARNING("CMessageRenderSubmit::ToJSVal not implemented"); return JS::UndefinedValue(); } CMessage* CMessageRenderSubmit::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { LOGWARNING("CMessageRenderSubmit::FromJSVal not implemented"); return NULL; } //////////////////////////////// JS::Value CMessageProgressiveLoad::ToJSVal(const ScriptRequest& UNUSED(rq)) const { LOGWARNING("CMessageProgressiveLoad::ToJSVal not implemented"); return JS::UndefinedValue(); } CMessage* CMessageProgressiveLoad::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { LOGWARNING("CMessageProgressiveLoad::FromJSVal not implemented"); return NULL; } //////////////////////////////// JS::Value CMessageDeserialized::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageDeserialized::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); return new CMessageDeserialized(); } //////////////////////////////// JS::Value CMessageCreate::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); return JS::ObjectValue(*obj); } CMessage* CMessageCreate::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); return new CMessageCreate(entity); } //////////////////////////////// JS::Value CMessageDestroy::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); return JS::ObjectValue(*obj); } CMessage* CMessageDestroy::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); return new CMessageDestroy(entity); } //////////////////////////////// JS::Value CMessageOwnershipChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); SET_MSG_PROPERTY(from); SET_MSG_PROPERTY(to); return JS::ObjectValue(*obj); } CMessage* CMessageOwnershipChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); GET_MSG_PROPERTY(player_id_t, from); GET_MSG_PROPERTY(player_id_t, to); return new CMessageOwnershipChanged(entity, from, to); } //////////////////////////////// JS::Value CMessagePositionChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); SET_MSG_PROPERTY(inWorld); SET_MSG_PROPERTY(x); SET_MSG_PROPERTY(z); SET_MSG_PROPERTY(a); return JS::ObjectValue(*obj); } CMessage* CMessagePositionChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); GET_MSG_PROPERTY(bool, inWorld); GET_MSG_PROPERTY(entity_pos_t, x); GET_MSG_PROPERTY(entity_pos_t, z); GET_MSG_PROPERTY(entity_angle_t, a); return new CMessagePositionChanged(entity, inWorld, x, z, a); } //////////////////////////////// JS::Value CMessageInterpolatedPositionChanged::ToJSVal(const ScriptRequest& UNUSED(rq)) const { LOGWARNING("CMessageInterpolatedPositionChanged::ToJSVal not implemented"); return JS::UndefinedValue(); } CMessage* CMessageInterpolatedPositionChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { LOGWARNING("CMessageInterpolatedPositionChanged::FromJSVal not implemented"); return NULL; } //////////////////////////////// const std::array CMessageMotionUpdate::UpdateTypeStr = { { "likelySuccess", "likelyFailure", "obstructed", "veryObstructed" } }; JS::Value CMessageMotionUpdate::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); JS::RootedValue prop(rq.cx); if (!JS_SetProperty(rq.cx, obj, UpdateTypeStr[updateType], JS::TrueHandleValue)) return JS::UndefinedValue(); return JS::ObjectValue(*obj); } CMessage* CMessageMotionUpdate::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(std::wstring, updateString); if (updateString == L"likelySuccess") return new CMessageMotionUpdate(CMessageMotionUpdate::LIKELY_SUCCESS); if (updateString == L"likelyFailure") return new CMessageMotionUpdate(CMessageMotionUpdate::LIKELY_FAILURE); if (updateString == L"obstructed") return new CMessageMotionUpdate(CMessageMotionUpdate::OBSTRUCTED); if (updateString == L"veryObstructed") return new CMessageMotionUpdate(CMessageMotionUpdate::VERY_OBSTRUCTED); LOGWARNING("CMessageMotionUpdate::FromJSVal passed wrong updateString"); return NULL; } //////////////////////////////// JS::Value CMessageTerrainChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(i0); SET_MSG_PROPERTY(j0); SET_MSG_PROPERTY(i1); SET_MSG_PROPERTY(j1); return JS::ObjectValue(*obj); } CMessage* CMessageTerrainChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(int32_t, i0); GET_MSG_PROPERTY(int32_t, j0); GET_MSG_PROPERTY(int32_t, i1); GET_MSG_PROPERTY(int32_t, j1); return new CMessageTerrainChanged(i0, i1, j0, j1); } //////////////////////////////// JS::Value CMessageVisibilityChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(player); SET_MSG_PROPERTY(ent); SET_MSG_PROPERTY(oldVisibility); SET_MSG_PROPERTY(newVisibility); return JS::ObjectValue(*obj); } CMessage* CMessageVisibilityChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(player_id_t, player); GET_MSG_PROPERTY(entity_id_t, ent); GET_MSG_PROPERTY(int, oldVisibility); GET_MSG_PROPERTY(int, newVisibility); return new CMessageVisibilityChanged(player, ent, oldVisibility, newVisibility); } //////////////////////////////// JS::Value CMessageWaterChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageWaterChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageWaterChanged(); } //////////////////////////////// JS::Value CMessageMovementObstructionChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageMovementObstructionChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageMovementObstructionChanged(); } //////////////////////////////// JS::Value CMessageObstructionMapShapeChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageObstructionMapShapeChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageObstructionMapShapeChanged(); } //////////////////////////////// JS::Value CMessageTerritoriesChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageTerritoriesChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageTerritoriesChanged(); } //////////////////////////////// JS::Value CMessageRangeUpdate::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(tag); SET_MSG_PROPERTY(added); SET_MSG_PROPERTY(removed); return JS::ObjectValue(*obj); } CMessage* CMessageRangeUpdate::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { LOGWARNING("CMessageRangeUpdate::FromJSVal not implemented"); return NULL; } //////////////////////////////// JS::Value CMessagePathResult::ToJSVal(const ScriptRequest& UNUSED(rq)) const { LOGWARNING("CMessagePathResult::ToJSVal not implemented"); return JS::UndefinedValue(); } CMessage* CMessagePathResult::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { LOGWARNING("CMessagePathResult::FromJSVal not implemented"); return NULL; } //////////////////////////////// JS::Value CMessageValueModification::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entities); SET_MSG_PROPERTY(component); SET_MSG_PROPERTY(valueNames); return JS::ObjectValue(*obj); } CMessage* CMessageValueModification::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(std::vector, entities); GET_MSG_PROPERTY(std::wstring, component); GET_MSG_PROPERTY(std::vector, valueNames); return new CMessageValueModification(entities, component, valueNames); } //////////////////////////////// JS::Value CMessageTemplateModification::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(player); SET_MSG_PROPERTY(component); SET_MSG_PROPERTY(valueNames); return JS::ObjectValue(*obj); } CMessage* CMessageTemplateModification::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(player_id_t, player); GET_MSG_PROPERTY(std::wstring, component); GET_MSG_PROPERTY(std::vector, valueNames); return new CMessageTemplateModification(player, component, valueNames); } //////////////////////////////// JS::Value CMessageVisionRangeChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); SET_MSG_PROPERTY(oldRange); SET_MSG_PROPERTY(newRange); return JS::ObjectValue(*obj); } CMessage* CMessageVisionRangeChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); GET_MSG_PROPERTY(entity_pos_t, oldRange); GET_MSG_PROPERTY(entity_pos_t, newRange); return new CMessageVisionRangeChanged(entity, oldRange, newRange); } JS::Value CMessageVisionSharingChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(entity); SET_MSG_PROPERTY(player); SET_MSG_PROPERTY(add); return JS::ObjectValue(*obj); } CMessage* CMessageVisionSharingChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(entity_id_t, entity); GET_MSG_PROPERTY(player_id_t, player); GET_MSG_PROPERTY(bool, add); return new CMessageVisionSharingChanged(entity, player, add); } //////////////////////////////// JS::Value CMessageMinimapPing::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageMinimapPing::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val)) { return new CMessageMinimapPing(); } //////////////////////////////// JS::Value CMessageCinemaPathEnded::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(name); return JS::ObjectValue(*obj); } CMessage* CMessageCinemaPathEnded::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(CStrW, name); return new CMessageCinemaPathEnded(name); } //////////////////////////////// JS::Value CMessageCinemaQueueEnded::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); return JS::ObjectValue(*obj); } CMessage* CMessageCinemaQueueEnded::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); return new CMessageCinemaQueueEnded(); } //////////////////////////////////////////////////////////////// JS::Value CMessagePlayerColorChanged::ToJSVal(const ScriptRequest& rq) const { TOJSVAL_SETUP(); SET_MSG_PROPERTY(player); return JS::ObjectValue(*obj); } CMessage* CMessagePlayerColorChanged::FromJSVal(const ScriptRequest& rq, JS::HandleValue val) { FROMJSVAL_SETUP(); GET_MSG_PROPERTY(player_id_t, player); return new CMessagePlayerColorChanged(player); } //////////////////////////////////////////////////////////////// CMessage* CMessageFromJSVal(int mtid, const ScriptRequest& rq, JS::HandleValue val) { switch (mtid) { #define MESSAGE(name) case MT_##name: return CMessage##name::FromJSVal(rq, val); #define INTERFACE(name) #define COMPONENT(name) #include "simulation2/TypeList.h" #undef COMPONENT #undef INTERFACE #undef MESSAGE } return NULL; }