فهرست منبع

Removed GameEngine::curInt member

- event processing is now initiated by GameEngine instead of weird chain
engine -> player interface -> engine
- introduced GameEngineUser interface (implemented by GameInstance) to
remove mutual depedency between GameEngine and GameInstance (some
technically still remains for now, in form of some free functions)
Ivan Savenko 7 ماه پیش
والد
کامیت
d3de0d525f

+ 1 - 0
client/CMakeLists.txt

@@ -398,6 +398,7 @@ set(vcmiclientcommon_HEADERS
 	CMT.h
 	CPlayerInterface.h
 	GameEngine.h
+	GameEngineUser.h
 	GameInstance.h
 	PlayerLocalState.h
 	CServerHandler.h

+ 0 - 18
client/CPlayerInterface.cpp

@@ -315,7 +315,6 @@ void CPlayerInterface::yourTurn(QueryID queryID)
 	bool hotseatWait = humanPlayersCount > 1;
 
 		GAME->setInterfaceInstance(this);
-		ENGINE->curInt = this;
 
 		NotificationHandler::notify("Your turn");
 		if(settings["general"]["startTurnAutosave"].Bool())
@@ -1493,7 +1492,6 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
 		{
 			//one of our players who isn't last in order got attacked not by our another player (happens for example in hotseat mode)
 			GAME->setInterfaceInstance(this);
-			ENGINE->curInt = this;
 			adventureInt->onCurrentPlayerChanged(playerID);
 			std::string msg = LIBRARY->generaltexth->translate("vcmi.adventureMap.playerAttacked");
 			boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name);
@@ -1509,13 +1507,6 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
 
 void CPlayerInterface::update()
 {
-	// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request
-	std::shared_lock gsLock(CGameState::mutex);
-
-	// While mutexes were locked away we may be have stopped being the active interface
-	if (GAME->interface() != this)
-		return;
-
 	//if there are any waiting dialogs, show them
 	if (makingTurn && !dialogs.empty() && !showingDialog->isBusy())
 	{
@@ -1523,12 +1514,6 @@ void CPlayerInterface::update()
 		ENGINE->windows().pushWindow(dialogs.front());
 		dialogs.pop_front();
 	}
-
-	assert(adventureInt);
-
-	// Handles mouse and key input
-	ENGINE->handleEvents();
-	ENGINE->windows().simpleRedraw();
 }
 
 void CPlayerInterface::endNetwork()
@@ -1576,11 +1561,9 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 		if (victoryLossCheckResult.loss())
 			showInfoDialog(LIBRARY->generaltexth->allTexts[95]);
 
-		assert(ENGINE->curInt == GAME->interface());
 		auto previousInterface = GAME->interface(); //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily
 
 		GAME->setInterfaceInstance(this); //this is needed for dialog to show and avoid freeze, dialog showing logic should be reworked someday
-		ENGINE->curInt = this; //waiting for dialogs requires this to get events
 
 		if(!makingTurn)
 		{
@@ -1591,7 +1574,6 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 		else
 			waitForAllDialogs();
 
-		ENGINE->curInt = previousInterface;
 		GAME->setInterfaceInstance(previousInterface);
 	}
 }

+ 2 - 2
client/CPlayerInterface.h

@@ -57,7 +57,7 @@ namespace boost
 }
 
 /// Central class for managing user interface logic
-class CPlayerInterface : public CGameInterface, public IUpdateable
+class CPlayerInterface : public CGameInterface
 {
 	bool ignoreEvents;
 	int autosaveCount;
@@ -90,7 +90,6 @@ public: // TODO: make private
 
 protected: // Call-ins from server, should not be called directly, but only via GameInterface
 
-	void update() override;
 	void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
 
 	void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
@@ -173,6 +172,7 @@ protected: // Call-ins from server, should not be called directly, but only via
 public: // public interface for use by client via GAME->interface() access
 
 	// part of GameInterface that is also used by client code
+	void update();
 	void showPuzzleMap() override;
 	void viewWorldMap() override;
 	void showQuestLog() override;

+ 2 - 2
client/CServerHandler.cpp

@@ -666,15 +666,15 @@ void CServerHandler::endGameplay()
 
 	if(CMM)
 	{
-		ENGINE->curInt = CMM.get();
 		CMM->enable();
 		CMM->playMusic();
+		CMM->makeActiveInterface();
 	}
 	else
 	{
 		auto mainMenu = CMainMenu::create();
-		ENGINE->curInt = mainMenu.get();
 		mainMenu->playMusic();
+		mainMenu->makeActiveInterface();
 	}
 }
 

+ 0 - 2
client/Client.cpp

@@ -191,7 +191,6 @@ void CClient::endGame()
 	for(auto & i : playerint)
 		i.second->finish();
 
-	ENGINE->curInt = nullptr;
 	{
 		logNetwork->info("Ending current game!");
 		removeGUI();
@@ -518,7 +517,6 @@ void CClient::reinitScripting()
 void CClient::removeGUI() const
 {
 	// CClient::endGame
-	ENGINE->curInt = nullptr;
 	ENGINE->windows().clear();
 	adventureInt.reset();
 	logGlobal->info("Removed GUI.");

+ 11 - 5
client/GameEngine.cpp

@@ -33,7 +33,7 @@
 #include "renderSDL/ScreenHandler.h"
 #include "renderSDL/RenderHandler.h"
 #include "CMT.h"
-#include "GameInstance.h"
+#include "GameEngineUser.h"
 #include "battle/BattleInterface.h"
 
 #include "../lib/CThreadHelper.h"
@@ -91,7 +91,7 @@ void GameEngine::handleEvents()
 	events().dispatchTimer(framerate().getElapsedMilliseconds());
 
 	//player interface may want special event handling
-	if(GAME->interface() && GAME->interface()->capturedAllEvents())
+	if(engineUser->capturedAllEvents())
 		return;
 
 	input().processEvents();
@@ -109,8 +109,10 @@ void GameEngine::renderFrame()
 	{
 		std::scoped_lock interfaceLock(ENGINE->interfaceMutex);
 
-		if (nullptr != curInt)
-			curInt->update();
+		engineUser->onUpdate();
+
+		handleEvents();
+		windows().simpleRedraw();
 
 		if (settings["video"]["showfps"].Bool())
 			drawFPSCounter();
@@ -127,7 +129,6 @@ void GameEngine::renderFrame()
 
 GameEngine::GameEngine()
 	: captureChildren(false)
-	, curInt(nullptr)
 	, fakeStatusBar(std::make_shared<EmptyStatusBar>())
 {
 }
@@ -251,3 +252,8 @@ void GameEngine::onScreenResize(bool resolutionChanged)
 	windows().onScreenResize();
 	ENGINE->cursor().onScreenResize();
 }
+
+void GameEngine::setEngineUser(IGameEngineUser * user)
+{
+	engineUser = user;
+}

+ 5 - 4
client/GameEngine.h

@@ -19,8 +19,7 @@ class ShortcutHandler;
 class FramerateManager;
 class IStatusBar;
 class CIntObject;
-class IUpdateable;
-class IShowActivatable;
+class IGameEngineUser;
 class IRenderHandler;
 class IScreenHandler;
 class WindowHandler;
@@ -54,6 +53,8 @@ private:
 	std::unique_ptr<CursorHandler> cursorHandlerInstance;
 	std::unique_ptr<IVideoPlayer> videoPlayerInstance;
 
+	IGameEngineUser *engineUser = nullptr;
+
 public:
 	std::mutex interfaceMutex;
 
@@ -65,6 +66,7 @@ public:
 	EventDispatcher & events();
 	InputHandler & input();
 
+	IGameEngineUser & user() { return *engineUser; }
 	ISoundPlayer & sound() { return *soundPlayerInstance; }
 	IMusicPlayer & music() { return *musicPlayerInstance; }
 	CursorHandler & cursor() { return *cursorHandlerInstance; }
@@ -96,8 +98,7 @@ public:
 
 	/// Set currently active status bar
 	void setStatusbar(std::shared_ptr<IStatusBar>);
-
-	IUpdateable *curInt;
+	void setEngineUser(IGameEngineUser * user);
 
 	bool captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
 	std::list<CIntObject *> createdObj; //stack of objs being created

+ 25 - 0
client/GameEngineUser.h

@@ -0,0 +1,25 @@
+/*
+ * GameEngineUser.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
+
+class IGameEngineUser
+{
+public:
+	~IGameEngineUser() = default;
+
+	/// Called when user presses hotkey that activates global lobby
+	virtual void onGlobalLobbyInterfaceActivated() = 0;
+
+	/// Called on every game tick for game to update its state
+	virtual void onUpdate() = 0;
+
+	/// Returns true if all input events should be captured and ignored
+	virtual bool capturedAllEvents() = 0;
+};

+ 21 - 0
client/GameInstance.cpp

@@ -10,8 +10,10 @@
 #include "StdInc.h"
 #include "GameInstance.h"
 
+#include "CPlayerInterface.h"
 #include "CServerHandler.h"
 #include "mapView/mapHandler.h"
+#include "globalLobby/GlobalLobbyClient.h"
 
 std::unique_ptr<GameInstance> GAME = nullptr;
 
@@ -58,3 +60,22 @@ void GameInstance::setInterfaceInstance(CPlayerInterface * ptr)
 {
 	interfaceInstance = std::move(ptr);
 }
+
+void GameInstance::onGlobalLobbyInterfaceActivated()
+{
+	server().getGlobalLobby().activateInterface();
+}
+
+void GameInstance::onUpdate()
+{
+	if (interfaceInstance)
+		interfaceInstance->update();
+}
+
+bool GameInstance::capturedAllEvents()
+{
+	if (interfaceInstance)
+		return interfaceInstance->capturedAllEvents();
+	else
+		return false;
+}

+ 7 - 1
client/GameInstance.h

@@ -9,6 +9,8 @@
  */
 #pragma once
 
+#include "GameEngineUser.h"
+
 class CServerHandler;
 class GlobalLobbyClient;
 class CPlayerInterface;
@@ -18,7 +20,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 class INetworkHandler;
 VCMI_LIB_NAMESPACE_END
 
-class GameInstance : boost::noncopyable
+class GameInstance final : boost::noncopyable, public IGameEngineUser
 {
 	std::unique_ptr<CServerHandler> serverInstance;
 	std::unique_ptr<CMapHandler> mapInstance;
@@ -36,6 +38,10 @@ public:
 	void setServerInstance(std::unique_ptr<CServerHandler> ptr);
 	void setMapInstance(std::unique_ptr<CMapHandler> ptr);
 	void setInterfaceInstance(CPlayerInterface * ptr);
+
+	void onGlobalLobbyInterfaceActivated() final;
+	void onUpdate() final;
+	bool capturedAllEvents() final;
 };
 
 extern std::unique_ptr<GameInstance> GAME;

+ 2 - 5
client/eventsSDL/InputSourceKeyboard.cpp

@@ -12,14 +12,11 @@
 #include "InputSourceKeyboard.h"
 
 #include "../../lib/CConfigHandler.h"
-#include "../CPlayerInterface.h"
 #include "../GameEngine.h"
-#include "../GameInstance.h"
+#include "../GameEngineUser.h"
 #include "../gui/EventDispatcher.h"
 #include "../gui/Shortcut.h"
 #include "../gui/ShortcutHandler.h"
-#include "../CServerHandler.h"
-#include "../globalLobby/GlobalLobbyClient.h"
 
 #include <SDL_clipboard.h>
 #include <SDL_events.h>
@@ -92,7 +89,7 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
 	auto shortcutsVector = ENGINE->shortcuts().translateKeycode(keyName);
 
 	if (vstd::contains(shortcutsVector, EShortcut::MAIN_MENU_LOBBY))
-		GAME->server().getGlobalLobby().activateInterface();
+		ENGINE->user().onGlobalLobbyInterfaceActivated();
 
 	if (vstd::contains(shortcutsVector, EShortcut::GLOBAL_FULLSCREEN))
 	{

+ 2 - 4
client/eventsSDL/InputSourceTouch.cpp

@@ -16,13 +16,11 @@
 #include "../../lib/CConfigHandler.h"
 #include "../gui/CursorHandler.h"
 #include "../GameEngine.h"
-#include "../GameInstance.h"
+#include "../GameEngineUser.h"
 #include "../gui/EventDispatcher.h"
 #include "../gui/MouseButton.h"
 #include "../gui/WindowHandler.h"
 #include "../render/IScreenHandler.h"
-#include "../CServerHandler.h"
-#include "../globalLobby/GlobalLobbyClient.h"
 
 #if defined(VCMI_ANDROID)
 #include "../../lib/CAndroidVMHelper.h"
@@ -170,7 +168,7 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
 		}
 		case TouchState::TAP_DOWN_DOUBLE:
 		{
-			GAME->server().getGlobalLobby().activateInterface();
+			ENGINE->user().onGlobalLobbyInterfaceActivated();
 			break;
 		}
 		case TouchState::TAP_DOWN_LONG_AWAIT:

+ 0 - 7
client/gui/CIntObject.h

@@ -23,13 +23,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 class CArmedInstance;
 VCMI_LIB_NAMESPACE_END
 
-class IUpdateable
-{
-public:
-	virtual void update()=0;
-	virtual ~IUpdateable() = default;
-};
-
 class IShowActivatable
 {
 public:

+ 5 - 19
client/mainmenu/CMainMenu.cpp

@@ -331,11 +331,7 @@ CMainMenu::CMainMenu()
 	backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), pos);
 }
 
-CMainMenu::~CMainMenu()
-{
-	if(ENGINE->curInt == this)
-		ENGINE->curInt = nullptr;
-}
+CMainMenu::~CMainMenu() = default;
 
 void CMainMenu::playIntroVideos()
 {
@@ -386,21 +382,11 @@ void CMainMenu::onScreenResize()
 	backgroundAroundMenu->pos = pos;
 }
 
-void CMainMenu::update()
+void CMainMenu::makeActiveInterface()
 {
-	if(CMM != this->shared_from_this()) //don't update if you are not a main interface
-		return;
-
-	if(ENGINE->windows().count() == 0)
-	{
-		ENGINE->windows().pushWindow(CMM);
-		ENGINE->windows().pushWindow(menu);
-		menu->switchToTab(menu->getActiveTab());
-	}
-
-	// Handles mouse and key input
-	ENGINE->handleEvents();
-	ENGINE->windows().simpleRedraw();
+	ENGINE->windows().pushWindow(CMM);
+	ENGINE->windows().pushWindow(menu);
+	menu->switchToTab(menu->getActiveTab());
 }
 
 void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode)

+ 2 - 2
client/mainmenu/CMainMenu.h

@@ -139,7 +139,7 @@ private:
 };
 
 /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
-class CMainMenu : public CIntObject, public IUpdateable, public std::enable_shared_from_this<CMainMenu>
+class CMainMenu : public CIntObject, public std::enable_shared_from_this<CMainMenu>
 {
 	std::shared_ptr<CFilledTexture> backgroundAroundMenu;
 
@@ -153,7 +153,7 @@ public:
 	~CMainMenu();
 	void activate() override;
 	void onScreenResize() override;
-	void update() override;
+	void makeActiveInterface();
 	static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode);
 	static void openCampaignLobby(const std::string & campaignFileName, std::string campaignSet = "");
 	static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);

+ 2 - 1
clientapp/EntryPoint.cpp

@@ -309,6 +309,7 @@ int main(int argc, char * argv[])
 		ENGINE->init();
 
 	GAME = std::make_unique<GameInstance>();
+	ENGINE->setEngineUser(GAME.get());
 	
 #ifndef VCMI_NO_THREADED_LOAD
 	//we can properly play intro only in the main thread, so we have to move loading to the separate thread
@@ -373,7 +374,7 @@ int main(int argc, char * argv[])
 	else
 	{
 		auto mmenu = CMainMenu::create();
-		ENGINE->curInt = mmenu.get();
+		mmenu->makeActiveInterface();
 
 		bool playIntroVideo = !settings["session"]["headless"].Bool() && !vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool();
 		if(playIntroVideo)