1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00
Files
vcmi/client/Client.h

197 lines
5.3 KiB
C++
Raw Normal View History

/*
* 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>
#include "../lib/callback/IClient.h"
#include "../lib/callback/CGameInfoCallback.h"
2025-03-02 12:56:01 +00:00
#include "../lib/ConditionalWait.h"
#include "../lib/ResourceSet.h"
2025-03-02 12:56:01 +00:00
VCMI_LIB_NAMESPACE_BEGIN
struct CPackForClient;
struct CPackForServer;
class IBattleEventsReceiver;
class CBattleGameInterface;
class CGameInterface;
class BattleAction;
class BattleInfo;
struct BankConfig;
2025-05-11 18:41:14 +03:00
class CCallback;
class CBattleCallback;
#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>;
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;
public:
2025-03-02 12:56:01 +00:00
void requestTermination()
{
isTerminating = true;
clear();
}
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);
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();
}
bool tryRemovingElement(const T & item) //returns false if element was not present
{
2025-03-02 12:56:01 +00:00
assert(!isTerminating);
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
class CClient : public Environment, public IClient
{
std::shared_ptr<CGameState> gamestate;
2025-06-24 18:28:40 +03:00
int requestCounter = 1;
2025-06-28 21:42:54 +03: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;
std::shared_ptr<CGameState> gameStatePtr() { return gamestate; }
CGameState & gameState() { return *gamestate; }
const CGameState & gameState() const { return *gamestate; }
IGameInfoCallback & gameInfo();
void newGame(std::shared_ptr<CGameState> gameState);
void loadGame(std::shared_ptr<CGameState> gameState);
void endNetwork();
2025-03-02 12:56:01 +00:00
void finishGameplay();
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;
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
//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;
void handlePack(CPackForClient & pack); //applies the given pack and deletes it
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;
2025-04-10 12:27:18 +03:00
void battleStarted(const BattleID & battle);
void battleFinished(const BattleID & battleID);
void startPlayerBattleAction(const BattleID & battleID, PlayerColor color);
friend class CCallback; //handling players actions
friend class CBattleCallback; //handling players actions
void removeGUI() const;
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();
};