mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Store whether a player is activ in C++
This prevents mods from mutating this value and revealing the map. Part of this commit is written by @phosit.
This commit is contained in:
parent
f932b8b9cc
commit
c9e76efe7b
12 changed files with 187 additions and 39 deletions
|
|
@ -512,6 +512,9 @@ Player.prototype.SetState = function(newState, message)
|
|||
});
|
||||
}
|
||||
|
||||
Engine.PostMessage(this.entity, won ? MT_PlayerWon : MT_PlayerDefeated,
|
||||
{ "playerId": this.playerID });
|
||||
|
||||
if (message)
|
||||
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({
|
||||
"type": won ? "won" : "defeat",
|
||||
|
|
@ -519,8 +522,6 @@ Player.prototype.SetState = function(newState, message)
|
|||
"allies": [this.playerID],
|
||||
"message": message
|
||||
});
|
||||
|
||||
Engine.PostMessage(this.entity, won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
|
||||
};
|
||||
|
||||
Player.prototype.GetFormations = function()
|
||||
|
|
|
|||
|
|
@ -10,18 +10,6 @@ Engine.RegisterMessageType("DisabledTechnologiesChanged");
|
|||
*/
|
||||
Engine.RegisterMessageType("DisabledTemplatesChanged");
|
||||
|
||||
/**
|
||||
* Message of the form { "playerID": number }
|
||||
* sent from Player component when a player is defeated.
|
||||
*/
|
||||
Engine.RegisterMessageType("PlayerDefeated");
|
||||
|
||||
/**
|
||||
* Message of the form { "playerID": number }
|
||||
* sent from Player component when a player has won.
|
||||
*/
|
||||
Engine.RegisterMessageType("PlayerWon");
|
||||
|
||||
/**
|
||||
* Message of the form { "to": number, "from": number, "amounts": object }
|
||||
* sent from Player component whenever a tribute is sent.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2024 Wildfire Games.
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -486,6 +486,7 @@ bool CGame::IsGameFinished() const
|
|||
return false;
|
||||
}
|
||||
|
||||
// This function is implemented so that mods can't change it's result. See ICmpPlayer.cpp
|
||||
bool CGame::PlayerFinished(player_id_t playerID) const
|
||||
{
|
||||
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
|
||||
|
|
@ -493,5 +494,5 @@ bool CGame::PlayerFinished(player_id_t playerID) const
|
|||
return false;
|
||||
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(playerID));
|
||||
return cmpPlayer && cmpPlayer->GetState() != "active";
|
||||
return cmpPlayer && !cmpPlayer->IsActive();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2023 Wildfire Games.
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -511,6 +511,38 @@ public:
|
|||
player_id_t player;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent by the Player component when a specific player has won.
|
||||
*/
|
||||
class CMessagePlayerWon final : public CMessage
|
||||
{
|
||||
public:
|
||||
DEFAULT_MESSAGE_IMPL(PlayerWon)
|
||||
|
||||
CMessagePlayerWon(player_id_t playerId) :
|
||||
playerId{playerId}
|
||||
{
|
||||
}
|
||||
|
||||
player_id_t playerId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent by the Player component when an specific player has been defeated.
|
||||
*/
|
||||
class CMessagePlayerDefeated final : public CMessage
|
||||
{
|
||||
public:
|
||||
DEFAULT_MESSAGE_IMPL(PlayerDefeated)
|
||||
|
||||
CMessagePlayerDefeated(player_id_t playerId) :
|
||||
playerId{playerId}
|
||||
{
|
||||
}
|
||||
|
||||
player_id_t playerId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent by aura and tech managers when a value of a certain template's component is changed
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2023 Wildfire Games.
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -61,6 +61,8 @@ MESSAGE(MinimapPing)
|
|||
MESSAGE(CinemaPathEnded)
|
||||
MESSAGE(CinemaQueueEnded)
|
||||
MESSAGE(PlayerColorChanged)
|
||||
MESSAGE(PlayerWon)
|
||||
MESSAGE(PlayerDefeated)
|
||||
|
||||
// TemplateManager must come before all other (non-test) components,
|
||||
// so that it is the first to be (de)serialized
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "graphics/Color.h"
|
||||
#include "maths/FixedVector3D.h"
|
||||
#include "simulation2/system/Component.h"
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
#include "simulation2/scripting/ScriptComponent.h"
|
||||
|
||||
|
|
@ -30,7 +31,41 @@ END_INTERFACE_WRAPPER(Player)
|
|||
class CCmpPlayerScripted : public ICmpPlayer
|
||||
{
|
||||
public:
|
||||
DEFAULT_SCRIPT_WRAPPER(PlayerScripted)
|
||||
DEFAULT_SCRIPT_WRAPPER_BASIC(PlayerScripted)
|
||||
|
||||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
componentManager.SubscribeToMessageType(MT_PlayerWon);
|
||||
componentManager.SubscribeToMessageType(MT_PlayerDefeated);
|
||||
}
|
||||
|
||||
void Serialize(ISerializer& serialize) final
|
||||
{
|
||||
serialize.Bool("isActive", m_IsActive);
|
||||
m_Script.Serialize(serialize);
|
||||
}
|
||||
|
||||
void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) final
|
||||
{
|
||||
deserialize.Bool("isActive", m_IsActive);
|
||||
m_Script.Deserialize(paramNode, deserialize, GetEntityId());
|
||||
}
|
||||
|
||||
void HandleMessage(const CMessage& msg, bool global) final
|
||||
{
|
||||
const int msgType{msg.GetType()};
|
||||
|
||||
// Handle messages that were subscribed to in ClassInit.
|
||||
if (msgType == MT_PlayerWon || msgType == MT_PlayerDefeated)
|
||||
{
|
||||
m_IsActive = false;
|
||||
if (!m_Script.HasMessageHandler(msg, global))
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle messages that were subscribed to within the JS implementation of the interface.
|
||||
m_Script.HandleMessage(msg, global);
|
||||
}
|
||||
|
||||
CColor GetDisplayedColor() override
|
||||
{
|
||||
|
|
@ -56,6 +91,18 @@ public:
|
|||
{
|
||||
return m_Script.Call<std::string>("GetState");
|
||||
}
|
||||
|
||||
bool IsActive() final
|
||||
{
|
||||
return m_IsActive;
|
||||
}
|
||||
|
||||
private:
|
||||
// Serialize this player state variable in C++ so that mods can't manipulate this value in order to
|
||||
// reveal the map locally.
|
||||
// Once it's set to `false` it's never set to true again. To prevent mods from temporarily changing
|
||||
// it.
|
||||
bool m_IsActive{true};
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(PlayerScripted)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2022 Wildfire Games.
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -39,6 +39,9 @@ public:
|
|||
virtual bool HasStartingCamera() = 0;
|
||||
virtual std::string GetState() = 0;
|
||||
|
||||
// See the cpp file for why this is implemented in C++.
|
||||
virtual bool IsActive() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Player)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2023 Wildfire Games.
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -541,6 +541,38 @@ CMessage* CMessagePlayerColorChanged::FromJSVal(const ScriptRequest& rq, JS::Han
|
|||
return new CMessagePlayerColorChanged(player);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
JS::Value CMessagePlayerWon::ToJSVal(const ScriptRequest& rq) const
|
||||
{
|
||||
TOJSVAL_SETUP();
|
||||
SET_MSG_PROPERTY(playerId);
|
||||
return JS::ObjectValue(*obj);
|
||||
}
|
||||
|
||||
CMessage* CMessagePlayerWon::FromJSVal(const ScriptRequest& rq, JS::HandleValue val)
|
||||
{
|
||||
FROMJSVAL_SETUP();
|
||||
GET_MSG_PROPERTY(player_id_t, playerId);
|
||||
return new CMessagePlayerWon(playerId);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
JS::Value CMessagePlayerDefeated::ToJSVal(const ScriptRequest& rq) const
|
||||
{
|
||||
TOJSVAL_SETUP();
|
||||
SET_MSG_PROPERTY(playerId);
|
||||
return JS::ObjectValue(*obj);
|
||||
}
|
||||
|
||||
CMessage* CMessagePlayerDefeated::FromJSVal(const ScriptRequest& rq, JS::HandleValue val)
|
||||
{
|
||||
FROMJSVAL_SETUP();
|
||||
GET_MSG_PROPERTY(player_id_t, playerId);
|
||||
return new CMessagePlayerDefeated(playerId);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
CMessage* CMessageFromJSVal(int mtid, const ScriptRequest& rq, JS::HandleValue val)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2023 Wildfire Games.
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -46,6 +46,13 @@ void CComponentTypeScript::Deinit()
|
|||
ScriptFunction::CallVoid(rq, m_Instance, "Deinit");
|
||||
}
|
||||
|
||||
bool CComponentTypeScript::HasMessageHandler(const CMessage& msg, const bool global)
|
||||
{
|
||||
const ScriptRequest rq(m_ScriptInterface);
|
||||
return Script::HasProperty(rq, m_Instance, global ? msg.GetScriptGlobalHandlerName() :
|
||||
msg.GetScriptHandlerName());
|
||||
}
|
||||
|
||||
void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global)
|
||||
{
|
||||
ScriptRequest rq(m_ScriptInterface);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public:
|
|||
|
||||
void Init(const CParamNode& paramNode, entity_id_t ent);
|
||||
void Deinit();
|
||||
bool HasMessageHandler(const CMessage& msg, const bool global);
|
||||
void HandleMessage(const CMessage& msg, bool global);
|
||||
|
||||
void Serialize(ISerializer& serialize);
|
||||
|
|
@ -88,8 +89,7 @@ private:
|
|||
}
|
||||
|
||||
|
||||
#define DEFAULT_SCRIPT_WRAPPER(cname) \
|
||||
static void ClassInit(CComponentManager& UNUSED(componentManager)) { } \
|
||||
#define DEFAULT_SCRIPT_WRAPPER_BASIC(cname) \
|
||||
static IComponent* Allocate(const ScriptInterface& scriptInterface, JS::HandleValue instance) \
|
||||
{ \
|
||||
return new CCmp##cname(scriptInterface, instance); \
|
||||
|
|
@ -111,18 +111,6 @@ private:
|
|||
{ \
|
||||
m_Script.Deinit(); \
|
||||
} \
|
||||
void HandleMessage(const CMessage& msg, bool global) override \
|
||||
{ \
|
||||
m_Script.HandleMessage(msg, global); \
|
||||
} \
|
||||
void Serialize(ISerializer& serialize) override \
|
||||
{ \
|
||||
m_Script.Serialize(serialize); \
|
||||
} \
|
||||
void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override \
|
||||
{ \
|
||||
m_Script.Deserialize(paramNode, deserialize, GetEntityId()); \
|
||||
} \
|
||||
JS::Value GetJSInstance() const override \
|
||||
{ \
|
||||
return m_Script.GetInstance(); \
|
||||
|
|
@ -135,4 +123,21 @@ private:
|
|||
CComponentTypeScript m_Script; \
|
||||
public:
|
||||
|
||||
|
||||
#define DEFAULT_SCRIPT_WRAPPER(cname) \
|
||||
static void ClassInit(CComponentManager& UNUSED(componentManager)) { } \
|
||||
void HandleMessage(const CMessage& msg, bool global) override \
|
||||
{ \
|
||||
m_Script.HandleMessage(msg, global); \
|
||||
} \
|
||||
void Serialize(ISerializer& serialize) override \
|
||||
{ \
|
||||
m_Script.Serialize(serialize); \
|
||||
} \
|
||||
void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override \
|
||||
{ \
|
||||
m_Script.Deserialize(paramNode, deserialize, GetEntityId()); \
|
||||
} \
|
||||
DEFAULT_SCRIPT_WRAPPER_BASIC(cname)
|
||||
|
||||
#endif // INCLUDED_SCRIPTCOMPONENT
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "ComponentManager.h"
|
||||
|
||||
#include "lib/utf8.h"
|
||||
#include "ps/algorithm.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Profile.h"
|
||||
|
|
@ -303,10 +304,17 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
|
|||
return;
|
||||
}
|
||||
|
||||
// If we have already subscribed in classInit, do not subscribe again
|
||||
if (isGlobal)
|
||||
SubscribeGloballyToMessageType(mit->second);
|
||||
{
|
||||
if (!IsGloballySubscribed(mit->second))
|
||||
SubscribeGloballyToMessageType(mit->second);
|
||||
}
|
||||
else
|
||||
SubscribeToMessageType(mit->second);
|
||||
{
|
||||
if (!IsLocallySubscribed(mit->second))
|
||||
SubscribeToMessageType(mit->second);
|
||||
}
|
||||
}
|
||||
|
||||
m_CurrentComponent = CID__Invalid;
|
||||
|
|
@ -571,6 +579,18 @@ void CComponentManager::SubscribeGloballyToMessageType(MessageTypeId mtid)
|
|||
std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
|
||||
}
|
||||
|
||||
bool CComponentManager::IsLocallySubscribed(MessageTypeId mtid)
|
||||
{
|
||||
ENSURE(m_CurrentComponent != CID__Invalid);
|
||||
return PS::contains(m_LocalMessageSubscriptions[mtid], m_CurrentComponent);
|
||||
}
|
||||
|
||||
bool CComponentManager::IsGloballySubscribed(MessageTypeId mtid)
|
||||
{
|
||||
ENSURE(m_CurrentComponent != CID__Invalid);
|
||||
return PS::contains(m_GlobalMessageSubscriptions[mtid], m_CurrentComponent);
|
||||
}
|
||||
|
||||
void CComponentManager::FlattenDynamicSubscriptions()
|
||||
{
|
||||
std::map<MessageTypeId, CDynamicSubscription>::iterator it;
|
||||
|
|
|
|||
|
|
@ -113,6 +113,16 @@ public:
|
|||
*/
|
||||
void SubscribeGloballyToMessageType(MessageTypeId mtid);
|
||||
|
||||
/**
|
||||
* Check if the current component type is subscribed to messages of the given type.
|
||||
*/
|
||||
bool IsLocallySubscribed(MessageTypeId mtid);
|
||||
|
||||
/**
|
||||
* Check if the current component type is globally subscribed to messages of the given type.
|
||||
*/
|
||||
bool IsGloballySubscribed(MessageTypeId mtid);
|
||||
|
||||
/**
|
||||
* Subscribe the given component instance to all messages of the given message type.
|
||||
* The component's HandleMessage will be called on any BroadcastMessage or PostMessage of
|
||||
|
|
|
|||
Loading…
Reference in a new issue