mirror of
https://github.com/vcmi/vcmi.git
synced 2026-06-19 22:57:37 +02:00
Schedule turn-start hero visits on towns; fixes multiple hero level-ups on map start with more than 2 towns with built War Academies
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
#include "TurnTimerHandler.h"
|
#include "TurnTimerHandler.h"
|
||||||
#include "ServerNetPackVisitors.h"
|
#include "ServerNetPackVisitors.h"
|
||||||
#include "ServerSpellCastEnvironment.h"
|
#include "ServerSpellCastEnvironment.h"
|
||||||
|
#include "TurnStartVisitScheduler.h"
|
||||||
#include "battles/BattleProcessor.h"
|
#include "battles/BattleProcessor.h"
|
||||||
#include "processors/HeroPoolProcessor.h"
|
#include "processors/HeroPoolProcessor.h"
|
||||||
#include "processors/NewTurnProcessor.h"
|
#include "processors/NewTurnProcessor.h"
|
||||||
@@ -542,6 +543,7 @@ CGameHandler::CGameHandler(IGameServer & server)
|
|||||||
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
, heroPool(std::make_unique<HeroPoolProcessor>(this))
|
||||||
, battles(std::make_unique<BattleProcessor>(this))
|
, battles(std::make_unique<BattleProcessor>(this))
|
||||||
, queries(std::make_unique<QueriesProcessor>())
|
, queries(std::make_unique<QueriesProcessor>())
|
||||||
|
, turnStartVisitScheduler(std::make_unique<TurnStartVisitScheduler>(*this, *queries))
|
||||||
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
||||||
, turnTimerHandler(std::make_unique<TurnTimerHandler>(*this))
|
, turnTimerHandler(std::make_unique<TurnTimerHandler>(*this))
|
||||||
, newTurnProcessor(std::make_unique<NewTurnProcessor>(this))
|
, newTurnProcessor(std::make_unique<NewTurnProcessor>(this))
|
||||||
@@ -553,6 +555,7 @@ CGameHandler::CGameHandler(IGameServer & server)
|
|||||||
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
||||||
, complainInvalidSlot("Invalid slot accessed!")
|
, complainInvalidSlot("Invalid slot accessed!")
|
||||||
{
|
{
|
||||||
|
queries->setListener(turnStartVisitScheduler.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameHandler::~CGameHandler() = default;
|
CGameHandler::~CGameHandler() = default;
|
||||||
@@ -4216,6 +4219,9 @@ bool CGameHandler::isBlockedByQueries(const CPackForServer *pack, PlayerColor pl
|
|||||||
if (dynamic_cast<const SaveLocalState *>(pack) != nullptr)
|
if (dynamic_cast<const SaveLocalState *>(pack) != nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if(dynamic_cast<const AdvInterfaceReady *>(pack) != nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
auto query = queries->topQuery(player);
|
auto query = queries->topQuery(player);
|
||||||
if (query && query->blocksPack(pack))
|
if (query && query->blocksPack(pack))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class QueriesProcessor;
|
|||||||
class CObjectVisitQuery;
|
class CObjectVisitQuery;
|
||||||
class NewTurnProcessor;
|
class NewTurnProcessor;
|
||||||
class IGameServer;
|
class IGameServer;
|
||||||
|
class TurnStartVisitScheduler;
|
||||||
|
|
||||||
class CGameHandler : public Environment, public IGameEventCallback
|
class CGameHandler : public Environment, public IGameEventCallback
|
||||||
{
|
{
|
||||||
@@ -60,6 +61,7 @@ public:
|
|||||||
std::unique_ptr<HeroPoolProcessor> heroPool;
|
std::unique_ptr<HeroPoolProcessor> heroPool;
|
||||||
std::unique_ptr<BattleProcessor> battles;
|
std::unique_ptr<BattleProcessor> battles;
|
||||||
std::unique_ptr<QueriesProcessor> queries;
|
std::unique_ptr<QueriesProcessor> queries;
|
||||||
|
std::unique_ptr<TurnStartVisitScheduler> turnStartVisitScheduler;
|
||||||
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
||||||
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
|
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
|
||||||
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
|
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ set(vcmiservercommon_SRCS
|
|||||||
NetPacksServer.cpp
|
NetPacksServer.cpp
|
||||||
NetPacksLobbyServer.cpp
|
NetPacksLobbyServer.cpp
|
||||||
TurnTimerHandler.cpp
|
TurnTimerHandler.cpp
|
||||||
|
TurnStartVisitScheduler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(vcmiservercommon_HEADERS
|
set(vcmiservercommon_HEADERS
|
||||||
@@ -39,6 +40,7 @@ set(vcmiservercommon_HEADERS
|
|||||||
queries/MapQueries.h
|
queries/MapQueries.h
|
||||||
queries/VisitQueries.h
|
queries/VisitQueries.h
|
||||||
queries/QueriesProcessor.h
|
queries/QueriesProcessor.h
|
||||||
|
queries/IQueryStackListener.h
|
||||||
|
|
||||||
processors/HeroPoolProcessor.h
|
processors/HeroPoolProcessor.h
|
||||||
processors/NewTurnProcessor.h
|
processors/NewTurnProcessor.h
|
||||||
@@ -53,6 +55,7 @@ set(vcmiservercommon_HEADERS
|
|||||||
LobbyNetPackVisitors.h
|
LobbyNetPackVisitors.h
|
||||||
ServerNetPackVisitors.h
|
ServerNetPackVisitors.h
|
||||||
TurnTimerHandler.h
|
TurnTimerHandler.h
|
||||||
|
TurnStartVisitScheduler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_source_group(${vcmiservercommon_SRCS} ${vcmiservercommon_HEADERS})
|
assign_source_group(${vcmiservercommon_SRCS} ${vcmiservercommon_HEADERS})
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* TurnStartVisitScheduler.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 "TurnStartVisitScheduler.h"
|
||||||
|
|
||||||
|
#include "CGameHandler.h"
|
||||||
|
#include "lib/gameState/CGameState.h"
|
||||||
|
#include "queries/QueriesProcessor.h"
|
||||||
|
|
||||||
|
TurnStartVisitScheduler::TurnStartVisitScheduler(CGameHandler & gameHandler, QueriesProcessor & queries)
|
||||||
|
: gameHandler(gameHandler)
|
||||||
|
, queries(queries)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnStartVisitScheduler::enqueue(PlayerColor player, std::deque<PendingTurnStartVisit> visits)
|
||||||
|
{
|
||||||
|
sessions[player] = std::move(visits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnStartVisitScheduler::processNext(PlayerColor player)
|
||||||
|
{
|
||||||
|
auto & session = sessions[player];
|
||||||
|
|
||||||
|
if(session.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(queries.topQuery(player))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!session.empty())
|
||||||
|
{
|
||||||
|
const auto next = session.front();
|
||||||
|
session.pop_front();
|
||||||
|
|
||||||
|
const auto * object = gameHandler.gameState().getObjInstance(next.objectId);
|
||||||
|
const auto * hero = gameHandler.gameState().getHero(next.heroId);
|
||||||
|
|
||||||
|
gameHandler.objectVisited(object, hero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnStartVisitScheduler::onQueryStackChanged(PlayerColor player)
|
||||||
|
{
|
||||||
|
processNext(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TurnStartVisitScheduler::clear(PlayerColor player)
|
||||||
|
{
|
||||||
|
sessions[player].clear();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* TurnStartVisitScheduler.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 "queries/IQueryStackListener.h"
|
||||||
|
|
||||||
|
class CGameHandler;
|
||||||
|
class QueriesProcessor;
|
||||||
|
|
||||||
|
struct PendingTurnStartVisit
|
||||||
|
{
|
||||||
|
PlayerColor player;
|
||||||
|
ObjectInstanceID objectId;
|
||||||
|
ObjectInstanceID heroId;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Schedules deferred turn-start object visits and runs them one at a time
|
||||||
|
// per player, resuming only after the previous visit's query chain finishes.
|
||||||
|
class TurnStartVisitScheduler final : public IQueryStackListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TurnStartVisitScheduler(CGameHandler & gameHandler, QueriesProcessor & queries);
|
||||||
|
|
||||||
|
void enqueue(PlayerColor player, std::deque<PendingTurnStartVisit> visits);
|
||||||
|
void processNext(PlayerColor player);
|
||||||
|
void onQueryStackChanged(PlayerColor player) override;
|
||||||
|
void clear(PlayerColor player);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CGameHandler & gameHandler;
|
||||||
|
QueriesProcessor & queries;
|
||||||
|
std::array<std::deque<PendingTurnStartVisit>, PlayerColor::PLAYER_LIMIT_I> sessions;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "../../lib/networkPacks/StackLocation.h"
|
#include "../../lib/networkPacks/StackLocation.h"
|
||||||
#include "../../lib/pathfinder/TurnInfo.h"
|
#include "../../lib/pathfinder/TurnInfo.h"
|
||||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||||
|
#include "../TurnStartVisitScheduler.h"
|
||||||
|
|
||||||
#include <vstd/RNG.h>
|
#include <vstd/RNG.h>
|
||||||
|
|
||||||
@@ -149,15 +150,20 @@ void NewTurnProcessor::onPlayerTurnStarted(PlayerColor which)
|
|||||||
for (const auto * t : playerState->getTowns())
|
for (const auto * t : playerState->getTowns())
|
||||||
handleTownEvents(t);
|
handleTownEvents(t);
|
||||||
|
|
||||||
|
std::deque<PendingTurnStartVisit> visits;
|
||||||
|
|
||||||
for (const auto * t : playerState->getTowns())
|
for (const auto * t : playerState->getTowns())
|
||||||
{
|
{
|
||||||
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
|
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
|
||||||
if (t->getGarrisonHero() != nullptr)
|
if(t->getGarrisonHero() != nullptr)
|
||||||
gameHandler->objectVisited(t, t->getGarrisonHero());
|
visits.push_back({which, t->id, t->getGarrisonHero()->id});
|
||||||
|
|
||||||
if (t->getVisitingHero() != nullptr)
|
if(t->getVisitingHero() != nullptr)
|
||||||
gameHandler->objectVisited(t, t->getVisitingHero());
|
visits.push_back({which, t->id, t->getVisitingHero()->id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameHandler->turnStartVisitScheduler->enqueue(which, std::move(visits));
|
||||||
|
gameHandler->turnStartVisitScheduler->processNext(which);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
|
void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* IQueryStackListener.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 "../../lib/constants/EntityIdentifiers.h"
|
||||||
|
|
||||||
|
class IQueryStackListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IQueryStackListener() = default;
|
||||||
|
|
||||||
|
// Called after a player's query stack changes and local query lifecycle
|
||||||
|
// handling for that operation is complete.
|
||||||
|
virtual void onQueryStackChanged(PlayerColor player) = 0;
|
||||||
|
};
|
||||||
@@ -33,6 +33,9 @@ void QueriesProcessor::popQuery(PlayerColor player, QueryPtr query)
|
|||||||
//Exposure on query below happens only if removal didn't trigger any new query
|
//Exposure on query below happens only if removal didn't trigger any new query
|
||||||
if(nextQuery && nextQuery == topQuery(player))
|
if(nextQuery && nextQuery == topQuery(player))
|
||||||
nextQuery->onExposure(query);
|
nextQuery->onExposure(query);
|
||||||
|
|
||||||
|
if(queriesStackListener)
|
||||||
|
queriesStackListener->onQueryStackChanged(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueriesProcessor::popQuery(const CQuery &query)
|
void QueriesProcessor::popQuery(const CQuery &query)
|
||||||
@@ -82,6 +85,9 @@ void QueriesProcessor::addQuery(PlayerColor player, QueryPtr query)
|
|||||||
query->onAdding(player);
|
query->onAdding(player);
|
||||||
queries[player].push_back(query);
|
queries[player].push_back(query);
|
||||||
query->onAdded(player);
|
query->onAdded(player);
|
||||||
|
|
||||||
|
if(queriesStackListener)
|
||||||
|
queriesStackListener->onQueryStackChanged(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryPtr QueriesProcessor::topQuery(PlayerColor player)
|
QueryPtr QueriesProcessor::topQuery(PlayerColor player)
|
||||||
@@ -145,3 +151,8 @@ int QueriesProcessor::countQuery(const QueryPtr & query) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QueriesProcessor::setListener(IQueryStackListener * listener)
|
||||||
|
{
|
||||||
|
queriesStackListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "../../lib/GameConstants.h"
|
#include "../../lib/GameConstants.h"
|
||||||
#include "constants/EntityIdentifiers.h"
|
#include "constants/EntityIdentifiers.h"
|
||||||
#include "queries/CQuery.h"
|
#include "queries/CQuery.h"
|
||||||
|
#include "IQueryStackListener.h"
|
||||||
|
|
||||||
class CQuery;
|
class CQuery;
|
||||||
using QueryPtr = std::shared_ptr<CQuery>;
|
using QueryPtr = std::shared_ptr<CQuery>;
|
||||||
@@ -22,11 +23,15 @@ public:
|
|||||||
using QueriesStack = std::vector<QueryPtr>;
|
using QueriesStack = std::vector<QueryPtr>;
|
||||||
using QueriesPerPlayer = std::array<QueriesStack, PlayerColor::PLAYER_LIMIT_I>;
|
using QueriesPerPlayer = std::array<QueriesStack, PlayerColor::PLAYER_LIMIT_I>;
|
||||||
|
|
||||||
|
// Sets an optional listener notified when a player's query stack changes.
|
||||||
|
void setListener(IQueryStackListener * listener);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addQuery(PlayerColor player, QueryPtr query);
|
void addQuery(PlayerColor player, QueryPtr query);
|
||||||
void popQuery(PlayerColor player, QueryPtr query);
|
void popQuery(PlayerColor player, QueryPtr query);
|
||||||
|
|
||||||
QueriesPerPlayer queries;
|
QueriesPerPlayer queries;
|
||||||
|
IQueryStackListener * queriesStackListener = nullptr;
|
||||||
|
|
||||||
template<typename StorageT>
|
template<typename StorageT>
|
||||||
class AllQueriesViewT
|
class AllQueriesViewT
|
||||||
@@ -55,7 +60,7 @@ private:
|
|||||||
|
|
||||||
decltype(auto) operator*() const
|
decltype(auto) operator*() const
|
||||||
{
|
{
|
||||||
return (*storage)[outer][inner]; // QueryPtr& albo const QueryPtr&
|
return (*storage)[outer][inner]; // QueryPtr& or const QueryPtr&
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator & operator++()
|
iterator & operator++()
|
||||||
|
|||||||
Reference in New Issue
Block a user