1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 
20 #include "ui/graphical/menu/control/menucontrollermultiplayerclient.h"
21 #include "ui/graphical/application.h"
22 #include "ui/graphical/menu/windows/windownetworklobbyclient/windownetworklobbyclient.h"
23 #include "ui/graphical/menu/windows/windowclanselection/windowclanselection.h"
24 #include "ui/graphical/menu/windows/windowlandingunitselection/windowlandingunitselection.h"
25 #include "ui/graphical/menu/windows/windowlandingpositionselection/windowlandingpositionselection.h"
26 #include "ui/graphical/menu/windows/windowgamesettings/gamesettings.h"
27 #include "ui/graphical/menu/dialogs/dialogok.h"
28 #include "ui/graphical/menu/dialogs/dialogyesno.h"
29 #include "ui/graphical/menu/widgets/special/lobbychatboxlistviewitem.h"
30 #include "ui/graphical/menu/widgets/special/chatboxlandingplayerlistviewitem.h"
31 #include "ui/graphical/game/widgets/chatbox.h"
32 #include "game/startup/network/client/networkclientgamenew.h"
33 #include "game/startup/network/client/networkclientgamereconnection.h"
34 #include "game/startup/network/client/networkclientgamesaved.h"
35 #include "main.h"
36 #include "game/data/map/map.h"
37 #include "game/data/player/player.h"
38 #include "game/data/units/landingunit.h"
39 #include "network.h"
40 #include "utility/log.h"
41 #include "menuevents.h"
42 #include "game/logic/serverevents.h"
43 #include "netmessage.h"
44 #include "mapdownload.h"
45 
46 // TODO: remove
47 std::vector<std::pair<sID, int>> createInitialLandingUnitsList (int clan, const cGameSettings& gameSettings); // defined in windowsingleplayer.cpp
48 
49 //------------------------------------------------------------------------------
cMenuControllerMultiplayerClient(cApplication & application_)50 cMenuControllerMultiplayerClient::cMenuControllerMultiplayerClient (cApplication& application_) :
51 	application (application_),
52 	windowLandingPositionSelection (nullptr)
53 {}
54 
55 //------------------------------------------------------------------------------
~cMenuControllerMultiplayerClient()56 cMenuControllerMultiplayerClient::~cMenuControllerMultiplayerClient()
57 {
58 }
59 
60 //------------------------------------------------------------------------------
start()61 void cMenuControllerMultiplayerClient::start()
62 {
63 	network = std::make_shared<cTCP> ();
64 
65 	network->setMessageReceiver (this);
66 
67 	windowNetworkLobby = std::make_shared<cWindowNetworkLobbyClient> ();
68 	triedLoadMapName = "";
69 
70 	application.show (windowNetworkLobby);
71 	application.addRunnable (shared_from_this());
72 
73 	signalConnectionManager.connect (windowNetworkLobby->terminated, std::bind (&cMenuControllerMultiplayerClient::reset, this));
74 	signalConnectionManager.connect (windowNetworkLobby->backClicked, [this]()
75 	{
76 		windowNetworkLobby->close();
77 		saveOptions();
78 	});
79 
80 	signalConnectionManager.connect (windowNetworkLobby->wantLocalPlayerReadyChange, std::bind (&cMenuControllerMultiplayerClient::handleWantLocalPlayerReadyChange, this));
81 	signalConnectionManager.connect (windowNetworkLobby->triggeredChatMessage, std::bind (&cMenuControllerMultiplayerClient::handleChatMessageTriggered, this));
82 
83 	signalConnectionManager.connect (windowNetworkLobby->triggeredConnect, std::bind (&cMenuControllerMultiplayerClient::connect, this));
84 
85 	signalConnectionManager.connect (windowNetworkLobby->getLocalPlayer()->nameChanged, std::bind (&cMenuControllerMultiplayerClient::handleLocalPlayerAttributesChanged, this));
86 	signalConnectionManager.connect (windowNetworkLobby->getLocalPlayer()->colorChanged, std::bind (&cMenuControllerMultiplayerClient::handleLocalPlayerAttributesChanged, this));
87 	signalConnectionManager.connect (windowNetworkLobby->getLocalPlayer()->readyChanged, std::bind (&cMenuControllerMultiplayerClient::handleLocalPlayerAttributesChanged, this));
88 }
89 
90 //------------------------------------------------------------------------------
reset()91 void cMenuControllerMultiplayerClient::reset()
92 {
93 	network = nullptr;
94 	windowNetworkLobby = nullptr;
95 	windowLandingPositionSelection = nullptr;
96 	playersLandingStatus.clear();
97 	newGame = nullptr;
98 	application.removeRunnable (*this);
99 }
100 
101 //------------------------------------------------------------------------------
pushEvent(std::unique_ptr<cNetMessage> message)102 void cMenuControllerMultiplayerClient::pushEvent (std::unique_ptr<cNetMessage> message)
103 {
104 	messageQueue.push (std::move (message));
105 }
106 
107 //------------------------------------------------------------------------------
popEvent()108 std::unique_ptr<cNetMessage> cMenuControllerMultiplayerClient::popEvent()
109 {
110 	std::unique_ptr<cNetMessage> message;
111 	messageQueue.try_pop(message);
112 	return message;
113 }
114 
115 //------------------------------------------------------------------------------
run()116 void cMenuControllerMultiplayerClient::run()
117 {
118 	std::unique_ptr<cNetMessage> message;
119 	while (messageQueue.try_pop (message))
120 	{
121 		handleNetMessage (*message);
122 	}
123 }
124 
125 //------------------------------------------------------------------------------
handleWantLocalPlayerReadyChange()126 void cMenuControllerMultiplayerClient::handleWantLocalPlayerReadyChange()
127 {
128 	if (!network || !windowNetworkLobby) return;
129 
130 	auto& localPlayer = windowNetworkLobby->getLocalPlayer();
131 
132 	if (!localPlayer) return;
133 
134 	if (!windowNetworkLobby->getStaticMap() && !triedLoadMapName.empty())
135 	{
136 		if (!localPlayer->isReady()) windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~No_Map_No_Ready", triedLoadMapName));
137 		localPlayer->setReady (false);
138 	}
139 	else localPlayer->setReady (!localPlayer->isReady());
140 }
141 
142 //------------------------------------------------------------------------------
handleChatMessageTriggered()143 void cMenuControllerMultiplayerClient::handleChatMessageTriggered()
144 {
145 	if (!network || !windowNetworkLobby) return;
146 
147 	const auto& chatMessage = windowNetworkLobby->getChatMessage();
148 
149 	if (chatMessage.empty()) return;
150 
151 	auto& localPlayer = windowNetworkLobby->getLocalPlayer();
152 
153 	if (localPlayer)
154 	{
155 		windowNetworkLobby->addChatEntry (localPlayer->getName(), chatMessage);
156 		sendMenuChatMessage (*network, chatMessage, nullptr, localPlayer->getNr());
157 	}
158 }
159 
160 //------------------------------------------------------------------------------
handleLocalPlayerAttributesChanged()161 void cMenuControllerMultiplayerClient::handleLocalPlayerAttributesChanged()
162 {
163 	if (!network || !windowNetworkLobby) return;
164 
165 	if (network->getConnectionStatus() == 0) return;
166 
167 	sendIdentification (*network, *windowNetworkLobby->getLocalPlayer());
168 }
169 
170 //------------------------------------------------------------------------------
connect()171 void cMenuControllerMultiplayerClient::connect()
172 {
173 	if (!network || !windowNetworkLobby) return;
174 
175 	// Connect only if there isn't a connection yet
176 	if (network->getConnectionStatus() != 0) return;
177 
178 	const auto& ip = windowNetworkLobby->getIp();
179 	const auto& port = windowNetworkLobby->getPort();
180 
181 	windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Network_Connecting") + ip + ":" + iToStr (port));    // e.g. Connecting to 127.0.0.1:55800
182 	Log.write (("Connecting to " + ip + ":" + iToStr (port)), cLog::eLOG_TYPE_INFO);
183 
184 	// TODO: make this non blocking!
185 	if (network->connect (ip, port) == -1)
186 	{
187 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Network_Error_Connect") + ip + ":" + iToStr (port));
188 		Log.write ("Error on connecting " + ip + ":" + iToStr (port), cLog::eLOG_TYPE_WARNING);
189 	}
190 	else
191 	{
192 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Network_Connected"));
193 		Log.write ("Connected", cLog::eLOG_TYPE_INFO);
194 		windowNetworkLobby->disablePortEdit();
195 		windowNetworkLobby->disableIpEdit();
196 	}
197 }
198 
199 //------------------------------------------------------------------------------
startSavedGame()200 void cMenuControllerMultiplayerClient::startSavedGame()
201 {
202 	if (!network || !windowNetworkLobby || !windowNetworkLobby->getStaticMap() || !windowNetworkLobby->getGameSettings()) return;
203 
204 	auto savedGame = std::make_shared<cNetworkClientGameSaved> ();
205 
206 	savedGame->setNetwork (network);
207 	savedGame->setStaticMap (windowNetworkLobby->getStaticMap());
208 	savedGame->setGameSettings (windowNetworkLobby->getGameSettings());
209 	savedGame->setPlayers (windowNetworkLobby->getPlayersNotShared(), *windowNetworkLobby->getLocalPlayer());
210 
211 	application.closeTill (*windowNetworkLobby);
212 	windowNetworkLobby->close();
213 	signalConnectionManager.connect (windowNetworkLobby->terminated, [&]() { windowNetworkLobby = nullptr; });
214 
215 	savedGame->start (application);
216 }
217 
218 //------------------------------------------------------------------------------
startGamePreparation()219 void cMenuControllerMultiplayerClient::startGamePreparation()
220 {
221 	const auto& staticMap = windowNetworkLobby->getStaticMap();
222 	const auto& gameSettings = windowNetworkLobby->getGameSettings();
223 
224 	if (!staticMap || !gameSettings || !network) return;
225 
226 	newGame = std::make_shared<cNetworkClientGameNew> ();
227 
228 	newGame->setPlayers (windowNetworkLobby->getPlayersNotShared(), *windowNetworkLobby->getLocalPlayer());
229 	newGame->setGameSettings (gameSettings);
230 	newGame->setStaticMap (staticMap);
231 	newGame->setNetwork (network);
232 
233 	if (newGame->getGameSettings()->getClansEnabled())
234 	{
235 		startClanSelection(true);
236 	}
237 	else
238 	{
239 		startLandingUnitSelection(true);
240 	}
241 }
242 
243 //------------------------------------------------------------------------------
startClanSelection(bool isFirstWindowOnGamePreparation)244 void cMenuControllerMultiplayerClient::startClanSelection(bool isFirstWindowOnGamePreparation)
245 {
246 	if (!newGame) return;
247 
248 	auto windowClanSelection = application.show (std::make_shared<cWindowClanSelection> ());
249 
250 	signalConnectionManager.connect (windowClanSelection->canceled, [this, windowClanSelection, isFirstWindowOnGamePreparation]()
251 	{
252 		if(isFirstWindowOnGamePreparation)
253 		{
254 			checkReallyWantsToQuit();
255 		}
256 		else
257 		{
258 			windowClanSelection->close();
259 		}
260 	});
261 	signalConnectionManager.connect (windowClanSelection->done, [this, windowClanSelection]()
262 	{
263 		newGame->setLocalPlayerClan (windowClanSelection->getSelectedClan());
264 
265 		startLandingUnitSelection(false);
266 	});
267 }
268 
269 //------------------------------------------------------------------------------
startLandingUnitSelection(bool isFirstWindowOnGamePreparation)270 void cMenuControllerMultiplayerClient::startLandingUnitSelection(bool isFirstWindowOnGamePreparation)
271 {
272 	if (!newGame || !newGame->getGameSettings()) return;
273 
274 	auto initialLandingUnits = createInitialLandingUnitsList (newGame->getLocalPlayerClan(), *newGame->getGameSettings());
275 
276 	auto windowLandingUnitSelection = application.show (std::make_shared<cWindowLandingUnitSelection> (cPlayerColor(), newGame->getLocalPlayerClan(), initialLandingUnits, newGame->getGameSettings()->getStartCredits()));
277 
278 	signalConnectionManager.connect (windowLandingUnitSelection->canceled, [this, windowLandingUnitSelection, isFirstWindowOnGamePreparation]()
279 	{
280 		if(isFirstWindowOnGamePreparation)
281 		{
282 			checkReallyWantsToQuit();
283 		}
284 		else
285 		{
286 			windowLandingUnitSelection->close();
287 		}
288 	});
289 	signalConnectionManager.connect (windowLandingUnitSelection->done, [this, windowLandingUnitSelection]()
290 	{
291 		newGame->setLocalPlayerLandingUnits (windowLandingUnitSelection->getLandingUnits());
292 		newGame->setLocalPlayerUnitUpgrades (windowLandingUnitSelection->getUnitUpgrades());
293 
294 		startLandingPositionSelection();
295 	});
296 }
297 
298 //------------------------------------------------------------------------------
startLandingPositionSelection()299 void cMenuControllerMultiplayerClient::startLandingPositionSelection()
300 {
301 	if (!newGame || !newGame->getStaticMap() || !network) return;
302 
303 	windowLandingPositionSelection = std::make_shared<cWindowLandingPositionSelection> (newGame->getStaticMap(), true);
304 
305 	signalConnectionManager.connect (windowLandingPositionSelection->opened, [this]()
306 	{
307 		sendInLandingPositionSelectionStatus (*network, newGame->getLocalPlayer(), true, nullptr);
308 	});
309 	signalConnectionManager.connect (windowLandingPositionSelection->closed, [this]()
310 	{
311 		sendInLandingPositionSelectionStatus (*network, newGame->getLocalPlayer(), false, nullptr);
312 	});
313 
314 	for (const auto& status : playersLandingStatus)
315 	{
316 		windowLandingPositionSelection->getChatBox()->addPlayerEntry (std::make_unique<cChatBoxLandingPlayerListViewItem> (*status));
317 	}
318 
319 	application.show (windowLandingPositionSelection);
320 
321 	signalConnectionManager.connect (windowLandingPositionSelection->canceled, [this]() { windowLandingPositionSelection->close(); });
322 	signalConnectionManager.connect (windowLandingPositionSelection->selectedPosition, [this] (cPosition landingPosition)
323 	{
324 		newGame->setLocalPlayerLandingPosition (landingPosition);
325 
326 		sendLandingPosition (*network, landingPosition, newGame->getLocalPlayer());
327 	});
328 	signalConnectionManager.connect (windowLandingPositionSelection->getChatBox()->commandEntered, [this] (const std::string & command)
329 	{
330 		const auto& localPlayer = newGame->getLocalPlayer();
331 		windowLandingPositionSelection->getChatBox()->addChatEntry (std::make_unique<cLobbyChatBoxListViewItem> (localPlayer.getName(), command));
332 		cSoundDevice::getInstance().playSoundEffect (SoundData.SNDChat);
333 		sendMenuChatMessage (*network, command, nullptr, localPlayer.getNr());
334 	});
335 }
336 
337 //------------------------------------------------------------------------------
startNewGame()338 void cMenuControllerMultiplayerClient::startNewGame()
339 {
340 	if (!newGame) return;
341 
342 	application.closeTill (*windowNetworkLobby);
343 	windowNetworkLobby->close();
344 	signalConnectionManager.connect (windowNetworkLobby->terminated, [&]() { windowNetworkLobby = nullptr; });
345 
346 	newGame->start (application);
347 }
348 
349 //------------------------------------------------------------------------------
checkReallyWantsToQuit()350 void cMenuControllerMultiplayerClient::checkReallyWantsToQuit()
351 {
352 	auto yesNoDialog = application.show(std::make_shared<cDialogYesNo>("Are you sure you want to abort the game preparation?")); // TODO: translate
353 
354 	signalConnectionManager.connect(yesNoDialog->yesClicked, [this]()
355 	{
356 		sendPlayerHasAbortedGamePreparation(*network, newGame->getLocalPlayer());
357 		application.closeTill(*windowNetworkLobby);
358 	});
359 }
360 
361 //------------------------------------------------------------------------------
handleNetMessage(cNetMessage & message)362 void cMenuControllerMultiplayerClient::handleNetMessage (cNetMessage& message)
363 {
364 	Log.write ("Menu: <-- " + message.getTypeAsString() + ", Hexdump: " + message.getHexDump(), cLog::eLOG_TYPE_NET_DEBUG);
365 
366 	switch (message.iType)
367 	{
368 		case MU_MSG_CHAT: handleNetMessage_MU_MSG_CHAT (message); break;
369 		case TCP_CLOSE: handleNetMessage_TCP_CLOSE (message); break;
370 		case MU_MSG_REQ_IDENTIFIKATION: handleNetMessage_MU_MSG_REQ_IDENTIFIKATION (message); break;
371 		case MU_MSG_PLAYER_NUMBER: handleNetMessage_MU_MSG_PLAYER_NUMBER (message); break;
372 		case MU_MSG_PLAYERLIST: handleNetMessage_MU_MSG_PLAYERLIST (message); break;
373 		case MU_MSG_OPTINS: handleNetMessage_MU_MSG_OPTINS (message); break;
374 		case MU_MSG_START_MAP_DOWNLOAD: initMapDownload (message); break;
375 		case MU_MSG_MAP_DOWNLOAD_DATA: receiveMapData (message); break;
376 		case MU_MSG_CANCELED_MAP_DOWNLOAD: canceledMapDownload (message); break;
377 		case MU_MSG_FINISHED_MAP_DOWNLOAD: finishedMapDownload (message); break;
378 		case MU_MSG_GO: handleNetMessage_MU_MSG_GO (message); break;
379 		case MU_MSG_LANDING_STATE: handleNetMessage_MU_MSG_LANDING_STATE (message); break;
380 		case MU_MSG_ALL_LANDED: handleNetMessage_MU_MSG_ALL_LANDED (message); break;
381 		case GAME_EV_REQ_RECON_IDENT: handleNetMessage_GAME_EV_REQ_RECON_IDENT (message); break;
382 		case GAME_EV_RECONNECT_ANSWER: handleNetMessage_GAME_EV_RECONNECT_ANSWER (message); break;
383 		case MU_MSG_IN_LANDING_POSITION_SELECTION_STATUS: handleNetMessage_MU_MSG_IN_LANDING_POSITION_SELECTION_STATUS (message); break;
384 		case MU_MSG_PLAYER_HAS_SELECTED_LANDING_POSITION: handleNetMessage_MU_MSG_PLAYER_HAS_SELECTED_LANDING_POSITION (message); break;
385 		case MU_MSG_PLAYER_HAS_ABORTED_GAME_PREPARATION: handleNetMessage_MU_MSG_PLAYER_HAS_ABORTED_GAME_PREPARATION(message); break;
386 		default:
387 			Log.write ("Client Menu Controller: Can not handle message type " + message.getTypeAsString(), cLog::eLOG_TYPE_NET_ERROR);
388 			break;
389 	}
390 }
391 
392 //------------------------------------------------------------------------------
handleNetMessage_TCP_CLOSE(cNetMessage & message)393 void cMenuControllerMultiplayerClient::handleNetMessage_TCP_CLOSE (cNetMessage& message)
394 {
395 	assert (message.iType == TCP_CLOSE);
396 
397 	if (!network || !windowNetworkLobby) return;
398 
399 	windowNetworkLobby->removeNonLocalPlayers();
400 	const auto& localPlayer = windowNetworkLobby->getLocalPlayer();
401 	localPlayer->setReady (false);
402 	windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Lost_Connection", "server"));
403 }
404 
405 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_CHAT(cNetMessage & message)406 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_CHAT (cNetMessage& message)
407 {
408 	assert (message.iType == MU_MSG_CHAT);
409 
410 	if (!network || !windowNetworkLobby) return;
411 
412 	bool translationText = message.popBool();
413 	auto chatText = message.popString();
414 
415 	auto players = windowNetworkLobby->getPlayers();
416 	auto iter = std::find_if (players.begin(), players.end(), [ = ] (const std::shared_ptr<cPlayerBasicData>& player) { return player->getNr() == message.iPlayerNr; });
417 
418 	const auto playerName = iter == players.end() ? "unknown" : (*iter)->getName();
419 
420 	if (windowLandingPositionSelection)
421 	{
422 		if (translationText)
423 		{
424 			windowLandingPositionSelection->getChatBox()->addChatEntry (std::make_unique<cLobbyChatBoxListViewItem> (lngPack.i18n (chatText)));
425 		}
426 		else
427 		{
428 			windowLandingPositionSelection->getChatBox()->addChatEntry (std::make_unique<cLobbyChatBoxListViewItem> (playerName, chatText));
429 			cSoundDevice::getInstance().playSoundEffect (SoundData.SNDChat);
430 		}
431 	}
432 	else
433 	{
434 		if (translationText)
435 		{
436 			windowNetworkLobby->addInfoEntry (lngPack.i18n (chatText));
437 		}
438 		else
439 		{
440 			windowNetworkLobby->addChatEntry (playerName, chatText);
441 		}
442 	}
443 }
444 
445 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_REQ_IDENTIFIKATION(cNetMessage & message)446 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_REQ_IDENTIFIKATION (cNetMessage& message)
447 {
448 	assert (message.iType == MU_MSG_REQ_IDENTIFIKATION);
449 
450 	if (!network || !windowNetworkLobby) return;
451 
452 	Log.write ("game version of server is: " + message.popString(), cLog::eLOG_TYPE_NET_DEBUG);
453 	windowNetworkLobby->getLocalPlayer()->setNr (message.popInt16());
454 	sendIdentification (*network, *windowNetworkLobby->getLocalPlayer());
455 }
456 
457 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_PLAYER_NUMBER(cNetMessage & message)458 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_PLAYER_NUMBER (cNetMessage& message)
459 {
460 	assert (message.iType == MU_MSG_PLAYER_NUMBER);
461 
462 	if (!network || !windowNetworkLobby) return;
463 
464 	windowNetworkLobby->getLocalPlayer()->setNr (message.popInt16());
465 }
466 
467 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_PLAYERLIST(cNetMessage & message)468 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_PLAYERLIST (cNetMessage& message)
469 {
470 	assert (message.iType == MU_MSG_PLAYERLIST);
471 
472 	if (!network || !windowNetworkLobby) return;
473 
474 	const auto& localPlayer = windowNetworkLobby->getLocalPlayer();
475 	windowNetworkLobby->removeNonLocalPlayers();
476 
477 	int playerCount = message.popInt16();
478 	for (int i = 0; i < playerCount; i++)
479 	{
480 		auto name = message.popString();
481 		auto color = message.popColor();
482 		auto ready = message.popBool();
483 		auto nr = message.popInt16();
484 
485 		if (nr == localPlayer->getNr())
486 		{
487 			localPlayer->setName (name);
488 			localPlayer->setColor (cPlayerColor (color));
489 			localPlayer->setReady (ready);
490 		}
491 		else
492 		{
493 			auto newPlayer = std::make_shared<cPlayerBasicData> (name, cPlayerColor (color), nr);
494 			newPlayer->setReady (ready);
495 			windowNetworkLobby->addPlayer (std::move (newPlayer));
496 		}
497 	}
498 }
499 
500 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_OPTINS(cNetMessage & message)501 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_OPTINS (cNetMessage& message)
502 {
503 	assert (message.iType == MU_MSG_OPTINS);
504 
505 	if (!network || !windowNetworkLobby) return;
506 
507 	//pop game settings
508 	if (message.popBool())
509 	{
510 		auto settings = std::make_unique<cGameSettings> ();
511 		settings->popFrom (message);
512 		windowNetworkLobby->setGameSettings (std::move (settings));
513 	}
514 	else
515 	{
516 		windowNetworkLobby->setGameSettings (nullptr);
517 	}
518 
519 	//pop map setting
520 	if (message.popBool())
521 	{
522 		auto mapName = message.popString();
523 		int32_t mapCheckSum = message.popInt32();
524 		const auto& staticMap = windowNetworkLobby->getStaticMap();
525 		if (!staticMap || staticMap->getName() != mapName)
526 		{
527 			bool mapCheckSumsEqual = (MapDownload::calculateCheckSum (mapName) == mapCheckSum);
528 			auto newStaticMap = std::make_shared<cStaticMap>();
529 			if (mapCheckSumsEqual && newStaticMap->loadMap (mapName))
530 			{
531 				triedLoadMapName = "";
532 			}
533 			else
534 			{
535 				const auto& localPlayer = windowNetworkLobby->getLocalPlayer();
536 				if (localPlayer->isReady())
537 				{
538 					windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~No_Map_No_Ready", mapName));
539 					localPlayer->setReady (false);
540 				}
541 				triedLoadMapName = mapName;
542 
543 				auto existingMapFilePath = MapDownload::getExistingMapFilePath (mapName);
544 				bool existsMap = !existingMapFilePath.empty();
545 				if (!mapCheckSumsEqual && existsMap)
546 				{
547 					windowNetworkLobby->addInfoEntry ("You have an incompatible version of the");
548 					windowNetworkLobby->addInfoEntry (std::string ("map \"") + mapName + "\" at");
549 					windowNetworkLobby->addInfoEntry (std::string ("\"") + existingMapFilePath + "\" !");
550 					windowNetworkLobby->addInfoEntry ("Move it away or delete it, then reconnect.");
551 				}
552 				else
553 				{
554 					if (MapDownload::isMapOriginal (mapName, mapCheckSum) == false)
555 					{
556 						if (mapName != lastRequestedMapName)
557 						{
558 							lastRequestedMapName = mapName;
559 							sendRequestMap (*network, mapName, localPlayer->getNr());
560 							windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~MapDL_DownloadRequest"));
561 							windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~MapDL_Download", mapName));
562 						}
563 					}
564 					else
565 					{
566 						windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~MapDL_DownloadRequestInvalid"));
567 						windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~MapDL_DownloadInvalid", mapName));
568 					}
569 				}
570 			}
571 			windowNetworkLobby->setStaticMap (std::move (newStaticMap));
572 		}
573 	}
574 	else
575 	{
576 		windowNetworkLobby->setStaticMap (nullptr);
577 	}
578 
579 	//pop save geame settings
580 	std::vector<cPlayerBasicData> savePlayerList;
581 	for (int nr = message.popInt32(); nr > 0; nr--)
582 	{
583 		std::string playerName = message.popString();
584 		int playerNr = message.popInt32();
585 		savePlayerList.push_back (cPlayerBasicData (playerName, cPlayerColor (cRgbColor (0, 0, 0)), playerNr));
586 	}
587 	std::string saveGameName = message.popString();
588 
589 	windowNetworkLobby->setSaveGame (savePlayerList, saveGameName);
590 
591 }
592 
593 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_GO(cNetMessage & message)594 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_GO (cNetMessage& message)
595 {
596 	assert (message.iType == MU_MSG_GO);
597 
598 	windowLandingPositionSelection = nullptr;
599 
600 	saveOptions();
601 
602 	if (windowNetworkLobby->getSaveGamePlayers().size() != 0)
603 	{
604 		startSavedGame();
605 	}
606 	else
607 	{
608 		startGamePreparation();
609 	}
610 }
611 
612 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_LANDING_STATE(cNetMessage & message)613 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_LANDING_STATE (cNetMessage& message)
614 {
615 	assert (message.iType == MU_MSG_LANDING_STATE);
616 
617 	if (!windowLandingPositionSelection) return;
618 
619 	eLandingPositionState state = (eLandingPositionState)message.popInt32();
620 
621 	windowLandingPositionSelection->applyReselectionState (state);
622 }
623 
624 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_ALL_LANDED(cNetMessage & message)625 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_ALL_LANDED (cNetMessage& message)
626 {
627 	assert (message.iType == MU_MSG_ALL_LANDED);
628 
629 	if (!newGame) return;
630 
631 	startNewGame();
632 }
633 
634 //------------------------------------------------------------------------------
handleNetMessage_GAME_EV_REQ_RECON_IDENT(cNetMessage & message)635 void cMenuControllerMultiplayerClient::handleNetMessage_GAME_EV_REQ_RECON_IDENT (cNetMessage& message)
636 {
637 	assert (message.iType == GAME_EV_REQ_RECON_IDENT);
638 
639 	if (!network || !windowNetworkLobby) return;
640 
641 	auto yesNoDialog = application.show (std::make_shared<cDialogYesNo> (lngPack.i18n ("Text~Multiplayer~Reconnect")));
642 
643 	const auto socket = message.popInt16();
644 
645 	signalConnectionManager.connect (yesNoDialog->yesClicked, [this, socket]()
646 	{
647 		sendGameIdentification (*network, *windowNetworkLobby->getLocalPlayer(), socket);
648 	});
649 
650 	signalConnectionManager.connect (yesNoDialog->noClicked, [this]()
651 	{
652 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Connection_Terminated"));
653 		network->close (0);
654 	});
655 }
656 
657 //------------------------------------------------------------------------------
handleNetMessage_GAME_EV_RECONNECT_ANSWER(cNetMessage & message)658 void cMenuControllerMultiplayerClient::handleNetMessage_GAME_EV_RECONNECT_ANSWER (cNetMessage& message)
659 {
660 	assert (message.iType == GAME_EV_RECONNECT_ANSWER);
661 
662 	if (!network || !windowNetworkLobby) return;
663 
664 	if (message.popBool())
665 	{
666 		const int localPlayerNumber = message.popInt16();
667 		const auto localPlayerColor = message.popColor();
668 
669 		const auto mapName = message.popString();
670 
671 		auto staticMap = std::make_shared<cStaticMap> ();
672 		if (!staticMap->loadMap (mapName)) return; // TODO: error message
673 
674 		auto reconnectionGame = std::make_shared<cNetworkClientGameReconnection> ();
675 
676 		int playerCount = message.popInt16();
677 
678 		std::vector<cPlayerBasicData> players;
679 		players.push_back (cPlayerBasicData (windowNetworkLobby->getLocalPlayer()->getName(), cPlayerColor (localPlayerColor), localPlayerNumber));
680 		const auto localPlayer = players.back();
681 		while (playerCount > 1)
682 		{
683 			const auto playerName = message.popString();
684 			const auto playerColor = message.popColor();
685 			const int playerNr = message.popInt16();
686 			players.push_back (cPlayerBasicData (playerName, cPlayerColor (playerColor), playerNr));
687 			playerCount--;
688 		}
689 
690 		reconnectionGame->setNetwork (network);
691 		reconnectionGame->setStaticMap (staticMap);
692 		reconnectionGame->setPlayers (players, localPlayer);
693 
694 		application.closeTill (*windowNetworkLobby);
695 		windowNetworkLobby->close();
696 		signalConnectionManager.connect (windowNetworkLobby->terminated, [&]() { windowNetworkLobby = nullptr; });
697 
698 		reconnectionGame->start (application);
699 	}
700 	else
701 	{
702 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Reconnect_Forbidden"));
703 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~Connection_Terminated"));
704 		network->close (0);
705 	}
706 }
707 
708 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_IN_LANDING_POSITION_SELECTION_STATUS(cNetMessage & message)709 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_IN_LANDING_POSITION_SELECTION_STATUS (cNetMessage& message)
710 {
711 	assert (message.iType == MU_MSG_IN_LANDING_POSITION_SELECTION_STATUS);
712 
713 	if (!network || !windowNetworkLobby) return;
714 
715 	auto players = windowNetworkLobby->getPlayers();
716 
717 	const auto isIn = message.popBool();
718 	const auto playerNr = message.popInt32();
719 
720 	if (isIn)
721 	{
722 		auto players = windowNetworkLobby->getPlayers();
723 		auto iter = std::find_if (players.begin(), players.end(), [playerNr] (const std::shared_ptr<cPlayerBasicData>& player) { return player->getNr() == playerNr; });
724 
725 		assert (iter != players.end());
726 
727 		const auto& player = **iter;
728 
729 		playersLandingStatus.push_back (std::make_unique<cPlayerLandingStatus> (player));
730 		if (windowLandingPositionSelection) windowLandingPositionSelection->getChatBox()->addPlayerEntry (std::make_unique<cChatBoxLandingPlayerListViewItem> (*playersLandingStatus.back()));
731 	}
732 	else
733 	{
734 		if (windowLandingPositionSelection) windowLandingPositionSelection->getChatBox()->removePlayerEntry (playerNr);
735 		playersLandingStatus.erase (std::remove_if (playersLandingStatus.begin(), playersLandingStatus.end(), [playerNr] (const std::unique_ptr<cPlayerLandingStatus>& status) { return status->getPlayer().getNr() == playerNr; }), playersLandingStatus.end());
736 	}
737 }
738 
739 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_PLAYER_HAS_SELECTED_LANDING_POSITION(cNetMessage & message)740 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_PLAYER_HAS_SELECTED_LANDING_POSITION (cNetMessage& message)
741 {
742 	assert (message.iType == MU_MSG_PLAYER_HAS_SELECTED_LANDING_POSITION);
743 
744 	if (!network || !windowNetworkLobby) return;
745 
746 	const auto playerNr = message.popInt32();
747 
748 	auto iter = std::find_if (playersLandingStatus.begin(), playersLandingStatus.end(), [playerNr] (const std::unique_ptr<cPlayerLandingStatus>& entry) { return entry->getPlayer().getNr() == playerNr; });
749 
750 	assert (iter != playersLandingStatus.end());
751 
752 	auto& playerLandingStatus = **iter;
753 
754 	playerLandingStatus.setHasSelectedPosition (true);
755 }
756 
757 //------------------------------------------------------------------------------
handleNetMessage_MU_MSG_PLAYER_HAS_ABORTED_GAME_PREPARATION(cNetMessage & message)758 void cMenuControllerMultiplayerClient::handleNetMessage_MU_MSG_PLAYER_HAS_ABORTED_GAME_PREPARATION(cNetMessage & message)
759 {
760 	auto players = windowNetworkLobby->getPlayers();
761 
762 	const auto playerNr = message.popInt32();
763 
764 	auto iter = std::find_if(players.begin(), players.end(), [playerNr](const std::shared_ptr<cPlayerBasicData>& player) { return player->getNr() == playerNr; });
765 	if(iter == players.end()) return;
766 
767 	const auto& player = **iter;
768 
769 	auto yesNoDialog = application.show(std::make_shared<cDialogOk>("Player " + player.getName() + " has quit from game preparation")); // TODO: translate
770 
771 	signalConnectionManager.connect(yesNoDialog->done, [this]()
772 	{
773 		application.closeTill(*windowNetworkLobby);
774 	});
775 }
776 
777 //------------------------------------------------------------------------------
initMapDownload(cNetMessage & message)778 void cMenuControllerMultiplayerClient::initMapDownload (cNetMessage& message)
779 {
780 	const auto mapSize = message.popInt32();
781 	const auto mapName = message.popString();
782 
783 	mapReceiver = std::make_unique<cMapReceiver> (mapName, mapSize);
784 }
785 
786 //------------------------------------------------------------------------------
receiveMapData(cNetMessage & message)787 void cMenuControllerMultiplayerClient::receiveMapData (cNetMessage& message)
788 {
789 	if (mapReceiver == nullptr) return;
790 
791 	mapReceiver->receiveData (message);
792 
793 	if (windowNetworkLobby != nullptr)
794 	{
795 		const int percent = mapReceiver->getBytesReceivedPercent();
796 
797 		windowNetworkLobby->setMapDownloadPercent (percent);
798 	}
799 }
800 
801 //------------------------------------------------------------------------------
canceledMapDownload(cNetMessage & message)802 void cMenuControllerMultiplayerClient::canceledMapDownload (cNetMessage& message)
803 {
804 	if (mapReceiver == nullptr) return;
805 
806 	mapReceiver = nullptr;
807 
808 	if (windowNetworkLobby != nullptr)
809 	{
810 		windowNetworkLobby->setMapDownloadCanceled();
811 	}
812 }
813 
814 //------------------------------------------------------------------------------
finishedMapDownload(cNetMessage & message)815 void cMenuControllerMultiplayerClient::finishedMapDownload (cNetMessage& message)
816 {
817 	if (mapReceiver == nullptr) return;
818 
819 	mapReceiver->finished();
820 
821 	auto staticMap = std::make_shared<cStaticMap> ();
822 
823 	if (!staticMap->loadMap (mapReceiver->getMapName())) staticMap = nullptr;
824 
825 	if (windowNetworkLobby != nullptr)
826 	{
827 		windowNetworkLobby->setStaticMap (staticMap);
828 
829 		windowNetworkLobby->addInfoEntry (lngPack.i18n ("Text~Multiplayer~MapDL_Finished"));
830 	}
831 
832 	mapReceiver = nullptr;
833 }
834 
835 //------------------------------------------------------------------------------
saveOptions()836 void cMenuControllerMultiplayerClient::saveOptions()
837 {
838 	if (!windowNetworkLobby) return;
839 
840 	cSettings::getInstance().setPlayerName (windowNetworkLobby->getLocalPlayer()->getName().c_str());
841 	cSettings::getInstance().setPort (windowNetworkLobby->getPort());
842 	cSettings::getInstance().setPlayerColor (windowNetworkLobby->getLocalPlayer()->getColor().getColor());
843 	cSettings::getInstance().setIP (windowNetworkLobby->getIp().c_str());
844 }
845