1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00
Files
vcmi/client/Client.h
2025-05-14 13:42:20 +03:00

197 lines
5.3 KiB
C++

/*
* 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"
#include "../lib/ConditionalWait.h"
#include "../lib/ResourceSet.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CPackForClient;
struct CPackForServer;
class IBattleEventsReceiver;
class CBattleGameInterface;
class CGameInterface;
class BattleAction;
class BattleInfo;
struct BankConfig;
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
{
using TLock = std::unique_lock<std::mutex>;
std::vector<T> items;
std::mutex mx;
std::condition_variable cond;
std::atomic<bool> isTerminating = false;
public:
void requestTermination()
{
isTerminating = true;
clear();
}
void clear()
{
TLock lock(mx);
items.clear();
cond.notify_all();
}
void pushBack(const T & item)
{
assert(!isTerminating);
TLock lock(mx);
items.push_back(item);
cond.notify_all();
}
void waitWhileContains(const T & item)
{
TLock lock(mx);
cond.wait(lock, [this, &item](){ return !vstd::contains(items, item);});
if (isTerminating)
throw TerminationRequestedException();
}
bool tryRemovingElement(const T & item) //returns false if element was not present
{
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 CGameInfoCallback, public Environment, public IClient
{
std::shared_ptr<CGameState> gamestate;
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;
CGameState & gameState() final { return *gamestate; }
const CGameState & gameState() const final { return *gamestate; }
void newGame(std::shared_ptr<CGameState> gameState);
void loadGame(std::shared_ptr<CGameState> gameState);
void endNetwork();
void finishGameplay();
void endGame();
void initMapHandler();
void initPlayerEnvironments();
void initPlayerInterfaces();
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);
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;
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;
#if SCRIPTING_ENABLED
scripting::Pool * getGlobalContextPool() const override;
#endif
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();
};