Browse Source

Merge pull request #5487 from IvanSavenko/game_engine_2

Reduce number of global variables
Ivan Savenko 9 months ago
parent
commit
f348e20a63

+ 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

+ 1 - 19
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);
 	}
 }
@@ -1819,7 +1801,7 @@ void CPlayerInterface::proposeLoadingGame()
 		[]()
 		{
 			GAME->server().endGameplay();
-			CMM->menu->switchToTab("load");
+			GAME->mainmenu()->menu->switchToTab("load");
 		},
 		nullptr
 	);

+ 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;

+ 12 - 18
client/CServerHandler.cpp

@@ -15,6 +15,7 @@
 #include "GameChatHandler.h"
 #include "CPlayerInterface.h"
 #include "GameEngine.h"
+#include "GameInstance.h"
 #include "gui/WindowHandler.h"
 
 #include "globalLobby/GlobalLobbyClient.h"
@@ -610,8 +611,8 @@ void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to)
 
 void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState)
 {
-	if(CMM)
-		CMM->disable();
+	if(GAME->mainmenu())
+		GAME->mainmenu()->disable();
 
 	switch(si->mode)
 	{
@@ -649,7 +650,7 @@ void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victo
 		scenarioHighScores.isCampaign = false;
 
 		endGameplay();
-		CMM->menu->switchToTab("main");
+		GAME->mainmenu()->menu->switchToTab("main");
 		ENGINE->windows().createAndPushWindow<CHighScoreInputScreen>(victory, scenarioHighScores, statistic);
 	}
 }
@@ -664,17 +665,11 @@ void CServerHandler::endGameplay()
 	client->endGame();
 	client.reset();
 
-	if(CMM)
+	if (GAME->mainmenu())
 	{
-		ENGINE->curInt = CMM.get();
-		CMM->enable();
-		CMM->playMusic();
-	}
-	else
-	{
-		auto mainMenu = CMainMenu::create();
-		ENGINE->curInt = mainMenu.get();
-		mainMenu->playMusic();
+		GAME->mainmenu()->enable();
+		GAME->mainmenu()->playMusic();
+		GAME->mainmenu()->makeActiveInterface();
 	}
 }
 
@@ -710,14 +705,13 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared
 			entry->Bool() = true;
 		}
 
-		ENGINE->windows().pushWindow(CMM);
-		ENGINE->windows().pushWindow(CMM->menu);
+		GAME->mainmenu()->makeActiveInterface();
 
 		if(!ourCampaign->isCampaignFinished())
-			CMM->openCampaignLobby(ourCampaign);
+			GAME->mainmenu()->openCampaignLobby(ourCampaign);
 		else
 		{
-			CMM->openCampaignScreen(ourCampaign->campaignSet);
+			GAME->mainmenu()->openCampaignScreen(ourCampaign->campaignSet);
 			if(!ourCampaign->getOutroVideo().empty() && ENGINE->video().open(ourCampaign->getOutroVideo(), 1))
 			{
 				ENGINE->music().stopMusic();
@@ -890,7 +884,7 @@ void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> &
 	if(client)
 	{
 		endGameplay();
-		CMM->menu->switchToTab("main");
+		GAME->mainmenu()->menu->switchToTab("main");
 		showServerError(LIBRARY->generaltexth->translate("vcmi.server.errors.disconnected"));
 	}
 	else

+ 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;
+};

+ 36 - 1
client/GameInstance.cpp

@@ -10,8 +10,13 @@
 #include "StdInc.h"
 #include "GameInstance.h"
 
+#include "CPlayerInterface.h"
 #include "CServerHandler.h"
 #include "mapView/mapHandler.h"
+#include "globalLobby/GlobalLobbyClient.h"
+#include "mainmenu/CMainMenu.h"
+
+#include "../lib/CConfigHandler.h"
 
 std::unique_ptr<GameInstance> GAME = nullptr;
 
@@ -39,6 +44,17 @@ CMapHandler & GameInstance::map()
 	return *mapInstance;
 }
 
+std::shared_ptr<CMainMenu> GameInstance::mainmenu()
+{
+	if(settings["session"]["headless"].Bool())
+		return nullptr;
+
+	if (!mainMenuInstance)
+		mainMenuInstance = std::make_shared<CMainMenu>();
+
+	return mainMenuInstance;
+}
+
 CPlayerInterface * GameInstance::interface()
 {
 	return interfaceInstance;
@@ -56,5 +72,24 @@ void GameInstance::setMapInstance(std::unique_ptr<CMapHandler> ptr)
 
 void GameInstance::setInterfaceInstance(CPlayerInterface * ptr)
 {
-	interfaceInstance = std::move(ptr);
+	interfaceInstance = 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;
 }

+ 10 - 1
client/GameInstance.h

@@ -9,19 +9,23 @@
  */
 #pragma once
 
+#include "GameEngineUser.h"
+
 class CServerHandler;
 class GlobalLobbyClient;
 class CPlayerInterface;
 class CMapHandler;
+class CMainMenu;
 
 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;
+	std::shared_ptr<CMainMenu> mainMenuInstance;
 	CPlayerInterface * interfaceInstance;
 
 public:
@@ -31,11 +35,16 @@ public:
 	CServerHandler & server();
 	CMapHandler & map();
 
+	std::shared_ptr<CMainMenu> mainmenu();
 	CPlayerInterface * interface();
 
 	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 - 2
client/adventureMap/AdventureMapShortcuts.cpp

@@ -331,7 +331,7 @@ void AdventureMapShortcuts::toMainMenu()
 		[]()
 		{
 			GAME->server().endGameplay();
-			CMM->menu->switchToTab("main");
+			GAME->mainmenu()->menu->switchToTab("main");
 		},
 		0
 		);
@@ -344,7 +344,7 @@ void AdventureMapShortcuts::newGame()
 		[]()
 		{
 			GAME->server().endGameplay();
-			CMM->menu->switchToTab("new");
+			GAME->mainmenu()->menu->switchToTab("new");
 		},
 		nullptr
 		);

+ 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:

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -398,7 +398,7 @@ void CBonusSelection::goBack()
 	if(GAME->server().getState() != EClientState::GAMEPLAY)
 	{
 		ENGINE->windows().popWindows(2);
-		CMM->playMusic();
+		GAME->mainmenu()->playMusic();
 	}
 	else
 	{

+ 3 - 2
client/mainmenu/CHighScoreScreen.cpp

@@ -14,6 +14,7 @@
 #include "CStatisticScreen.h"
 #include "CMainMenu.h"
 #include "../GameEngine.h"
+#include "../GameInstance.h"
 #include "../gui/WindowHandler.h"
 #include "../gui/Shortcut.h"
 #include "../media/IMusicPlayer.h"
@@ -169,7 +170,7 @@ void CHighScoreScreen::buttonResetClick()
 void CHighScoreScreen::buttonExitClick()
 {
 	close();
-	CMM->playMusic();
+	GAME->mainmenu()->playMusic();
 }
 
 void CHighScoreScreen::showAll(Canvas & to)
@@ -221,7 +222,7 @@ CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc
 
 void CHighScoreInputScreen::stopMusicAndClose()
 {
-	CMM->playMusic();
+	GAME->mainmenu()->playMusic();
 	close();
 }
 

+ 6 - 29
client/mainmenu/CMainMenu.cpp

@@ -61,7 +61,6 @@
 #include "../../lib/CRandomGenerator.h"
 #include "../../lib/GameLibrary.h"
 
-std::shared_ptr<CMainMenu> CMM;
 ISelectionScreenInfo * SEL = nullptr;
 
 static void do_quit()
@@ -171,7 +170,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
 			}
 			case 1: //open campaign selection window
 			{
-				return std::bind(&CMainMenu::openCampaignScreen, CMM, commands.front());
+				return std::bind(&CMainMenu::openCampaignScreen, GAME->mainmenu(), commands.front());
 				break;
 			}
 			case 2: //start
@@ -331,11 +330,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 +381,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(GAME->mainmenu());
+	ENGINE->windows().pushWindow(menu);
+	menu->switchToTab(menu->getActiveTab());
 }
 
 void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode)
@@ -475,14 +460,6 @@ void CMainMenu::openHighScoreScreen()
 	return;
 }
 
-std::shared_ptr<CMainMenu> CMainMenu::create()
-{
-	if(!CMM)
-		CMM = std::shared_ptr<CMainMenu>(new CMainMenu());
-
-	return CMM;
-}
-
 std::shared_ptr<CPicture> CMainMenu::createPicture(const JsonNode & config)
 {
 	return std::make_shared<CPicture>(ImagePath::fromJson(config["name"]), (int)config["x"].Float(), (int)config["y"].Float());

+ 5 - 7
client/mainmenu/CMainMenu.h

@@ -139,21 +139,21 @@ 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 final : public CIntObject, public std::enable_shared_from_this<CMainMenu>
 {
 	std::shared_ptr<CFilledTexture> backgroundAroundMenu;
 
 	std::vector<VideoPath> videoPlayList;
 
-	CMainMenu(); //Use CMainMenu::create
-
 public:
+	CMainMenu();
+
 	std::shared_ptr<CMenuScreen> menu;
 
 	~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);
@@ -161,8 +161,6 @@ public:
 	static void openHighScoreScreen();
 	void openCampaignScreen(std::string name);
 
-	static std::shared_ptr<CMainMenu> create();
-
 	static std::shared_ptr<CPicture> createPicture(const JsonNode & config);
 
 	void playIntroVideos();
@@ -203,4 +201,4 @@ public:
 	void tick(uint32_t msPassed) override;
 };
 
-extern std::shared_ptr<CMainMenu> CMM;
+

+ 1 - 1
client/windows/settings/SettingsMainWindow.cpp

@@ -147,7 +147,7 @@ void SettingsMainWindow::mainMenuButtonCallback()
 		{
 			close();
 			GAME->server().endGameplay();
-			CMM->menu->switchToTab("main");
+			GAME->mainmenu()->menu->switchToTab("main");
 		},
 		0
 	);

+ 23 - 18
clientapp/EntryPoint.cpp

@@ -34,6 +34,7 @@
 #include "../client/windows/CMessage.h"
 #include "../client/windows/InfoWindows.h"
 
+#include "../lib/CConsoleHandler.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/ExceptionsCommon.h"
 #include "../lib/filesystem/Filesystem.h"
@@ -210,8 +211,13 @@ int main(int argc, char * argv[])
 	CStopWatch total;
 	CStopWatch pomtime;
 	std::cout.flags(std::ios::unitbuf);
+
+	setThreadNameLoggingOnly("MainGUI");
+	boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
+	if(vm.count("logLocation"))
+		logPath = vm["logLocation"].as<std::string>() + "/VCMI_Client_log.txt";
+
 #ifndef VCMI_IOS
-	console = new CConsoleHandler();
 
 	auto callbackFunction = [](std::string buffer, bool calledFromIngameConsole)
 	{
@@ -219,16 +225,15 @@ int main(int argc, char * argv[])
 		commandController.processCommand(buffer, calledFromIngameConsole);
 	};
 
-	*console->cb = callbackFunction;
-	console->start();
+	CConsoleHandler console(callbackFunction);
+	console.start();
+
+	CBasicLogConfigurator logConfigurator(logPath, &console);
+#else
+	CBasicLogConfigurator logConfigurator(logPath, nullptr);
 #endif
 
-	setThreadNameLoggingOnly("MainGUI");
-	boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
-	if(vm.count("logLocation"))
-		logPath = vm["logLocation"].as<std::string>() + "/VCMI_Client_log.txt";
-	logConfig = new CBasicLogConfigurator(logPath, console);
-	logConfig->configureDefault();
+	logConfigurator.configureDefault();
 	logGlobal->info("Starting client of '%s'", GameConstants::VCMI_VERSION);
 	logGlobal->info("Creating console and configuring logger: %d ms", pomtime.getDiff());
 	logGlobal->info("The log file will be saved to %s", logPath);
@@ -236,7 +241,7 @@ int main(int argc, char * argv[])
 	// Init filesystem and settings
 	try
 	{
-		preinitDLL(::console, false);
+		preinitDLL(false);
 	}
 	catch (const DataLoadingException & e)
 	{
@@ -285,7 +290,7 @@ int main(int argc, char * argv[])
 	setSettingInteger("general/saveFrequency", "savefrequency", 1);
 
 	// Initialize logging based on settings
-	logConfig->configure();
+	logConfigurator.configure();
 	logGlobal->debug("settings = %s", settings.toJsonNode().toString());
 
 	// Some basic data validation to produce better error messages in cases of incorrect install
@@ -309,6 +314,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
@@ -370,16 +376,15 @@ int main(int argc, char * argv[])
 		session["onlyai"].Bool() = true;
 		boost::thread(&CServerHandler::debugStartTest, &GAME->server(), session["testsave"].String(), true);
 	}
-	else
+	else if (!settings["session"]["headless"].Bool())
 	{
-		auto mmenu = CMainMenu::create();
-		ENGINE->curInt = mmenu.get();
+		GAME->mainmenu()->makeActiveInterface();
 
-		bool playIntroVideo = !settings["session"]["headless"].Bool() && !vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool();
+		bool playIntroVideo = !vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool();
 		if(playIntroVideo)
-			mmenu->playIntroVideos();
+			GAME->mainmenu()->playIntroVideos();
 		else
-			mmenu->playMusic();
+			GAME->mainmenu()->playMusic();
 	}
 	
 	std::vector<std::string> names;
@@ -443,7 +448,7 @@ static void mainLoop()
 	}
 
 	GAME.reset();
-	CMM.reset();
+	GAME->mainmenu().reset();
 
 	if(!settings["session"]["headless"].Bool())
 	{

+ 7 - 3
launcher/mainwindow_moc.cpp

@@ -14,6 +14,7 @@
 #include <QDir>
 
 #include "../lib/CConfigHandler.h"
+#include "../lib/CConsoleHandler.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
@@ -31,10 +32,13 @@ void MainWindow::load()
 	QDir::setCurrent(QApplication::applicationDirPath());
 
 #ifndef VCMI_MOBILE
-	console = new CConsoleHandler();
+	console = std::make_unique<CConsoleHandler>();
+	CBasicLogConfigurator logConfigurator(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", console.get());
+#else
+	CBasicLogConfigurator logConfigurator(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", nullptr);
 #endif
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Launcher_log.txt", console);
-	logConfig.configureDefault();
+
+	logConfigurator.configureDefault();
 
 	try
 	{

+ 8 - 0
launcher/mainwindow_moc.h

@@ -12,6 +12,10 @@
 #include <QStringList>
 #include <QTranslator>
 
+VCMI_LIB_NAMESPACE_BEGIN
+class CConsoleHandler;
+VCMI_LIB_NAMESPACE_END
+
 namespace Ui
 {
 class MainWindow;
@@ -40,6 +44,10 @@ class MainWindow : public QMainWindow
 #endif
 	Ui::MainWindow * ui;
 
+#ifndef VCMI_MOBILE
+	std::unique_ptr<CConsoleHandler> console;
+#endif
+
 	void load();
 
 	enum TabRows

+ 38 - 51
lib/CConsoleHandler.cpp

@@ -15,14 +15,6 @@
 
 #include <boost/stacktrace.hpp>
 
-VCMI_LIB_NAMESPACE_BEGIN
-
-std::mutex CConsoleHandler::smx;
-
-DLL_LINKAGE CConsoleHandler * console = nullptr;
-
-VCMI_LIB_NAMESPACE_END
-
 #if defined(NDEBUG) && !defined(VCMI_ANDROID)
 #define USE_ON_TERMINATE
 #endif
@@ -33,37 +25,31 @@ VCMI_LIB_NAMESPACE_END
 #endif
 
 #ifndef VCMI_WINDOWS
-	using TColor = std::string;
-	#define CONSOLE_GREEN "\x1b[1;32m"
-	#define CONSOLE_RED "\x1b[1;31m"
-	#define CONSOLE_MAGENTA "\x1b[1;35m"
-	#define CONSOLE_YELLOW "\x1b[1;33m"
-	#define CONSOLE_WHITE "\x1b[1;37m"
-	#define CONSOLE_GRAY "\x1b[1;30m"
-	#define CONSOLE_TEAL "\x1b[1;36m"
+constexpr const char * CONSOLE_GREEN = "\x1b[1;32m";
+constexpr const char * CONSOLE_RED = "\x1b[1;31m";
+constexpr const char * CONSOLE_MAGENTA = "\x1b[1;35m";
+constexpr const char * CONSOLE_YELLOW = "\x1b[1;33m";
+constexpr const char * CONSOLE_WHITE = "\x1b[1;37m";
+constexpr const char * CONSOLE_GRAY = "\x1b[1;30m";
+constexpr const char * CONSOLE_TEAL = "\x1b[1;36m";
 #else
-	#include <windows.h>
-	#include <dbghelp.h>
+#include <windows.h>
+#include <dbghelp.h>
 #ifndef __MINGW32__
 	#pragma comment(lib, "dbghelp.lib")
 #endif
-	typedef WORD TColor;
-	HANDLE handleIn;
-	HANDLE handleOut;
-	HANDLE handleErr;
-	#define CONSOLE_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
-	#define CONSOLE_RED FOREGROUND_RED | FOREGROUND_INTENSITY
-	#define CONSOLE_MAGENTA FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY
-	#define CONSOLE_YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
-	#define CONSOLE_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
-	#define CONSOLE_GRAY FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
-	#define CONSOLE_TEAL FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
-
-	static TColor defErrColor;
+HANDLE handleIn;
+HANDLE handleOut;
+HANDLE handleErr;
+constexpr int32_t CONSOLE_GREEN = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+constexpr int32_t CONSOLE_RED = FOREGROUND_RED | FOREGROUND_INTENSITY;
+constexpr int32_t CONSOLE_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+constexpr int32_t CONSOLE_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+constexpr int32_t CONSOLE_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+constexpr int32_t CONSOLE_GRAY = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+constexpr int32_t CONSOLE_TEAL = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
 #endif
 
-static TColor defColor;
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 #ifdef CREATE_MEMORY_DUMP
@@ -202,7 +188,7 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception)
 }
 #endif
 
-void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color)
+void CConsoleHandler::setColor(EConsoleTextColor color)
 {
 	TColor colorCode;
 	switch(color)
@@ -245,12 +231,12 @@ void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color)
 #endif
 }
 
-int CConsoleHandler::run() const
+int CConsoleHandler::run()
 {
 	setThreadName("consoleHandler");
 	//disabling sync to make in_avail() work (othervice always returns 0)
 	{
-		TLockGuard _(smx);
+		std::lock_guard guard(smx);
 		std::ios::sync_with_stdio(false);
 	}
 	std::string buffer;
@@ -262,8 +248,8 @@ int CConsoleHandler::run() const
 		if (std::cin.rdbuf()->in_avail())
 		{
 			if ( getline(std::cin, buffer).good() )
-				if ( cb && *cb )
-					(*cb)(buffer, false);
+				if ( cb )
+					cb(buffer, false);
 		}
 		else
 			boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
@@ -271,15 +257,19 @@ int CConsoleHandler::run() const
 		boost::this_thread::interruption_point();
 #else
 		std::getline(std::cin, buffer);
-		if ( cb && *cb )
-			(*cb)(buffer, false);
+		if ( cb )
+			cb(buffer, false);
 #endif
 	}
 	return -1;
 }
-CConsoleHandler::CConsoleHandler():
-	cb(new std::function<void(const std::string &, bool)>), 
-	thread(nullptr)
+
+CConsoleHandler::CConsoleHandler()
+	:CConsoleHandler(std::function<void(const std::string &, bool)>{})
+{}
+
+CConsoleHandler::CConsoleHandler(const std::function<void(const std::string &, bool)> & callback)
+	:cb(callback)
 {
 #ifdef VCMI_WINDOWS
 	handleIn = GetStdHandle(STD_INPUT_HANDLE);
@@ -309,27 +299,24 @@ CConsoleHandler::~CConsoleHandler()
 {
 	logGlobal->info("Killing console...");
 	end();
-	delete cb;
 	logGlobal->info("Killing console... done!");
 }
 void CConsoleHandler::end()
 {
-	if (thread)
+	if (thread.joinable())
 	{
 #ifndef VCMI_WINDOWS
-		thread->interrupt();
+		thread.interrupt();
 #else
-		TerminateThread(thread->native_handle(),0);
+		TerminateThread(thread.native_handle(),0);
 #endif
-		thread->join();
-		delete thread;
-		thread = nullptr;
+		thread.join();
 	}
 }
 
 void CConsoleHandler::start()
 {
-	thread = new boost::thread(std::bind(&CConsoleHandler::run,console));
+	thread = boost::thread(std::bind(&CConsoleHandler::run, this));
 }
 
 VCMI_LIB_NAMESPACE_END

+ 58 - 54
lib/CConsoleHandler.h

@@ -11,88 +11,92 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-namespace EConsoleTextColor
-{
 /** The color enum is used for colored text console output. */
-enum EConsoleTextColor
+enum class EConsoleTextColor : int8_t
 {
-    DEFAULT = -1,
-    GREEN,
-    RED,
-    MAGENTA,
-    YELLOW,
-    WHITE,
-    GRAY,
-    TEAL = -2
+	DEFAULT = -1,
+	GREEN,
+	RED,
+	MAGENTA,
+	YELLOW,
+	WHITE,
+	GRAY,
+	TEAL = -2
 };
-}
 
 /// Class which wraps the native console. It can print text based on
 /// the chosen color
 class DLL_LINKAGE CConsoleHandler
 {
 public:
-    CConsoleHandler();
-    ~CConsoleHandler();
-    void start(); //starts listening thread
+	CConsoleHandler(const std::function<void(const std::string &, bool)> & callback);
+	CConsoleHandler();
+	~CConsoleHandler();
+	void start(); //starts listening thread
 
-    template<typename T> void print(const T &data, bool addNewLine = false, EConsoleTextColor::EConsoleTextColor color = EConsoleTextColor::DEFAULT, bool printToStdErr = false)
+	template<typename T> void print(const T &data, bool addNewLine = false, EConsoleTextColor color = EConsoleTextColor::DEFAULT, bool printToStdErr = false)
 	{
-        TLockGuard _(smx);
+		TLockGuard _(smx);
 #ifndef VCMI_WINDOWS
 		// with love from ffmpeg - library is trying to print some warnings from separate thread
 		// this results in broken console on Linux. Lock stdout to print all our data at once
 		flockfile(stdout);
 #endif
-        if(color != EConsoleTextColor::DEFAULT) setColor(color);
-        if(printToStdErr)
-        {
-            std::cerr << data;
-            if(addNewLine)
-            {
-                std::cerr << std::endl;
-            }
-            else
-            {
-                std::cerr << std::flush;
-            }
-        }
-        else
-        {
-            std::cout << data;
-            if(addNewLine)
-            {
-                std::cout << std::endl;
-            }
-            else
-            {
-                std::cout << std::flush;
-            }
-        }
+		if(color != EConsoleTextColor::DEFAULT) setColor(color);
+		if(printToStdErr)
+		{
+			std::cerr << data;
+			if(addNewLine)
+			{
+				std::cerr << std::endl;
+			}
+			else
+			{
+				std::cerr << std::flush;
+			}
+		}
+		else
+		{
+			std::cout << data;
+			if(addNewLine)
+			{
+				std::cout << std::endl;
+			}
+			else
+			{
+				std::cout << std::flush;
+			}
+		}
 
-        if(color != EConsoleTextColor::DEFAULT) setColor(EConsoleTextColor::DEFAULT);
+		if(color != EConsoleTextColor::DEFAULT) setColor(EConsoleTextColor::DEFAULT);
 #ifndef VCMI_WINDOWS
 		funlockfile(stdout);
 #endif
 	}
-	//function to be called when message is received - string: message, bool: whether call was made from in-game console
-	std::function<void(const std::string &, bool)> *cb;
 
 private:
-	int run() const;
+
+#ifndef VCMI_WINDOWS
+	using TColor = std::string;
+#else
+	using TColor = int32_t;
+#endif
+
+	int run();
 
 	void end(); //kills listening thread
 
-	static void setColor(EConsoleTextColor::EConsoleTextColor color); //sets color of text appropriate for given logging level
+	void setColor(EConsoleTextColor color); //sets color of text appropriate for given logging level
 
-	/// FIXME: Implement CConsoleHandler as singleton, move some logic into CLogConsoleTarget, etc... needs to be discussed:)
-	/// Without static, application will crash complaining about mutex deleted. In short: CConsoleHandler gets deleted before
-	/// the logging system.
-	static std::mutex smx;
+	TColor defColor;
+	TColor defErrColor;
 
-	boost::thread * thread;
-};
+	//function to be called when message is received - string: message, bool: whether call was made from in-game console
+	std::function<void(const std::string &, bool)> cb;
 
-extern DLL_LINKAGE CConsoleHandler * console;
+	std::mutex smx;
+
+	boost::thread thread;
+};
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 3
lib/GameLibrary.cpp

@@ -32,7 +32,6 @@
 #include "CStopWatch.h"
 #include "VCMIDirs.h"
 #include "filesystem/Filesystem.h"
-#include "CConsoleHandler.h"
 #include "rmg/CRmgTemplateStorage.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
 #include "mapObjects/CObjectHandler.h"
@@ -47,9 +46,8 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 GameLibrary * LIBRARY = nullptr;
 
-DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool extractArchives)
+DLL_LINKAGE void preinitDLL(bool extractArchives)
 {
-	console = Console;
 	LIBRARY = new GameLibrary();
 	LIBRARY->loadFilesystem(extractArchives);
 	settings.init("config/settings.json", "vcmi:settings");

+ 1 - 1
lib/GameLibrary.h

@@ -119,7 +119,7 @@ public:
 
 extern DLL_LINKAGE GameLibrary * LIBRARY;
 
-DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool extractArchives);
+DLL_LINKAGE void preinitDLL(bool extractArchives);
 DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);
 
 

+ 3 - 2
lib/logging/CBasicLogConfigurator.cpp

@@ -12,6 +12,7 @@
 #include "CLogger.h"
 
 #include "../CConfigHandler.h"
+#include "../CConsoleHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -122,9 +123,9 @@ ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & leve
 		throw std::runtime_error("Log level " + level + " unknown.");
 }
 
-EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName)
+EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName)
 {
-	static const std::map<std::string, EConsoleTextColor::EConsoleTextColor> colorMap =
+	static const std::map<std::string, EConsoleTextColor> colorMap =
 	{
 		{"default", EConsoleTextColor::DEFAULT},
 		{"green", EConsoleTextColor::GREEN},

+ 2 - 3
lib/logging/CBasicLogConfigurator.h

@@ -10,12 +10,11 @@
 
 #pragma once
 
-#include "../CConsoleHandler.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CConsoleHandler;
 class JsonNode;
+enum class EConsoleTextColor : int8_t;
 
 /// The class CBasicLogConfigurator reads log properties from settings.json and
 /// sets up the logging system. Make sure that you use the same configurator object to
@@ -42,7 +41,7 @@ private:
 	static ELogLevel::ELogLevel getLogLevel(const std::string & level);
 	// Gets EConsoleTextColor enum from strings. (Should be moved to CLogger as a separate function?)
 	// Throws: std::runtime_error
-	static EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName);
+	static EConsoleTextColor getConsoleColor(const std::string & colorName);
 
 	boost::filesystem::path filePath;
 	CConsoleHandler * console;

+ 4 - 3
lib/logging/CLogger.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 #include "CLogger.h"
 #include "../CThreadHelper.h"
+#include "../CConsoleHandler.h"
 
 #ifdef VCMI_ANDROID
 #include <android/log.h>
@@ -313,13 +314,13 @@ CColorMapping::CColorMapping()
 	levelMap[ELogLevel::ERROR] = EConsoleTextColor::RED;
 }
 
-void CColorMapping::setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor::EConsoleTextColor color)
+void CColorMapping::setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor color)
 {
 	assert(level != ELogLevel::NOT_SET);
 	map[domain.getName()][level] = color;
 }
 
-EConsoleTextColor::EConsoleTextColor CColorMapping::getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const
+EConsoleTextColor CColorMapping::getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const
 {
 	CLoggerDomain currentDomain = domain;
 	while(true)
@@ -400,7 +401,7 @@ void CLogConsoleTarget::write(const LogRecord & record)
 	const bool printToStdErr = record.level >= ELogLevel::WARN;
 	if(console)
 	{
-		const EConsoleTextColor::EConsoleTextColor textColor =
+		const EConsoleTextColor textColor =
 			coloredOutputEnabled ? colorMapping.getColorFor(record.domain, record.level) : EConsoleTextColor::DEFAULT;
 
 		console->print(message, true, textColor, printToStdErr);

+ 5 - 5
lib/logging/CLogger.h

@@ -9,14 +9,14 @@
  */
 #pragma once
 
-#include "../CConsoleHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CLogger;
+class CConsoleHandler;
 struct LogRecord;
 class ILogTarget;
-
+enum class EConsoleTextColor : int8_t;
 
 namespace ELogLevel
 {
@@ -157,11 +157,11 @@ class DLL_LINKAGE CColorMapping
 public:
 	CColorMapping();
 
-	void setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor::EConsoleTextColor color);
-	EConsoleTextColor::EConsoleTextColor getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const;
+	void setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level, EConsoleTextColor color);
+	EConsoleTextColor getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const;
 
 private:
-	std::map<std::string, std::map<ELogLevel::ELogLevel, EConsoleTextColor::EConsoleTextColor> > map;
+	std::map<std::string, std::map<ELogLevel::ELogLevel, EConsoleTextColor> > map;
 };
 
 /// This target is a logging target which writes message to the console.

+ 4 - 3
lobby/EntryPoint.cpp

@@ -11,6 +11,7 @@
 
 #include "LobbyServer.h"
 
+#include "../lib/CConsoleHandler.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/filesystem/CFilesystemLoader.h"
 #include "../lib/filesystem/Filesystem.h"
@@ -24,10 +25,10 @@ int main(int argc, const char * argv[])
 	CResourceHandler::load("config/filesystem.json"); // FIXME: we actually need only config directory for schemas, can be reduced
 
 #ifndef VCMI_IOS
-	console = new CConsoleHandler();
+	CConsoleHandler console;
 #endif
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Lobby_log.txt", console);
-	logConfig.configureDefault();
+	CBasicLogConfigurator logConfigurator(VCMIDirs::get().userLogsPath() / "VCMI_Lobby_log.txt", &console);
+	logConfigurator.configureDefault();
 
 	auto databasePath = VCMIDirs::get().userDataPath() / "vcmiLobby.db";
 	logGlobal->info("Opening database %s", databasePath.string());

+ 1 - 1
mapeditor/inspector/heroskillswidget.cpp

@@ -15,7 +15,7 @@
 #include "../../lib/CSkillHandler.h"
 #include "inspector.h"
 
-static QList<std::pair<QString, QVariant>> LevelIdentifiers
+static const QList<std::pair<QString, QVariant>> LevelIdentifiers
 {
 	{QObject::tr("Beginner"), QVariant::fromValue(1)},
 	{QObject::tr("Advanced"), QVariant::fromValue(2)},

+ 4 - 5
mapeditor/mainwindow.cpp

@@ -23,6 +23,7 @@
 #include "../lib/GameLibrary.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/CConfigHandler.h"
+#include "../lib/CConsoleHandler.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/CMemoryBuffer.h"
 #include "../lib/GameConstants.h"
@@ -51,8 +52,6 @@
 #include "playersettings.h"
 #include "validator.h"
 
-static CBasicLogConfigurator * logConfig;
-
 QJsonValue jsonFromPixmap(const QPixmap &p)
 {
   QBuffer buffer;
@@ -184,14 +183,14 @@ MainWindow::MainWindow(QWidget* parent) :
 
 	//configure logging
 	const boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Editor_log.txt";
-	console = new CConsoleHandler();
-	logConfig = new CBasicLogConfigurator(logPath, console);
+	console = std::make_unique<CConsoleHandler>();
+	logConfig = std::make_unique<CBasicLogConfigurator>(logPath, console.get());
 	logConfig->configureDefault();
 	logGlobal->info("Starting map editor of '%s'", GameConstants::VCMI_VERSION);
 	logGlobal->info("The log file will be saved to %s", logPath);
 
 	//init
-	preinitDLL(::console, extractionOptions.extractArchives);
+	preinitDLL(extractionOptions.extractArchives);
 
 	// Initialize logging based on settings
 	logConfig->configure();

+ 8 - 1
mapeditor/mainwindow.h

@@ -12,6 +12,8 @@ class ObjectBrowserProxyModel;
 VCMI_LIB_NAMESPACE_BEGIN
 class CMap;
 class CampaignState;
+class CConsoleHandler;
+class CBasicLogConfigurator;
 class CGObjectInstance;
 VCMI_LIB_NAMESPACE_END
 
@@ -34,7 +36,12 @@ class MainWindow : public QMainWindow
 #ifdef ENABLE_QT_TRANSLATIONS
 	QTranslator translator;
 #endif
-	
+
+#ifndef VCMI_MOBILE
+	std::unique_ptr<CConsoleHandler> console;
+#endif
+	std::unique_ptr<CBasicLogConfigurator> logConfig;
+
 	std::unique_ptr<CMap> openMapInternal(const QString &);
 	std::shared_ptr<CampaignState> openCampaignInternal(const QString &);
 

+ 6 - 6
serverapp/EntryPoint.cpp

@@ -71,15 +71,15 @@ int main(int argc, const char * argv[])
 	// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
 	boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
 
-	console = new CConsoleHandler();
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userLogsPath() / "VCMI_Server_log.txt", console);
-	logConfig.configureDefault();
+	CConsoleHandler console;
+	CBasicLogConfigurator logConfigurator(VCMIDirs::get().userLogsPath() / "VCMI_Server_log.txt", &console);
+	logConfigurator.configureDefault();
 	logGlobal->info(SERVER_NAME);
 
 	boost::program_options::variables_map opts;
 	handleCommandOptions(argc, argv, opts);
-	preinitDLL(console, false);
-	logConfig.configure();
+	preinitDLL(false);
+	logConfigurator.configure();
 
 	loadDLLClasses();
 	std::srand(static_cast<uint32_t>(time(nullptr)));
@@ -98,7 +98,7 @@ int main(int argc, const char * argv[])
 		// CVCMIServer destructor must be called here - before LIBRARY cleanup
 	}
 
-	logConfig.deconfigure();
+	logConfigurator.deconfigure();
 	vstd::clear_pointer(LIBRARY);
 
 	return 0;

+ 1 - 3
test/CVcmiTestConfig.cpp

@@ -11,7 +11,6 @@
 #include "StdInc.h"
 #include "CVcmiTestConfig.h"
 
-#include "../lib/CConsoleHandler.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/GameLibrary.h"
@@ -23,8 +22,7 @@
 
 void CVcmiTestConfig::SetUp()
 {
-	console = new CConsoleHandler();
-	preinitDLL(console, true);
+	preinitDLL(true);
 	loadDLLClasses(true);
 
 	/* TEST_DATA_DIR may be wrong, if yes below test don't run,