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-11 11:58:09 +03:00
# include "../lib/callback/IGameCallback.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
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-11 17:50:50 +03:00
class CClient : public IGameCallback , 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 ;
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-04-19 14:14:12 +03:00
CGameState & gameState ( ) final { return * gamestate ; }
const CGameState & gameState ( ) const final { return * gamestate ; }
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
void changeSpells ( const CGHeroInstance * hero , bool give , const std : : set < SpellID > & spells ) override { } ;
2024-10-10 20:31:11 +00:00
void setResearchedSpells ( const CGTownInstance * town , int level , const std : : vector < SpellID > & spells , bool accepted ) override { } ;
2023-10-19 16:19:09 +02:00
bool removeObject ( const CGObjectInstance * obj , const PlayerColor & initiator ) override { return false ; } ;
2024-07-12 16:51:27 +00:00
void createBoat ( const int3 & visitablePosition , BoatId type , PlayerColor initiator ) override { } ;
2023-10-19 16:19:09 +02:00
void setOwner ( const CGObjectInstance * obj , PlayerColor owner ) override { } ;
2024-01-04 23:57:36 +02:00
void giveExperience ( const CGHeroInstance * hero , TExpType val ) override { } ;
2023-10-19 16:19:09 +02:00
void changePrimSkill ( const CGHeroInstance * hero , PrimarySkill which , si64 val , bool abs = false ) override { } ;
void changeSecSkill ( const CGHeroInstance * hero , SecondarySkill which , int val , bool abs = false ) override { } ;
2024-09-04 15:14:56 +00:00
void showBlockingDialog ( const IObjectInterface * caller , BlockingDialog * iw ) override { } ;
2023-10-19 16:19:09 +02:00
void showGarrisonDialog ( ObjectInstanceID upobj , ObjectInstanceID hid , bool removableUnits ) override { } ;
void showTeleportDialog ( TeleportDialog * iw ) override { } ;
void showObjectWindow ( const CGObjectInstance * object , EOpenWindowMode window , const CGHeroInstance * visitor , bool addQuery ) override { } ;
void giveResource ( PlayerColor player , GameResID which , int val ) override { } ;
2025-05-11 11:58:09 +03:00
void giveResources ( PlayerColor player , ResourceSet resources ) override { } ;
2023-10-19 16:19:09 +02:00
void giveCreatures ( const CArmedInstance * objid , const CGHeroInstance * h , const CCreatureSet & creatures , bool remove ) override { } ;
2025-05-07 19:16:58 +03:00
void takeCreatures ( ObjectInstanceID objid , const std : : vector < CStackBasicDescriptor > & creatures , bool forceRemoval ) override { } ;
2023-10-19 16:19:09 +02:00
bool changeStackType ( const StackLocation & sl , const CCreature * c ) override { return false ; } ;
bool changeStackCount ( const StackLocation & sl , TQuantity count , bool absoluteValue = false ) override { return false ; } ;
bool insertNewStack ( const StackLocation & sl , const CCreature * c , TQuantity count ) override { return false ; } ;
bool eraseStack ( const StackLocation & sl , bool forceRemoval = false ) override { return false ; } ;
bool swapStacks ( const StackLocation & sl1 , const StackLocation & sl2 ) override { return false ; }
bool addToSlot ( const StackLocation & sl , const CCreature * c , TQuantity count ) override { return false ; }
void tryJoiningArmy ( const CArmedInstance * src , const CArmedInstance * dst , bool removeObjWhenFinished , bool allowMerging ) override { }
bool moveStack ( const StackLocation & src , const StackLocation & dst , TQuantity count = - 1 ) override { return false ; }
2025-04-05 17:45:24 +02:00
void removeAfterVisit ( const ObjectInstanceID & id ) override { } ;
2023-10-19 16:19:09 +02:00
bool swapGarrisonOnSiege ( ObjectInstanceID tid ) override { return false ; } ;
2024-09-04 20:39:13 +03:00
bool giveHeroNewArtifact ( const CGHeroInstance * h , const ArtifactID & artId , const ArtifactPosition & pos ) override { return false ; } ;
bool giveHeroNewScroll ( const CGHeroInstance * h , const SpellID & spellId , const ArtifactPosition & pos ) override { return false ; } ;
2024-09-06 17:59:40 +03:00
bool putArtifact ( const ArtifactLocation & al , const ArtifactInstanceID & id , std : : optional < bool > askAssemble ) override { return false ; } ;
2023-10-19 16:19:09 +02:00
void removeArtifact ( const ArtifactLocation & al ) override { } ;
2024-03-07 16:52:50 +02:00
bool moveArtifact ( const PlayerColor & player , const ArtifactLocation & al1 , const ArtifactLocation & al2 ) override { return false ; } ;
2023-10-19 16:19:09 +02:00
void heroVisitCastle ( const CGTownInstance * obj , const CGHeroInstance * hero ) override { } ;
void visitCastleObjects ( const CGTownInstance * obj , const CGHeroInstance * hero ) override { } ;
void stopHeroVisitCastle ( const CGTownInstance * obj , const CGHeroInstance * hero ) override { } ;
2024-08-31 21:04:32 +00:00
void startBattle ( const CArmedInstance * army1 , const CArmedInstance * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , const BattleLayout & layout , const CGTownInstance * town ) override { } ; //use hero=nullptr for no hero
void startBattle ( const CArmedInstance * army1 , const CArmedInstance * army2 ) override { } ; //if any of armies is hero, hero will be used
2024-05-07 20:05:23 +00:00
bool moveHero ( ObjectInstanceID hid , int3 dst , EMovementMode movementMode , bool transit = false , PlayerColor asker = PlayerColor : : NEUTRAL ) override { return false ; } ;
2023-10-19 16:19:09 +02:00
void giveHeroBonus ( GiveBonus * bonus ) override { } ;
void setMovePoints ( SetMovePoints * smp ) override { } ;
2024-01-15 22:25:52 +02:00
void setMovePoints ( ObjectInstanceID hid , int val , bool absolute ) override { } ;
2023-10-19 16:19:09 +02:00
void setManaPoints ( ObjectInstanceID hid , int val ) override { } ;
void giveHero ( ObjectInstanceID id , PlayerColor player , ObjectInstanceID boatId = ObjectInstanceID ( ) ) override { } ;
void changeObjPos ( ObjectInstanceID objid , int3 newPos , const PlayerColor & initiator ) override { } ;
2024-10-04 18:59:51 +00:00
void sendAndApply ( CPackForClient & pack ) override { } ;
2023-10-19 16:19:09 +02:00
void heroExchange ( ObjectInstanceID hero1 , ObjectInstanceID hero2 ) override { } ;
void castSpell ( const spells : : Caster * caster , SpellID spellID , const int3 & pos ) override { } ;
2023-10-22 18:36:41 +03:00
void changeFogOfWar ( int3 center , ui32 radius , PlayerColor player , ETileVisibility mode ) override { }
2024-08-26 13:49:50 +00:00
void changeFogOfWar ( const std : : unordered_set < int3 > & tiles , PlayerColor player , ETileVisibility mode ) override { }
2023-10-19 16:19:09 +02:00
2023-11-06 18:27:16 +02:00
void setObjPropertyValue ( ObjectInstanceID objid , ObjProperty prop , int32_t value ) override { } ;
void setObjPropertyID ( ObjectInstanceID objid , ObjProperty prop , ObjPropertyID identifier ) override { } ;
2024-07-12 15:10:03 +00:00
void setRewardableObjectConfiguration ( ObjectInstanceID objid , const Rewardable : : Configuration & configuration ) override { } ;
void setRewardableObjectConfiguration ( ObjectInstanceID townInstanceID , BuildingID buildingID , const Rewardable : : Configuration & configuration ) override { } ;
2023-10-19 16:19:09 +02:00
void showInfoDialog ( InfoWindow * iw ) override { } ;
2023-10-28 17:09:07 +00:00
void removeGUI ( ) const ;
2023-10-19 16:19:09 +02:00
2024-06-01 16:09:14 +00:00
vstd : : RNG & getRandomGenerator ( ) override ;
2023-10-19 16:19:09 +02:00
# 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 ( ) ;
} ;