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