Index: ps/trunk/source/simulation2/MessageTypes.h
===================================================================
--- ps/trunk/source/simulation2/MessageTypes.h (revision 27727)
+++ ps/trunk/source/simulation2/MessageTypes.h (revision 27728)
@@ -1,606 +1,606 @@
/* 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 .
*/
#ifndef INCLUDED_MESSAGETYPES
#define INCLUDED_MESSAGETYPES
#include "simulation2/system/Components.h"
#include "simulation2/system/Entity.h"
#include "simulation2/system/Message.h"
#include "simulation2/helpers/Player.h"
#include "simulation2/helpers/Position.h"
#include "simulation2/components/ICmpPathfinder.h"
#include "maths/Vector3D.h"
#include "ps/CStr.h"
#define DEFAULT_MESSAGE_IMPL(name) \
virtual int GetType() const { return MT_##name; } \
virtual const char* GetScriptHandlerName() const { return "On" #name; } \
virtual const char* GetScriptGlobalHandlerName() const { return "OnGlobal" #name; } \
- virtual JS::Value ToJSVal(const ScriptInterface& scriptInterface) const; \
- static CMessage* FromJSVal(const ScriptInterface&, JS::HandleValue val);
+ virtual JS::Value ToJSVal(const ScriptRequest& rq) const; \
+ static CMessage* FromJSVal(const ScriptRequest&, JS::HandleValue val);
class SceneCollector;
class CFrustum;
class CMessageTurnStart final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TurnStart)
CMessageTurnStart()
{
}
};
// The update process is split into a number of phases, in an attempt
// to cope with dependencies between components. Each phase is implemented
// as a separate message. Simulation2.cpp sends them in sequence.
/**
* Generic per-turn update message, for things that don't care much about ordering.
*/
class CMessageUpdate final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update)
CMessageUpdate(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Update phase for formation controller movement (must happen before individual
* units move to follow their formation).
*/
class CMessageUpdate_MotionFormation final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_MotionFormation)
CMessageUpdate_MotionFormation(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Update phase for non-formation-controller unit movement.
*/
class CMessageUpdate_MotionUnit final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_MotionUnit)
CMessageUpdate_MotionUnit(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Final update phase, after all other updates.
*/
class CMessageUpdate_Final final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_Final)
CMessageUpdate_Final(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Prepare for rendering a new frame (set up model positions etc).
*/
class CMessageInterpolate final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Interpolate)
CMessageInterpolate(float deltaSimTime, float offset, float deltaRealTime) :
deltaSimTime(deltaSimTime), offset(offset), deltaRealTime(deltaRealTime)
{
}
/// Elapsed simulation time since previous interpolate, in seconds. This is similar to the elapsed real time, except
/// it is scaled by the current simulation rate (and might indeed be zero).
float deltaSimTime;
/// Range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns.
float offset;
/// Elapsed real time since previous interpolate, in seconds.
float deltaRealTime;
};
/**
* Add renderable objects to the scene collector.
* Called after CMessageInterpolate.
*/
class CMessageRenderSubmit final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(RenderSubmit)
CMessageRenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) :
collector(collector), frustum(frustum), culling(culling)
{
}
SceneCollector& collector;
const CFrustum& frustum;
bool culling;
};
/**
* Handle progressive loading of resources.
* A component that listens to this message must do the following:
* - Increase *msg.total by the non-zero number of loading tasks this component can perform.
* - If *msg.progressed == true, return and do nothing.
* - If you've loaded everything, increase *msg.progress by the value you added to .total
* - Otherwise do some loading, set *msg.progressed = true, and increase *msg.progress by a
* value indicating how much progress you've made in total (0 <= p <= what you added to .total)
* In some situations these messages will never be sent - components must ensure they
* load all their data themselves before using it in that case.
*/
class CMessageProgressiveLoad final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(ProgressiveLoad)
CMessageProgressiveLoad(bool* progressed, int* total, int* progress) :
progressed(progressed), total(total), progress(progress)
{
}
bool* progressed;
int* total;
int* progress;
};
/**
* Broadcast after the entire simulation state has been deserialized.
* Components should do all their self-contained work in their Deserialize
* function when possible. But any reinitialisation that depends on other
* components or other entities, that might not be constructed until later
* in the deserialization process, may be done in response to this message
* instead.
*/
class CMessageDeserialized final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Deserialized)
CMessageDeserialized()
{
}
};
/**
* This is sent immediately after a new entity's components have all been created
* and initialised.
*/
class CMessageCreate final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Create)
CMessageCreate(entity_id_t entity) :
entity(entity)
{
}
entity_id_t entity;
};
/**
* This is sent immediately before a destroyed entity is flushed and really destroyed.
* (That is, after CComponentManager::DestroyComponentsSoon and inside FlushDestroyedComponents).
* The entity will still exist at the time this message is sent.
* It's possible for this message to be sent multiple times for one entity, but all its components
* will have been deleted after the first time.
*/
class CMessageDestroy final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Destroy)
CMessageDestroy(entity_id_t entity) :
entity(entity)
{
}
entity_id_t entity;
};
class CMessageOwnershipChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(OwnershipChanged)
CMessageOwnershipChanged(entity_id_t entity, player_id_t from, player_id_t to) :
entity(entity), from(from), to(to)
{
}
entity_id_t entity;
player_id_t from;
player_id_t to;
};
/**
* Sent by CCmpPosition whenever anything has changed that will affect the
* return value of GetPosition2D() or GetRotation().Y
*
* If @c inWorld is false, then the other fields are invalid and meaningless.
* Otherwise they represent the current position.
*/
class CMessagePositionChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(PositionChanged)
CMessagePositionChanged(entity_id_t entity, bool inWorld, entity_pos_t x, entity_pos_t z, entity_angle_t a) :
entity(entity), inWorld(inWorld), x(x), z(z), a(a)
{
}
entity_id_t entity;
bool inWorld;
entity_pos_t x, z;
entity_angle_t a;
};
/**
* Sent by CCmpPosition whenever anything has changed that will affect the
* return value of GetInterpolatedTransform()
*/
class CMessageInterpolatedPositionChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(InterpolatedPositionChanged)
CMessageInterpolatedPositionChanged(entity_id_t entity, bool inWorld, const CVector3D& pos0, const CVector3D& pos1) :
entity(entity), inWorld(inWorld), pos0(pos0), pos1(pos1)
{
}
entity_id_t entity;
bool inWorld;
CVector3D pos0;
CVector3D pos1;
};
/**
* Sent by CCmpUnitMotion during Update if an event happened that might interest other components.
*/
class CMessageMotionUpdate final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(MotionUpdate)
enum UpdateType {
LIKELY_SUCCESS, // UnitMotion considers it is arrived at destination.
LIKELY_FAILURE, // UnitMotion says it cannot reach the destination.
OBSTRUCTED, // UnitMotion was obstructed. This does not mean stuck, but can be a hint to run range checks.
VERY_OBSTRUCTED, // Sent when obstructed several time in a row.
LENGTH
};
static const std::array UpdateTypeStr;
CMessageMotionUpdate(UpdateType ut) : updateType(ut)
{
}
UpdateType updateType;
};
/**
* Sent when water height has been changed.
*/
class CMessageWaterChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(WaterChanged)
CMessageWaterChanged()
{
}
};
/**
* Sent when terrain (texture or elevation) has been changed.
*/
class CMessageTerrainChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TerrainChanged)
CMessageTerrainChanged(int32_t i0, int32_t j0, int32_t i1, int32_t j1) :
i0(i0), j0(j0), i1(i1), j1(j1)
{
}
int32_t i0, j0, i1, j1; // inclusive lower bound, exclusive upper bound, in tiles
};
/**
* Sent, at most once per turn, when the visibility of an entity changed
*/
class CMessageVisibilityChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(VisibilityChanged)
CMessageVisibilityChanged(player_id_t player, entity_id_t ent, int oldVisibility, int newVisibility) :
player(player), ent(ent), oldVisibility(oldVisibility), newVisibility(newVisibility)
{
}
player_id_t player;
entity_id_t ent;
int oldVisibility;
int newVisibility;
};
/**
* Sent when then obstruction of an entity has changed in a manner
* that changes 'block movement' properties.
*/
class CMessageMovementObstructionChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(MovementObstructionChanged)
CMessageMovementObstructionChanged()
{
}
};
/**
* Sent when ObstructionManager's view of the shape of the world has changed
* (changing the TILE_OUTOFBOUNDS tiles returned by Rasterise).
*/
class CMessageObstructionMapShapeChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(ObstructionMapShapeChanged)
CMessageObstructionMapShapeChanged()
{
}
};
/**
* Sent when territory assignments have changed.
*/
class CMessageTerritoriesChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TerritoriesChanged)
CMessageTerritoriesChanged()
{
}
};
/**
* Sent by CCmpRangeManager at most once per turn, when an active range query
* has had matching units enter/leave the range since the last RangeUpdate.
*/
class CMessageRangeUpdate final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(RangeUpdate)
u32 tag;
std::vector added;
std::vector removed;
// CCmpRangeManager wants to store a vector of messages and wants to
// swap vectors instead of copying (to save on memory allocations),
// so add some constructors for it:
// don't init tag in empty ctor
CMessageRangeUpdate()
{
}
CMessageRangeUpdate(u32 tag) : tag(tag)
{
}
CMessageRangeUpdate(u32 tag, const std::vector& added, const std::vector& removed)
: tag(tag), added(added), removed(removed)
{
}
CMessageRangeUpdate(const CMessageRangeUpdate& other)
: CMessage(), tag(other.tag), added(other.added), removed(other.removed)
{
}
CMessageRangeUpdate& operator=(const CMessageRangeUpdate& other)
{
tag = other.tag;
added = other.added;
removed = other.removed;
return *this;
}
};
/**
* Sent by CCmpPathfinder after async path requests.
*/
class CMessagePathResult final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(PathResult)
CMessagePathResult(u32 ticket, const WaypointPath& path) :
ticket(ticket), path(path)
{
}
u32 ticket;
WaypointPath path;
};
/**
* Sent by aura manager when a value of a certain entity's component is changed
*/
class CMessageValueModification final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(ValueModification)
CMessageValueModification(const std::vector& entities, std::wstring component, const std::vector& valueNames) :
entities(entities),
component(component),
valueNames(valueNames)
{
}
std::vector entities;
std::wstring component;
std::vector valueNames;
};
/**
* Sent by atlas if the playercolor has been changed.
*/
class CMessagePlayerColorChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(PlayerColorChanged)
CMessagePlayerColorChanged(player_id_t player) :
player(player)
{
}
player_id_t player;
};
/**
* Sent by aura and tech managers when a value of a certain template's component is changed
*/
class CMessageTemplateModification final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TemplateModification)
CMessageTemplateModification(player_id_t player, std::wstring component, const std::vector& valueNames) :
player(player),
component(component),
valueNames(valueNames)
{
}
player_id_t player;
std::wstring component;
std::vector valueNames;
};
/**
* Sent by CCmpVision when an entity's vision range changes.
*/
class CMessageVisionRangeChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(VisionRangeChanged)
CMessageVisionRangeChanged(entity_id_t entity, entity_pos_t oldRange, entity_pos_t newRange) :
entity(entity), oldRange(oldRange), newRange(newRange)
{
}
entity_id_t entity;
entity_pos_t oldRange;
entity_pos_t newRange;
};
/**
* Sent by CCmpVision when an entity's vision sharing changes.
*/
class CMessageVisionSharingChanged final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(VisionSharingChanged)
CMessageVisionSharingChanged(entity_id_t entity, player_id_t player, bool add) :
entity(entity), player(player), add(add)
{
}
entity_id_t entity;
player_id_t player;
bool add;
};
/**
* Sent when an entity pings the minimap
*/
class CMessageMinimapPing final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(MinimapPing)
CMessageMinimapPing()
{
}
};
/**
* Cinematics events
*/
class CMessageCinemaPathEnded final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(CinemaPathEnded)
CMessageCinemaPathEnded(CStrW name) :
name(name)
{
}
CStrW name;
};
class CMessageCinemaQueueEnded final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(CinemaQueueEnded)
};
#endif // INCLUDED_MESSAGETYPES
Index: ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp
===================================================================
--- ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp (revision 27727)
+++ ps/trunk/source/simulation2/scripting/MessageTypeConversions.cpp (revision 27728)
@@ -1,563 +1,560 @@
/* 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() \
- ScriptRequest rq(scriptInterface); \
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() \
- ScriptRequest rq(scriptInterface); \
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 ScriptInterface& scriptInterface) const
+JS::Value CMessage::ToJSValCached(const ScriptRequest& rq) const
{
- ScriptRequest rq(scriptInterface);
if (!m_Cached)
- m_Cached.reset(new JS::PersistentRootedValue(rq.cx, ToJSVal(scriptInterface)));
+ m_Cached.reset(new JS::PersistentRootedValue(rq.cx, ToJSVal(rq)));
return m_Cached->get();
}
////////////////////////////////
-JS::Value CMessageTurnStart::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageTurnStart::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageTurnStart::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+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 ScriptInterface& scriptInterface) const \
+ JS::Value CMessage##name::ToJSVal(const ScriptRequest& rq) const \
{ \
TOJSVAL_SETUP(); \
SET_MSG_PROPERTY(a0); \
return JS::ObjectValue(*obj); \
} \
- CMessage* CMessage##name::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val) \
+ 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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& UNUSED(scriptInterface)) const
+JS::Value CMessageRenderSubmit::ToJSVal(const ScriptRequest& UNUSED(rq)) const
{
LOGWARNING("CMessageRenderSubmit::ToJSVal not implemented");
return JS::UndefinedValue();
}
-CMessage* CMessageRenderSubmit::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageRenderSubmit::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
LOGWARNING("CMessageRenderSubmit::FromJSVal not implemented");
return NULL;
}
////////////////////////////////
-JS::Value CMessageProgressiveLoad::ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const
+JS::Value CMessageProgressiveLoad::ToJSVal(const ScriptRequest& UNUSED(rq)) const
{
LOGWARNING("CMessageProgressiveLoad::ToJSVal not implemented");
return JS::UndefinedValue();
}
-CMessage* CMessageProgressiveLoad::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageProgressiveLoad::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
LOGWARNING("CMessageProgressiveLoad::FromJSVal not implemented");
return NULL;
}
////////////////////////////////
-JS::Value CMessageDeserialized::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageDeserialized::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageDeserialized::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+CMessage* CMessageDeserialized::FromJSVal(const ScriptRequest& rq, JS::HandleValue val)
{
FROMJSVAL_SETUP();
return new CMessageDeserialized();
}
////////////////////////////////
-JS::Value CMessageCreate::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageCreate::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(entity);
return JS::ObjectValue(*obj);
}
-CMessage* CMessageCreate::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+JS::Value CMessageDestroy::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(entity);
return JS::ObjectValue(*obj);
}
-CMessage* CMessageDestroy::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& UNUSED(scriptInterface)) const
+JS::Value CMessageInterpolatedPositionChanged::ToJSVal(const ScriptRequest& UNUSED(rq)) const
{
LOGWARNING("CMessageInterpolatedPositionChanged::ToJSVal not implemented");
return JS::UndefinedValue();
}
-CMessage* CMessageInterpolatedPositionChanged::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+JS::Value CMessageWaterChanged::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageWaterChanged::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageWaterChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
return new CMessageWaterChanged();
}
////////////////////////////////
-JS::Value CMessageMovementObstructionChanged::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageMovementObstructionChanged::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageMovementObstructionChanged::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageMovementObstructionChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
return new CMessageMovementObstructionChanged();
}
////////////////////////////////
-JS::Value CMessageObstructionMapShapeChanged::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageObstructionMapShapeChanged::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageObstructionMapShapeChanged::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageObstructionMapShapeChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
return new CMessageObstructionMapShapeChanged();
}
////////////////////////////////
-JS::Value CMessageTerritoriesChanged::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageTerritoriesChanged::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageTerritoriesChanged::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageTerritoriesChanged::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
return new CMessageTerritoriesChanged();
}
////////////////////////////////
-JS::Value CMessageRangeUpdate::ToJSVal(const ScriptInterface& scriptInterface) const
+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 ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageRangeUpdate::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
LOGWARNING("CMessageRangeUpdate::FromJSVal not implemented");
return NULL;
}
////////////////////////////////
-JS::Value CMessagePathResult::ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const
+JS::Value CMessagePathResult::ToJSVal(const ScriptRequest& UNUSED(rq)) const
{
LOGWARNING("CMessagePathResult::ToJSVal not implemented");
return JS::UndefinedValue();
}
-CMessage* CMessagePathResult::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessagePathResult::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
LOGWARNING("CMessagePathResult::FromJSVal not implemented");
return NULL;
}
////////////////////////////////
-JS::Value CMessageValueModification::ToJSVal(const ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+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 ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+JS::Value CMessageMinimapPing::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageMinimapPing::FromJSVal(const ScriptInterface& UNUSED(scriptInterface), JS::HandleValue UNUSED(val))
+CMessage* CMessageMinimapPing::FromJSVal(const ScriptRequest& UNUSED(rq), JS::HandleValue UNUSED(val))
{
return new CMessageMinimapPing();
}
////////////////////////////////
-JS::Value CMessageCinemaPathEnded::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessageCinemaPathEnded::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(name);
return JS::ObjectValue(*obj);
}
-CMessage* CMessageCinemaPathEnded::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptInterface) const
+JS::Value CMessageCinemaQueueEnded::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
return JS::ObjectValue(*obj);
}
-CMessage* CMessageCinemaQueueEnded::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+CMessage* CMessageCinemaQueueEnded::FromJSVal(const ScriptRequest& rq, JS::HandleValue val)
{
FROMJSVAL_SETUP();
return new CMessageCinemaQueueEnded();
}
////////////////////////////////////////////////////////////////
-JS::Value CMessagePlayerColorChanged::ToJSVal(const ScriptInterface& scriptInterface) const
+JS::Value CMessagePlayerColorChanged::ToJSVal(const ScriptRequest& rq) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(player);
return JS::ObjectValue(*obj);
}
-CMessage* CMessagePlayerColorChanged::FromJSVal(const ScriptInterface& scriptInterface, JS::HandleValue val)
+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 ScriptInterface& scriptingInterface, JS::HandleValue val)
+CMessage* CMessageFromJSVal(int mtid, const ScriptRequest& rq, JS::HandleValue val)
{
switch (mtid)
{
-#define MESSAGE(name) case MT_##name: return CMessage##name::FromJSVal(scriptingInterface, val);
+#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;
}
Index: ps/trunk/source/simulation2/scripting/ScriptComponent.cpp
===================================================================
--- ps/trunk/source/simulation2/scripting/ScriptComponent.cpp (revision 27727)
+++ ps/trunk/source/simulation2/scripting/ScriptComponent.cpp (revision 27728)
@@ -1,89 +1,89 @@
-/* Copyright (C) 2021 Wildfire Games.
+/* 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 "ScriptComponent.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/Object.h"
#include "simulation2/serialization/ISerializer.h"
#include "simulation2/serialization/IDeserializer.h"
CComponentTypeScript::CComponentTypeScript(const ScriptInterface& scriptInterface, JS::HandleValue instance) :
m_ScriptInterface(scriptInterface)
{
m_Instance.init(ScriptRequest(m_ScriptInterface).cx, instance);
}
void CComponentTypeScript::Init(const CParamNode& paramNode, entity_id_t ent)
{
ScriptRequest rq(m_ScriptInterface);
Script::SetProperty(rq, m_Instance, "entity", (int)ent, true, false);
Script::SetProperty(rq, m_Instance, "template", paramNode, true, false);
ScriptFunction::CallVoid(rq, m_Instance, "Init");
}
void CComponentTypeScript::Deinit()
{
ScriptRequest rq(m_ScriptInterface);
ScriptFunction::CallVoid(rq, m_Instance, "Deinit");
}
void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global)
{
ScriptRequest rq(m_ScriptInterface);
const char* name = global ? msg.GetScriptGlobalHandlerName() : msg.GetScriptHandlerName();
- JS::RootedValue msgVal(rq.cx, msg.ToJSValCached(m_ScriptInterface));
+ JS::RootedValue msgVal(rq.cx, msg.ToJSValCached(rq));
if (!ScriptFunction::CallVoid(rq, m_Instance, name, msgVal))
LOGERROR("Script message handler %s failed", name);
}
void CComponentTypeScript::Serialize(ISerializer& serialize)
{
ScriptRequest rq(m_ScriptInterface);
try
{
serialize.ScriptVal("comp", &m_Instance);
}
catch(PSERROR_Serialize& err)
{
int ent = INVALID_ENTITY;
Script::GetProperty(rq, m_Instance, "entity", ent);
std::string name = "(error)";
Script::GetObjectClassName(rq, m_Instance, name);
LOGERROR("Script component %s of entity %i failed to serialize: %s\nSerializing:\n%s", name, ent, err.what(), Script::ToString(rq, &m_Instance));
// Rethrow now that we added more details
throw;
}
}
void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize, entity_id_t ent)
{
ScriptRequest rq(m_ScriptInterface);
Script::SetProperty(rq, m_Instance, "entity", (int)ent, true, false);
Script::SetProperty(rq, m_Instance, "template", paramNode, true, false);
deserialize.ScriptObjectAssign("comp", m_Instance);
}
Index: ps/trunk/source/simulation2/system/ComponentManager.cpp
===================================================================
--- ps/trunk/source/simulation2/system/ComponentManager.cpp (revision 27727)
+++ ps/trunk/source/simulation2/system/ComponentManager.cpp (revision 27728)
@@ -1,1163 +1,1164 @@
-/* Copyright (C) 2022 Wildfire Games.
+/* 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 "ComponentManager.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Profile.h"
#include "ps/scripting/JSInterface_VFS.h"
#include "scriptinterface/FunctionWrapper.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/system/DynamicSubscription.h"
#include "simulation2/system/IComponent.h"
#include "simulation2/system/ParamNode.h"
#include "simulation2/system/SimContext.h"
#include
/**
* Used for script-only message types.
*/
class CMessageScripted final : public CMessage
{
public:
virtual int GetType() const { return mtid; }
virtual const char* GetScriptHandlerName() const { return handlerName.c_str(); }
virtual const char* GetScriptGlobalHandlerName() const { return globalHandlerName.c_str(); }
- virtual JS::Value ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); }
+ virtual JS::Value ToJSVal(const ScriptRequest& UNUSED(rq)) const { return msg.get(); }
- CMessageScripted(const ScriptInterface& scriptInterface, int mtid, const std::string& name, JS::HandleValue msg) :
- mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetGeneralJSContext(), msg)
+ CMessageScripted(const ScriptRequest& rq, int mtid, const std::string& name, JS::HandleValue msg) :
+ mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(rq.cx, msg)
{
}
int mtid;
std::string handlerName;
std::string globalHandlerName;
JS::PersistentRootedValue msg;
};
CComponentManager::CComponentManager(CSimContext& context, std::shared_ptr cx, bool skipScriptFunctions) :
m_NextScriptComponentTypeId(CID__LastNative),
m_ScriptInterface("Engine", "Simulation", cx),
m_SimContext(context), m_CurrentlyHotloading(false)
{
context.SetComponentManager(this);
m_ScriptInterface.SetCallbackData(static_cast (this));
m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
// For component script tests, the test system sets up its own scripted implementation of
// these functions, so we skip registering them here in those cases
if (!skipScriptFunctions)
{
JSI_VFS::RegisterScriptFunctions_ReadOnlySimulation(m_ScriptInterface);
ScriptRequest rq(m_ScriptInterface);
constexpr ScriptFunction::ObjectGetter Getter = &ScriptInterface::ObjectFromCBData;
ScriptFunction::Register<&CComponentManager::Script_RegisterComponentType, Getter>(rq, "RegisterComponentType");
ScriptFunction::Register<&CComponentManager::Script_RegisterSystemComponentType, Getter>(rq, "RegisterSystemComponentType");
ScriptFunction::Register<&CComponentManager::Script_ReRegisterComponentType, Getter>(rq, "ReRegisterComponentType");
ScriptFunction::Register<&CComponentManager::Script_RegisterInterface, Getter>(rq, "RegisterInterface");
ScriptFunction::Register<&CComponentManager::Script_RegisterMessageType, Getter>(rq, "RegisterMessageType");
ScriptFunction::Register<&CComponentManager::Script_RegisterGlobal, Getter>(rq, "RegisterGlobal");
ScriptFunction::Register<&CComponentManager::Script_GetEntitiesWithInterface, Getter>(rq, "GetEntitiesWithInterface");
ScriptFunction::Register<&CComponentManager::Script_GetComponentsWithInterface, Getter>(rq, "GetComponentsWithInterface");
ScriptFunction::Register<&CComponentManager::Script_PostMessage, Getter>(rq, "PostMessage");
ScriptFunction::Register<&CComponentManager::Script_BroadcastMessage, Getter>(rq, "BroadcastMessage");
ScriptFunction::Register<&CComponentManager::Script_AddEntity, Getter>(rq, "AddEntity");
ScriptFunction::Register<&CComponentManager::Script_AddLocalEntity, Getter>(rq, "AddLocalEntity");
ScriptFunction::Register<&CComponentManager::QueryInterface, Getter>(rq, "QueryInterface");
ScriptFunction::Register<&CComponentManager::DestroyComponentsSoon, Getter>(rq, "DestroyEntity");
ScriptFunction::Register<&CComponentManager::FlushDestroyedComponents, Getter>(rq, "FlushDestroyedEntities");
ScriptFunction::Register<&CComponentManager::Script_GetTemplate, Getter>(rq, "GetTemplate");
}
// Globalscripts may use VFS script functions
m_ScriptInterface.LoadGlobalScripts();
// Define MT_*, IID_* as script globals, and store their names
#define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name);
#define INTERFACE(name) \
m_ScriptInterface.SetGlobal("IID_" #name, (int)IID_##name); \
m_InterfaceIdsByName[#name] = IID_##name;
#define COMPONENT(name)
#include "simulation2/TypeList.h"
#undef MESSAGE
#undef INTERFACE
#undef COMPONENT
m_ScriptInterface.SetGlobal("INVALID_ENTITY", (int)INVALID_ENTITY);
m_ScriptInterface.SetGlobal("INVALID_PLAYER", (int)INVALID_PLAYER);
m_ScriptInterface.SetGlobal("SYSTEM_ENTITY", (int)SYSTEM_ENTITY);
m_ComponentsByInterface.resize(IID__LastNative);
ResetState();
}
CComponentManager::~CComponentManager()
{
ResetState();
}
void CComponentManager::LoadComponentTypes()
{
#define MESSAGE(name) \
RegisterMessageType(MT_##name, #name);
#define INTERFACE(name) \
extern void RegisterComponentInterface_##name(ScriptInterface&); \
RegisterComponentInterface_##name(m_ScriptInterface);
#define COMPONENT(name) \
extern void RegisterComponentType_##name(CComponentManager&); \
m_CurrentComponent = CID_##name; \
RegisterComponentType_##name(*this);
#include "simulation2/TypeList.h"
m_CurrentComponent = CID__Invalid;
#undef MESSAGE
#undef INTERFACE
#undef COMPONENT
}
bool CComponentManager::LoadScript(const VfsPath& filename, bool hotload)
{
m_CurrentlyHotloading = hotload;
CVFSFile file;
PSRETURN loadOk = file.Load(g_VFS, filename);
if (loadOk != PSRETURN_OK) // VFS will log the failed file and the reason
return false;
std::string content = file.DecodeUTF8(); // assume it's UTF-8
bool ok = m_ScriptInterface.LoadScript(filename, content);
m_CurrentlyHotloading = false;
return ok;
}
void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent)
{
ScriptRequest rq(m_ScriptInterface);
// Find the C++ component that wraps the interface
int cidWrapper = GetScriptWrapper(iid);
if (cidWrapper == CID__Invalid)
{
ScriptException::Raise(rq, "Invalid interface id");
return;
}
const ComponentType& ctWrapper = m_ComponentTypesById[cidWrapper];
bool mustReloadComponents = false; // for hotloading
ComponentTypeId cid = LookupCID(cname);
if (cid == CID__Invalid)
{
if (reRegister)
{
ScriptException::Raise(rq, "ReRegistering component type that was not registered before '%s'", cname.c_str());
return;
}
// Allocate a new cid number
cid = m_NextScriptComponentTypeId++;
m_ComponentTypeIdsByName[cname] = cid;
if (systemComponent)
MarkScriptedComponentForSystemEntity(cid);
}
else
{
// Component type is already loaded, so do hotloading:
if (!m_CurrentlyHotloading && !reRegister)
{
ScriptException::Raise(rq, "Registering component type with already-registered name '%s'", cname.c_str());
return;
}
const ComponentType& ctPrevious = m_ComponentTypesById[cid];
// We can only replace scripted component types, not native ones
if (ctPrevious.type != CT_Script)
{
ScriptException::Raise(rq, "Loading script component type with same name '%s' as native component", cname.c_str());
return;
}
// We don't support changing the IID of a component type (it would require fiddling
// around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity)
if (ctPrevious.iid != iid)
{
// ...though it only matters if any components exist with this type
if (!m_ComponentsByTypeId[cid].empty())
{
ScriptException::Raise(rq, "Hotloading script component type mustn't change interface ID");
return;
}
}
// Remove the old component type's message subscriptions
std::map >::iterator it;
for (it = m_LocalMessageSubscriptions.begin(); it != m_LocalMessageSubscriptions.end(); ++it)
{
std::vector& types = it->second;
std::vector::iterator ctit = find(types.begin(), types.end(), cid);
if (ctit != types.end())
types.erase(ctit);
}
for (it = m_GlobalMessageSubscriptions.begin(); it != m_GlobalMessageSubscriptions.end(); ++it)
{
std::vector& types = it->second;
std::vector::iterator ctit = find(types.begin(), types.end(), cid);
if (ctit != types.end())
types.erase(ctit);
}
mustReloadComponents = true;
}
JS::RootedValue protoVal(rq.cx);
if (!Script::GetProperty(rq, ctor, "prototype", &protoVal))
{
ScriptException::Raise(rq, "Failed to get property 'prototype'");
return;
}
if (!protoVal.isObject())
{
ScriptException::Raise(rq, "Component has no constructor");
return;
}
std::string schema = "";
if (Script::HasProperty(rq, protoVal, "Schema"))
Script::GetProperty(rq, protoVal, "Schema", schema);
// Construct a new ComponentType, using the wrapper's alloc functions
ComponentType ct{
CT_Script,
iid,
ctWrapper.alloc,
ctWrapper.dealloc,
cname,
schema,
std::make_unique(rq.cx, ctor)
};
m_ComponentTypesById[cid] = std::move(ct);
m_CurrentComponent = cid; // needed by Subscribe
// Find all the ctor prototype's On* methods, and subscribe to the appropriate messages:
std::vector methods;
if (!Script::EnumeratePropertyNames(rq, protoVal, false, methods))
{
ScriptException::Raise(rq, "Failed to enumerate component properties.");
return;
}
for (const std::string& method : methods)
{
if (std::string_view{method}.substr(0, 2) != "On")
continue;
std::string_view name{std::string_view{method}.substr(2)}; // strip the "On" prefix
// Handle "OnGlobalFoo" functions specially
bool isGlobal = false;
if (std::string_view{name}.substr(0, 6) == "Global")
{
isGlobal = true;
name.remove_prefix(6);
}
auto mit = m_MessageTypeIdsByName.find(std::string{name});
if (mit == m_MessageTypeIdsByName.end())
{
ScriptException::Raise(rq,
"Registered component has unrecognized '%s' message handler method",
method.c_str());
return;
}
if (isGlobal)
SubscribeGloballyToMessageType(mit->second);
else
SubscribeToMessageType(mit->second);
}
m_CurrentComponent = CID__Invalid;
if (mustReloadComponents)
{
// For every script component with this cid, we need to switch its
// prototype from the old constructor's prototype property to the new one's
const std::map& comps = m_ComponentsByTypeId[cid];
std::map::const_iterator eit = comps.begin();
for (; eit != comps.end(); ++eit)
{
JS::RootedValue instance(rq.cx, eit->second->GetJSInstance());
if (!instance.isNull())
m_ScriptInterface.SetPrototype(instance, protoVal);
}
}
}
void CComponentManager::Script_RegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
{
Script_RegisterComponentType_Common(iid, cname, ctor, false, false);
m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
}
void CComponentManager::Script_RegisterSystemComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
{
Script_RegisterComponentType_Common(iid, cname, ctor, false, true);
m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
}
void CComponentManager::Script_ReRegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
{
Script_RegisterComponentType_Common(iid, cname, ctor, true, false);
}
void CComponentManager::Script_RegisterInterface(const std::string& name)
{
std::map::iterator it = m_InterfaceIdsByName.find(name);
if (it != m_InterfaceIdsByName.end())
{
// Redefinitions are fine (and just get ignored) when hotloading; otherwise
// they're probably unintentional and should be reported
if (!m_CurrentlyHotloading)
{
ScriptRequest rq(m_ScriptInterface);
ScriptException::Raise(rq, "Registering interface with already-registered name '%s'", name.c_str());
}
return;
}
// IIDs start at 1, so size+1 is the next unused one
size_t id = m_InterfaceIdsByName.size() + 1;
m_InterfaceIdsByName[name] = (InterfaceId)id;
m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId
m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id);
}
void CComponentManager::Script_RegisterMessageType(const std::string& name)
{
std::map::iterator it = m_MessageTypeIdsByName.find(name);
if (it != m_MessageTypeIdsByName.end())
{
// Redefinitions are fine (and just get ignored) when hotloading; otherwise
// they're probably unintentional and should be reported
if (!m_CurrentlyHotloading)
{
ScriptRequest rq(m_ScriptInterface);
ScriptException::Raise(rq, "Registering message type with already-registered name '%s'", name.c_str());
}
return;
}
// MTIDs start at 1, so size+1 is the next unused one
size_t id = m_MessageTypeIdsByName.size() + 1;
RegisterMessageType((MessageTypeId)id, name.c_str());
m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id);
}
void CComponentManager::Script_RegisterGlobal(const std::string& name, JS::HandleValue value)
{
m_ScriptInterface.SetGlobal(name.c_str(), value, m_CurrentlyHotloading);
}
const CParamNode& CComponentManager::Script_GetTemplate(const std::string& templateName)
{
static CParamNode nullNode(false);
ICmpTemplateManager* cmpTemplateManager = static_cast (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
if (!cmpTemplateManager)
{
LOGERROR("Template manager is not loaded");
return nullNode;
}
const CParamNode* tmpl = cmpTemplateManager->GetTemplate(templateName);
if (!tmpl)
return nullNode;
return *tmpl;
}
std::vector CComponentManager::Script_GetEntitiesWithInterface(int iid)
{
std::vector ret;
const InterfaceListUnordered& ents = GetEntitiesWithInterfaceUnordered(iid);
for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
if (!ENTITY_IS_LOCAL(it->first))
ret.push_back(it->first);
std::sort(ret.begin(), ret.end());
return ret;
}
std::vector CComponentManager::Script_GetComponentsWithInterface(int iid)
{
std::vector ret;
InterfaceList ents = GetEntitiesWithInterface(iid);
for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
ret.push_back(it->second); // TODO: maybe we should exclude local entities
return ret;
}
CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data)
{
if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here)
LOGERROR("PostMessage with invalid message type ID '%d'", mtid);
+ ScriptRequest rq(m_ScriptInterface);
if (mtid < MT__LastNative)
{
- return CMessageFromJSVal(mtid, m_ScriptInterface, data);
+ return CMessageFromJSVal(mtid, rq, data);
}
else
{
- return new CMessageScripted(m_ScriptInterface, mtid, m_MessageTypeNamesById[mtid], data);
+ return new CMessageScripted(rq, mtid, m_MessageTypeNamesById[mtid], data);
}
}
void CComponentManager::Script_PostMessage(int ent, int mtid, JS::HandleValue data)
{
CMessage* msg = ConstructMessage(mtid, data);
if (!msg)
return; // error
PostMessage(ent, *msg);
delete msg;
}
void CComponentManager::Script_BroadcastMessage(int mtid, JS::HandleValue data)
{
CMessage* msg = ConstructMessage(mtid, data);
if (!msg)
return; // error
BroadcastMessage(*msg);
delete msg;
}
int CComponentManager::Script_AddEntity(const std::wstring& templateName)
{
// TODO: should validate the string to make sure it doesn't contain scary characters
// that will let it access non-component-template files
return AddEntity(templateName, AllocateNewEntity());
}
int CComponentManager::Script_AddLocalEntity(const std::wstring& templateName)
{
// TODO: should validate the string to make sure it doesn't contain scary characters
// that will let it access non-component-template files
return AddEntity(templateName, AllocateNewLocalEntity());
}
void CComponentManager::ResetState()
{
// Delete all dynamic message subscriptions
m_DynamicMessageSubscriptionsNonsync.clear();
m_DynamicMessageSubscriptionsNonsyncByComponent.clear();
// Delete all IComponents in reverse order of creation.
std::map >::reverse_iterator iit = m_ComponentsByTypeId.rbegin();
for (; iit != m_ComponentsByTypeId.rend(); ++iit)
{
std::map::iterator eit = iit->second.begin();
for (; eit != iit->second.end(); ++eit)
{
eit->second->Deinit();
m_ComponentTypesById[iit->first].dealloc(eit->second);
}
}
std::vector >::iterator ifcit = m_ComponentsByInterface.begin();
for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
ifcit->clear();
m_ComponentsByTypeId.clear();
// Delete all SEntityComponentCaches
std::unordered_map::iterator ccit = m_ComponentCaches.begin();
for (; ccit != m_ComponentCaches.end(); ++ccit)
free(ccit->second);
m_ComponentCaches.clear();
m_SystemEntity = CEntityHandle();
m_DestructionQueue.clear();
// Reset IDs
m_NextEntityId = SYSTEM_ENTITY + 1;
m_NextLocalEntityId = FIRST_LOCAL_ENTITY;
}
void CComponentManager::SetRNGSeed(u32 seed)
{
m_RNG.seed(seed);
}
void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc,
const char* name, const std::string& schema)
{
ComponentType c{ CT_Native, iid, alloc, dealloc, name, schema, std::unique_ptr() };
m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
m_ComponentTypeIdsByName[name] = cid;
}
void CComponentManager::RegisterComponentTypeScriptWrapper(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc,
DeallocFunc dealloc, const char* name, const std::string& schema)
{
ComponentType c{ CT_ScriptWrapper, iid, alloc, dealloc, name, schema, std::unique_ptr() };
m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
m_ComponentTypeIdsByName[name] = cid;
// TODO: merge with RegisterComponentType
}
void CComponentManager::MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid)
{
m_ScriptedSystemComponents.push_back(cid);
}
void CComponentManager::RegisterMessageType(MessageTypeId mtid, const char* name)
{
m_MessageTypeIdsByName[name] = mtid;
m_MessageTypeNamesById[mtid] = name;
}
void CComponentManager::SubscribeToMessageType(MessageTypeId mtid)
{
// TODO: verify mtid
ENSURE(m_CurrentComponent != CID__Invalid);
std::vector& types = m_LocalMessageSubscriptions[mtid];
types.push_back(m_CurrentComponent);
std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
}
void CComponentManager::SubscribeGloballyToMessageType(MessageTypeId mtid)
{
// TODO: verify mtid
ENSURE(m_CurrentComponent != CID__Invalid);
std::vector& types = m_GlobalMessageSubscriptions[mtid];
types.push_back(m_CurrentComponent);
std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
}
void CComponentManager::FlattenDynamicSubscriptions()
{
std::map::iterator it;
for (it = m_DynamicMessageSubscriptionsNonsync.begin();
it != m_DynamicMessageSubscriptionsNonsync.end(); ++it)
{
it->second.Flatten();
}
}
void CComponentManager::DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent* component, bool enable)
{
if (enable)
{
bool newlyInserted = m_DynamicMessageSubscriptionsNonsyncByComponent[component].insert(mtid).second;
if (newlyInserted)
m_DynamicMessageSubscriptionsNonsync[mtid].Add(component);
}
else
{
size_t numRemoved = m_DynamicMessageSubscriptionsNonsyncByComponent[component].erase(mtid);
if (numRemoved)
m_DynamicMessageSubscriptionsNonsync[mtid].Remove(component);
}
}
void CComponentManager::RemoveComponentDynamicSubscriptions(IComponent* component)
{
std::map >::iterator it = m_DynamicMessageSubscriptionsNonsyncByComponent.find(component);
if (it == m_DynamicMessageSubscriptionsNonsyncByComponent.end())
return;
std::set::iterator mtit;
for (mtit = it->second.begin(); mtit != it->second.end(); ++mtit)
{
m_DynamicMessageSubscriptionsNonsync[*mtit].Remove(component);
// Need to flatten the subscription lists immediately to avoid dangling IComponent* references
m_DynamicMessageSubscriptionsNonsync[*mtit].Flatten();
}
m_DynamicMessageSubscriptionsNonsyncByComponent.erase(it);
}
CComponentManager::ComponentTypeId CComponentManager::LookupCID(const std::string& cname) const
{
std::map::const_iterator it = m_ComponentTypeIdsByName.find(cname);
if (it == m_ComponentTypeIdsByName.end())
return CID__Invalid;
return it->second;
}
std::string CComponentManager::LookupComponentTypeName(ComponentTypeId cid) const
{
std::map::const_iterator it = m_ComponentTypesById.find(cid);
if (it == m_ComponentTypesById.end())
return "";
return it->second.name;
}
CComponentManager::ComponentTypeId CComponentManager::GetScriptWrapper(InterfaceId iid)
{
if (iid >= IID__LastNative && iid <= (int)m_InterfaceIdsByName.size()) // use <= since IDs start at 1
return CID_UnknownScript;
std::map::const_iterator it = m_ComponentTypesById.begin();
for (; it != m_ComponentTypesById.end(); ++it)
if (it->second.iid == iid && it->second.type == CT_ScriptWrapper)
return it->first;
std::map::const_iterator iiit = m_InterfaceIdsByName.begin();
for (; iiit != m_InterfaceIdsByName.end(); ++iiit)
if (iiit->second == iid)
{
LOGERROR("No script wrapper found for interface id %d '%s'", iid, iiit->first.c_str());
return CID__Invalid;
}
LOGERROR("No script wrapper found for interface id %d", iid);
return CID__Invalid;
}
entity_id_t CComponentManager::AllocateNewEntity()
{
entity_id_t id = m_NextEntityId++;
// TODO: check for overflow
return id;
}
entity_id_t CComponentManager::AllocateNewLocalEntity()
{
entity_id_t id = m_NextLocalEntityId++;
// TODO: check for overflow
return id;
}
entity_id_t CComponentManager::AllocateNewEntity(entity_id_t preferredId)
{
// TODO: ensure this ID hasn't been allocated before
// (this might occur with broken map files)
// Trying to actually add two entities with the same id will fail in AddEntitiy
entity_id_t id = preferredId;
// Ensure this ID won't be allocated again
if (id >= m_NextEntityId)
m_NextEntityId = id+1;
// TODO: check for overflow
return id;
}
bool CComponentManager::AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode)
{
IComponent* component = ConstructComponent(ent, cid);
if (!component)
return false;
component->Init(paramNode);
return true;
}
void CComponentManager::AddSystemComponents(bool skipScriptedComponents, bool skipAI)
{
CParamNode noParam;
AddComponent(m_SystemEntity, CID_TemplateManager, noParam);
AddComponent(m_SystemEntity, CID_CinemaManager, noParam);
AddComponent(m_SystemEntity, CID_CommandQueue, noParam);
AddComponent(m_SystemEntity, CID_ObstructionManager, noParam);
AddComponent(m_SystemEntity, CID_ParticleManager, noParam);
AddComponent(m_SystemEntity, CID_Pathfinder, noParam);
AddComponent(m_SystemEntity, CID_ProjectileManager, noParam);
AddComponent(m_SystemEntity, CID_RangeManager, noParam);
AddComponent(m_SystemEntity, CID_SoundManager, noParam);
AddComponent(m_SystemEntity, CID_Terrain, noParam);
AddComponent(m_SystemEntity, CID_TerritoryManager, noParam);
AddComponent(m_SystemEntity, CID_UnitMotionManager, noParam);
AddComponent(m_SystemEntity, CID_UnitRenderer, noParam);
AddComponent(m_SystemEntity, CID_WaterManager, noParam);
// Add scripted system components:
if (!skipScriptedComponents)
{
for (uint32_t i = 0; i < m_ScriptedSystemComponents.size(); ++i)
AddComponent(m_SystemEntity, m_ScriptedSystemComponents[i], noParam);
if (!skipAI)
AddComponent(m_SystemEntity, CID_AIManager, noParam);
}
}
IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
{
ScriptRequest rq(m_ScriptInterface);
std::map::const_iterator it = m_ComponentTypesById.find(cid);
if (it == m_ComponentTypesById.end())
{
LOGERROR("Invalid component id %d", cid);
return NULL;
}
const ComponentType& ct = it->second;
ENSURE((size_t)ct.iid < m_ComponentsByInterface.size());
std::unordered_map& emap1 = m_ComponentsByInterface[ct.iid];
if (emap1.find(ent.GetId()) != emap1.end())
{
LOGERROR("Multiple components for interface %d", ct.iid);
return NULL;
}
std::map& emap2 = m_ComponentsByTypeId[cid];
// If this is a scripted component, construct the appropriate JS object first
JS::RootedValue obj(rq.cx);
if (ct.type == CT_Script)
{
m_ScriptInterface.CallConstructor(*ct.ctor, JS::HandleValueArray::empty(), &obj);
if (obj.isNull())
{
LOGERROR("Script component constructor failed");
return NULL;
}
}
// Construct the new component
// NB: The unit motion manager relies on components not moving in memory once constructed.
IComponent* component = ct.alloc(m_ScriptInterface, obj);
ENSURE(component);
component->SetEntityHandle(ent);
component->SetSimContext(m_SimContext);
// Store a reference to the new component
emap1.insert(std::make_pair(ent.GetId(), component));
emap2.insert(std::make_pair(ent.GetId(), component));
// TODO: We need to more careful about this - if an entity is constructed by a component
// while we're iterating over all components, this will invalidate the iterators and everything
// will break.
// We probably need some kind of delayed addition, so they get pushed onto a queue and then
// inserted into the world later on. (Be careful about immediation deletion in that case, too.)
SEntityComponentCache* cache = ent.GetComponentCache();
ENSURE(cache != NULL && ct.iid < (int)cache->numInterfaces && cache->interfaces[ct.iid] == NULL);
cache->interfaces[ct.iid] = component;
return component;
}
void CComponentManager::AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component)
{
// Just add it into the by-interface map, not the by-component-type map,
// so it won't be considered for messages or deletion etc
std::unordered_map& emap1 = m_ComponentsByInterface.at(iid);
if (emap1.find(ent.GetId()) != emap1.end())
debug_warn(L"Multiple components for interface");
emap1.insert(std::make_pair(ent.GetId(), &component));
SEntityComponentCache* cache = ent.GetComponentCache();
ENSURE(cache != NULL && iid < (int)cache->numInterfaces && cache->interfaces[iid] == NULL);
cache->interfaces[iid] = &component;
}
CEntityHandle CComponentManager::AllocateEntityHandle(entity_id_t ent)
{
ENSURE(!EntityExists(ent));
// Interface IDs start at 1, and SEntityComponentCache is defined with a 1-sized array,
// so we need space for an extra m_InterfaceIdsByName.size() items
SEntityComponentCache* cache = (SEntityComponentCache*)calloc(1,
sizeof(SEntityComponentCache) + sizeof(IComponent*) * m_InterfaceIdsByName.size());
ENSURE(cache != NULL);
cache->numInterfaces = m_InterfaceIdsByName.size() + 1;
m_ComponentCaches[ent] = cache;
return CEntityHandle(ent, cache);
}
CEntityHandle CComponentManager::LookupEntityHandle(entity_id_t ent, bool allowCreate)
{
std::unordered_map::iterator it;
it = m_ComponentCaches.find(ent);
if (it == m_ComponentCaches.end())
{
if (allowCreate)
return AllocateEntityHandle(ent);
else
return CEntityHandle(ent, NULL);
}
else
return CEntityHandle(ent, it->second);
}
void CComponentManager::InitSystemEntity()
{
ENSURE(m_SystemEntity.GetId() == INVALID_ENTITY);
m_SystemEntity = AllocateEntityHandle(SYSTEM_ENTITY);
m_SimContext.SetSystemEntity(m_SystemEntity);
}
entity_id_t CComponentManager::AddEntity(const std::wstring& templateName, entity_id_t ent)
{
ICmpTemplateManager *cmpTemplateManager = static_cast (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
if (!cmpTemplateManager)
{
debug_warn(L"No ICmpTemplateManager loaded");
return INVALID_ENTITY;
}
const CParamNode* tmpl = cmpTemplateManager->LoadTemplate(ent, utf8_from_wstring(templateName));
if (!tmpl)
return INVALID_ENTITY; // LoadTemplate will have reported the error
// This also ensures that ent does not exist
CEntityHandle handle = AllocateEntityHandle(ent);
// Construct a component for each child of the root element
const CParamNode::ChildrenMap& tmplChilds = tmpl->GetChildren();
for (CParamNode::ChildrenMap::const_iterator it = tmplChilds.begin(); it != tmplChilds.end(); ++it)
{
// Ignore attributes on the root element
if (it->first.length() && it->first[0] == '@')
continue;
CComponentManager::ComponentTypeId cid = LookupCID(it->first);
if (cid == CID__Invalid)
{
LOGERROR("Unrecognized component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
return INVALID_ENTITY;
}
if (!AddComponent(handle, cid, it->second))
{
LOGERROR("Failed to construct component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
return INVALID_ENTITY;
}
// TODO: maybe we should delete already-constructed components if one of them fails?
}
CMessageCreate msg(ent);
PostMessage(ent, msg);
return ent;
}
bool CComponentManager::EntityExists(entity_id_t ent) const
{
return m_ComponentCaches.find(ent) != m_ComponentCaches.end();
}
void CComponentManager::DestroyComponentsSoon(entity_id_t ent)
{
m_DestructionQueue.push_back(ent);
}
void CComponentManager::FlushDestroyedComponents()
{
PROFILE2("Flush Destroyed Components");
while (!m_DestructionQueue.empty())
{
// Make a copy of the destruction queue, so that the iterators won't be invalidated if the
// CMessageDestroy handlers try to destroy more entities themselves
std::vector queue;
queue.swap(m_DestructionQueue);
for (std::vector::iterator it = queue.begin(); it != queue.end(); ++it)
{
entity_id_t ent = *it;
// Do nothing if invalid, destroyed, etc.
if (!EntityExists(ent))
continue;
CEntityHandle handle = LookupEntityHandle(ent);
CMessageDestroy msg(ent);
PostMessage(ent, msg);
// Flatten all the dynamic subscriptions to ensure there are no dangling
// references in the 'removed' lists to components we're going to delete
// Some components may have dynamically unsubscribed following the Destroy message
FlattenDynamicSubscriptions();
// Destroy the components, and remove from m_ComponentsByTypeId:
std::map >::iterator iit = m_ComponentsByTypeId.begin();
for (; iit != m_ComponentsByTypeId.end(); ++iit)
{
std::map::iterator eit = iit->second.find(ent);
if (eit != iit->second.end())
{
eit->second->Deinit();
RemoveComponentDynamicSubscriptions(eit->second);
m_ComponentTypesById[iit->first].dealloc(eit->second);
iit->second.erase(ent);
handle.GetComponentCache()->interfaces[m_ComponentTypesById[iit->first].iid] = NULL;
}
}
free(handle.GetComponentCache());
m_ComponentCaches.erase(ent);
// Remove from m_ComponentsByInterface
std::vector >::iterator ifcit = m_ComponentsByInterface.begin();
for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
{
ifcit->erase(ent);
}
}
}
}
IComponent* CComponentManager::QueryInterface(entity_id_t ent, InterfaceId iid) const
{
if ((size_t)iid >= m_ComponentsByInterface.size())
{
// Invalid iid
return NULL;
}
std::unordered_map::const_iterator eit = m_ComponentsByInterface[iid].find(ent);
if (eit == m_ComponentsByInterface[iid].end())
{
// This entity doesn't implement this interface
return NULL;
}
return eit->second;
}
CComponentManager::InterfaceList CComponentManager::GetEntitiesWithInterface(InterfaceId iid) const
{
std::vector > ret;
if ((size_t)iid >= m_ComponentsByInterface.size())
{
// Invalid iid
return ret;
}
ret.reserve(m_ComponentsByInterface[iid].size());
std::unordered_map::const_iterator it = m_ComponentsByInterface[iid].begin();
for (; it != m_ComponentsByInterface[iid].end(); ++it)
ret.push_back(*it);
std::sort(ret.begin(), ret.end()); // lexicographic pair comparison means this'll sort by entity ID
return ret;
}
static CComponentManager::InterfaceListUnordered g_EmptyEntityMap;
const CComponentManager::InterfaceListUnordered& CComponentManager::GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
{
if ((size_t)iid >= m_ComponentsByInterface.size())
{
// Invalid iid
return g_EmptyEntityMap;
}
return m_ComponentsByInterface[iid];
}
void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg)
{
PROFILE2_IFSPIKE("Post Message", 0.0005);
PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
// Send the message to components of ent, that subscribed locally to this message
std::map >::const_iterator it;
it = m_LocalMessageSubscriptions.find(msg.GetType());
if (it != m_LocalMessageSubscriptions.end())
{
std::vector::const_iterator ctit = it->second.begin();
for (; ctit != it->second.end(); ++ctit)
{
// Find the component instances of this type (if any)
std::map >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
if (emap == m_ComponentsByTypeId.end())
continue;
// Send the message to all of them
std::map::const_iterator eit = emap->second.find(ent);
if (eit != emap->second.end())
eit->second->HandleMessage(msg, false);
}
}
SendGlobalMessage(ent, msg);
}
void CComponentManager::BroadcastMessage(const CMessage& msg)
{
// Send the message to components of all entities that subscribed locally to this message
std::map >::const_iterator it;
it = m_LocalMessageSubscriptions.find(msg.GetType());
if (it != m_LocalMessageSubscriptions.end())
{
std::vector::const_iterator ctit = it->second.begin();
for (; ctit != it->second.end(); ++ctit)
{
// Find the component instances of this type (if any)
std::map >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
if (emap == m_ComponentsByTypeId.end())
continue;
// Send the message to all of them
std::map::const_iterator eit = emap->second.begin();
for (; eit != emap->second.end(); ++eit)
eit->second->HandleMessage(msg, false);
}
}
SendGlobalMessage(INVALID_ENTITY, msg);
}
void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg)
{
PROFILE2_IFSPIKE("SendGlobalMessage", 0.001);
PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
// (Common functionality for PostMessage and BroadcastMessage)
// Send the message to components of all entities that subscribed globally to this message
std::map >::const_iterator it;
it = m_GlobalMessageSubscriptions.find(msg.GetType());
if (it != m_GlobalMessageSubscriptions.end())
{
std::vector::const_iterator ctit = it->second.begin();
for (; ctit != it->second.end(); ++ctit)
{
// Special case: Messages for local entities shouldn't be sent to script
// components that subscribed globally, so that we don't have to worry about
// them accidentally picking up non-network-synchronised data.
if (ENTITY_IS_LOCAL(ent))
{
std::map::const_iterator cit = m_ComponentTypesById.find(*ctit);
if (cit != m_ComponentTypesById.end() && cit->second.type == CT_Script)
continue;
}
// Find the component instances of this type (if any)
std::map >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
if (emap == m_ComponentsByTypeId.end())
continue;
// Send the message to all of them
std::map::const_iterator eit = emap->second.begin();
for (; eit != emap->second.end(); ++eit)
eit->second->HandleMessage(msg, true);
}
}
// Send the message to component instances that dynamically subscribed to this message
std::map::iterator dit = m_DynamicMessageSubscriptionsNonsync.find(msg.GetType());
if (dit != m_DynamicMessageSubscriptionsNonsync.end())
{
dit->second.Flatten();
const std::vector& dynamic = dit->second.GetComponents();
for (size_t i = 0; i < dynamic.size(); i++)
dynamic[i]->HandleMessage(msg, false);
}
}
std::string CComponentManager::GenerateSchema() const
{
std::string schema =
""
""
""
""
""
"0"
""
""
"0"
""
""
""
""
""
""
""
""
""
""
""
""
"";
std::map > interfaceComponentTypes;
std::vector componentTypes;
for (std::map::const_iterator it = m_ComponentTypesById.begin(); it != m_ComponentTypesById.end(); ++it)
{
schema +=
""
""
"" + it->second.schema + ""
""
"";
interfaceComponentTypes[it->second.iid].push_back(it->second.name);
componentTypes.push_back(it->second.name);
}
// Declare the implementation of each interface, for documentation
for (std::map::const_iterator it = m_InterfaceIdsByName.begin(); it != m_InterfaceIdsByName.end(); ++it)
{
schema += "";
std::vector& cts = interfaceComponentTypes[it->second];
for (size_t i = 0; i < cts.size(); ++i)
schema += "";
schema += "";
}
// List all the component types, in alphabetical order (to match the reordering performed by CParamNode).
// (We do it this way, rather than ing all the interface definitions (which would additionally perform
// a check that we don't use multiple component types of the same interface in one file), because libxml2 gives
// useless error messages in the latter case; this way lets it report the real error.)
std::sort(componentTypes.begin(), componentTypes.end());
schema +=
""
""
""
"";
for (std::vector::const_iterator it = componentTypes.begin(); it != componentTypes.end(); ++it)
schema += "";
schema +=
""
"";
schema += "";
return schema;
}
Index: ps/trunk/source/simulation2/system/Message.h
===================================================================
--- ps/trunk/source/simulation2/system/Message.h (revision 27727)
+++ ps/trunk/source/simulation2/system/Message.h (revision 27728)
@@ -1,43 +1,45 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* 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 .
*/
#ifndef INCLUDED_MESSAGE
#define INCLUDED_MESSAGE
#include "scriptinterface/ScriptTypes.h"
+class ScriptRequest;
+
class CMessage
{
NONCOPYABLE(CMessage);
protected:
CMessage() { }
public:
virtual ~CMessage() { }
virtual int GetType() const = 0;
virtual const char* GetScriptHandlerName() const = 0;
virtual const char* GetScriptGlobalHandlerName() const = 0;
- virtual JS::Value ToJSVal(const ScriptInterface&) const = 0;
- JS::Value ToJSValCached(const ScriptInterface&) const;
+ virtual JS::Value ToJSVal(const ScriptRequest&) const = 0;
+ JS::Value ToJSValCached(const ScriptRequest&) const;
private:
mutable std::unique_ptr m_Cached;
};
// TODO: GetType could be replaced with a plain member variable to avoid some
// virtual calls, if that turns out to be worthwhile
-CMessage* CMessageFromJSVal(int mtid, const ScriptInterface&, JS::HandleValue);
+CMessage* CMessageFromJSVal(int mtid, const ScriptRequest&, JS::HandleValue);
#endif // INCLUDED_MESSAGE