2023-10-19 16:19:09 +02:00
|
|
|
/*
|
|
|
|
|
* Client.h, part of VCMI engine
|
|
|
|
|
*
|
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
|
*
|
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <vcmi/Environment.h>
|
|
|
|
|
|
2025-05-11 17:50:50 +03:00
|
|
|
#include "../lib/callback/IClient.h"
|
2025-05-14 13:39:37 +03:00
|
|
|
#include "../lib/callback/CGameInfoCallback.h"
|
2025-03-02 12:56:01 +00:00
|
|
|
#include "../lib/ConditionalWait.h"
|
2025-05-11 11:58:09 +03:00
|
|
|
#include "../lib/ResourceSet.h"
|
2025-03-02 12:56:01 +00:00
|
|
|
|
2023-10-19 16:19:09 +02:00
|
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
|
2025-05-13 15:24:45 +03:00
|
|
|
struct CPackForClient;
|
2023-10-19 16:19:09 +02:00
|
|
|
struct CPackForServer;
|
|
|
|
|
class IBattleEventsReceiver;
|
|
|
|
|
class CBattleGameInterface;
|
|
|
|
|
class CGameInterface;
|
|
|
|
|
class BattleAction;
|
|
|
|
|
class BattleInfo;
|
2024-07-13 15:46:55 +00:00
|
|
|
struct BankConfig;
|
2025-05-11 18:41:14 +03:00
|
|
|
class CCallback;
|
|
|
|
|
class CBattleCallback;
|
2023-10-19 16:19:09 +02:00
|
|
|
|
|
|
|
|
#if SCRIPTING_ENABLED
|
|
|
|
|
namespace scripting
|
|
|
|
|
{
|
|
|
|
|
class PoolImpl;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace events
|
|
|
|
|
{
|
|
|
|
|
class EventBus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_END
|
|
|
|
|
|
|
|
|
|
class CClient;
|
|
|
|
|
class CBaseForCLApply;
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
class ThreadSafeVector
|
|
|
|
|
{
|
2025-02-28 15:25:58 +01:00
|
|
|
using TLock = std::unique_lock<std::mutex>;
|
2023-10-19 16:19:09 +02:00
|
|
|
std::vector<T> items;
|
2025-02-28 15:25:58 +01:00
|
|
|
std::mutex mx;
|
|
|
|
|
std::condition_variable cond;
|
2025-03-02 12:56:01 +00:00
|
|
|
std::atomic<bool> isTerminating = false;
|
2023-10-19 16:19:09 +02:00
|
|
|
|
|
|
|
|
public:
|
2025-03-02 12:56:01 +00:00
|
|
|
void requestTermination()
|
|
|
|
|
{
|
|
|
|
|
isTerminating = true;
|
|
|
|
|
clear();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-19 16:19:09 +02:00
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
TLock lock(mx);
|
|
|
|
|
items.clear();
|
|
|
|
|
cond.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pushBack(const T & item)
|
|
|
|
|
{
|
2025-03-02 12:56:01 +00:00
|
|
|
assert(!isTerminating);
|
|
|
|
|
|
2023-10-19 16:19:09 +02:00
|
|
|
TLock lock(mx);
|
|
|
|
|
items.push_back(item);
|
|
|
|
|
cond.notify_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void waitWhileContains(const T & item)
|
|
|
|
|
{
|
|
|
|
|
TLock lock(mx);
|
2025-05-05 16:05:59 +03:00
|
|
|
cond.wait(lock, [this, &item](){ return !vstd::contains(items, item);});
|
2025-03-02 12:56:01 +00:00
|
|
|
|
|
|
|
|
if (isTerminating)
|
|
|
|
|
throw TerminationRequestedException();
|
2023-10-19 16:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool tryRemovingElement(const T & item) //returns false if element was not present
|
|
|
|
|
{
|
2025-03-02 12:56:01 +00:00
|
|
|
assert(!isTerminating);
|
|
|
|
|
|
2023-10-19 16:19:09 +02:00
|
|
|
TLock lock(mx);
|
|
|
|
|
auto itr = vstd::find(items, item);
|
|
|
|
|
if(itr == items.end()) //not in container
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
items.erase(itr);
|
|
|
|
|
cond.notify_all();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class CPlayerEnvironment : public Environment
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PlayerColor player;
|
|
|
|
|
CClient * cl;
|
|
|
|
|
std::shared_ptr<CCallback> mainCallback;
|
|
|
|
|
|
|
|
|
|
CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_);
|
|
|
|
|
const Services * services() const override;
|
|
|
|
|
vstd::CLoggerBase * logger() const override;
|
|
|
|
|
events::EventBus * eventBus() const override;
|
|
|
|
|
const BattleCb * battle(const BattleID & battle) const override;
|
|
|
|
|
const GameCb * game() const override;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Class which handles client - server logic
|
2025-05-20 23:16:28 +03:00
|
|
|
class CClient : public Environment, public IClient
|
2023-10-19 16:19:09 +02:00
|
|
|
{
|
2025-03-20 18:07:08 +00:00
|
|
|
std::shared_ptr<CGameState> gamestate;
|
2025-06-24 18:28:40 +03:00
|
|
|
int requestCounter = 1;
|
2025-06-28 21:42:54 +03:00
|
|
|
|
2023-10-19 16:19:09 +02:00
|
|
|
public:
|
|
|
|
|
std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
|
|
|
|
|
std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;
|
|
|
|
|
|
|
|
|
|
std::map<PlayerColor, std::vector<std::shared_ptr<IBattleEventsReceiver>>> additionalBattleInts;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<BattleAction> currentBattleAction;
|
|
|
|
|
|
|
|
|
|
CClient();
|
|
|
|
|
~CClient();
|
|
|
|
|
|
|
|
|
|
const Services * services() const override;
|
|
|
|
|
const BattleCb * battle(const BattleID & battle) const override;
|
|
|
|
|
const GameCb * game() const override;
|
|
|
|
|
vstd::CLoggerBase * logger() const override;
|
|
|
|
|
events::EventBus * eventBus() const override;
|
|
|
|
|
|
2025-07-28 18:13:41 +03:00
|
|
|
std::shared_ptr<CGameState> gameStatePtr() { return gamestate; }
|
2025-05-20 23:16:28 +03:00
|
|
|
CGameState & gameState() { return *gamestate; }
|
|
|
|
|
const CGameState & gameState() const { return *gamestate; }
|
|
|
|
|
IGameInfoCallback & gameInfo();
|
2025-03-20 18:07:08 +00:00
|
|
|
|
|
|
|
|
void newGame(std::shared_ptr<CGameState> gameState);
|
|
|
|
|
void loadGame(std::shared_ptr<CGameState> gameState);
|
2023-10-19 16:19:09 +02:00
|
|
|
|
2024-05-18 11:04:10 +00:00
|
|
|
void endNetwork();
|
2025-03-02 12:56:01 +00:00
|
|
|
void finishGameplay();
|
2023-10-19 16:19:09 +02:00
|
|
|
void endGame();
|
|
|
|
|
|
|
|
|
|
void initMapHandler();
|
|
|
|
|
void initPlayerEnvironments();
|
|
|
|
|
void initPlayerInterfaces();
|
2024-05-17 16:36:07 +00:00
|
|
|
std::string aiNameForPlayer(const PlayerSettings & ps, bool battleAI, bool alliedToHuman) const; //empty means no AI -> human
|
|
|
|
|
std::string aiNameForPlayer(bool battleAI, bool alliedToHuman) const;
|
2023-10-19 16:19:09 +02:00
|
|
|
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
|
|
|
|
|
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
|
|
|
|
|
|
2025-05-11 17:50:50 +03:00
|
|
|
//Set of metrhods that allows adding more interfaces for this player that'll receive game event call-ins.
|
|
|
|
|
void registerBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents, PlayerColor color);
|
|
|
|
|
void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents, PlayerColor color);
|
|
|
|
|
|
2025-03-02 12:56:01 +00:00
|
|
|
ThreadSafeVector<int> waitingRequest;
|
2023-10-19 16:19:09 +02:00
|
|
|
|
2024-10-04 14:01:10 +00:00
|
|
|
void handlePack(CPackForClient & pack); //applies the given pack and deletes it
|
2025-05-11 17:50:50 +03:00
|
|
|
int sendRequest(const CPackForServer & request, PlayerColor player, bool waitTillRealize) override; //returns ID given to that request
|
|
|
|
|
std::optional<BattleAction> makeSurrenderRetreatDecision(PlayerColor player, const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
|
2023-10-19 16:19:09 +02:00
|
|
|
|
2025-04-10 12:27:18 +03:00
|
|
|
void battleStarted(const BattleID & battle);
|
2023-10-19 16:19:09 +02:00
|
|
|
void battleFinished(const BattleID & battleID);
|
|
|
|
|
void startPlayerBattleAction(const BattleID & battleID, PlayerColor color);
|
|
|
|
|
|
|
|
|
|
friend class CCallback; //handling players actions
|
|
|
|
|
friend class CBattleCallback; //handling players actions
|
|
|
|
|
|
2023-10-28 17:09:07 +00:00
|
|
|
void removeGUI() const;
|
2023-10-19 16:19:09 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::map<PlayerColor, std::shared_ptr<CBattleCallback>> battleCallbacks; //callbacks given to player interfaces
|
|
|
|
|
std::map<PlayerColor, std::shared_ptr<CPlayerEnvironment>> playerEnvironments;
|
|
|
|
|
|
|
|
|
|
#if SCRIPTING_ENABLED
|
|
|
|
|
std::shared_ptr<scripting::PoolImpl> clientScripts;
|
|
|
|
|
#endif
|
|
|
|
|
std::unique_ptr<events::EventBus> clientEventBus;
|
|
|
|
|
|
|
|
|
|
void reinitScripting();
|
|
|
|
|
};
|