mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Merge branch 'develop' into random_template_description
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,12 +1,15 @@
|
||||
/client/vcmiclient
|
||||
/server/vcmiserver
|
||||
/launcher/.lupdate
|
||||
/launcher/vcmilauncher
|
||||
/mapeditor/.lupdate
|
||||
/launcher/vcmilauncher_automoc.cpp
|
||||
/conan-*
|
||||
|
||||
build/
|
||||
.cache/*
|
||||
out/
|
||||
/.qt
|
||||
*.dll
|
||||
*.exe
|
||||
*.depend
|
||||
@@ -42,6 +45,7 @@ VCMI_VS11.opensdf
|
||||
.DS_Store
|
||||
CMakeUserPresets.json
|
||||
compile_commands.json
|
||||
fuzzylite.pc
|
||||
|
||||
# Visual Studio
|
||||
*.suo
|
||||
@@ -62,5 +66,8 @@ compile_commands.json
|
||||
/deps
|
||||
.vs/
|
||||
|
||||
# Visual Studio Code
|
||||
/.vscode/
|
||||
|
||||
# CLion
|
||||
.idea/
|
||||
|
@@ -843,7 +843,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
}
|
||||
}
|
||||
|
||||
if(!reachable && vstd::contains(reachabilityMap[hex], unit))
|
||||
if(!reachable && std::count(reachabilityMap[hex].begin(), reachabilityMap[hex].end(), unit) > 1)
|
||||
{
|
||||
blockingScore += ratio * (enemyUnit ? BLOCKING_OWN_ATTACK_PENALTY : BLOCKING_OWN_MOVE_PENALTY);
|
||||
}
|
||||
|
@@ -19,8 +19,8 @@ class CEmptyAI : public CGlobalAI
|
||||
std::shared_ptr<CCallback> cb;
|
||||
|
||||
public:
|
||||
virtual void saveGame(BinarySerializer & h) override;
|
||||
virtual void loadGame(BinaryDeserializer & h) override;
|
||||
void saveGame(BinarySerializer & h) override;
|
||||
void loadGame(BinaryDeserializer & h) override;
|
||||
|
||||
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
|
||||
void yourTurn(QueryID queryID) override;
|
||||
|
@@ -27,7 +27,7 @@ namespace Goals
|
||||
|
||||
virtual Goals::TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual bool operator==(const BuildingBehavior & other) const override
|
||||
bool operator==(const BuildingBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ namespace Goals
|
||||
|
||||
virtual Goals::TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual bool operator==(const BuyArmyBehavior & other) const override
|
||||
bool operator==(const BuyArmyBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ namespace Goals
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool operator==(const CaptureObjectsBehavior & other) const override;
|
||||
bool operator==(const CaptureObjectsBehavior & other) const override;
|
||||
|
||||
static Goals::TGoalVec getVisitGoals(const std::vector<AIPath> & paths, const CGObjectInstance * objToVisit = nullptr);
|
||||
|
||||
|
@@ -28,10 +28,10 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const ClusterBehavior & other) const override
|
||||
bool operator==(const ClusterBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ namespace Goals
|
||||
virtual Goals::TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const DefenceBehavior & other) const override
|
||||
bool operator==(const DefenceBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const GatherArmyBehavior & other) const override
|
||||
bool operator==(const GatherArmyBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const RecruitHeroBehavior & other) const override
|
||||
bool operator==(const RecruitHeroBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const StartupBehavior & other) const override
|
||||
bool operator==(const StartupBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -25,10 +25,10 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual TGoalVec decompose() const override;
|
||||
TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual bool operator==(const StayAtTownBehavior & other) const override
|
||||
bool operator==(const StayAtTownBehavior & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const AdventureSpellCast & other) const override;
|
||||
bool operator==(const AdventureSpellCast & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
virtual bool operator==(const Build & other) const override
|
||||
bool operator==(const Build & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const BuildBoat & other) const override;
|
||||
bool operator==(const BuildBoat & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@ namespace Goals
|
||||
}
|
||||
BuildThis(BuildingID Bid, const CGTownInstance * tid);
|
||||
|
||||
virtual bool operator==(const BuildThis & other) const override;
|
||||
bool operator==(const BuildThis & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(AIGateway * ai) override;
|
||||
};
|
||||
|
@@ -36,11 +36,11 @@ namespace Goals
|
||||
priority = 3;//TODO: evaluate?
|
||||
}
|
||||
|
||||
virtual bool operator==(const BuyArmy & other) const override;
|
||||
bool operator==(const BuyArmy & other) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual void accept(AIGateway * ai) override;
|
||||
void accept(AIGateway * ai) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,7 @@ namespace Goals
|
||||
//h & value & resID & objid & aid & tile & hero & town & bid;
|
||||
}
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const override
|
||||
bool operator==(const AbstractGoal & g) const override
|
||||
{
|
||||
if(goalType != g.goalType)
|
||||
return false;
|
||||
@@ -54,7 +54,7 @@ namespace Goals
|
||||
|
||||
virtual bool operator==(const T & other) const = 0;
|
||||
|
||||
virtual TGoalVec decompose() const override
|
||||
TGoalVec decompose() const override
|
||||
{
|
||||
TSubgoal single = decomposeSingle();
|
||||
|
||||
@@ -90,11 +90,11 @@ namespace Goals
|
||||
return *((T *)this);
|
||||
}
|
||||
|
||||
virtual bool isElementar() const override { return true; }
|
||||
bool isElementar() const override { return true; }
|
||||
|
||||
virtual HeroPtr getHero() const override { return AbstractGoal::hero; }
|
||||
|
||||
virtual int getHeroExchangeCount() const override { return 0; }
|
||||
int getHeroExchangeCount() const override { return 0; }
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -34,11 +34,11 @@ namespace Goals
|
||||
name = obj->getObjectName();
|
||||
}
|
||||
|
||||
virtual bool operator==(const CaptureObject & other) const override;
|
||||
bool operator==(const CaptureObject & other) const override;
|
||||
virtual Goals::TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual bool hasHash() const override { return true; }
|
||||
virtual uint64_t getHash() const override;
|
||||
bool hasHash() const override { return true; }
|
||||
uint64_t getHash() const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -31,10 +31,10 @@ namespace Goals
|
||||
|
||||
virtual Goals::TGoalVec decompose() const override;
|
||||
virtual std::string toString() const override;
|
||||
virtual bool hasHash() const override { return true; }
|
||||
virtual uint64_t getHash() const override;
|
||||
bool hasHash() const override { return true; }
|
||||
uint64_t getHash() const override;
|
||||
|
||||
virtual bool operator==(const CompleteQuest & other) const override;
|
||||
bool operator==(const CompleteQuest & other) const override;
|
||||
|
||||
private:
|
||||
TGoalVec tryCompleteQuest() const;
|
||||
|
@@ -26,15 +26,15 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const Composition & other) const override;
|
||||
bool operator==(const Composition & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(AIGateway * ai) override;
|
||||
Composition & addNext(const AbstractGoal & goal);
|
||||
Composition & addNext(TSubgoal goal);
|
||||
Composition & addNextSequence(const TGoalVec & taskSequence);
|
||||
virtual TGoalVec decompose() const override;
|
||||
virtual bool isElementar() const override;
|
||||
virtual int getHeroExchangeCount() const override;
|
||||
TGoalVec decompose() const override;
|
||||
bool isElementar() const override;
|
||||
int getHeroExchangeCount() const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ namespace Goals
|
||||
{
|
||||
tile = Tile;
|
||||
}
|
||||
virtual bool operator==(const DigAtTile & other) const override;
|
||||
bool operator==(const DigAtTile & other) const override;
|
||||
|
||||
private:
|
||||
//TSubgoal decomposeSingle() const override;
|
||||
|
@@ -26,7 +26,7 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const DismissHero & other) const override;
|
||||
bool operator==(const DismissHero & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,7 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
|
||||
bool operator==(const ExchangeSwapTownHeroes & other) const override;
|
||||
|
||||
const CGHeroInstance * getGarrisonHero() const { return garrisonHero; }
|
||||
HeroLockedReason getLockingReason() const { return lockingReason; }
|
||||
|
@@ -30,10 +30,10 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||
bool operator==(const ExecuteHeroChain & other) const override;
|
||||
const AIPath & getPath() const { return chainPath; }
|
||||
|
||||
virtual int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
||||
int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
||||
|
||||
private:
|
||||
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
|
||||
|
@@ -36,7 +36,7 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const GatherArmy & other) const override;
|
||||
bool operator==(const GatherArmy & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ namespace Goals
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
virtual bool operator==(const Invalid & other) const override
|
||||
bool operator==(const Invalid & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace Goals
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
virtual void accept(AIGateway * ai) override
|
||||
void accept(AIGateway * ai) override
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not fulfill Invalid goal!");
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ namespace Goals
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const RecruitHero & other) const override
|
||||
bool operator==(const RecruitHero & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ namespace Goals
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
virtual bool operator==(const SaveResources & other) const override;
|
||||
bool operator==(const SaveResources & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ namespace Goals
|
||||
public:
|
||||
StayAtTown(const CGTownInstance * town, AIPath & path);
|
||||
|
||||
virtual bool operator==(const StayAtTown & other) const override;
|
||||
bool operator==(const StayAtTown & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(AIGateway * ai) override;
|
||||
float getMovementWasted() const { return movementWasted; }
|
||||
|
@@ -34,7 +34,7 @@ namespace Goals
|
||||
value = val;
|
||||
objid = Objid;
|
||||
}
|
||||
virtual bool operator==(const Trade & other) const override;
|
||||
bool operator==(const Trade & other) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ namespace Goals
|
||||
ArmyUpgrade(const AIPath & upgradePath, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade);
|
||||
ArmyUpgrade(const CGHeroInstance * targetMain, const CGObjectInstance * upgrader, const ArmyUpgradeInfo & upgrade);
|
||||
|
||||
virtual bool operator==(const ArmyUpgrade & other) const override;
|
||||
bool operator==(const ArmyUpgrade & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
uint64_t getUpgradeValue() const { return upgradeValue; }
|
||||
|
@@ -30,7 +30,7 @@ namespace Goals
|
||||
DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath, bool isCounterAttack = false);
|
||||
DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const CGHeroInstance * defender);
|
||||
|
||||
virtual bool operator==(const DefendTown & other) const override;
|
||||
bool operator==(const DefendTown & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
const HitMapInfo & getTreat() const { return treat; }
|
||||
|
@@ -28,7 +28,7 @@ namespace Goals
|
||||
sethero(targetHero);
|
||||
}
|
||||
|
||||
virtual bool operator==(const HeroExchange & other) const override;
|
||||
bool operator==(const HeroExchange & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
|
||||
uint64_t getReinforcementArmyStrength() const;
|
||||
|
@@ -36,7 +36,7 @@ namespace Goals
|
||||
sethero(pathToCenter.targetHero);
|
||||
}
|
||||
|
||||
virtual bool operator==(const UnlockCluster & other) const override;
|
||||
bool operator==(const UnlockCluster & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
std::shared_ptr<ObjectCluster> getCluster() const { return cluster; }
|
||||
const AIPath & getPathToCenter() { return pathToCenter; }
|
||||
|
@@ -200,7 +200,7 @@ public:
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper) override;
|
||||
|
||||
virtual void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
|
||||
void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
|
||||
|
||||
void commit(
|
||||
AIPathNode * destination,
|
||||
|
@@ -34,7 +34,7 @@ namespace AIPathfinding
|
||||
|
||||
~AIPathfinderConfig();
|
||||
|
||||
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ namespace AIPathfinding
|
||||
public:
|
||||
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd = DayFlags::NONE);
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
@@ -38,7 +38,7 @@ namespace AIPathfinding
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
|
||||
virtual bool canAct(const AIPathNode * source) const override;
|
||||
bool canAct(const AIPathNode * source) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
@@ -28,7 +28,7 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
@@ -25,7 +25,7 @@ namespace AIPathfinding
|
||||
class SummonBoatAction : public VirtualBoatAction
|
||||
{
|
||||
public:
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
@@ -34,9 +34,9 @@ namespace AIPathfinding
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
|
||||
virtual bool canAct(const AIPathNode * source) const override;
|
||||
bool canAct(const AIPathNode * source) const override;
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
@@ -56,17 +56,17 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool canAct(const AIPathNode * source) const override;
|
||||
bool canAct(const AIPathNode * source) const override;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual const CGObjectInstance * targetObject() const override;
|
||||
const CGObjectInstance * targetObject() const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -28,11 +28,11 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool canAct(const AIPathNode * node) const override;
|
||||
bool canAct(const AIPathNode * node) const override;
|
||||
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
@@ -29,7 +29,7 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
@@ -28,7 +28,7 @@ class HeroExchangeArmy : public CArmedInstance
|
||||
public:
|
||||
TResources armyCost;
|
||||
bool requireBuyArmy;
|
||||
virtual bool needsLastStack() const override;
|
||||
bool needsLastStack() const override;
|
||||
std::shared_ptr<SpecialAction> getActorAction() const;
|
||||
|
||||
HeroExchangeArmy(): CArmedInstance(nullptr, true), requireBuyArmy(false) {}
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
HeroActor(const ChainActor * carrier, const ChainActor * other, const HeroExchangeArmy * army, const Nullkiller * ai);
|
||||
|
||||
protected:
|
||||
virtual ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const override;
|
||||
ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const override;
|
||||
};
|
||||
|
||||
class ObjectActor : public ChainActor
|
||||
|
@@ -39,6 +39,6 @@ namespace Goals
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const AdventureSpellCast & other) const override;
|
||||
bool operator==(const AdventureSpellCast & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
|
||||
virtual bool operator==(const Build & other) const override
|
||||
bool operator==(const Build & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -32,6 +32,6 @@ namespace Goals
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuildBoat & other) const override;
|
||||
bool operator==(const BuildBoat & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -43,6 +43,6 @@ namespace Goals
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
//bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const BuildThis & other) const override;
|
||||
bool operator==(const BuildThis & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -36,6 +36,6 @@ namespace Goals
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const BuyArmy & other) const override;
|
||||
bool operator==(const BuyArmy & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ namespace Goals
|
||||
//h & value & resID & objid & aid & tile & hero & town & bid;
|
||||
}
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const override
|
||||
bool operator==(const AbstractGoal & g) const override
|
||||
{
|
||||
if(goalType != g.goalType)
|
||||
return false;
|
||||
|
@@ -40,6 +40,6 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const ClearWayTo & other) const override;
|
||||
bool operator==(const ClearWayTo & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -35,6 +35,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
TSubgoal whatToDoToTrade();
|
||||
bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
|
||||
virtual bool operator==(const CollectRes & other) const override;
|
||||
bool operator==(const CollectRes & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const CompleteQuest & other) const override;
|
||||
bool operator==(const CompleteQuest & other) const override;
|
||||
|
||||
private:
|
||||
TGoalVec tryCompleteQuest() const;
|
||||
|
@@ -27,6 +27,6 @@ namespace Goals
|
||||
}
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const Conquer & other) const override;
|
||||
bool operator==(const Conquer & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -36,6 +36,6 @@ namespace Goals
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const DigAtTile & other) const override;
|
||||
bool operator==(const DigAtTile & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const Explore & other) const override;
|
||||
bool operator==(const Explore & other) const override;
|
||||
|
||||
private:
|
||||
TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||
|
@@ -42,6 +42,6 @@ namespace Goals
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const FindObj & other) const override;
|
||||
bool operator==(const FindObj & other) const override;
|
||||
};
|
||||
}
|
@@ -33,6 +33,6 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const GatherArmy & other) const override;
|
||||
bool operator==(const GatherArmy & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
virtual bool operator==(const GatherTroops & other) const override;
|
||||
bool operator==(const GatherTroops & other) const override;
|
||||
|
||||
private:
|
||||
int getCreaturesCount(const CArmedInstance * army);
|
||||
|
@@ -35,6 +35,6 @@ namespace Goals
|
||||
return TGoalVec();
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const GetArtOfType & other) const override;
|
||||
bool operator==(const GetArtOfType & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ namespace Goals
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
virtual bool operator==(const Invalid & other) const override
|
||||
bool operator==(const Invalid & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ namespace Goals
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
|
||||
virtual bool operator==(const RecruitHero & other) const override
|
||||
bool operator==(const RecruitHero & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -33,6 +33,6 @@ namespace Goals
|
||||
priority = 3; //trading is instant, but picking resources is free
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
virtual bool operator==(const Trade & other) const override;
|
||||
bool operator==(const Trade & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -37,6 +37,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitHero & other) const override;
|
||||
bool operator==(const VisitHero & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -27,6 +27,6 @@ namespace Goals
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
bool fulfillsMe(TSubgoal goal) override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitObj & other) const override;
|
||||
bool operator==(const VisitObj & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -32,6 +32,6 @@ namespace Goals
|
||||
TGoalVec getAllPossibleSubgoals() override;
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const VisitTile & other) const override;
|
||||
bool operator==(const VisitTile & other) const override;
|
||||
};
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ namespace Goals
|
||||
}
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
|
||||
virtual bool operator==(const Win & other) const override
|
||||
bool operator==(const Win & other) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -99,7 +99,7 @@ public:
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper) override;
|
||||
|
||||
virtual void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
|
||||
void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
|
||||
|
||||
const AIPathNode * getAINode(const CGPathNode * node) const;
|
||||
void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
|
||||
|
@@ -30,6 +30,6 @@ namespace AIPathfinding
|
||||
|
||||
~AIPathfinderConfig();
|
||||
|
||||
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
};
|
||||
}
|
||||
|
@@ -7,4 +7,4 @@ sudo apt-get install libboost-all-dev \
|
||||
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools \
|
||||
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||
libminizip-dev libfuzzylite-dev # Optional dependencies
|
||||
libminizip-dev libfuzzylite-dev libsqlite3-dev # Optional dependencies
|
||||
|
@@ -7,4 +7,4 @@ sudo apt-get install libboost-all-dev \
|
||||
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||
qtbase5-dev \
|
||||
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||
libminizip-dev libfuzzylite-dev qttools5-dev # Optional dependencies
|
||||
libminizip-dev libfuzzylite-dev qttools5-dev libsqlite3-dev # Optional dependencies
|
||||
|
@@ -41,6 +41,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
set(buildLobby OFF)
|
||||
set(singleProcess OFF)
|
||||
set(staticAI OFF)
|
||||
if(ANDROID)
|
||||
@@ -89,6 +90,14 @@ if(NOT APPLE_IOS AND NOT ANDROID)
|
||||
option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linux and Mac" OFF)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(buildLobby ON)
|
||||
endif()
|
||||
|
||||
if(NOT APPLE_IOS AND NOT ANDROID)
|
||||
option(ENABLE_LOBBY "Enable compilation of lobby server" ${buildLobby})
|
||||
endif()
|
||||
|
||||
option(ENABLE_CCACHE "Speed up recompilation by caching previous compilations" OFF)
|
||||
if(ENABLE_CCACHE)
|
||||
find_program(CCACHE ccache REQUIRED)
|
||||
@@ -475,6 +484,10 @@ if(TARGET SDL2_ttf::SDL2_ttf)
|
||||
add_library(SDL2::TTF ALIAS SDL2_ttf::SDL2_ttf)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LOBBY)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
# Widgets finds its own dependencies (QtGui and QtCore).
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
|
||||
@@ -622,6 +635,9 @@ endif()
|
||||
if(ENABLE_EDITOR)
|
||||
add_subdirectory(mapeditor)
|
||||
endif()
|
||||
if(ENABLE_LOBBY)
|
||||
add_subdirectory(lobby)
|
||||
endif()
|
||||
add_subdirectory(client)
|
||||
add_subdirectory(server)
|
||||
if(ENABLE_TEST)
|
||||
|
@@ -20,6 +20,7 @@
|
||||
"vcmi.adventureMap.playerAttacked" : "玩家遭受攻击: %s",
|
||||
"vcmi.adventureMap.moveCostDetails" : "移动点数 - 花费: %TURNS 轮 + %POINTS 点移动力, 剩余移动力: %REMAINING",
|
||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "移动点数 - 花费: %POINTS 点移动力, 剩余移动力: %REMAINING",
|
||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "抱歉,重放对手行动功能目前暂未实现!",
|
||||
|
||||
"vcmi.capitalColors.0" : "红色",
|
||||
"vcmi.capitalColors.1" : "蓝色",
|
||||
@@ -70,7 +71,9 @@
|
||||
"vcmi.lobby.mapPreview" : "地图预览",
|
||||
"vcmi.lobby.noPreview" : "无地上部分",
|
||||
"vcmi.lobby.noUnderground" : "无地下部分",
|
||||
"vcmi.lobby.sortDate" : "以修改时间排序地图",
|
||||
|
||||
"vcmi.client.errors.invalidMap" : "{非法地图或战役}\n\n启动游戏失败,选择的地图或者战役,无效或被污染。原因:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{找不到数据文件}\n\n没有找到战役数据文件!你可能使用了不完整或损坏的英雄无敌3数据文件,请重新安装数据文件。",
|
||||
"vcmi.server.errors.existingProcess" : "一个VCMI进程已经在运行,启动新进程前请结束它。",
|
||||
"vcmi.server.errors.modsToEnable" : "{需要启用的mod列表}",
|
||||
@@ -81,9 +84,9 @@
|
||||
"vcmi.server.errors.unknownEntity" : "加载保存失败! 在保存的游戏中发现未知实体'%s'! 保存可能与当前安装的mod版本不兼容!",
|
||||
|
||||
"vcmi.settingsMainWindow.generalTab.hover" : "常规",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "切换到“常规”选项卡 - 设置游戏客户端呈现",
|
||||
"vcmi.settingsMainWindow.generalTab.help" : "切换到“常规”选项卡 - 配置客户端常规内容",
|
||||
"vcmi.settingsMainWindow.battleTab.hover" : "战斗",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "切换到“战斗”选项卡 - 这些设置允许配置战斗界面和相关内容",
|
||||
"vcmi.settingsMainWindow.battleTab.help" : "切换到“战斗”选项卡 - 配置游戏战斗界面内容",
|
||||
"vcmi.settingsMainWindow.adventureTab.hover" : "冒险地图",
|
||||
"vcmi.settingsMainWindow.adventureTab.help" : "切换到“冒险地图”选项卡 - 冒险地图即玩家能操作英雄移动的界面",
|
||||
|
||||
@@ -93,43 +96,43 @@
|
||||
"vcmi.systemOptions.townsGroup" : "城镇画面",
|
||||
|
||||
"vcmi.systemOptions.fullscreenBorderless.hover" : "全屏 (无边框)",
|
||||
"vcmi.systemOptions.fullscreenBorderless.help" : "{全屏}\n\n选中时,VCMI将以无边框全屏模式运行。在这种模式下,游戏会使用和桌面一致的分辨率而非设置的分辨率。 ",
|
||||
"vcmi.systemOptions.fullscreenBorderless.help" : "{全屏}\n\n选中时,VCMI将以无边框全屏模式运行。该模式下,游戏会始终和桌面分辨率保持一致,无视设置的分辨率。 ",
|
||||
"vcmi.systemOptions.fullscreenExclusive.hover" : "全屏 (独占)",
|
||||
"vcmi.systemOptions.fullscreenExclusive.help" : "{全屏}\n\n选中时,VCMI将以独占全屏模式运行。在这种模式下,游戏会将显示器分辨率改变为设置值。",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "分辨率",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{分辨率选择}\n\n改变游戏内的分辨率,更改后需要重启游戏使其生效。",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "分辨率选择",
|
||||
"vcmi.systemOptions.fullscreenExclusive.help" : "{全屏}\n\n选中时,VCMI将以独占全屏模式运行。该模式下,游戏会将显示器分辨率改变为设置值。",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "分辨率: %wx%h",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{分辨率选择}\n\n改变游戏内的分辨率。",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "选择分辨率",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "修改游戏运行时的分辨率。",
|
||||
"vcmi.systemOptions.scalingButton.hover" : "界面大小: %p%",
|
||||
"vcmi.systemOptions.scalingButton.help" : "{界面大小}\n\n改变界面的大小",
|
||||
"vcmi.systemOptions.scalingMenu.hover" : "选择界面大小",
|
||||
"vcmi.systemOptions.scalingMenu.help" : "改变游戏界面大小。",
|
||||
"vcmi.systemOptions.longTouchButton.hover" : "触控间距: %d 毫秒", // Translation note: "ms" = "milliseconds"
|
||||
"vcmi.systemOptions.longTouchButton.help" : "{触控间距}\n\n使用触摸屏时,触摸屏幕指定持续时间(以毫秒为单位)后将出现弹出窗口。",
|
||||
"vcmi.systemOptions.longTouchMenu.hover" : "选择触控间距",
|
||||
"vcmi.systemOptions.longTouchMenu.help" : "改变触控间距。",
|
||||
"vcmi.systemOptions.scalingButton.hover" : "界面缩放: %p%",
|
||||
"vcmi.systemOptions.scalingButton.help" : "{界面缩放}\n\n改变用户界面的缩放比例。",
|
||||
"vcmi.systemOptions.scalingMenu.hover" : "选择界面缩放",
|
||||
"vcmi.systemOptions.scalingMenu.help" : "改变游戏内界面缩放。",
|
||||
"vcmi.systemOptions.longTouchButton.hover" : "长触延迟: %d 毫秒", // Translation note: "ms" = "milliseconds"
|
||||
"vcmi.systemOptions.longTouchButton.help" : "{长触延迟}\n\n使用触摸屏时,长触屏幕一定时间后出现弹出窗口(单位:毫秒)。",
|
||||
"vcmi.systemOptions.longTouchMenu.hover" : "选择长触延迟",
|
||||
"vcmi.systemOptions.longTouchMenu.help" : "改变长触延迟。",
|
||||
"vcmi.systemOptions.longTouchMenu.entry" : "%d 毫秒",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "显示FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{显示FPS}\n\n打开/关闭在游戏窗口角落的FPS指示器。",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{显示FPS}\n\n切换在游戏窗口角落显FPS指示器。",
|
||||
"vcmi.systemOptions.hapticFeedbackButton.hover" : "触觉反馈",
|
||||
"vcmi.systemOptions.hapticFeedbackButton.help" : "{触觉反馈}\n\n切换触摸输入的触觉反馈。",
|
||||
"vcmi.systemOptions.enableUiEnhancementsButton.hover" : "界面增强",
|
||||
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{界面增强}\n\n显示所有界面增强内容,如大背包和魔法书等。",
|
||||
"vcmi.systemOptions.enableLargeSpellbookButton.hover" : "增大魔法书界面",
|
||||
"vcmi.systemOptions.enableLargeSpellbookButton.help" : "{增大魔法书界面}\n\n可以在魔法书单页中显示更多的魔法,从而获得更好的视觉效果。",
|
||||
"vcmi.systemOptions.audioMuteFocus.hover" : "切换窗口时静音",
|
||||
"vcmi.systemOptions.audioMuteFocus.help" : "{切换窗口时静音}\n\n快速切换窗口时将静音,在工作时,切换游戏窗口不会有声音。",
|
||||
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{界面增强}\n\n显示所有界面优化增强,如背包按钮等。关闭该项以贴近经典模式。",
|
||||
"vcmi.systemOptions.enableLargeSpellbookButton.hover" : "扩展魔法书",
|
||||
"vcmi.systemOptions.enableLargeSpellbookButton.help" : "{扩展魔法书}\n\n启用更大的魔法书界面,每页展示更多魔法,但魔法书翻页特效在该模式下无法呈现。",
|
||||
"vcmi.systemOptions.audioMuteFocus.hover" : "失去焦点时静音",
|
||||
"vcmi.systemOptions.audioMuteFocus.help" : "{失去焦点时静音}\n\n当窗口失去焦点时静音,游戏内消息提示和新回合提示除外。",
|
||||
|
||||
"vcmi.adventureOptions.infoBarPick.hover" : "在信息面板显示消息",
|
||||
"vcmi.adventureOptions.infoBarPick.help" : "{在信息面板显示消息}\n\n来自访问地图物件的信息将显示在信息面板,而不是弹出窗口。",
|
||||
"vcmi.adventureOptions.infoBarPick.help" : "{在信息面板显示消息}\n\n尽可能将来自访问地图物件的信息将显示在信息面板,而不是弹出窗口。",
|
||||
"vcmi.adventureOptions.numericQuantities.hover" : "生物数量显示",
|
||||
"vcmi.adventureOptions.numericQuantities.help" : "{生物数量显示}\n\n以数字 A-B 格式显示不准确的敌方生物数量。",
|
||||
"vcmi.adventureOptions.forceMovementInfo.hover" : "在状态栏中显示移动力",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help" : "{在状态栏中显示移动力}\n\n不需要按ALT就可以显示移动力。",
|
||||
"vcmi.adventureOptions.forceMovementInfo.hover" : "总是显示移动花费",
|
||||
"vcmi.adventureOptions.forceMovementInfo.help" : "{总是显示移动花费}\n\n总是在状态栏中显示行动力花费(否则仅在按下ALT时显示)。",
|
||||
"vcmi.adventureOptions.showGrid.hover" : "显示网格",
|
||||
"vcmi.adventureOptions.showGrid.help" : "{显示网格}\n\n显示网格覆盖层,高亮冒险地图物件的边沿。",
|
||||
"vcmi.adventureOptions.borderScroll.hover" : "滚动边界",
|
||||
"vcmi.adventureOptions.borderScroll.help" : "{滚动边界}\n\n当光标靠近窗口边缘时滚动冒险地图。 可以通过按住 CTRL 键来禁用。",
|
||||
"vcmi.adventureOptions.borderScroll.hover" : "边缘滚动",
|
||||
"vcmi.adventureOptions.borderScroll.help" : "{边缘滚动}\n\n当光标靠近窗口边缘时滚动冒险地图。 可以通过按住 CTRL 键来禁用。",
|
||||
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "信息面板生物管理",
|
||||
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{信息面板生物管理}\n\n允许在信息面板中重新排列生物,而不是在默认组件之间循环。",
|
||||
"vcmi.adventureOptions.leftButtonDrag.hover" : "左键拖动地图",
|
||||
@@ -137,13 +140,15 @@
|
||||
"vcmi.adventureOptions.smoothDragging.hover" : "平滑地图拖动",
|
||||
"vcmi.adventureOptions.smoothDragging.help" : "{平滑地图拖动}\n\n启用后,地图拖动会产生柔和的羽化效果。",
|
||||
"vcmi.adventureOptions.skipAdventureMapAnimations.hover" : "关闭淡入淡出特效",
|
||||
"vcmi.adventureOptions.skipAdventureMapAnimations.help" : "{关闭淡入淡出特效}\n\n启用后,跳过物体淡出或类似特效(资源收集,登船等)。设置此项能在渲染开销重时能够加快UI的响应,尤其是在PvP对战中。当移动速度被设置为最大时忽略此项设置。",
|
||||
"vcmi.adventureOptions.skipAdventureMapAnimations.help" : "{关闭淡入淡出特效}\n\n启用后,跳过物体淡出或类似特效(资源收集,登船等)。设置此项能在渲染开销重时能够加快UI的响应,尤其是在PvP对战中。当移动速度被设置为最大时忽略此项设置。",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.hover": "",
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.help": "将地图卷动速度设置为非常慢",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "将地图卷动速度设置为非常快",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.help": "将地图卷动速度设置为即刻。",
|
||||
"vcmi.adventureOptions.hideBackground.hover" : "隐藏背景",
|
||||
"vcmi.adventureOptions.hideBackground.help" : "{隐藏背景}\n\n隐藏冒险地图背景,以显示贴图代替。",
|
||||
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "回合顺序指示器",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.hover": "关闭",
|
||||
@@ -168,6 +173,8 @@
|
||||
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{显示英雄统计数据窗口}\n\n永久切换并显示主要统计数据和法术点的英雄统计数据窗口。",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "跳过战斗开始音乐",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{跳过战斗开始音乐}\n\n战斗开始音乐播放期间,你也能够进行操作。",
|
||||
"vcmi.battleOptions.endWithAutocombat.hover": "结束战斗",
|
||||
"vcmi.battleOptions.endWithAutocombat.help": "{结束战斗}\n\n以自动战斗立即结束剩余战斗过程",
|
||||
|
||||
"vcmi.adventureMap.revisitObject.hover" : "重新访问",
|
||||
"vcmi.adventureMap.revisitObject.help" : "{重新访问}\n\n让当前英雄重新访问地图建筑或城镇。",
|
||||
@@ -183,17 +190,22 @@
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "%d 伤害",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d 将被消灭",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d 将被消灭",
|
||||
"vcmi.battleWindow.killed" : "已消灭",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s 死于精准射击",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s 死于精准射击",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.2" : "%d %s 死于精准射击",
|
||||
"vcmi.battleWindow.endWithAutocombat" : "您确定想以自动战斗立即结束吗?",
|
||||
|
||||
"vcmi.battleResultsWindow.applyResultsLabel" : "接受战斗结果",
|
||||
|
||||
"vcmi.tutorialWindow.title" : "触摸屏介绍",
|
||||
"vcmi.tutorialWindow.decription.RightClick" : "触摸并按住要右键单击的元素。 触摸可用区域以关闭。",
|
||||
"vcmi.tutorialWindow.decription.MapPanning" : "用一根手指触摸并拖动来移动地图。",
|
||||
"vcmi.tutorialWindow.decription.MapZooming" : "用两根手指捏合可更改地图缩放比例。",
|
||||
"vcmi.tutorialWindow.decription.RightClick" : "长按要右键单击的元素。 触摸其他区域以关闭。",
|
||||
"vcmi.tutorialWindow.decription.MapPanning" : "单指拖拽以移动地图。",
|
||||
"vcmi.tutorialWindow.decription.MapZooming" : "两指开合更改地图缩放比例。",
|
||||
"vcmi.tutorialWindow.decription.RadialWheel" : "滑动可打开径向轮以执行各种操作,例如生物/英雄管理和城镇排序。",
|
||||
"vcmi.tutorialWindow.decription.BattleDirection" : "要从特定方向攻击,请向要进行攻击的方向滑动。",
|
||||
"vcmi.tutorialWindow.decription.BattleDirectionAbort" : "如果手指距离足够远,可以取消攻击方向手势。",
|
||||
"vcmi.tutorialWindow.decription.AbortSpell" : "触摸并按住可取消魔法。",
|
||||
"vcmi.tutorialWindow.decription.BattleDirectionAbort" : "将长触状态的手指拉远足够距离,可以取消攻击方向手势。",
|
||||
"vcmi.tutorialWindow.decription.AbortSpell" : "长触以取消魔法。",
|
||||
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "显示可招募生物",
|
||||
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{显示可招募生物}\n\n在城镇摘要(城镇屏幕的左下角)中显示可招募的生物数量,而不是增长。",
|
||||
@@ -212,7 +224,7 @@
|
||||
"vcmi.townHall.greetingDefence" : "在%s中稍待片刻,富有战斗经验的战士会教你防御技巧(防御力+1)。",
|
||||
"vcmi.townHall.hasNotProduced" : "本周%s并没有产生什么资源。",
|
||||
"vcmi.townHall.hasProduced" : "本周%s产生了%d个%s。",
|
||||
"vcmi.townHall.greetingCustomBonus" : "当你的英雄访问%s 时,这个神奇的建筑使你的英雄 +%d %s%s。",
|
||||
"vcmi.townHall.greetingCustomBonus" : "%s 给予英雄 +%d %s%s。",
|
||||
"vcmi.townHall.greetingCustomUntil" : "直到下一场战斗。",
|
||||
"vcmi.townHall.greetingInTownMagicWell" : "%s使你的魔法值恢复到最大值。",
|
||||
|
||||
@@ -225,13 +237,15 @@
|
||||
"vcmi.heroWindow.openBackpack.hover" : "开启宝物背包界面",
|
||||
"vcmi.heroWindow.openBackpack.help" : "用更大的界面显示所有获得的宝物",
|
||||
|
||||
"vcmi.tavernWindow.inviteHero" : "邀请英雄",
|
||||
|
||||
"vcmi.commanderWindow.artifactMessage" : "你要把这个宝物还给英雄吗?",
|
||||
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "属性视图",
|
||||
"vcmi.creatureWindow.showBonuses.help" : "显示指挥官的所有属性增益",
|
||||
"vcmi.creatureWindow.showSkills.hover" : "技能视图",
|
||||
"vcmi.creatureWindow.showSkills.help" : "显示指挥官的所有学习的技能",
|
||||
"vcmi.creatureWindow.returnArtifact.hover" : "交换宝物",
|
||||
"vcmi.creatureWindow.returnArtifact.hover" : "返还宝物",
|
||||
"vcmi.creatureWindow.returnArtifact.help" : "点击这个按钮将宝物反还到英雄的背包里",
|
||||
|
||||
"vcmi.questLog.hideComplete.hover" : "隐藏完成任务",
|
||||
@@ -321,6 +335,7 @@
|
||||
"vcmi.map.victoryCondition.collectArtifacts.message" : "获得所有三件宝物",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "祝贺你!你取得了天使联盟且消灭了所有敌人,取得了胜利!",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.message" : "击败所有敌人并取得天使联盟",
|
||||
"vcmi.map.victoryCondition.angelicAlliancePartLost.toSelf" : "功亏一篑,你已失去了天使联盟的一个组件。彻底的失败。",
|
||||
|
||||
// few strings from WoG used by vcmi
|
||||
"vcmi.stackExperience.description" : "» 经 验 获 得 明 细 «\n\n生物类型 ................... : %s\n经验等级 ................. : %s (%i)\n经验点数 ............... : %i\n下一个等级所需经验 .. : %i\n每次战斗最大获得经验 ... : %i%% (%i)\n获得经验的生物数量 .... : %i\n最大招募数量\n不会丢失经验升级 .... : %i\n经验倍数 ........... : %.2f\n升级倍数 .............. : %.2f\n10级后经验值 ........ : %i\n最大招募数量下\n 升级到10级所需经验数量: %i",
|
||||
@@ -374,8 +389,10 @@
|
||||
"core.bonus.ENCHANTER.description": "每回合群体施放${subtype.spell}",
|
||||
"core.bonus.ENCHANTED.name": "法术加持",
|
||||
"core.bonus.ENCHANTED.description": "永久处于${subtype.spell}影响",
|
||||
"core.bonus.ENEMY_ATTACK_REDUCTION.name": "忽略攻击 (${val}%)",
|
||||
"core.bonus.ENEMY_ATTACK_REDUCTION.description": "被攻击时,进攻方${val}%的攻击力将被无视。",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "忽略防御 (${val}%)",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "当攻击时,目标生物${val}%的防御力将被无视。",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "发动攻击时,防御方${val}%的防御力将被无视。",
|
||||
"core.bonus.FIRE_IMMUNITY.name": "火系免疫",
|
||||
"core.bonus.FIRE_IMMUNITY.description": "免疫所有火系魔法。",
|
||||
"core.bonus.FIRE_SHIELD.name": "烈火神盾 (${val}%)",
|
||||
@@ -386,7 +403,9 @@
|
||||
"core.bonus.FEAR.description": "使得敌方一只部队恐惧",
|
||||
"core.bonus.FEARLESS.name": "无惧",
|
||||
"core.bonus.FEARLESS.description": "免疫恐惧特质",
|
||||
"core.bonus.FLYING.name": "飞行兵种",
|
||||
"core.bonus.FEROCITY.name": "凶猛追击",
|
||||
"core.bonus.FEROCITY.description": "杀死任意生物后额外攻击${val}次",
|
||||
"core.bonus.FLYING.name": "飞行能力",
|
||||
"core.bonus.FLYING.description": "以飞行的方式移动(无视障碍)",
|
||||
"core.bonus.FREE_SHOOTING.name": "近身射击",
|
||||
"core.bonus.FREE_SHOOTING.description": "能在近战范围内进行射击",
|
||||
@@ -396,50 +415,52 @@
|
||||
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "减少从远程和近战中遭受的物理伤害",
|
||||
"core.bonus.HATE.name": "${subtype.creature}的死敌",
|
||||
"core.bonus.HATE.description": "对${subtype.creature}造成额外${val}%伤害",
|
||||
"core.bonus.HEALER.name": "治疗",
|
||||
"core.bonus.HEALER.name": "治疗者",
|
||||
"core.bonus.HEALER.description": "可以治疗友军单位",
|
||||
"core.bonus.HP_REGENERATION.name": "再生",
|
||||
"core.bonus.HP_REGENERATION.description": "每回合恢复${val}点生命值",
|
||||
"core.bonus.JOUSTING.name": "冲锋",
|
||||
"core.bonus.JOUSTING.name": "勇士冲锋",
|
||||
"core.bonus.JOUSTING.description": "每移动一格 +${val}%伤害",
|
||||
"core.bonus.KING.name": "顶级怪物",
|
||||
"core.bonus.KING.name": "王牌",
|
||||
"core.bonus.KING.description": "受${val}级或更高级屠戮成性影响",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "免疫1-${val}级魔法",
|
||||
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "免疫1-${val}级的魔法",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "受限射击距离",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "无法以${val}格外的单位为射击目标",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name": "射程限制",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description": "无法瞄准${val}格以外的单位",
|
||||
"core.bonus.LIFE_DRAIN.name": "吸取生命 (${val}%)",
|
||||
"core.bonus.LIFE_DRAIN.description": "吸取${val}%伤害回复自身",
|
||||
"core.bonus.MANA_CHANNELING.name": "魔法虹吸${val}%",
|
||||
"core.bonus.MANA_CHANNELING.name": "法力虹吸${val}%",
|
||||
"core.bonus.MANA_CHANNELING.description": "使你的英雄有${val}%几率获得敌人施法的魔法值",
|
||||
"core.bonus.MANA_DRAIN.name": "吸取魔力",
|
||||
"core.bonus.MANA_DRAIN.name": "吸取法力",
|
||||
"core.bonus.MANA_DRAIN.description": "每回合吸取${val}魔法值",
|
||||
"core.bonus.MAGIC_MIRROR.name": "魔法神镜 (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "${val}%几率将进攻性魔法导向一个敌人单位",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "魔法抵抗 (${val}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "${val}%几率抵抗敌人的魔法",
|
||||
"core.bonus.MIND_IMMUNITY.name": "免疫心智",
|
||||
"core.bonus.MIND_IMMUNITY.description": "不受心智魔法的影响",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "无视距离惩罚",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "任意距离均造成全额伤害",
|
||||
"core.bonus.MIND_IMMUNITY.name": "免疫心智魔法",
|
||||
"core.bonus.MIND_IMMUNITY.description": "不受心智相关的魔法影响",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "无视射程惩罚",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.description": "任意射程造成全额伤害",
|
||||
"core.bonus.NO_MELEE_PENALTY.name": "无近战惩罚",
|
||||
"core.bonus.NO_MELEE_PENALTY.description": "该生物没有近战伤害惩罚",
|
||||
"core.bonus.NO_MORALE.name": "无士气",
|
||||
"core.bonus.NO_MORALE.description": "生物不受士气影响",
|
||||
"core.bonus.NO_WALL_PENALTY.name": "无城墙影响",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "攻城战中不被城墙阻挡造成全额伤害",
|
||||
"core.bonus.NO_WALL_PENALTY.name": "无城墙惩罚",
|
||||
"core.bonus.NO_WALL_PENALTY.description": "攻城战中无视城墙阻挡,造成全额伤害",
|
||||
"core.bonus.NON_LIVING.name": "无生命",
|
||||
"core.bonus.NON_LIVING.description": "免疫大多数的效果",
|
||||
"core.bonus.RANDOM_SPELLCASTER.name": "随机施法",
|
||||
"core.bonus.RANDOM_SPELLCASTER.description": "可以施放随机魔法",
|
||||
"core.bonus.RANGED_RETALIATION.name": "远程反击",
|
||||
"core.bonus.RANGED_RETALIATION.description": "可以对远程攻击进行反击",
|
||||
"core.bonus.RECEPTIVE.name": "接受",
|
||||
"core.bonus.RECEPTIVE.name": "接纳",
|
||||
"core.bonus.RECEPTIVE.description": "不会免疫有益魔法",
|
||||
"core.bonus.REBIRTH.name": "复生 (${val}%)",
|
||||
"core.bonus.REBIRTH.description": "当整支部队死亡后${val}%会复活",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "攻击后返回",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "近战攻击后回到初始位置",
|
||||
"core.bonus.REVENGE.name": "复仇",
|
||||
"core.bonus.REVENGE.description": "根据攻击者在战斗中失去的生命值造成额外伤害",
|
||||
"core.bonus.SHOOTER.name": "远程攻击",
|
||||
"core.bonus.SHOOTER.description": "生物可以射击",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.name": "范围远程攻击",
|
||||
@@ -471,7 +492,7 @@
|
||||
"core.bonus.TRANSMUTATION.name": "变形术",
|
||||
"core.bonus.TRANSMUTATION.description": "${val}%机会将被攻击单位变成其他生物",
|
||||
"core.bonus.UNDEAD.name": "不死生物",
|
||||
"core.bonus.UNDEAD.description": "该生物属于丧尸",
|
||||
"core.bonus.UNDEAD.description": "该生物属于不死生物",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.name": "无限反击",
|
||||
"core.bonus.UNLIMITED_RETALIATIONS.description": "每回合可以无限反击敌人",
|
||||
"core.bonus.WATER_IMMUNITY.name": "水系免疫",
|
||||
|
@@ -63,7 +63,6 @@
|
||||
"vcmi.mainMenu.serverClosing" : "Closing...",
|
||||
"vcmi.mainMenu.hostTCP" : "Host TCP/IP game",
|
||||
"vcmi.mainMenu.joinTCP" : "Join TCP/IP game",
|
||||
"vcmi.mainMenu.playerName" : "Player",
|
||||
|
||||
"vcmi.lobby.filepath" : "File path",
|
||||
"vcmi.lobby.creationDate" : "Creation date",
|
||||
@@ -72,13 +71,34 @@
|
||||
"vcmi.lobby.noPreview" : "no preview",
|
||||
"vcmi.lobby.noUnderground" : "no underground",
|
||||
"vcmi.lobby.sortDate" : "Sorts maps by change date",
|
||||
|
||||
"vcmi.lobby.login.title" : "VCMI Lobby",
|
||||
"vcmi.lobby.login.username" : "Username:",
|
||||
"vcmi.lobby.login.connecting" : "Connecting...",
|
||||
"vcmi.lobby.login.error" : "Connection error: %s",
|
||||
"vcmi.lobby.login.create" : "New Account",
|
||||
"vcmi.lobby.login.login" : "Login",
|
||||
|
||||
"vcmi.lobby.room.create" : "Create Room",
|
||||
"vcmi.lobby.room.players.limit" : "Players Limit",
|
||||
"vcmi.lobby.room.public" : "Public",
|
||||
"vcmi.lobby.room.private" : "Private",
|
||||
"vcmi.lobby.room.description.public" : "Any player can join public room.",
|
||||
"vcmi.lobby.room.description.private" : "Only invited players can join private room.",
|
||||
"vcmi.lobby.room.description.new" : "To start the game, select a scenario or set up a random map.",
|
||||
"vcmi.lobby.room.description.load" : "To start the game, use one of your saved games.",
|
||||
"vcmi.lobby.room.description.limit" : "Up to %d players can enter your room, including you.",
|
||||
"vcmi.lobby.room.new" : "New Game",
|
||||
"vcmi.lobby.room.load" : "Load Game",
|
||||
"vcmi.lobby.room.type" : "Room Type",
|
||||
"vcmi.lobby.room.mode" : "Game Mode",
|
||||
|
||||
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
||||
"vcmi.server.errors.disconnected" : "{Network Error}\n\nConnection to game server has been lost!",
|
||||
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
||||
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
||||
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}",
|
||||
"vcmi.server.confirmReconnect" : "Do you want to reconnect to the last session?",
|
||||
"vcmi.server.errors.modNoDependency" : "Failed to load mod {'%s'}!\n It depends on mod {'%s'} which is not active!\n",
|
||||
"vcmi.server.errors.modConflict" : "Failed to load mod {'%s'}!\n Conflicts with active mod {'%s'}!\n",
|
||||
"vcmi.server.errors.unknownEntity" : "Failed to load save! Unknown entity '%s' found in saved game! Save may not be compatible with currently installed version of mods!",
|
||||
|
@@ -20,6 +20,7 @@
|
||||
"vcmi.adventureMap.playerAttacked" : "El jugador ha sido atacado: %s",
|
||||
"vcmi.adventureMap.moveCostDetails" : "Puntos de movimiento - Coste: %TURNS turnos + %POINTS puntos, Puntos restantes: %REMAINING",
|
||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Puntos de movimiento - Coste: %POINTS puntos, Puntos restantes: %REMAINING",
|
||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Disculpe, la repetición del turno del oponente aún no está implementada.",
|
||||
|
||||
"vcmi.capitalColors.0" : "Rojo",
|
||||
"vcmi.capitalColors.1" : "Azul",
|
||||
@@ -70,7 +71,9 @@
|
||||
"vcmi.lobby.mapPreview" : "Vista previa del mapa",
|
||||
"vcmi.lobby.noPreview" : "sin vista previa",
|
||||
"vcmi.lobby.noUnderground" : "sin subterráneo",
|
||||
"vcmi.lobby.sortDate" : "Ordena los mapas por la fecha de modificación",
|
||||
|
||||
"vcmi.client.errors.invalidMap" : "{Mapa o Campaña invalido}\n\n¡No se pudo iniciar el juego! El mapa o la campaña seleccionados pueden no ser válidos o estar dañados. Motivo:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{Archivos de datos faltantes}\n\n¡No se encontraron los archivos de datos de las campañas! Quizás estés utilizando archivos de datos incompletos o dañados de Heroes 3. Por favor, reinstala los datos del juego.",
|
||||
"vcmi.server.errors.existingProcess" : "Otro servidor VCMI está en ejecución. Por favor, termínalo antes de comenzar un nuevo juego.",
|
||||
"vcmi.server.errors.modsToEnable" : "{Se requieren los siguientes mods}",
|
||||
@@ -144,6 +147,8 @@
|
||||
"vcmi.adventureOptions.mapScrollSpeed1.help": "Establece la velocidad de desplazamiento del mapa como muy lenta",
|
||||
"vcmi.adventureOptions.mapScrollSpeed5.help": "Establece la velocidad de desplazamiento del mapa como muy rápida",
|
||||
"vcmi.adventureOptions.mapScrollSpeed6.help": "Establece la velocidad de desplazamiento del mapa como instantánea.",
|
||||
"vcmi.adventureOptions.hideBackground.hover" : "Ocultar fondo",
|
||||
"vcmi.adventureOptions.hideBackground.help" : "{Ocultar fondo}\n\nOculta el mapa de aventuras en el fondo y muestra una textura en su lugar..",
|
||||
|
||||
"vcmi.battleOptions.queueSizeLabel.hover": "Mostrar orden de turno de criaturas",
|
||||
"vcmi.battleOptions.queueSizeNoneButton.hover": "APAGADO",
|
||||
@@ -168,6 +173,8 @@
|
||||
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Mostrar ventanas de estadísticas de héroes}\n\nAlternar permanentemente las ventanas de estadísticas de héroes que muestran estadísticas primarias y puntos de hechizo.",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Omitir música de introducción",
|
||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Omitir música de introducción}\n\nPermitir acciones durante la música de introducción que se reproduce al comienzo de cada batalla.",
|
||||
"vcmi.battleOptions.endWithAutocombat.hover": "Finaliza la batalla",
|
||||
"vcmi.battleOptions.endWithAutocombat.help": "{Finaliza la batalla}\n\nAutomatiza la batalla y la finaliza al instante",
|
||||
|
||||
"vcmi.adventureMap.revisitObject.hover" : "Revisitar objeto",
|
||||
"vcmi.adventureMap.revisitObject.help" : "{Revisitar objeto}\n\nSi un héroe se encuentra actualmente en un objeto del mapa, puede volver a visitar la ubicación.",
|
||||
@@ -183,6 +190,11 @@
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "%d daño",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d perecerán",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d perecerá",
|
||||
"vcmi.battleWindow.killed" : "Eliminados",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s han sido eliminados por disparos certeros",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s ha sido eliminado por un disparo certero",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.2" : "%d %s han sido eliminados por disparos certeros",
|
||||
"vcmi.battleWindow.endWithAutocombat" : "¿Quieres finalizar la batalla con combate automatizado?",
|
||||
|
||||
"vcmi.battleResultsWindow.applyResultsLabel" : "Aplicar resultado de la batalla",
|
||||
|
||||
@@ -225,6 +237,8 @@
|
||||
"vcmi.heroWindow.openBackpack.hover" : "Abrir ventana de mochila de artefactos",
|
||||
"vcmi.heroWindow.openBackpack.help" : "Abre la ventana que facilita la gestión de la mochila de artefactos.",
|
||||
|
||||
"vcmi.tavernWindow.inviteHero" : "Invitar heroe",
|
||||
|
||||
"vcmi.commanderWindow.artifactMessage" : "¿Quieres devolver este artefacto al héroe?",
|
||||
|
||||
"vcmi.creatureWindow.showBonuses.hover" : "Cambiar a vista de bonificaciones",
|
||||
@@ -321,6 +335,7 @@
|
||||
"vcmi.map.victoryCondition.collectArtifacts.message" : "Adquirir tres artefactos",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "¡Felicidades! Todos tus enemigos han sido derrotados y tienes la Alianza Angelical. ¡La victoria es tuya!",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.message" : "Derrota a todos los enemigos y crea la Alianza Angelical",
|
||||
"vcmi.map.victoryCondition.angelicAlliancePartLost.toSelf" : "Por desgracia, has perdido parte de la Alianza Angélica. Todo se ha perdido.",
|
||||
|
||||
// few strings from WoG used by vcmi
|
||||
"vcmi.stackExperience.description" : "» D e t a l l e s d e E x p e r i e n c i a d e l G r u p o «\n\nTipo de Criatura ................ : %s\nRango de Experiencia ............ : %s (%i)\nPuntos de Experiencia ............ : %i\nPuntos de Experiencia para el\nSiguiente Rango ............... : %i\nExperiencia Máxima por Batalla .. : %i%% (%i)\nNúmero de Criaturas en el grupo .. : %i\nMáximo de Nuevos Reclutas sin\nPerder el Rango Actual ......... : %i\nMultiplicador de Experiencia .... : %.2f\nMultiplicador de Actualización .. : %.2f\nExperiencia después del Rango 10 : %i\nMáximo de Nuevos Reclutas para\nMantener el Rango 10 si\nEstá en la Experiencia Máxima : %i",
|
||||
@@ -374,6 +389,8 @@
|
||||
"core.bonus.ENCHANTER.description": "Puede lanzar ${subtype.spell} masivo cada turno",
|
||||
"core.bonus.ENCHANTED.name": "Encantado",
|
||||
"core.bonus.ENCHANTED.description": "Afectado por el hechizo permanente ${subtype.spell}",
|
||||
"core.bonus.ENEMY_ATTACK_REDUCTION.name": "Ignorar ataque (${val}%)",
|
||||
"core.bonus.ENEMY_ATTACK_REDUCTION.description": "Al ser atacado, ${val}% del daño del atacante es ignorado",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignorar Defensa (${val}%)",
|
||||
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignora una parte de la defensa al atacar",
|
||||
"core.bonus.FIRE_IMMUNITY.name": "Inmunidad al Fuego",
|
||||
@@ -386,6 +403,8 @@
|
||||
"core.bonus.FEAR.description": "Causa miedo a un grupo enemigo",
|
||||
"core.bonus.FEARLESS.name": "Inmune al miedo",
|
||||
"core.bonus.FEARLESS.description": "Inmune a la habilidad de miedo",
|
||||
"core.bonus.FEROCITY.name": "Ferocidad",
|
||||
"core.bonus.FEROCITY.description": "Ataca ${val} veces adicionales en caso de eliminar a alguien",
|
||||
"core.bonus.FLYING.name": "Volar",
|
||||
"core.bonus.FLYING.description": "Puede volar (ignora obstáculos)",
|
||||
"core.bonus.FREE_SHOOTING.name": "Disparo cercano",
|
||||
@@ -416,8 +435,8 @@
|
||||
"core.bonus.MANA_DRAIN.description": "Drena ${val} de maná cada turno",
|
||||
"core.bonus.MAGIC_MIRROR.name": "Espejo mágico (${val}%)",
|
||||
"core.bonus.MAGIC_MIRROR.description": "Tiene una probabilidad del ${val}% de redirigir un hechizo ofensivo al enemigo",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "Resistencia mágica (${MR}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "Tiene una probabilidad del ${MR}% de resistir el hechizo del enemigo",
|
||||
"core.bonus.MAGIC_RESISTANCE.name": "Resistencia mágica (${val}%)",
|
||||
"core.bonus.MAGIC_RESISTANCE.description": "Tiene una probabilidad del ${val}% de resistir el hechizo del enemigo",
|
||||
"core.bonus.MIND_IMMUNITY.name": "Inmunidad a hechizos mentales",
|
||||
"core.bonus.MIND_IMMUNITY.description": "Inmune a hechizos de tipo mental",
|
||||
"core.bonus.NO_DISTANCE_PENALTY.name": "Sin penalización por distancia",
|
||||
@@ -440,10 +459,12 @@
|
||||
"core.bonus.REBIRTH.description": "El ${val}% del grupo resucitará después de la muerte",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.name": "Atacar y volver",
|
||||
"core.bonus.RETURN_AFTER_STRIKE.description": "Regresa después de un ataque cuerpo a cuerpo",
|
||||
"core.bonus.REVENGE.name": "Venganza",
|
||||
"core.bonus.REVENGE.description": "Inflige daño adicional según la salud perdida del atacante en la batalla.",
|
||||
"core.bonus.SHOOTER.name": "A distancia",
|
||||
"core.bonus.SHOOTER.description": "La criatura puede disparar",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Dispara en todas direcciones",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Los ataques a distancia de esta criatura impactan a todos los objetivos en un área pequeña",
|
||||
"core.bonus.SHOOTS_ALL_ADJACENT.description": "Los ataques a distancia de esta criatura impactan a todos los objetivos en un área reducida",
|
||||
"core.bonus.SOUL_STEAL.name": "Roba almas",
|
||||
"core.bonus.SOUL_STEAL.description": "Gana ${val} nuevas criaturas por cada enemigo eliminado",
|
||||
"core.bonus.SPELLCASTER.name": "Lanzador de hechizos",
|
||||
|
@@ -138,16 +138,7 @@ int main(int argc, char * argv[])
|
||||
("nointro,i", "skips intro movies")
|
||||
("donotstartserver,d","do not attempt to start server and just connect to it instead server")
|
||||
("serverport", po::value<si64>(), "override port specified in config file")
|
||||
("savefrequency", po::value<si64>(), "limit auto save creation to each N days")
|
||||
("lobby", "parameters address, port, uuid to connect ro remote lobby session")
|
||||
("lobby-address", po::value<std::string>(), "address to remote lobby")
|
||||
("lobby-port", po::value<ui16>(), "port to remote lobby")
|
||||
("lobby-host", "if this client hosts session")
|
||||
("lobby-uuid", po::value<std::string>(), "uuid to the server")
|
||||
("lobby-connections", po::value<ui16>(), "connections of server")
|
||||
("lobby-username", po::value<std::string>(), "player name")
|
||||
("lobby-gamemode", po::value<ui16>(), "use 0 for new game and 1 for load game")
|
||||
("uuid", po::value<std::string>(), "uuid for the client");
|
||||
("savefrequency", po::value<si64>(), "limit auto save creation to each N days");
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
@@ -371,46 +362,6 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
session["lobby"].Bool() = false;
|
||||
if(vm.count("lobby"))
|
||||
{
|
||||
session["lobby"].Bool() = true;
|
||||
session["host"].Bool() = false;
|
||||
session["address"].String() = vm["lobby-address"].as<std::string>();
|
||||
if(vm.count("lobby-username"))
|
||||
session["username"].String() = vm["lobby-username"].as<std::string>();
|
||||
else
|
||||
session["username"].String() = settings["launcher"]["lobbyUsername"].String();
|
||||
if(vm.count("lobby-gamemode"))
|
||||
session["gamemode"].Integer() = vm["lobby-gamemode"].as<ui16>();
|
||||
else
|
||||
session["gamemode"].Integer() = 0;
|
||||
CSH->uuid = vm["uuid"].as<std::string>();
|
||||
session["port"].Integer() = vm["lobby-port"].as<ui16>();
|
||||
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
|
||||
if(vm.count("lobby-host"))
|
||||
{
|
||||
session["host"].Bool() = true;
|
||||
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
|
||||
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
|
||||
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
|
||||
}
|
||||
|
||||
//we should not reconnect to previous game in online mode
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = false;
|
||||
|
||||
//start lobby immediately
|
||||
names.push_back(session["username"].String());
|
||||
ESelectionScreen sscreen = session["gamemode"].Integer() == 0 ? ESelectionScreen::newGame : ESelectionScreen::loadGame;
|
||||
CMM->openLobby(sscreen, session["host"].Bool(), &names, ELoadMode::MULTI);
|
||||
}
|
||||
|
||||
// Restore remote session - start game immediately
|
||||
if(settings["server"]["reconnect"].Bool())
|
||||
{
|
||||
CSH->restoreLastSession();
|
||||
}
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
@@ -451,7 +402,6 @@ static void mainLoop()
|
||||
while(1) //main SDL events loop
|
||||
{
|
||||
GH.input().fetchEvents();
|
||||
CSH->applyPacksOnLobbyScreen();
|
||||
GH.renderFrame();
|
||||
}
|
||||
}
|
||||
|
@@ -95,6 +95,12 @@ set(client_SRCS
|
||||
renderSDL/ScreenHandler.cpp
|
||||
renderSDL/SDL_Extensions.cpp
|
||||
|
||||
globalLobby/GlobalLobbyClient.cpp
|
||||
globalLobby/GlobalLobbyLoginWindow.cpp
|
||||
globalLobby/GlobalLobbyServerSetup.cpp
|
||||
globalLobby/GlobalLobbyWidget.cpp
|
||||
globalLobby/GlobalLobbyWindow.cpp
|
||||
|
||||
widgets/Buttons.cpp
|
||||
widgets/CArtifactHolder.cpp
|
||||
widgets/CComponent.cpp
|
||||
@@ -270,6 +276,13 @@ set(client_HEADERS
|
||||
renderSDL/SDL_Extensions.h
|
||||
renderSDL/SDL_PixelAccess.h
|
||||
|
||||
globalLobby/GlobalLobbyClient.h
|
||||
globalLobby/GlobalLobbyDefines.h
|
||||
globalLobby/GlobalLobbyLoginWindow.h
|
||||
globalLobby/GlobalLobbyServerSetup.h
|
||||
globalLobby/GlobalLobbyWidget.h
|
||||
globalLobby/GlobalLobbyWindow.h
|
||||
|
||||
widgets/Buttons.h
|
||||
widgets/CArtifactHolder.h
|
||||
widgets/CComponent.h
|
||||
|
@@ -1870,14 +1870,9 @@ void CPlayerInterface::proposeLoadingGame()
|
||||
CGI->generaltexth->allTexts[68],
|
||||
[]()
|
||||
{
|
||||
GH.dispatchMainThread(
|
||||
[]()
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("load");
|
||||
}
|
||||
);
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("load");
|
||||
},
|
||||
nullptr
|
||||
);
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
|
||||
#include "globalLobby/GlobalLobbyClient.h"
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "lobby/CLobbyScreen.h"
|
||||
#include "windows/InfoWindows.h"
|
||||
@@ -46,16 +47,15 @@
|
||||
#include "../lib/mapObjects/MiscObjects.h"
|
||||
#include "../lib/modding/ModIncompatibility.h"
|
||||
#include "../lib/rmg/CMapGenOptions.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/registerTypes/RegisterTypesLobbyPacks.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/serializer/CMemorySerializer.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include "../lib/serializer/Cast.h"
|
||||
#include "LobbyClientNetPackVisitors.h"
|
||||
|
||||
@@ -67,8 +67,6 @@
|
||||
|
||||
template<typename T> class CApplyOnLobby;
|
||||
|
||||
const std::string CServerHandler::localhostAddress{"127.0.0.1"};
|
||||
|
||||
#if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
|
||||
extern std::atomic_bool androidTestServerReadyFlag;
|
||||
#endif
|
||||
@@ -76,8 +74,8 @@ extern std::atomic_bool androidTestServerReadyFlag;
|
||||
class CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
virtual bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const = 0;
|
||||
virtual void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const = 0;
|
||||
virtual bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const = 0;
|
||||
virtual void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const = 0;
|
||||
virtual ~CBaseForLobbyApply(){};
|
||||
template<typename U> static CBaseForLobbyApply * getApplier(const U * t = nullptr)
|
||||
{
|
||||
@@ -88,124 +86,137 @@ public:
|
||||
template<typename T> class CApplyOnLobby : public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
auto & ref = static_cast<T&>(pack);
|
||||
ApplyOnLobbyHandlerNetPackVisitor visitor(*handler);
|
||||
|
||||
logNetwork->trace("\tImmediately apply on lobby: %s", typeid(ptr).name());
|
||||
ptr->visit(visitor);
|
||||
logNetwork->trace("\tImmediately apply on lobby: %s", typeid(ref).name());
|
||||
ref.visit(visitor);
|
||||
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
auto & ref = static_cast<T &>(pack);
|
||||
ApplyOnLobbyScreenNetPackVisitor visitor(*handler, lobby);
|
||||
|
||||
logNetwork->trace("\tApply on lobby from queue: %s", typeid(ptr).name());
|
||||
ptr->visit(visitor);
|
||||
logNetwork->trace("\tApply on lobby from queue: %s", typeid(ref).name());
|
||||
ref.visit(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template<> class CApplyOnLobby<CPack>: public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, void * pack) const override
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, void * pack) const override
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
static const std::string NAME_AFFIX = "client";
|
||||
static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
CServerHandler::~CServerHandler()
|
||||
{
|
||||
networkHandler->stop();
|
||||
try
|
||||
{
|
||||
threadNetwork.join();
|
||||
}
|
||||
catch (const std::runtime_error & e)
|
||||
{
|
||||
logGlobal->error("Failed to shut down network thread! Reason: %s", e.what());
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
CServerHandler::CServerHandler()
|
||||
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
|
||||
: networkHandler(INetworkHandler::createHandler())
|
||||
, lobbyClient(std::make_unique<GlobalLobbyClient>())
|
||||
, applier(std::make_unique<CApplier<CBaseForLobbyApply>>())
|
||||
, threadNetwork(&CServerHandler::threadRunNetwork, this)
|
||||
, state(EClientState::NONE)
|
||||
, serverPort(0)
|
||||
, campaignStateToSend(nullptr)
|
||||
, screenType(ESelectionScreen::unknown)
|
||||
, serverMode(EServerMode::NONE)
|
||||
, loadMode(ELoadMode::NONE)
|
||||
, client(nullptr)
|
||||
{
|
||||
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
||||
//read from file to restore last session
|
||||
if(!settings["server"]["uuid"].isNull() && !settings["server"]["uuid"].String().empty())
|
||||
uuid = settings["server"]["uuid"].String();
|
||||
applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
|
||||
registerTypesLobbyPacks(*applier);
|
||||
}
|
||||
|
||||
CServerHandler::~CServerHandler() = default;
|
||||
void CServerHandler::threadRunNetwork()
|
||||
{
|
||||
logGlobal->info("Starting network thread");
|
||||
setThreadName("runNetwork");
|
||||
networkHandler->run();
|
||||
logGlobal->info("Ending network thread");
|
||||
}
|
||||
|
||||
void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names)
|
||||
void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen, EServerMode newServerMode, const std::vector<std::string> & names)
|
||||
{
|
||||
hostClientId = -1;
|
||||
state = EClientState::NONE;
|
||||
setState(EClientState::NONE);
|
||||
serverMode = newServerMode;
|
||||
mapToStart = nullptr;
|
||||
th = std::make_unique<CStopWatch>();
|
||||
packsForLobbyScreen.clear();
|
||||
c.reset();
|
||||
logicConnection.reset();
|
||||
si = std::make_shared<StartInfo>();
|
||||
playerNames.clear();
|
||||
si->difficulty = 1;
|
||||
si->mode = mode;
|
||||
screenType = screen;
|
||||
myNames.clear();
|
||||
if(names && !names->empty()) //if have custom set of player names - use it
|
||||
myNames = *names;
|
||||
if(!names.empty()) //if have custom set of player names - use it
|
||||
myNames = names;
|
||||
else
|
||||
myNames.push_back(settings["general"]["playerName"].String());
|
||||
}
|
||||
|
||||
void CServerHandler::startLocalServerAndConnect()
|
||||
GlobalLobbyClient & CServerHandler::getGlobalLobby()
|
||||
{
|
||||
if(threadRunLocalServer)
|
||||
threadRunLocalServer->join();
|
||||
return *lobbyClient;
|
||||
}
|
||||
|
||||
INetworkHandler & CServerHandler::getNetworkHandler()
|
||||
{
|
||||
return *networkHandler;
|
||||
}
|
||||
|
||||
void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
|
||||
{
|
||||
if(threadRunLocalServer.joinable())
|
||||
threadRunLocalServer.join();
|
||||
|
||||
th->update();
|
||||
|
||||
auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.existingProcess");
|
||||
try
|
||||
{
|
||||
CConnection testConnection(localhostAddress, getDefaultPort(), NAME, uuid);
|
||||
logNetwork->error("Port is busy, check if another instance of vcmiserver is working");
|
||||
CInfoWindow::showInfoDialog(errorMsg, {});
|
||||
return;
|
||||
}
|
||||
catch(std::runtime_error & error)
|
||||
{
|
||||
//no connection means that port is not busy and we can start local server
|
||||
}
|
||||
|
||||
|
||||
#if defined(SINGLE_PROCESS_APP)
|
||||
boost::condition_variable cond;
|
||||
std::vector<std::string> args{"--uuid=" + uuid, "--port=" + std::to_string(getHostPort())};
|
||||
if(settings["session"]["lobby"].Bool() && settings["session"]["host"].Bool())
|
||||
{
|
||||
args.push_back("--lobby=" + settings["session"]["address"].String());
|
||||
args.push_back("--connections=" + settings["session"]["hostConnections"].String());
|
||||
args.push_back("--lobby-port=" + std::to_string(settings["session"]["port"].Integer()));
|
||||
args.push_back("--lobby-uuid=" + settings["session"]["hostUuid"].String());
|
||||
}
|
||||
threadRunLocalServer = std::make_shared<boost::thread>([&cond, args, this] {
|
||||
std::vector<std::string> args{"--port=" + std::to_string(getLocalPort())};
|
||||
if(connectToLobby)
|
||||
args.push_back("--lobby");
|
||||
|
||||
threadRunLocalServer = boost::thread([&cond, args] {
|
||||
setThreadName("CVCMIServer");
|
||||
CVCMIServer::create(&cond, args);
|
||||
onServerFinished();
|
||||
});
|
||||
threadRunLocalServer->detach();
|
||||
#elif defined(VCMI_ANDROID)
|
||||
{
|
||||
CAndroidVMHelper envHelper;
|
||||
envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true);
|
||||
}
|
||||
#else
|
||||
threadRunLocalServer = std::make_shared<boost::thread>(&CServerHandler::threadRunServer, this); //runs server executable;
|
||||
threadRunLocalServer = boost::thread(&CServerHandler::threadRunServer, this, connectToLobby); //runs server executable;
|
||||
#endif
|
||||
logNetwork->trace("Setting up thread calling server: %d ms", th->getDiff());
|
||||
|
||||
@@ -236,7 +247,7 @@ void CServerHandler::startLocalServerAndConnect()
|
||||
while(!androidTestServerReadyFlag.load())
|
||||
{
|
||||
logNetwork->info("still waiting...");
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
}
|
||||
logNetwork->info("waiting for server finished...");
|
||||
androidTestServerReadyFlag = false;
|
||||
@@ -245,151 +256,165 @@ void CServerHandler::startLocalServerAndConnect()
|
||||
|
||||
th->update(); //put breakpoint here to attach to server before it does something stupid
|
||||
|
||||
justConnectToServer(localhostAddress, 0);
|
||||
connectToServer(getLocalHostname(), getLocalPort());
|
||||
|
||||
logNetwork->trace("\tConnecting to the server: %d ms", th->getDiff());
|
||||
}
|
||||
|
||||
void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port)
|
||||
void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
|
||||
{
|
||||
state = EClientState::CONNECTING;
|
||||
while(!c && state != EClientState::CONNECTION_CANCELLED)
|
||||
{
|
||||
try
|
||||
{
|
||||
logNetwork->info("Establishing connection...");
|
||||
c = std::make_shared<CConnection>(
|
||||
addr.size() ? addr : getHostAddress(),
|
||||
port ? port : getHostPort(),
|
||||
NAME, uuid);
|
||||
logNetwork->info("Establishing connection to %s:%d...", addr, port);
|
||||
setState(EClientState::CONNECTING);
|
||||
serverHostname = addr;
|
||||
serverPort = port;
|
||||
|
||||
nextClient = std::make_unique<CClient>();
|
||||
c->iser.cb = nextClient.get();
|
||||
}
|
||||
catch(std::runtime_error & error)
|
||||
{
|
||||
logNetwork->warn("\nCannot establish connection. %s Retrying in 1 second", error.what());
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
|
||||
}
|
||||
if (!isServerLocal())
|
||||
{
|
||||
Settings remoteAddress = settings.write["server"]["remoteHostname"];
|
||||
remoteAddress->String() = addr;
|
||||
|
||||
Settings remotePort = settings.write["server"]["remotePort"];
|
||||
remotePort->Integer() = port;
|
||||
}
|
||||
|
||||
if(state == EClientState::CONNECTION_CANCELLED)
|
||||
networkHandler->connectToRemote(*this, addr, port);
|
||||
}
|
||||
|
||||
void CServerHandler::onConnectionFailed(const std::string & errorMessage)
|
||||
{
|
||||
assert(getState() == EClientState::CONNECTING);
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
if (isServerLocal())
|
||||
{
|
||||
// retry - local server might be still starting up
|
||||
logNetwork->debug("\nCannot establish connection. %s. Retrying...", errorMessage);
|
||||
networkHandler->createTimer(*this, std::chrono::milliseconds(100));
|
||||
}
|
||||
else
|
||||
{
|
||||
// remote server refused connection - show error message
|
||||
setState(EClientState::NONE);
|
||||
CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.mainMenu.serverConnectionFailed"), {});
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::onTimer()
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
if(getState() == EClientState::CONNECTION_CANCELLED)
|
||||
{
|
||||
logNetwork->info("Connection aborted by player!");
|
||||
return;
|
||||
}
|
||||
|
||||
c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
|
||||
|
||||
if(!addr.empty() && addr != getHostAddress())
|
||||
{
|
||||
Settings serverAddress = settings.write["server"]["server"];
|
||||
serverAddress->String() = addr;
|
||||
}
|
||||
if(port && port != getHostPort())
|
||||
{
|
||||
Settings serverPort = settings.write["server"]["port"];
|
||||
serverPort->Integer() = port;
|
||||
}
|
||||
assert(isServerLocal());
|
||||
networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort());
|
||||
}
|
||||
|
||||
void CServerHandler::applyPacksOnLobbyScreen()
|
||||
void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)
|
||||
{
|
||||
if(!c || !c->handler)
|
||||
return;
|
||||
assert(getState() == EClientState::CONNECTING);
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
while(!packsForLobbyScreen.empty())
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
networkConnection = netConnection;
|
||||
|
||||
logNetwork->info("Connection established");
|
||||
|
||||
if (serverMode == EServerMode::LOBBY_GUEST)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
CPackForLobby * pack = packsForLobbyScreen.front();
|
||||
packsForLobbyScreen.pop_front();
|
||||
CBaseForLobbyApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(pack)); //find the applier
|
||||
apply->applyOnLobbyScreen(dynamic_cast<CLobbyScreen *>(SEL), this, pack);
|
||||
GH.windows().totalRedraw();
|
||||
delete pack;
|
||||
// say hello to lobby to switch connection to proxy mode
|
||||
getGlobalLobby().sendProxyConnectionLogin(netConnection);
|
||||
}
|
||||
|
||||
logicConnection = std::make_shared<CConnection>(netConnection);
|
||||
logicConnection->uuid = uuid;
|
||||
logicConnection->enterLobbyConnectionMode();
|
||||
sendClientConnecting();
|
||||
}
|
||||
|
||||
void CServerHandler::stopServerConnection()
|
||||
void CServerHandler::applyPackOnLobbyScreen(CPackForLobby & pack)
|
||||
{
|
||||
if(c->handler)
|
||||
{
|
||||
while(!c->handler->timed_join(boost::chrono::milliseconds(50)))
|
||||
applyPacksOnLobbyScreen();
|
||||
c->handler->join();
|
||||
}
|
||||
const CBaseForLobbyApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(&pack)); //find the applier
|
||||
apply->applyOnLobbyScreen(dynamic_cast<CLobbyScreen *>(SEL), this, pack);
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
std::set<PlayerColor> CServerHandler::getHumanColors()
|
||||
{
|
||||
return clientHumanColors(c->connectionID);
|
||||
return clientHumanColors(logicConnection->connectionID);
|
||||
}
|
||||
|
||||
|
||||
PlayerColor CServerHandler::myFirstColor() const
|
||||
{
|
||||
return clientFirstColor(c->connectionID);
|
||||
return clientFirstColor(logicConnection->connectionID);
|
||||
}
|
||||
|
||||
bool CServerHandler::isMyColor(PlayerColor color) const
|
||||
{
|
||||
return isClientColor(c->connectionID, color);
|
||||
return isClientColor(logicConnection->connectionID, color);
|
||||
}
|
||||
|
||||
ui8 CServerHandler::myFirstId() const
|
||||
{
|
||||
return clientFirstId(c->connectionID);
|
||||
return clientFirstId(logicConnection->connectionID);
|
||||
}
|
||||
|
||||
EClientState CServerHandler::getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
void CServerHandler::setState(EClientState newState)
|
||||
{
|
||||
state = newState;
|
||||
}
|
||||
|
||||
bool CServerHandler::isServerLocal() const
|
||||
{
|
||||
if(threadRunLocalServer)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return threadRunLocalServer.joinable();
|
||||
}
|
||||
|
||||
bool CServerHandler::isHost() const
|
||||
{
|
||||
return c && hostClientId == c->connectionID;
|
||||
return logicConnection && hostClientId == logicConnection->connectionID;
|
||||
}
|
||||
|
||||
bool CServerHandler::isGuest() const
|
||||
{
|
||||
return !c || hostClientId != c->connectionID;
|
||||
return !logicConnection || hostClientId != logicConnection->connectionID;
|
||||
}
|
||||
|
||||
ui16 CServerHandler::getDefaultPort()
|
||||
const std::string & CServerHandler::getLocalHostname() const
|
||||
{
|
||||
return static_cast<ui16>(settings["server"]["port"].Integer());
|
||||
return settings["server"]["localHostname"].String();
|
||||
}
|
||||
|
||||
std::string CServerHandler::getDefaultPortStr()
|
||||
ui16 CServerHandler::getLocalPort() const
|
||||
{
|
||||
return std::to_string(getDefaultPort());
|
||||
return settings["server"]["localPort"].Integer();
|
||||
}
|
||||
|
||||
std::string CServerHandler::getHostAddress() const
|
||||
const std::string & CServerHandler::getRemoteHostname() const
|
||||
{
|
||||
if(settings["session"]["lobby"].isNull() || !settings["session"]["lobby"].Bool())
|
||||
return settings["server"]["server"].String();
|
||||
|
||||
if(settings["session"]["host"].Bool())
|
||||
return localhostAddress;
|
||||
|
||||
return settings["session"]["address"].String();
|
||||
return settings["server"]["remoteHostname"].String();
|
||||
}
|
||||
|
||||
ui16 CServerHandler::getHostPort() const
|
||||
ui16 CServerHandler::getRemotePort() const
|
||||
{
|
||||
if(settings["session"]["lobby"].isNull() || !settings["session"]["lobby"].Bool())
|
||||
return getDefaultPort();
|
||||
|
||||
if(settings["session"]["host"].Bool())
|
||||
return getDefaultPort();
|
||||
|
||||
return settings["session"]["port"].Integer();
|
||||
return settings["server"]["remotePort"].Integer();
|
||||
}
|
||||
|
||||
const std::string & CServerHandler::getCurrentHostname() const
|
||||
{
|
||||
return serverHostname;
|
||||
}
|
||||
|
||||
ui16 CServerHandler::getCurrentPort() const
|
||||
{
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
void CServerHandler::sendClientConnecting() const
|
||||
@@ -404,13 +429,16 @@ void CServerHandler::sendClientConnecting() const
|
||||
void CServerHandler::sendClientDisconnecting()
|
||||
{
|
||||
// FIXME: This is workaround needed to make sure client not trying to sent anything to non existed server
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
if(getState() == EClientState::DISCONNECTING)
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
state = EClientState::DISCONNECTING;
|
||||
setState(EClientState::DISCONNECTING);
|
||||
mapToStart = nullptr;
|
||||
LobbyClientDisconnected lcd;
|
||||
lcd.clientId = c->connectionID;
|
||||
lcd.clientId = logicConnection->connectionID;
|
||||
logNetwork->info("Connection has been requested to be closed.");
|
||||
if(isServerLocal())
|
||||
{
|
||||
@@ -422,18 +450,14 @@ void CServerHandler::sendClientDisconnecting()
|
||||
logNetwork->info("Sent leaving signal to the server");
|
||||
}
|
||||
sendLobbyPack(lcd);
|
||||
|
||||
{
|
||||
// Network thread might be applying network pack at this moment
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
c->close();
|
||||
c.reset();
|
||||
}
|
||||
networkConnection->close();
|
||||
networkConnection.reset();
|
||||
logicConnection.reset();
|
||||
}
|
||||
|
||||
void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
|
||||
{
|
||||
state = EClientState::LOBBY_CAMPAIGN;
|
||||
setState(EClientState::LOBBY_CAMPAIGN);
|
||||
LobbySetCampaign lsc;
|
||||
lsc.ourCampaign = newCampaign;
|
||||
sendLobbyPack(lsc);
|
||||
@@ -441,7 +465,7 @@ void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign
|
||||
|
||||
void CServerHandler::setCampaignMap(CampaignScenarioID mapId) const
|
||||
{
|
||||
if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
if(getState() == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
return;
|
||||
|
||||
LobbySetCampaignMap lscm;
|
||||
@@ -451,7 +475,7 @@ void CServerHandler::setCampaignMap(CampaignScenarioID mapId) const
|
||||
|
||||
void CServerHandler::setCampaignBonus(int bonusId) const
|
||||
{
|
||||
if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
if(getState() == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
|
||||
return;
|
||||
|
||||
LobbySetCampaignBonus lscb;
|
||||
@@ -575,9 +599,7 @@ void CServerHandler::sendRestartGame() const
|
||||
{
|
||||
GH.windows().createAndPushWindow<CLoadingScreen>();
|
||||
|
||||
LobbyEndGame endGame;
|
||||
endGame.closeConnection = false;
|
||||
endGame.restart = true;
|
||||
LobbyRestartGame endGame;
|
||||
sendLobbyPack(endGame);
|
||||
}
|
||||
|
||||
@@ -621,17 +643,18 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
GH.windows().createAndPushWindow<CLoadingScreen>();
|
||||
|
||||
LobbyPrepareStartGame lpsg;
|
||||
sendLobbyPack(lpsg);
|
||||
|
||||
LobbyStartGame lsg;
|
||||
if(client)
|
||||
{
|
||||
lsg.initializedStartInfo = std::make_shared<StartInfo>(* const_cast<StartInfo *>(client->getStartInfo(true)));
|
||||
lsg.initializedStartInfo->mode = StartInfo::NEW_GAME;
|
||||
lsg.initializedStartInfo->mode = EStartMode::NEW_GAME;
|
||||
lsg.initializedStartInfo->seedToBeUsed = lsg.initializedStartInfo->seedPostInit = 0;
|
||||
* si = * lsg.initializedStartInfo;
|
||||
}
|
||||
sendLobbyPack(lsg);
|
||||
c->enterLobbyConnectionMode();
|
||||
c->disableStackSendingByID();
|
||||
}
|
||||
|
||||
void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
|
||||
@@ -644,83 +667,54 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
|
||||
if(CMM)
|
||||
CMM->disable();
|
||||
|
||||
std::swap(client, nextClient);
|
||||
|
||||
highScoreCalc = nullptr;
|
||||
|
||||
switch(si->mode)
|
||||
{
|
||||
case StartInfo::NEW_GAME:
|
||||
case EStartMode::NEW_GAME:
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case StartInfo::CAMPAIGN:
|
||||
case EStartMode::CAMPAIGN:
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case StartInfo::LOAD_GAME:
|
||||
case EStartMode::LOAD_GAME:
|
||||
client->loadGame(gameState);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid mode");
|
||||
}
|
||||
// After everything initialized we can accept CPackToClient netpacks
|
||||
c->enterGameplayConnectionMode(client->gameState());
|
||||
state = EClientState::GAMEPLAY;
|
||||
|
||||
//store settings to continue game
|
||||
if(!isServerLocal() && isGuest())
|
||||
{
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = true;
|
||||
Settings saveUuid = settings.write["server"]["uuid"];
|
||||
saveUuid->String() = uuid;
|
||||
Settings saveNames = settings.write["server"]["names"];
|
||||
saveNames->Vector().clear();
|
||||
for(auto & name : myNames)
|
||||
{
|
||||
JsonNode jsonName;
|
||||
jsonName.String() = name;
|
||||
saveNames->Vector().push_back(jsonName);
|
||||
}
|
||||
}
|
||||
logicConnection->enterGameplayConnectionMode(client->gameState());
|
||||
setState(EClientState::GAMEPLAY);
|
||||
}
|
||||
|
||||
void CServerHandler::endGameplay(bool closeConnection, bool restart)
|
||||
void CServerHandler::endGameplay()
|
||||
{
|
||||
if(closeConnection)
|
||||
{
|
||||
// Game is ending
|
||||
// Tell the network thread to reach a stable state
|
||||
CSH->sendClientDisconnecting();
|
||||
logNetwork->info("Closed connection.");
|
||||
}
|
||||
// Game is ending
|
||||
// Tell the network thread to reach a stable state
|
||||
CSH->sendClientDisconnecting();
|
||||
logNetwork->info("Closed connection.");
|
||||
|
||||
client->endGame();
|
||||
client.reset();
|
||||
|
||||
if(!restart)
|
||||
if(CMM)
|
||||
{
|
||||
if(CMM)
|
||||
{
|
||||
GH.curInt = CMM.get();
|
||||
CMM->enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
GH.curInt = CMainMenu::create().get();
|
||||
}
|
||||
GH.curInt = CMM.get();
|
||||
CMM->enable();
|
||||
}
|
||||
|
||||
if(c)
|
||||
else
|
||||
{
|
||||
nextClient = std::make_unique<CClient>();
|
||||
c->iser.cb = nextClient.get();
|
||||
c->enterLobbyConnectionMode();
|
||||
c->disableStackSendingByID();
|
||||
GH.curInt = CMainMenu::create().get();
|
||||
}
|
||||
|
||||
//reset settings
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = false;
|
||||
}
|
||||
|
||||
void CServerHandler::restartGameplay()
|
||||
{
|
||||
client->endGame();
|
||||
client.reset();
|
||||
|
||||
logicConnection->enterLobbyConnectionMode();
|
||||
}
|
||||
|
||||
void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs)
|
||||
@@ -741,7 +735,6 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared
|
||||
|
||||
GH.dispatchMainThread([ourCampaign, this]()
|
||||
{
|
||||
CSH->campaignServerRestartLock.set(true);
|
||||
CSH->endGameplay();
|
||||
|
||||
auto & epilogue = ourCampaign->scenario(*ourCampaign->lastScenario()).epilog;
|
||||
@@ -764,13 +757,14 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared
|
||||
GH.windows().createAndPushWindow<CHighScoreInputScreen>(true, *highScoreCalc);
|
||||
}
|
||||
};
|
||||
|
||||
threadRunLocalServer.join();
|
||||
if(epilogue.hasPrologEpilog)
|
||||
{
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(epilogue, finisher);
|
||||
}
|
||||
else
|
||||
{
|
||||
CSH->campaignServerRestartLock.waitUntil(false);
|
||||
finisher();
|
||||
}
|
||||
});
|
||||
@@ -796,15 +790,15 @@ int CServerHandler::howManyPlayerInterfaces()
|
||||
return playerInts;
|
||||
}
|
||||
|
||||
ui8 CServerHandler::getLoadMode()
|
||||
ELoadMode CServerHandler::getLoadMode()
|
||||
{
|
||||
if(loadMode != ELoadMode::TUTORIAL && state == EClientState::GAMEPLAY)
|
||||
if(loadMode != ELoadMode::TUTORIAL && getState() == EClientState::GAMEPLAY)
|
||||
{
|
||||
if(si->campState)
|
||||
return ELoadMode::CAMPAIGN;
|
||||
for(auto pn : playerNames)
|
||||
{
|
||||
if(pn.second.connection != c->connectionID)
|
||||
if(pn.second.connection != logicConnection->connectionID)
|
||||
return ELoadMode::MULTI;
|
||||
}
|
||||
if(howManyPlayerInterfaces() > 1) //this condition will work for hotseat mode OR multiplayer with allowed more than 1 color per player to control
|
||||
@@ -815,48 +809,24 @@ ui8 CServerHandler::getLoadMode()
|
||||
return loadMode;
|
||||
}
|
||||
|
||||
void CServerHandler::restoreLastSession()
|
||||
{
|
||||
auto loadSession = [this]()
|
||||
{
|
||||
uuid = settings["server"]["uuid"].String();
|
||||
for(auto & name : settings["server"]["names"].Vector())
|
||||
myNames.push_back(name.String());
|
||||
resetStateForLobby(StartInfo::LOAD_GAME, &myNames);
|
||||
screenType = ESelectionScreen::loadGame;
|
||||
justConnectToServer(settings["server"]["server"].String(), settings["server"]["port"].Integer());
|
||||
};
|
||||
|
||||
auto cleanUpSession = []()
|
||||
{
|
||||
//reset settings
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = false;
|
||||
};
|
||||
|
||||
CInfoWindow::showYesNoDialog(VLC->generaltexth->translate("vcmi.server.confirmReconnect"), {}, loadSession, cleanUpSession);
|
||||
}
|
||||
|
||||
void CServerHandler::debugStartTest(std::string filename, bool save)
|
||||
{
|
||||
logGlobal->info("Starting debug test with file: %s", filename);
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
if(save)
|
||||
{
|
||||
resetStateForLobby(StartInfo::LOAD_GAME);
|
||||
resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, EServerMode::LOCAL, {});
|
||||
mapInfo->saveInit(ResourcePath(filename, EResType::SAVEGAME));
|
||||
screenType = ESelectionScreen::loadGame;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetStateForLobby(StartInfo::NEW_GAME);
|
||||
resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOCAL, {});
|
||||
mapInfo->mapInit(filename);
|
||||
screenType = ESelectionScreen::newGame;
|
||||
}
|
||||
if(settings["session"]["donotstartserver"].Bool())
|
||||
justConnectToServer(localhostAddress, 3030);
|
||||
connectToServer(getLocalHostname(), getLocalPort());
|
||||
else
|
||||
startLocalServerAndConnect();
|
||||
startLocalServerAndConnect(false);
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
|
||||
@@ -899,91 +869,71 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool callTyped() override { return false; }
|
||||
bool callTyped() override { return false; }
|
||||
|
||||
virtual void visitForLobby(CPackForLobby & lobbyPack) override
|
||||
void visitForLobby(CPackForLobby & lobbyPack) override
|
||||
{
|
||||
handler.visitForLobby(lobbyPack);
|
||||
}
|
||||
|
||||
virtual void visitForClient(CPackForClient & clientPack) override
|
||||
void visitForClient(CPackForClient & clientPack) override
|
||||
{
|
||||
handler.visitForClient(clientPack);
|
||||
}
|
||||
};
|
||||
|
||||
void CServerHandler::threadHandleConnection()
|
||||
void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<std::byte> & message)
|
||||
{
|
||||
setThreadName("handleConnection");
|
||||
c->enterLobbyConnectionMode();
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
try
|
||||
if(getState() == EClientState::DISCONNECTING)
|
||||
{
|
||||
sendClientConnecting();
|
||||
while(c && c->connected)
|
||||
{
|
||||
while(state == EClientState::STARTING)
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
|
||||
|
||||
CPack * pack = c->retrievePack();
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
{
|
||||
// FIXME: server shouldn't really send netpacks after it's tells client to disconnect
|
||||
// Though currently they'll be delivered and might cause crash.
|
||||
vstd::clear_pointer(pack);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerHandlerCPackVisitor visitor(*this);
|
||||
pack->visit(visitor);
|
||||
}
|
||||
}
|
||||
assert(0); //Should not be possible - socket must be closed at this point
|
||||
return;
|
||||
}
|
||||
//catch only asio exceptions
|
||||
catch(const boost::system::system_error & e)
|
||||
|
||||
CPack * pack = logicConnection->retrievePack(message);
|
||||
ServerHandlerCPackVisitor visitor(*this);
|
||||
pack->visit(visitor);
|
||||
}
|
||||
|
||||
void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
|
||||
{
|
||||
if(getState() == EClientState::DISCONNECTING)
|
||||
{
|
||||
if(state == EClientState::DISCONNECTING)
|
||||
{
|
||||
logNetwork->info("Successfully closed connection to server, ending listening thread!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e.code() == boost::asio::error::eof)
|
||||
logNetwork->error("Lost connection to server, ending listening thread! Connection has been closed");
|
||||
else
|
||||
logNetwork->error("Lost connection to server, ending listening thread! Reason: %s", e.what());
|
||||
|
||||
if(client)
|
||||
{
|
||||
state = EClientState::DISCONNECTING;
|
||||
|
||||
GH.dispatchMainThread([]()
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lcd = new LobbyClientDisconnected();
|
||||
lcd->clientId = c->connectionID;
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
packsForLobbyScreen.push_back(lcd);
|
||||
}
|
||||
}
|
||||
assert(networkConnection == nullptr);
|
||||
// Note: this branch can be reached on app shutdown, when main thread holds mutex till destruction
|
||||
logNetwork->info("Successfully closed connection to server!");
|
||||
return;
|
||||
}
|
||||
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
logNetwork->error("Lost connection to server! Connection has been closed");
|
||||
|
||||
if(client)
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
CSH->showServerError(CGI->generaltexth->translate("vcmi.server.errors.disconnected"));
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyClientDisconnected lcd;
|
||||
lcd.clientId = logicConnection->connectionID;
|
||||
applyPackOnLobbyScreen(lcd);
|
||||
}
|
||||
|
||||
networkConnection.reset();
|
||||
}
|
||||
|
||||
void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
|
||||
{
|
||||
if(applier->getApplier(CTypeList::getInstance().getTypeID(&lobbyPack))->applyOnLobbyHandler(this, &lobbyPack))
|
||||
if(applier->getApplier(CTypeList::getInstance().getTypeID(&lobbyPack))->applyOnLobbyHandler(this, lobbyPack))
|
||||
{
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*mx);
|
||||
packsForLobbyScreen.push_back(&lobbyPack);
|
||||
}
|
||||
applyPackOnLobbyScreen(lobbyPack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,22 +942,16 @@ void CServerHandler::visitForClient(CPackForClient & clientPack)
|
||||
client->handlePack(&clientPack);
|
||||
}
|
||||
|
||||
void CServerHandler::threadRunServer()
|
||||
void CServerHandler::threadRunServer(bool connectToLobby)
|
||||
{
|
||||
#if !defined(VCMI_MOBILE)
|
||||
setThreadName("runServer");
|
||||
const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string();
|
||||
std::string comm = VCMIDirs::get().serverPath().string()
|
||||
+ " --port=" + std::to_string(getHostPort())
|
||||
+ " --run-by-client"
|
||||
+ " --uuid=" + uuid;
|
||||
if(settings["session"]["lobby"].Bool() && settings["session"]["host"].Bool())
|
||||
{
|
||||
comm += " --lobby=" + settings["session"]["address"].String();
|
||||
comm += " --connections=" + settings["session"]["hostConnections"].String();
|
||||
comm += " --lobby-port=" + std::to_string(settings["session"]["port"].Integer());
|
||||
comm += " --lobby-uuid=" + settings["session"]["hostUuid"].String();
|
||||
}
|
||||
+ " --port=" + std::to_string(getLocalPort())
|
||||
+ " --run-by-client";
|
||||
if(connectToLobby)
|
||||
comm += " --lobby";
|
||||
|
||||
comm += " > \"" + logName + '\"';
|
||||
logGlobal->info("Server command line: %s", comm);
|
||||
@@ -1035,22 +979,31 @@ void CServerHandler::threadRunServer()
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
if (getState() == EClientState::CONNECTING)
|
||||
{
|
||||
showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
|
||||
setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect
|
||||
}
|
||||
logNetwork->error("Error: server failed to close correctly or crashed!");
|
||||
logNetwork->error("Check %s for more info", logName);
|
||||
}
|
||||
onServerFinished();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CServerHandler::onServerFinished()
|
||||
{
|
||||
threadRunLocalServer.reset();
|
||||
if (CSH)
|
||||
CSH->campaignServerRestartLock.setn(false);
|
||||
}
|
||||
|
||||
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
|
||||
{
|
||||
if(state != EClientState::STARTING)
|
||||
c->sendPack(&pack);
|
||||
if(getState() != EClientState::STARTING)
|
||||
logicConnection->sendPack(&pack);
|
||||
}
|
||||
|
||||
bool CServerHandler::inLobbyRoom() const
|
||||
{
|
||||
return CSH->serverMode == EServerMode::LOBBY_HOST || CSH->serverMode == EServerMode::LOBBY_GUEST;
|
||||
}
|
||||
|
||||
bool CServerHandler::inGame() const
|
||||
{
|
||||
return logicConnection != nullptr;
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../lib/CStopWatch.h"
|
||||
|
||||
#include "../lib/network/NetworkInterface.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/CondSh.h"
|
||||
|
||||
@@ -34,10 +35,14 @@ VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CClient;
|
||||
class CBaseForLobbyApply;
|
||||
class GlobalLobbyClient;
|
||||
|
||||
class HighScoreCalculation;
|
||||
class HighScoreParameter;
|
||||
|
||||
enum class ESelectionScreen : ui8;
|
||||
enum class ELoadMode : ui8;
|
||||
|
||||
// TODO: Add mutex so we can't set CONNECTION_CANCELLED if client already connected, but thread not setup yet
|
||||
enum class EClientState : ui8
|
||||
{
|
||||
@@ -49,7 +54,14 @@ enum class EClientState : ui8
|
||||
STARTING, // Gameplay interfaces being created, we pause netpacks retrieving
|
||||
GAMEPLAY, // In-game, used by some UI
|
||||
DISCONNECTING, // We disconnecting, drop all netpacks
|
||||
CONNECTION_FAILED // We could not connect to server
|
||||
};
|
||||
|
||||
enum class EServerMode : uint8_t
|
||||
{
|
||||
NONE = 0,
|
||||
LOCAL, // no global lobby
|
||||
LOBBY_HOST, // We are hosting global server available via global lobby
|
||||
LOBBY_GUEST // Connecting to a remote server via proxy provided by global lobby
|
||||
};
|
||||
|
||||
class IServerAPI
|
||||
@@ -80,63 +92,68 @@ public:
|
||||
};
|
||||
|
||||
/// structure to handle running server and connecting to it
|
||||
class CServerHandler : public IServerAPI, public LobbyInfo
|
||||
class CServerHandler final : public IServerAPI, public LobbyInfo, public INetworkClientListener, public INetworkTimerListener, boost::noncopyable
|
||||
{
|
||||
friend class ApplyOnLobbyHandlerNetPackVisitor;
|
||||
|
||||
std::shared_ptr<CApplier<CBaseForLobbyApply>> applier;
|
||||
|
||||
std::shared_ptr<boost::recursive_mutex> mx;
|
||||
std::list<CPackForLobby *> packsForLobbyScreen; //protected by mx
|
||||
|
||||
std::unique_ptr<INetworkHandler> networkHandler;
|
||||
std::shared_ptr<INetworkConnection> networkConnection;
|
||||
std::unique_ptr<GlobalLobbyClient> lobbyClient;
|
||||
std::unique_ptr<CApplier<CBaseForLobbyApply>> applier;
|
||||
std::shared_ptr<CMapInfo> mapToStart;
|
||||
|
||||
std::vector<std::string> myNames;
|
||||
|
||||
std::shared_ptr<HighScoreCalculation> highScoreCalc;
|
||||
|
||||
/// temporary helper member that exists while game in lobby mode
|
||||
/// required to correctly deserialize gamestate using client-side game callback
|
||||
std::unique_ptr<CClient> nextClient;
|
||||
boost::thread threadRunLocalServer;
|
||||
boost::thread threadNetwork;
|
||||
|
||||
std::atomic<EClientState> state;
|
||||
|
||||
void threadRunNetwork();
|
||||
void threadRunServer(bool connectToLobby);
|
||||
|
||||
void threadHandleConnection();
|
||||
void threadRunServer();
|
||||
void onServerFinished();
|
||||
void sendLobbyPack(const CPackForLobby & pack) const override;
|
||||
|
||||
void onPacketReceived(const NetworkConnectionPtr &, const std::vector<std::byte> & message) override;
|
||||
void onConnectionFailed(const std::string & errorMessage) override;
|
||||
void onConnectionEstablished(const NetworkConnectionPtr &) override;
|
||||
void onDisconnected(const NetworkConnectionPtr &, const std::string & errorMessage) override;
|
||||
void onTimer() override;
|
||||
|
||||
void applyPackOnLobbyScreen(CPackForLobby & pack);
|
||||
|
||||
std::string serverHostname;
|
||||
ui16 serverPort;
|
||||
|
||||
bool isServerLocal() const;
|
||||
|
||||
public:
|
||||
std::atomic<EClientState> state;
|
||||
/// High-level connection overlay that is capable of (de)serializing network data
|
||||
std::shared_ptr<CConnection> logicConnection;
|
||||
|
||||
////////////////////
|
||||
// FIXME: Bunch of crutches to glue it all together
|
||||
|
||||
// For starting non-custom campaign and continue to next mission
|
||||
std::shared_ptr<CampaignState> campaignStateToSend;
|
||||
|
||||
ui8 screenType; // To create lobby UI only after server is setup
|
||||
ui8 loadMode; // For saves filtering in SelectionTab
|
||||
ESelectionScreen screenType; // To create lobby UI only after server is setup
|
||||
EServerMode serverMode;
|
||||
ELoadMode loadMode; // For saves filtering in SelectionTab
|
||||
////////////////////
|
||||
|
||||
std::unique_ptr<CStopWatch> th;
|
||||
std::shared_ptr<boost::thread> threadRunLocalServer;
|
||||
|
||||
std::shared_ptr<CConnection> c;
|
||||
std::unique_ptr<CClient> client;
|
||||
|
||||
CondSh<bool> campaignServerRestartLock;
|
||||
|
||||
static const std::string localhostAddress;
|
||||
|
||||
CServerHandler();
|
||||
~CServerHandler();
|
||||
|
||||
std::string getHostAddress() const;
|
||||
ui16 getHostPort() const;
|
||||
void resetStateForLobby(EStartMode mode, ESelectionScreen screen, EServerMode serverMode, const std::vector<std::string> & names);
|
||||
void startLocalServerAndConnect(bool connectToLobby);
|
||||
void connectToServer(const std::string & addr, const ui16 port);
|
||||
|
||||
void resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names = nullptr);
|
||||
void startLocalServerAndConnect();
|
||||
void justConnectToServer(const std::string & addr, const ui16 port);
|
||||
void applyPacksOnLobbyScreen();
|
||||
void stopServerConnection();
|
||||
GlobalLobbyClient & getGlobalLobby();
|
||||
INetworkHandler & getNetworkHandler();
|
||||
|
||||
// Helpers for lobby state access
|
||||
std::set<PlayerColor> getHumanColors();
|
||||
@@ -144,12 +161,21 @@ public:
|
||||
bool isMyColor(PlayerColor color) const;
|
||||
ui8 myFirstId() const; // Used by chat only!
|
||||
|
||||
bool isServerLocal() const;
|
||||
EClientState getState() const;
|
||||
void setState(EClientState newState);
|
||||
|
||||
bool isHost() const;
|
||||
bool isGuest() const;
|
||||
bool inLobbyRoom() const;
|
||||
bool inGame() const;
|
||||
|
||||
static ui16 getDefaultPort();
|
||||
static std::string getDefaultPortStr();
|
||||
const std::string & getCurrentHostname() const;
|
||||
const std::string & getLocalHostname() const;
|
||||
const std::string & getRemoteHostname() const;
|
||||
|
||||
ui16 getCurrentPort() const;
|
||||
ui16 getLocalPort() const;
|
||||
ui16 getRemotePort() const;
|
||||
|
||||
// Lobby server API for UI
|
||||
void sendClientConnecting() const override;
|
||||
@@ -175,15 +201,14 @@ public:
|
||||
void debugStartTest(std::string filename, bool save = false);
|
||||
|
||||
void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
|
||||
void endGameplay(bool closeConnection = true, bool restart = false);
|
||||
void endGameplay();
|
||||
void restartGameplay();
|
||||
void startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs = {});
|
||||
void showServerError(const std::string & txt) const;
|
||||
|
||||
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
|
||||
int howManyPlayerInterfaces();
|
||||
ui8 getLoadMode();
|
||||
|
||||
void restoreLastSession();
|
||||
ELoadMode getLoadMode();
|
||||
|
||||
void visitForLobby(CPackForLobby & lobbyPack);
|
||||
void visitForClient(CPackForClient & clientPack);
|
||||
|
@@ -30,11 +30,12 @@
|
||||
#include "../lib/UnlockGuard.h"
|
||||
#include "../lib/battle/BattleInfo.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/pathfinder/CGPathNode.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/registerTypes/RegisterTypesClientPacks.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vcmi/events/EventBus.h>
|
||||
@@ -297,7 +298,7 @@ void CClient::serialize(BinaryDeserializer & h)
|
||||
|
||||
bool shouldResetInterface = true;
|
||||
// Client no longer handle this player at all
|
||||
if(!vstd::contains(CSH->getAllClientPlayers(CSH->c->connectionID), pid))
|
||||
if(!vstd::contains(CSH->getAllClientPlayers(CSH->logicConnection->connectionID), pid))
|
||||
{
|
||||
logGlobal->trace("Player %s is not belong to this client. Destroying interface", pid);
|
||||
}
|
||||
@@ -397,7 +398,7 @@ void CClient::initPlayerEnvironments()
|
||||
{
|
||||
playerEnvironments.clear();
|
||||
|
||||
auto allPlayers = CSH->getAllClientPlayers(CSH->c->connectionID);
|
||||
auto allPlayers = CSH->getAllClientPlayers(CSH->logicConnection->connectionID);
|
||||
bool hasHumanPlayer = false;
|
||||
for(auto & color : allPlayers)
|
||||
{
|
||||
@@ -427,7 +428,7 @@ void CClient::initPlayerInterfaces()
|
||||
for(auto & playerInfo : gs->scenarioOps->playerInfos)
|
||||
{
|
||||
PlayerColor color = playerInfo.first;
|
||||
if(!vstd::contains(CSH->getAllClientPlayers(CSH->c->connectionID), color))
|
||||
if(!vstd::contains(CSH->getAllClientPlayers(CSH->logicConnection->connectionID), color))
|
||||
continue;
|
||||
|
||||
if(!vstd::contains(playerint, color))
|
||||
@@ -457,7 +458,7 @@ void CClient::initPlayerInterfaces()
|
||||
installNewPlayerInterface(std::make_shared<CPlayerInterface>(PlayerColor::SPECTATOR), PlayerColor::SPECTATOR, true);
|
||||
}
|
||||
|
||||
if(CSH->getAllClientPlayers(CSH->c->connectionID).count(PlayerColor::NEUTRAL))
|
||||
if(CSH->getAllClientPlayers(CSH->logicConnection->connectionID).count(PlayerColor::NEUTRAL))
|
||||
installNewBattleInterface(CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String()), PlayerColor::NEUTRAL);
|
||||
|
||||
logNetwork->trace("Initialized player interfaces %d ms", CSH->th->getDiff());
|
||||
@@ -520,7 +521,6 @@ void CClient::handlePack(CPack * pack)
|
||||
CBaseForCLApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(pack)); //find the applier
|
||||
if(apply)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
apply->applyOnClBefore(this, pack);
|
||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
|
||||
gs->apply(pack);
|
||||
@@ -545,7 +545,7 @@ int CClient::sendRequest(const CPackForServer * request, PlayerColor player)
|
||||
waitingRequest.pushBack(requestID);
|
||||
request->requestID = requestID;
|
||||
request->player = player;
|
||||
CSH->c->sendPack(request);
|
||||
CSH->logicConnection->sendPack(request);
|
||||
if(vstd::contains(playerint, player))
|
||||
playerint[player]->requestSent(request, requestID);
|
||||
|
||||
|
@@ -173,7 +173,7 @@ public:
|
||||
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 {};
|
||||
virtual void giveResources(PlayerColor player, TResources resources) override {};
|
||||
void giveResources(PlayerColor player, TResources resources) override {};
|
||||
|
||||
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet & creatures, bool remove) override {};
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures) override {};
|
||||
|
@@ -118,16 +118,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void visitChangeObjPos(ChangeObjPos & pack) override;
|
||||
virtual void visitRemoveObject(RemoveObject & pack) override;
|
||||
virtual void visitTryMoveHero(TryMoveHero & pack) override;
|
||||
virtual void visitGiveHero(GiveHero & pack) override;
|
||||
virtual void visitBattleStart(BattleStart & pack) override;
|
||||
virtual void visitBattleNextRound(BattleNextRound & pack) override;
|
||||
virtual void visitBattleUpdateGateState(BattleUpdateGateState & pack) override;
|
||||
virtual void visitBattleResult(BattleResult & pack) override;
|
||||
virtual void visitBattleStackMoved(BattleStackMoved & pack) override;
|
||||
virtual void visitBattleAttack(BattleAttack & pack) override;
|
||||
virtual void visitStartAction(StartAction & pack) override;
|
||||
virtual void visitSetObjectProperty(SetObjectProperty & pack) override;
|
||||
void visitChangeObjPos(ChangeObjPos & pack) override;
|
||||
void visitRemoveObject(RemoveObject & pack) override;
|
||||
void visitTryMoveHero(TryMoveHero & pack) override;
|
||||
void visitGiveHero(GiveHero & pack) override;
|
||||
void visitBattleStart(BattleStart & pack) override;
|
||||
void visitBattleNextRound(BattleNextRound & pack) override;
|
||||
void visitBattleUpdateGateState(BattleUpdateGateState & pack) override;
|
||||
void visitBattleResult(BattleResult & pack) override;
|
||||
void visitBattleStackMoved(BattleStackMoved & pack) override;
|
||||
void visitBattleAttack(BattleAttack & pack) override;
|
||||
void visitStartAction(StartAction & pack) override;
|
||||
void visitSetObjectProperty(SetObjectProperty & pack) override;
|
||||
};
|
||||
|
@@ -32,11 +32,12 @@ public:
|
||||
|
||||
bool getResult() const { return result; }
|
||||
|
||||
virtual void visitLobbyClientConnected(LobbyClientConnected & pack) override;
|
||||
virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
|
||||
virtual void visitLobbyEndGame(LobbyEndGame & pack) override;
|
||||
virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
|
||||
virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
|
||||
void visitLobbyClientConnected(LobbyClientConnected & pack) override;
|
||||
void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
|
||||
void visitLobbyRestartGame(LobbyRestartGame & pack) override;
|
||||
void visitLobbyPrepareStartGame(LobbyPrepareStartGame & pack) override;
|
||||
void visitLobbyStartGame(LobbyStartGame & pack) override;
|
||||
void visitLobbyUpdateState(LobbyUpdateState & pack) override;
|
||||
};
|
||||
|
||||
class ApplyOnLobbyScreenNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
|
||||
@@ -51,11 +52,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
|
||||
virtual void visitLobbyChatMessage(LobbyChatMessage & pack) override;
|
||||
virtual void visitLobbyGuiAction(LobbyGuiAction & pack) override;
|
||||
virtual void visitLobbyStartGame(LobbyStartGame & pack) override;
|
||||
virtual void visitLobbyLoadProgress(LobbyLoadProgress & pack) override;
|
||||
virtual void visitLobbyUpdateState(LobbyUpdateState & pack) override;
|
||||
virtual void visitLobbyShowMessage(LobbyShowMessage & pack) override;
|
||||
void visitLobbyClientDisconnected(LobbyClientDisconnected & pack) override;
|
||||
void visitLobbyChatMessage(LobbyChatMessage & pack) override;
|
||||
void visitLobbyGuiAction(LobbyGuiAction & pack) override;
|
||||
void visitLobbyStartGame(LobbyStartGame & pack) override;
|
||||
void visitLobbyLoadProgress(LobbyLoadProgress & pack) override;
|
||||
void visitLobbyUpdateState(LobbyUpdateState & pack) override;
|
||||
void visitLobbyShowMessage(LobbyShowMessage & pack) override;
|
||||
};
|
||||
|
@@ -27,8 +27,8 @@
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/FileInfo.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
@@ -424,7 +424,7 @@ void ApplyClientNetPackVisitor::visitPlayerReinitInterface(PlayerReinitInterface
|
||||
cl.initPlayerEnvironments();
|
||||
initInterfaces();
|
||||
}
|
||||
else if(pack.playerConnectionId == CSH->c->connectionID)
|
||||
else if(pack.playerConnectionId == CSH->logicConnection->connectionID)
|
||||
{
|
||||
plSettings.connectedPlayerIDs.insert(pack.playerConnectionId);
|
||||
cl.playerint.clear();
|
||||
|
@@ -19,9 +19,13 @@
|
||||
#include "lobby/ExtraOptionsTab.h"
|
||||
#include "lobby/SelectionTab.h"
|
||||
#include "lobby/CBonusSelection.h"
|
||||
#include "globalLobby/GlobalLobbyWindow.h"
|
||||
#include "globalLobby/GlobalLobbyServerSetup.h"
|
||||
#include "globalLobby/GlobalLobbyClient.h"
|
||||
|
||||
#include "CServerHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "Client.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "gui/WindowHandler.h"
|
||||
#include "widgets/Buttons.h"
|
||||
@@ -36,26 +40,50 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
|
||||
result = false;
|
||||
|
||||
// Check if it's LobbyClientConnected for our client
|
||||
if(pack.uuid == handler.c->uuid)
|
||||
if(pack.uuid == handler.logicConnection->uuid)
|
||||
{
|
||||
handler.c->connectionID = pack.clientId;
|
||||
handler.logicConnection->connectionID = pack.clientId;
|
||||
if(handler.mapToStart)
|
||||
{
|
||||
handler.setMapInfo(handler.mapToStart);
|
||||
}
|
||||
else if(!settings["session"]["headless"].Bool())
|
||||
GH.windows().createAndPushWindow<CLobbyScreen>(static_cast<ESelectionScreen>(handler.screenType));
|
||||
handler.state = EClientState::LOBBY;
|
||||
{
|
||||
if (GH.windows().topWindow<CSimpleJoinScreen>())
|
||||
GH.windows().popWindows(1);
|
||||
|
||||
if (!GH.windows().findWindows<GlobalLobbyServerSetup>().empty())
|
||||
{
|
||||
assert(handler.serverMode == EServerMode::LOBBY_HOST);
|
||||
// announce opened game room
|
||||
// TODO: find better approach?
|
||||
int roomType = settings["lobby"]["roomType"].Integer();
|
||||
|
||||
if (roomType != 0)
|
||||
handler.getGlobalLobby().sendOpenPrivateRoom();
|
||||
else
|
||||
handler.getGlobalLobby().sendOpenPublicRoom();
|
||||
}
|
||||
|
||||
while (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
|
||||
{
|
||||
// if global lobby is open, pop all dialogs on top of it as well as lobby itself
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
GH.windows().createAndPushWindow<CLobbyScreen>(handler.screenType);
|
||||
}
|
||||
handler.setState(EClientState::LOBBY);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
|
||||
{
|
||||
if(pack.clientId != handler.c->connectionID)
|
||||
if(pack.clientId != handler.logicConnection->connectionID)
|
||||
{
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
handler.stopServerConnection();
|
||||
}
|
||||
|
||||
void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack)
|
||||
@@ -106,30 +134,31 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack)
|
||||
{
|
||||
if(handler.state == EClientState::GAMEPLAY)
|
||||
{
|
||||
handler.endGameplay(pack.closeConnection, pack.restart);
|
||||
}
|
||||
|
||||
if(pack.restart)
|
||||
{
|
||||
if (handler.validateGameStart())
|
||||
handler.sendStartGame();
|
||||
}
|
||||
assert(handler.getState() == EClientState::GAMEPLAY);
|
||||
|
||||
handler.restartGameplay();
|
||||
handler.sendStartGame();
|
||||
}
|
||||
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyPrepareStartGame(LobbyPrepareStartGame & pack)
|
||||
{
|
||||
handler.client = std::make_unique<CClient>();
|
||||
handler.logicConnection->enterLobbyConnectionMode();
|
||||
handler.logicConnection->setCallback(handler.client.get());
|
||||
}
|
||||
|
||||
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)
|
||||
{
|
||||
if(pack.clientId != -1 && pack.clientId != handler.c->connectionID)
|
||||
if(pack.clientId != -1 && pack.clientId != handler.logicConnection->connectionID)
|
||||
{
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
handler.state = EClientState::STARTING;
|
||||
if(handler.si->mode != StartInfo::LOAD_GAME || pack.clientId == handler.c->connectionID)
|
||||
handler.setState(EClientState::STARTING);
|
||||
if(handler.si->mode != EStartMode::LOAD_GAME || pack.clientId == handler.logicConnection->connectionID)
|
||||
{
|
||||
auto modeBackup = handler.si->mode;
|
||||
handler.si = pack.initializedStartInfo;
|
||||
@@ -174,7 +203,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState &
|
||||
if(!lobby) //stub: ignore message for game mode
|
||||
return;
|
||||
|
||||
if(!lobby->bonusSel && handler.si->campState && handler.state == EClientState::LOBBY_CAMPAIGN)
|
||||
if(!lobby->bonusSel && handler.si->campState && handler.getState() == EClientState::LOBBY_CAMPAIGN)
|
||||
{
|
||||
lobby->bonusSel = std::make_shared<CBonusSelection>();
|
||||
GH.windows().pushWindow(lobby->bonusSel);
|
||||
|
@@ -852,12 +852,19 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner)
|
||||
owner(owner)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
|
||||
uint32_t queueSize = QUEUE_SIZE_BIG;
|
||||
|
||||
if(embedded)
|
||||
{
|
||||
pos.w = QUEUE_SIZE * 41;
|
||||
int32_t queueSmallOutsideYOffset = 65;
|
||||
bool queueSmallOutside = settings["battle"]["queueSmallOutside"].Bool() && (pos.y - queueSmallOutsideYOffset) >= 0;
|
||||
queueSize = std::clamp(static_cast<int>(settings["battle"]["queueSmallSlots"].Float()), 1, queueSmallOutside ? GH.screenDimensions().x / 41 : 19);
|
||||
|
||||
pos.w = queueSize * 41;
|
||||
pos.h = 49;
|
||||
pos.x += parent->pos.w/2 - pos.w/2;
|
||||
pos.y += 10;
|
||||
pos.y += queueSmallOutside ? -queueSmallOutsideYOffset : 10;
|
||||
|
||||
icons = GH.renderHandler().loadAnimation(AnimationPath::builtin("CPRSMALL"));
|
||||
stateIcons = GH.renderHandler().loadAnimation(AnimationPath::builtin("VCMI/BATTLEQUEUE/STATESSMALL"));
|
||||
@@ -878,7 +885,7 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner)
|
||||
}
|
||||
stateIcons->preload();
|
||||
|
||||
stackBoxes.resize(QUEUE_SIZE);
|
||||
stackBoxes.resize(queueSize);
|
||||
for (int i = 0; i < stackBoxes.size(); i++)
|
||||
{
|
||||
stackBoxes[i] = std::make_shared<StackBox>(this);
|
||||
|
@@ -239,7 +239,7 @@ class StackQueue : public CIntObject
|
||||
std::optional<uint32_t> getBoundUnitID() const;
|
||||
};
|
||||
|
||||
static const int QUEUE_SIZE = 10;
|
||||
static const int QUEUE_SIZE_BIG = 10;
|
||||
std::shared_ptr<CFilledTexture> background;
|
||||
std::vector<std::shared_ptr<StackBox>> stackBoxes;
|
||||
BattleInterface & owner;
|
||||
|
@@ -145,7 +145,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
full->Bool() = !full->Bool();
|
||||
|
||||
GH.onScreenResize();
|
||||
GH.onScreenResize(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
|
||||
#ifndef VCMI_IOS
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
GH.onScreenResize();
|
||||
GH.onScreenResize(false);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
@@ -16,6 +16,8 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/EventDispatcher.h"
|
||||
#include "../gui/ShortcutHandler.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../globalLobby/GlobalLobbyClient.h"
|
||||
|
||||
#include <SDL_clipboard.h>
|
||||
#include <SDL_events.h>
|
||||
@@ -31,6 +33,8 @@ InputSourceKeyboard::InputSourceKeyboard()
|
||||
|
||||
void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
assert(key.state == SDL_PRESSED);
|
||||
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||
{
|
||||
if(key.keysym.sym == SDLK_v && isKeyboardCtrlDown())
|
||||
@@ -51,7 +55,11 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
return; // ignore periodic event resends
|
||||
}
|
||||
|
||||
assert(key.state == SDL_PRESSED);
|
||||
|
||||
if(key.keysym.sym == SDLK_TAB && isKeyboardCtrlDown())
|
||||
{
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
}
|
||||
|
||||
if(key.keysym.sym >= SDLK_F1 && key.keysym.sym <= SDLK_F15 && settings["session"]["spectate"].Bool())
|
||||
{
|
||||
|
366
client/globalLobby/GlobalLobbyClient.cpp
Normal file
366
client/globalLobby/GlobalLobbyClient.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* GlobalLobbyClient.cpp, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyClient.h"
|
||||
|
||||
#include "GlobalLobbyLoginWindow.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
#include "../../lib/TextOperations.h"
|
||||
|
||||
GlobalLobbyClient::GlobalLobbyClient() = default;
|
||||
GlobalLobbyClient::~GlobalLobbyClient() = default;
|
||||
|
||||
static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0)
|
||||
{
|
||||
// FIXME: better/unified way to format date
|
||||
auto timeNowChrono = std::chrono::system_clock::now();
|
||||
timeNowChrono += std::chrono::seconds(timeOffsetSeconds);
|
||||
|
||||
return TextOperations::getFormattedTimeLocal(std::chrono::system_clock::to_time_t(timeNowChrono));
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<std::byte> & message)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
JsonNode json(message.data(), message.size());
|
||||
|
||||
if(json["type"].String() == "accountCreated")
|
||||
return receiveAccountCreated(json);
|
||||
|
||||
if(json["type"].String() == "operationFailed")
|
||||
return receiveOperationFailed(json);
|
||||
|
||||
if(json["type"].String() == "loginSuccess")
|
||||
return receiveLoginSuccess(json);
|
||||
|
||||
if(json["type"].String() == "chatHistory")
|
||||
return receiveChatHistory(json);
|
||||
|
||||
if(json["type"].String() == "chatMessage")
|
||||
return receiveChatMessage(json);
|
||||
|
||||
if(json["type"].String() == "activeAccounts")
|
||||
return receiveActiveAccounts(json);
|
||||
|
||||
if(json["type"].String() == "activeGameRooms")
|
||||
return receiveActiveGameRooms(json);
|
||||
|
||||
if(json["type"].String() == "joinRoomSuccess")
|
||||
return receiveJoinRoomSuccess(json);
|
||||
|
||||
if(json["type"].String() == "inviteReceived")
|
||||
return receiveInviteReceived(json);
|
||||
|
||||
logGlobal->error("Received unexpected message from lobby server: %s", json["type"].String());
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveAccountCreated(const JsonNode & json)
|
||||
{
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
|
||||
if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
|
||||
throw std::runtime_error("lobby connection finished without active login window!");
|
||||
|
||||
{
|
||||
Settings configID = settings.write["lobby"]["accountID"];
|
||||
configID->String() = json["accountID"].String();
|
||||
|
||||
Settings configName = settings.write["lobby"]["displayName"];
|
||||
configName->String() = json["displayName"].String();
|
||||
|
||||
Settings configCookie = settings.write["lobby"]["accountCookie"];
|
||||
configCookie->String() = json["accountCookie"].String();
|
||||
}
|
||||
|
||||
sendClientLogin();
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveOperationFailed(const JsonNode & json)
|
||||
{
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
|
||||
if(loginWindowPtr)
|
||||
loginWindowPtr->onConnectionFailed(json["reason"].String());
|
||||
|
||||
// TODO: handle errors in lobby menu
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveLoginSuccess(const JsonNode & json)
|
||||
{
|
||||
{
|
||||
Settings configCookie = settings.write["lobby"]["accountCookie"];
|
||||
configCookie->String() = json["accountCookie"].String();
|
||||
|
||||
Settings configName = settings.write["lobby"]["displayName"];
|
||||
configName->String() = json["displayName"].String();
|
||||
}
|
||||
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
|
||||
if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
|
||||
throw std::runtime_error("lobby connection finished without active login window!");
|
||||
|
||||
loginWindowPtr->onConnectionSuccess();
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveChatHistory(const JsonNode & json)
|
||||
{
|
||||
for(const auto & entry : json["messages"].Vector())
|
||||
{
|
||||
std::string accountID = entry["accountID"].String();
|
||||
std::string displayName = entry["displayName"].String();
|
||||
std::string messageText = entry["messageText"].String();
|
||||
int ageSeconds = entry["ageSeconds"].Integer();
|
||||
std::string timeFormatted = getCurrentTimeFormatted(-ageSeconds);
|
||||
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onGameChatMessage(displayName, messageText, timeFormatted);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveChatMessage(const JsonNode & json)
|
||||
{
|
||||
std::string accountID = json["accountID"].String();
|
||||
std::string displayName = json["displayName"].String();
|
||||
std::string messageText = json["messageText"].String();
|
||||
std::string timeFormatted = getCurrentTimeFormatted();
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onGameChatMessage(displayName, messageText, timeFormatted);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json)
|
||||
{
|
||||
activeAccounts.clear();
|
||||
|
||||
for (auto const & jsonEntry : json["accounts"].Vector())
|
||||
{
|
||||
GlobalLobbyAccount account;
|
||||
|
||||
account.accountID = jsonEntry["accountID"].String();
|
||||
account.displayName = jsonEntry["displayName"].String();
|
||||
account.status = jsonEntry["status"].String();
|
||||
|
||||
activeAccounts.push_back(account);
|
||||
}
|
||||
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onActiveAccounts(activeAccounts);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
|
||||
{
|
||||
activeRooms.clear();
|
||||
|
||||
for (auto const & jsonEntry : json["gameRooms"].Vector())
|
||||
{
|
||||
GlobalLobbyRoom room;
|
||||
|
||||
room.gameRoomID = jsonEntry["gameRoomID"].String();
|
||||
room.hostAccountID = jsonEntry["hostAccountID"].String();
|
||||
room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
|
||||
room.description = jsonEntry["description"].String();
|
||||
room.playersCount = jsonEntry["playersCount"].Integer();
|
||||
room.playersLimit = jsonEntry["playersLimit"].Integer();
|
||||
|
||||
activeRooms.push_back(room);
|
||||
}
|
||||
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onActiveRooms(activeRooms);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveInviteReceived(const JsonNode & json)
|
||||
{
|
||||
assert(0); //TODO
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json)
|
||||
{
|
||||
Settings configRoom = settings.write["lobby"]["roomID"];
|
||||
configRoom->String() = json["gameRoomID"].String();
|
||||
|
||||
if (json["proxyMode"].Bool())
|
||||
{
|
||||
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOBBY_GUEST, {});
|
||||
CSH->loadMode = ELoadMode::MULTI;
|
||||
|
||||
std::string hostname = settings["lobby"]["hostname"].String();
|
||||
int16_t port = settings["lobby"]["port"].Integer();
|
||||
CSH->connectToServer(hostname, port);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr<INetworkConnection> & connection)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
networkConnection = connection;
|
||||
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
|
||||
if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
|
||||
throw std::runtime_error("lobby connection established without active login window!");
|
||||
|
||||
loginWindowPtr->onConnectionSuccess();
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendClientRegister(const std::string & accountName)
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "clientRegister";
|
||||
toSend["displayName"].String() = accountName;
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendClientLogin()
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "clientLogin";
|
||||
toSend["accountID"] = settings["lobby"]["accountID"];
|
||||
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::onConnectionFailed(const std::string & errorMessage)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
|
||||
if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
|
||||
throw std::runtime_error("lobby connection failed without active login window!");
|
||||
|
||||
logGlobal->warn("Connection to game lobby failed! Reason: %s", errorMessage);
|
||||
loginWindowPtr->onConnectionFailed(errorMessage);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
|
||||
assert(connection == networkConnection);
|
||||
networkConnection.reset();
|
||||
|
||||
while (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
|
||||
{
|
||||
// if global lobby is open, pop all dialogs on top of it as well as lobby itself
|
||||
GH.windows().popWindows(1);
|
||||
}
|
||||
|
||||
CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {});
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendMessage(const JsonNode & data)
|
||||
{
|
||||
networkConnection->sendPacket(data.toBytes(true));
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendOpenPublicRoom()
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "openGameRoom";
|
||||
toSend["hostAccountID"] = settings["lobby"]["accountID"];
|
||||
toSend["roomType"].String() = "public";
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendOpenPrivateRoom()
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "openGameRoom";
|
||||
toSend["hostAccountID"] = settings["lobby"]["accountID"];
|
||||
toSend["roomType"].String() = "private";
|
||||
sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::connect()
|
||||
{
|
||||
std::string hostname = settings["lobby"]["hostname"].String();
|
||||
int16_t port = settings["lobby"]["port"].Integer();
|
||||
CSH->getNetworkHandler().connectToRemote(*this, hostname, port);
|
||||
}
|
||||
|
||||
bool GlobalLobbyClient::isConnected() const
|
||||
{
|
||||
return networkConnection != nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<GlobalLobbyLoginWindow> GlobalLobbyClient::createLoginWindow()
|
||||
{
|
||||
auto loginWindowPtr = loginWindow.lock();
|
||||
if(loginWindowPtr)
|
||||
return loginWindowPtr;
|
||||
|
||||
auto loginWindowNew = std::make_shared<GlobalLobbyLoginWindow>();
|
||||
loginWindow = loginWindowNew;
|
||||
|
||||
return loginWindowNew;
|
||||
}
|
||||
|
||||
std::shared_ptr<GlobalLobbyWindow> GlobalLobbyClient::createLobbyWindow()
|
||||
{
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
return lobbyWindowPtr;
|
||||
|
||||
lobbyWindowPtr = std::make_shared<GlobalLobbyWindow>();
|
||||
lobbyWindow = lobbyWindowPtr;
|
||||
lobbyWindowLock = lobbyWindowPtr;
|
||||
return lobbyWindowPtr;
|
||||
}
|
||||
|
||||
const std::vector<GlobalLobbyAccount> & GlobalLobbyClient::getActiveAccounts() const
|
||||
{
|
||||
return activeAccounts;
|
||||
}
|
||||
|
||||
const std::vector<GlobalLobbyRoom> & GlobalLobbyClient::getActiveRooms() const
|
||||
{
|
||||
return activeRooms;
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::activateInterface()
|
||||
{
|
||||
if (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
|
||||
return;
|
||||
|
||||
if (!GH.windows().findWindows<GlobalLobbyLoginWindow>().empty())
|
||||
return;
|
||||
|
||||
if (isConnected())
|
||||
GH.windows().pushWindow(createLobbyWindow());
|
||||
else
|
||||
GH.windows().pushWindow(createLoginWindow());
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection)
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "clientProxyLogin";
|
||||
toSend["accountID"] = settings["lobby"]["accountID"];
|
||||
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
|
||||
toSend["gameRoomID"] = settings["lobby"]["roomID"];
|
||||
|
||||
netConnection->sendPacket(toSend.toBytes(true));
|
||||
}
|
70
client/globalLobby/GlobalLobbyClient.h
Normal file
70
client/globalLobby/GlobalLobbyClient.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* GlobalLobbyClient.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 "GlobalLobbyDefines.h"
|
||||
#include "../../lib/network/NetworkInterface.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class JsonNode;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class GlobalLobbyLoginWindow;
|
||||
class GlobalLobbyWindow;
|
||||
|
||||
class GlobalLobbyClient final : public INetworkClientListener, boost::noncopyable
|
||||
{
|
||||
std::vector<GlobalLobbyAccount> activeAccounts;
|
||||
std::vector<GlobalLobbyRoom> activeRooms;
|
||||
|
||||
std::shared_ptr<INetworkConnection> networkConnection;
|
||||
|
||||
std::weak_ptr<GlobalLobbyLoginWindow> loginWindow;
|
||||
std::weak_ptr<GlobalLobbyWindow> lobbyWindow;
|
||||
std::shared_ptr<GlobalLobbyWindow> lobbyWindowLock; // helper strong reference to prevent window destruction on closing
|
||||
|
||||
void onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<std::byte> & message) override;
|
||||
void onConnectionFailed(const std::string & errorMessage) override;
|
||||
void onConnectionEstablished(const std::shared_ptr<INetworkConnection> &) override;
|
||||
void onDisconnected(const std::shared_ptr<INetworkConnection> &, const std::string & errorMessage) override;
|
||||
|
||||
void receiveAccountCreated(const JsonNode & json);
|
||||
void receiveOperationFailed(const JsonNode & json);
|
||||
void receiveLoginSuccess(const JsonNode & json);
|
||||
void receiveChatHistory(const JsonNode & json);
|
||||
void receiveChatMessage(const JsonNode & json);
|
||||
void receiveActiveAccounts(const JsonNode & json);
|
||||
void receiveActiveGameRooms(const JsonNode & json);
|
||||
void receiveJoinRoomSuccess(const JsonNode & json);
|
||||
void receiveInviteReceived(const JsonNode & json);
|
||||
|
||||
std::shared_ptr<GlobalLobbyLoginWindow> createLoginWindow();
|
||||
std::shared_ptr<GlobalLobbyWindow> createLobbyWindow();
|
||||
|
||||
public:
|
||||
explicit GlobalLobbyClient();
|
||||
~GlobalLobbyClient();
|
||||
|
||||
const std::vector<GlobalLobbyAccount> & getActiveAccounts() const;
|
||||
const std::vector<GlobalLobbyRoom> & getActiveRooms() const;
|
||||
|
||||
/// Activate interface and pushes lobby UI as top window
|
||||
void activateInterface();
|
||||
void sendMessage(const JsonNode & data);
|
||||
void sendClientRegister(const std::string & accountName);
|
||||
void sendClientLogin();
|
||||
void sendOpenPublicRoom();
|
||||
void sendOpenPrivateRoom();
|
||||
|
||||
void sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection);
|
||||
|
||||
void connect();
|
||||
bool isConnected() const;
|
||||
};
|
27
client/globalLobby/GlobalLobbyDefines.h
Normal file
27
client/globalLobby/GlobalLobbyDefines.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* GlobalLobbyClient.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
|
||||
|
||||
struct GlobalLobbyAccount
|
||||
{
|
||||
std::string accountID;
|
||||
std::string displayName;
|
||||
std::string status;
|
||||
};
|
||||
|
||||
struct GlobalLobbyRoom
|
||||
{
|
||||
std::string gameRoomID;
|
||||
std::string hostAccountID;
|
||||
std::string hostAccountDisplayName;
|
||||
std::string description;
|
||||
int playersCount;
|
||||
int playersLimit;
|
||||
};
|
128
client/globalLobby/GlobalLobbyLoginWindow.cpp
Normal file
128
client/globalLobby/GlobalLobbyLoginWindow.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* GlobalLobbyLoginWindow.cpp, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyLoginWindow.h"
|
||||
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
|
||||
GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
pos.w = 284;
|
||||
pos.h = 220;
|
||||
|
||||
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
||||
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title"));
|
||||
labelUsername = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.lobby.login.username"));
|
||||
backgroundUsername = std::make_shared<TransparentFilledRectangle>(Rect(10, 90, 264, 20), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||
inputUsername = std::make_shared<CTextInput>(Rect(15, 93, 260, 16), FONT_SMALL, nullptr, ETextAlignment::TOPLEFT, true);
|
||||
buttonLogin = std::make_shared<CButton>(Point(10, 180), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); });
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 180), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
|
||||
labelStatus = std::make_shared<CTextBox>( "", Rect(15, 115, 255, 60), 1, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
|
||||
|
||||
auto buttonRegister = std::make_shared<CToggleButton>(Point(10, 40), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
auto buttonLogin = std::make_shared<CToggleButton>(Point(146, 40), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
buttonRegister->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.create"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
buttonLogin->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.login.login"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
|
||||
toggleMode = std::make_shared<CToggleGroup>(nullptr);
|
||||
toggleMode->addToggle(0, buttonRegister);
|
||||
toggleMode->addToggle(1, buttonLogin);
|
||||
toggleMode->setSelected(settings["lobby"]["roomType"].Integer());
|
||||
toggleMode->addCallback([this](int index){onLoginModeChanged(index);});
|
||||
|
||||
if (settings["lobby"]["accountID"].String().empty())
|
||||
{
|
||||
buttonLogin->block(true);
|
||||
toggleMode->setSelected(0);
|
||||
}
|
||||
else
|
||||
toggleMode->setSelected(1);
|
||||
|
||||
filledBackground->playerColored(PlayerColor(1));
|
||||
inputUsername->cb += [this](const std::string & text)
|
||||
{
|
||||
this->buttonLogin->block(text.empty());
|
||||
};
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onLoginModeChanged(int value)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
inputUsername->setText("");
|
||||
}
|
||||
else
|
||||
{
|
||||
inputUsername->setText(settings["lobby"]["displayName"].String());
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onClose()
|
||||
{
|
||||
close();
|
||||
// TODO: abort ongoing connection attempt, if any
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onLogin()
|
||||
{
|
||||
labelStatus->setText(CGI->generaltexth->translate("vcmi.lobby.login.connecting"));
|
||||
if(!CSH->getGlobalLobby().isConnected())
|
||||
CSH->getGlobalLobby().connect();
|
||||
else
|
||||
onConnectionSuccess();
|
||||
|
||||
buttonClose->block(true);
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onConnectionSuccess()
|
||||
{
|
||||
std::string accountID = settings["lobby"]["accountID"].String();
|
||||
|
||||
if(toggleMode->getSelected() == 0)
|
||||
CSH->getGlobalLobby().sendClientRegister(inputUsername->getText());
|
||||
else
|
||||
CSH->getGlobalLobby().sendClientLogin();
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onLoginSuccess()
|
||||
{
|
||||
close();
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
}
|
||||
|
||||
void GlobalLobbyLoginWindow::onConnectionFailed(const std::string & reason)
|
||||
{
|
||||
MetaString formatter;
|
||||
formatter.appendTextID("vcmi.lobby.login.error");
|
||||
formatter.replaceRawString(reason);
|
||||
|
||||
labelStatus->setText(formatter.toString());
|
||||
buttonClose->block(false);
|
||||
}
|
45
client/globalLobby/GlobalLobbyLoginWindow.h
Normal file
45
client/globalLobby/GlobalLobbyLoginWindow.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* GlobalLobbyLoginWindow.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 "../windows/CWindowObject.h"
|
||||
|
||||
class CLabel;
|
||||
class CTextBox;
|
||||
class CTextInput;
|
||||
class CToggleGroup;
|
||||
class FilledTexturePlayerColored;
|
||||
class TransparentFilledRectangle;
|
||||
class CButton;
|
||||
|
||||
class GlobalLobbyLoginWindow : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<FilledTexturePlayerColored> filledBackground;
|
||||
std::shared_ptr<CLabel> labelTitle;
|
||||
std::shared_ptr<CLabel> labelUsername;
|
||||
std::shared_ptr<CTextBox> labelStatus;
|
||||
std::shared_ptr<TransparentFilledRectangle> backgroundUsername;
|
||||
std::shared_ptr<CTextInput> inputUsername;
|
||||
|
||||
std::shared_ptr<CButton> buttonLogin;
|
||||
std::shared_ptr<CButton> buttonClose;
|
||||
std::shared_ptr<CToggleGroup> toggleMode; // create account or use existing
|
||||
|
||||
void onLoginModeChanged(int value);
|
||||
void onClose();
|
||||
void onLogin();
|
||||
|
||||
public:
|
||||
GlobalLobbyLoginWindow();
|
||||
|
||||
void onConnectionSuccess();
|
||||
void onLoginSuccess();
|
||||
void onConnectionFailed(const std::string & reason);
|
||||
};
|
142
client/globalLobby/GlobalLobbyServerSetup.cpp
Normal file
142
client/globalLobby/GlobalLobbyServerSetup.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* GlobalLobbyServerSetup.cpp, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyServerSetup.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
|
||||
GlobalLobbyServerSetup::GlobalLobbyServerSetup()
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
pos.w = 284;
|
||||
pos.h = 340;
|
||||
|
||||
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
||||
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.room.create"));
|
||||
labelPlayerLimit = std::make_shared<CLabel>( pos.w / 2, 48, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.room.players.limit"));
|
||||
labelRoomType = std::make_shared<CLabel>( pos.w / 2, 108, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.room.type"));
|
||||
labelGameMode = std::make_shared<CLabel>( pos.w / 2, 158, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.room.mode"));
|
||||
|
||||
togglePlayerLimit = std::make_shared<CToggleGroup>(nullptr);
|
||||
togglePlayerLimit->addToggle(2, std::make_shared<CToggleButton>(Point(10 + 39*0, 60), AnimationPath::builtin("RanNum2"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(3, std::make_shared<CToggleButton>(Point(10 + 39*1, 60), AnimationPath::builtin("RanNum3"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(4, std::make_shared<CToggleButton>(Point(10 + 39*2, 60), AnimationPath::builtin("RanNum4"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(5, std::make_shared<CToggleButton>(Point(10 + 39*3, 60), AnimationPath::builtin("RanNum5"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(6, std::make_shared<CToggleButton>(Point(10 + 39*4, 60), AnimationPath::builtin("RanNum6"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(7, std::make_shared<CToggleButton>(Point(10 + 39*5, 60), AnimationPath::builtin("RanNum7"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->addToggle(8, std::make_shared<CToggleButton>(Point(10 + 39*6, 60), AnimationPath::builtin("RanNum8"), CButton::tooltip(), 0));
|
||||
togglePlayerLimit->setSelected(settings["lobby"]["roomPlayerLimit"].Integer());
|
||||
togglePlayerLimit->addCallback([this](int index){onPlayerLimitChanged(index);});
|
||||
|
||||
auto buttonPublic = std::make_shared<CToggleButton>(Point(10, 120), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
auto buttonPrivate = std::make_shared<CToggleButton>(Point(146, 120), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
buttonPublic->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.public"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
buttonPrivate->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.private"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
|
||||
toggleRoomType = std::make_shared<CToggleGroup>(nullptr);
|
||||
toggleRoomType->addToggle(0, buttonPublic);
|
||||
toggleRoomType->addToggle(1, buttonPrivate);
|
||||
toggleRoomType->setSelected(settings["lobby"]["roomType"].Integer());
|
||||
toggleRoomType->addCallback([this](int index){onRoomTypeChanged(index);});
|
||||
|
||||
auto buttonNewGame = std::make_shared<CToggleButton>(Point(10, 170), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
auto buttonLoadGame = std::make_shared<CToggleButton>(Point(146, 170), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
buttonNewGame->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.new"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
buttonLoadGame->addTextOverlay(CGI->generaltexth->translate("vcmi.lobby.room.load"), EFonts::FONT_SMALL, Colors::YELLOW);
|
||||
|
||||
toggleGameMode = std::make_shared<CToggleGroup>(nullptr);
|
||||
toggleGameMode->addToggle(0, buttonNewGame);
|
||||
toggleGameMode->addToggle(1, buttonLoadGame);
|
||||
toggleGameMode->setSelected(settings["lobby"]["roomMode"].Integer());
|
||||
toggleGameMode->addCallback([this](int index){onGameModeChanged(index);});
|
||||
|
||||
labelDescription = std::make_shared<CTextBox>("", Rect(10, 195, pos.w - 20, 80), 1, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
|
||||
|
||||
buttonCreate = std::make_shared<CButton>(Point(10, 300), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onCreate(); });
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 300), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
|
||||
|
||||
filledBackground->playerColored(PlayerColor(1));
|
||||
|
||||
updateDescription();
|
||||
center();
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::updateDescription()
|
||||
{
|
||||
MetaString description;
|
||||
description.appendRawString("%s %s %s");
|
||||
if(toggleRoomType->getSelected() == 0)
|
||||
description.replaceTextID("vcmi.lobby.room.description.public");
|
||||
else
|
||||
description.replaceTextID("vcmi.lobby.room.description.private");
|
||||
|
||||
if(toggleGameMode->getSelected() == 0)
|
||||
description.replaceTextID("vcmi.lobby.room.description.new");
|
||||
else
|
||||
description.replaceTextID("vcmi.lobby.room.description.load");
|
||||
|
||||
description.replaceTextID("vcmi.lobby.room.description.limit");
|
||||
description.replaceNumber(togglePlayerLimit->getSelected());
|
||||
|
||||
labelDescription->setText(description.toString());
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onPlayerLimitChanged(int value)
|
||||
{
|
||||
Settings config = settings.write["lobby"]["roomPlayerLimit"];
|
||||
config->Integer() = value;
|
||||
updateDescription();
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onRoomTypeChanged(int value)
|
||||
{
|
||||
Settings config = settings.write["lobby"]["roomType"];
|
||||
config->Integer() = value;
|
||||
updateDescription();
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onGameModeChanged(int value)
|
||||
{
|
||||
Settings config = settings.write["lobby"]["roomMode"];
|
||||
config->Integer() = value;
|
||||
updateDescription();
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onCreate()
|
||||
{
|
||||
if(toggleGameMode->getSelected() == 0)
|
||||
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOBBY_HOST, {});
|
||||
else
|
||||
CSH->resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, EServerMode::LOBBY_HOST, {});
|
||||
|
||||
CSH->loadMode = ELoadMode::MULTI;
|
||||
CSH->startLocalServerAndConnect(true);
|
||||
|
||||
buttonCreate->block(true);
|
||||
buttonClose->block(true);
|
||||
}
|
||||
|
||||
void GlobalLobbyServerSetup::onClose()
|
||||
{
|
||||
close();
|
||||
}
|
49
client/globalLobby/GlobalLobbyServerSetup.h
Normal file
49
client/globalLobby/GlobalLobbyServerSetup.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* GlobalLobbyServerSetup.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 "../windows/CWindowObject.h"
|
||||
|
||||
class CLabel;
|
||||
class CTextBox;
|
||||
class FilledTexturePlayerColored;
|
||||
class CButton;
|
||||
class CToggleGroup;
|
||||
|
||||
class GlobalLobbyServerSetup : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<FilledTexturePlayerColored> filledBackground;
|
||||
std::shared_ptr<CLabel> labelTitle;
|
||||
|
||||
std::shared_ptr<CLabel> labelPlayerLimit;
|
||||
std::shared_ptr<CLabel> labelRoomType;
|
||||
std::shared_ptr<CLabel> labelGameMode;
|
||||
|
||||
std::shared_ptr<CToggleGroup> togglePlayerLimit; // 2-8
|
||||
std::shared_ptr<CToggleGroup> toggleRoomType; // public or private
|
||||
std::shared_ptr<CToggleGroup> toggleGameMode; // new game or load game
|
||||
|
||||
std::shared_ptr<CTextBox> labelDescription;
|
||||
std::shared_ptr<CTextBox> labelStatus;
|
||||
|
||||
std::shared_ptr<CButton> buttonCreate;
|
||||
std::shared_ptr<CButton> buttonClose;
|
||||
|
||||
void updateDescription();
|
||||
void onPlayerLimitChanged(int value);
|
||||
void onRoomTypeChanged(int value);
|
||||
void onGameModeChanged(int value);
|
||||
|
||||
void onCreate();
|
||||
void onClose();
|
||||
|
||||
public:
|
||||
GlobalLobbyServerSetup();
|
||||
};
|
154
client/globalLobby/GlobalLobbyWidget.cpp
Normal file
154
client/globalLobby/GlobalLobbyWidget.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* GlobalLobbyWidget.cpp, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyWidget.h"
|
||||
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../../lib/MetaString.h"
|
||||
GlobalLobbyWidget::GlobalLobbyWidget(GlobalLobbyWindow * window)
|
||||
: window(window)
|
||||
{
|
||||
addCallback("closeWindow", [](int) { GH.windows().popWindows(1); });
|
||||
addCallback("sendMessage", [this](int) { this->window->doSendChatMessage(); });
|
||||
addCallback("createGameRoom", [this](int) { this->window->doCreateGameRoom(); });
|
||||
|
||||
REGISTER_BUILDER("accountList", &GlobalLobbyWidget::buildAccountList);
|
||||
REGISTER_BUILDER("roomList", &GlobalLobbyWidget::buildRoomList);
|
||||
|
||||
const JsonNode config(JsonPath::builtin("config/widgets/lobbyWindow.json"));
|
||||
build(config);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> GlobalLobbyWidget::buildAccountList(const JsonNode & config) const
|
||||
{
|
||||
const auto & createCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
|
||||
{
|
||||
const auto & accounts = CSH->getGlobalLobby().getActiveAccounts();
|
||||
|
||||
if(index < accounts.size())
|
||||
return std::make_shared<GlobalLobbyAccountCard>(this->window, accounts[index]);
|
||||
return std::make_shared<CIntObject>();
|
||||
};
|
||||
|
||||
auto position = readPosition(config["position"]);
|
||||
auto itemOffset = readPosition(config["itemOffset"]);
|
||||
auto sliderPosition = readPosition(config["sliderPosition"]);
|
||||
auto sliderSize = readPosition(config["sliderSize"]);
|
||||
size_t visibleSize = 4; // FIXME: how many items can fit into UI?
|
||||
size_t totalSize = 4; //FIXME: how many items are there in total
|
||||
int sliderMode = 1 | 4; // present, vertical, blue
|
||||
int initialPos = 0;
|
||||
|
||||
return std::make_shared<CListBox>(createCallback, position, itemOffset, visibleSize, totalSize, initialPos, sliderMode, Rect(sliderPosition, sliderSize) );
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> GlobalLobbyWidget::buildRoomList(const JsonNode & config) const
|
||||
{
|
||||
const auto & createCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
|
||||
{
|
||||
const auto & rooms = CSH->getGlobalLobby().getActiveRooms();
|
||||
|
||||
if(index < rooms.size())
|
||||
return std::make_shared<GlobalLobbyRoomCard>(this->window, rooms[index]);
|
||||
return std::make_shared<CIntObject>();
|
||||
};
|
||||
|
||||
auto position = readPosition(config["position"]);
|
||||
auto itemOffset = readPosition(config["itemOffset"]);
|
||||
auto sliderPosition = readPosition(config["sliderPosition"]);
|
||||
auto sliderSize = readPosition(config["sliderSize"]);
|
||||
size_t visibleSize = 4; // FIXME: how many items can fit into UI?
|
||||
size_t totalSize = 4; //FIXME: how many items are there in total
|
||||
int sliderMode = 1 | 4; // present, vertical, blue
|
||||
int initialPos = 0;
|
||||
|
||||
return std::make_shared<CListBox>(createCallback, position, itemOffset, visibleSize, totalSize, initialPos, sliderMode, Rect(sliderPosition, sliderSize) );
|
||||
}
|
||||
|
||||
std::shared_ptr<CLabel> GlobalLobbyWidget::getAccountNameLabel()
|
||||
{
|
||||
return widget<CLabel>("accountNameLabel");
|
||||
}
|
||||
|
||||
std::shared_ptr<CTextInput> GlobalLobbyWidget::getMessageInput()
|
||||
{
|
||||
return widget<CTextInput>("messageInput");
|
||||
}
|
||||
|
||||
std::shared_ptr<CTextBox> GlobalLobbyWidget::getGameChat()
|
||||
{
|
||||
return widget<CTextBox>("gameChat");
|
||||
}
|
||||
|
||||
std::shared_ptr<CListBox> GlobalLobbyWidget::getAccountList()
|
||||
{
|
||||
return widget<CListBox>("accountList");
|
||||
}
|
||||
|
||||
std::shared_ptr<CListBox> GlobalLobbyWidget::getRoomList()
|
||||
{
|
||||
return widget<CListBox>("roomList");
|
||||
}
|
||||
|
||||
GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
const auto & onInviteClicked = [window, accountID=accountDescription.accountID]()
|
||||
{
|
||||
window->doInviteAccount(accountID);
|
||||
};
|
||||
|
||||
pos.w = 130;
|
||||
pos.h = 40;
|
||||
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||
labelName = std::make_shared<CLabel>( 5, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, accountDescription.displayName);
|
||||
labelStatus = std::make_shared<CLabel>( 5, 20, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, accountDescription.status);
|
||||
|
||||
if (CSH->inLobbyRoom())
|
||||
buttonInvite = std::make_shared<CButton>(Point(95, 8), AnimationPath::builtin("settingsWindow/button32"), CButton::tooltip(), onInviteClicked);
|
||||
}
|
||||
|
||||
GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
const auto & onJoinClicked = [window, roomID=roomDescription.gameRoomID]()
|
||||
{
|
||||
window->doJoinRoom(roomID);
|
||||
};
|
||||
|
||||
auto roomSizeText = MetaString::createFromRawString("%d/%d");
|
||||
roomSizeText.replaceNumber(roomDescription.playersCount);
|
||||
roomSizeText.replaceNumber(roomDescription.playersLimit);
|
||||
|
||||
pos.w = 230;
|
||||
pos.h = 40;
|
||||
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||
labelName = std::make_shared<CLabel>( 5, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, roomDescription.hostAccountDisplayName);
|
||||
labelStatus = std::make_shared<CLabel>( 5, 20, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, roomDescription.description);
|
||||
labelRoomSize = std::make_shared<CLabel>( 160, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, roomSizeText.toString());
|
||||
|
||||
if (!CSH->inGame())
|
||||
buttonJoin = std::make_shared<CButton>(Point(195, 8), AnimationPath::builtin("settingsWindow/button32"), CButton::tooltip(), onJoinClicked);
|
||||
}
|
57
client/globalLobby/GlobalLobbyWidget.h
Normal file
57
client/globalLobby/GlobalLobbyWidget.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* GlobalLobbyWidget.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 "../gui/InterfaceObjectConfigurable.h"
|
||||
|
||||
class GlobalLobbyWindow;
|
||||
struct GlobalLobbyAccount;
|
||||
struct GlobalLobbyRoom;
|
||||
class CListBox;
|
||||
|
||||
class GlobalLobbyWidget : public InterfaceObjectConfigurable
|
||||
{
|
||||
GlobalLobbyWindow * window;
|
||||
|
||||
std::shared_ptr<CIntObject> buildAccountList(const JsonNode &) const;
|
||||
std::shared_ptr<CIntObject> buildRoomList(const JsonNode &) const;
|
||||
|
||||
public:
|
||||
explicit GlobalLobbyWidget(GlobalLobbyWindow * window);
|
||||
|
||||
std::shared_ptr<CLabel> getAccountNameLabel();
|
||||
std::shared_ptr<CTextInput> getMessageInput();
|
||||
std::shared_ptr<CTextBox> getGameChat();
|
||||
std::shared_ptr<CListBox> getAccountList();
|
||||
std::shared_ptr<CListBox> getRoomList();
|
||||
};
|
||||
|
||||
class GlobalLobbyAccountCard : public CIntObject
|
||||
{
|
||||
public:
|
||||
GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription);
|
||||
|
||||
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
|
||||
std::shared_ptr<CLabel> labelName;
|
||||
std::shared_ptr<CLabel> labelStatus;
|
||||
std::shared_ptr<CButton> buttonInvite;
|
||||
};
|
||||
|
||||
class GlobalLobbyRoomCard : public CIntObject
|
||||
{
|
||||
public:
|
||||
GlobalLobbyRoomCard(GlobalLobbyWindow * window, const GlobalLobbyRoom & roomDescription);
|
||||
|
||||
std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
|
||||
std::shared_ptr<CLabel> labelName;
|
||||
std::shared_ptr<CLabel> labelRoomSize;
|
||||
std::shared_ptr<CLabel> labelStatus;
|
||||
std::shared_ptr<CButton> buttonJoin;
|
||||
};
|
105
client/globalLobby/GlobalLobbyWindow.cpp
Normal file
105
client/globalLobby/GlobalLobbyWindow.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* GlobalLobbyWindow.cpp, 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyServerSetup.h"
|
||||
#include "GlobalLobbyWidget.h"
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
|
||||
GlobalLobbyWindow::GlobalLobbyWindow()
|
||||
: CWindowObject(BORDERED)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
widget = std::make_shared<GlobalLobbyWidget>(this);
|
||||
pos = widget->pos;
|
||||
center();
|
||||
|
||||
widget->getAccountNameLabel()->setText(settings["lobby"]["displayName"].String());
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doSendChatMessage()
|
||||
{
|
||||
std::string messageText = widget->getMessageInput()->getText();
|
||||
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "sendChatMessage";
|
||||
toSend["messageText"].String() = messageText;
|
||||
|
||||
CSH->getGlobalLobby().sendMessage(toSend);
|
||||
|
||||
widget->getMessageInput()->setText("");
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doCreateGameRoom()
|
||||
{
|
||||
GH.windows().createAndPushWindow<GlobalLobbyServerSetup>();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doInviteAccount(const std::string & accountID)
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "sendInvite";
|
||||
toSend["accountID"].String() = accountID;
|
||||
|
||||
CSH->getGlobalLobby().sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::doJoinRoom(const std::string & roomID)
|
||||
{
|
||||
JsonNode toSend;
|
||||
toSend["type"].String() = "joinGameRoom";
|
||||
toSend["gameRoomID"].String() = roomID;
|
||||
|
||||
CSH->getGlobalLobby().sendMessage(toSend);
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when)
|
||||
{
|
||||
MetaString chatMessageFormatted;
|
||||
chatMessageFormatted.appendRawString("[%s] {%s}: %s\n");
|
||||
chatMessageFormatted.replaceRawString(when);
|
||||
chatMessageFormatted.replaceRawString(sender);
|
||||
chatMessageFormatted.replaceRawString(message);
|
||||
|
||||
chatHistory += chatMessageFormatted.toString();
|
||||
|
||||
widget->getGameChat()->setText(chatHistory);
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts)
|
||||
{
|
||||
widget->getAccountList()->reset();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms)
|
||||
{
|
||||
widget->getRoomList()->reset();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onJoinedRoom()
|
||||
{
|
||||
widget->getAccountList()->reset();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onLeftRoom()
|
||||
{
|
||||
widget->getAccountList()->reset();
|
||||
}
|
38
client/globalLobby/GlobalLobbyWindow.h
Normal file
38
client/globalLobby/GlobalLobbyWindow.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* GlobalLobbyWindow.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 "../windows/CWindowObject.h"
|
||||
|
||||
class GlobalLobbyWidget;
|
||||
struct GlobalLobbyAccount;
|
||||
struct GlobalLobbyRoom;
|
||||
|
||||
class GlobalLobbyWindow : public CWindowObject
|
||||
{
|
||||
std::string chatHistory;
|
||||
|
||||
std::shared_ptr<GlobalLobbyWidget> widget;
|
||||
|
||||
public:
|
||||
GlobalLobbyWindow();
|
||||
|
||||
void doSendChatMessage();
|
||||
void doCreateGameRoom();
|
||||
|
||||
void doInviteAccount(const std::string & accountID);
|
||||
void doJoinRoom(const std::string & roomID);
|
||||
|
||||
void onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when);
|
||||
void onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts);
|
||||
void onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms);
|
||||
void onJoinedRoom();
|
||||
void onLeftRoom();
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user