Merge lp:~widelands-dev/widelands/bug-1434875-net-client-disconnect into lp:widelands

Proposed by Notabilis
Status: Merged
Merged at revision: 8831
Proposed branch: lp:~widelands-dev/widelands/bug-1434875-net-client-disconnect
Merge into: lp:widelands
Diff against target: 600 lines (+401/-29)
12 files modified
src/network/gamehost.cc (+49/-2)
src/network/gamehost.h (+2/-0)
src/network/network_gaming_messages.cc (+1/-0)
src/ui_basic/unique_window.cc (+3/-2)
src/wui/CMakeLists.txt (+4/-0)
src/wui/game_client_disconnected.cc (+178/-0)
src/wui/game_client_disconnected.h (+62/-0)
src/wui/game_exit_confirm_box.cc (+43/-0)
src/wui/game_exit_confirm_box.h (+41/-0)
src/wui/game_options_menu.cc (+2/-25)
src/wui/interactive_gamebase.cc (+13/-0)
src/wui/interactive_gamebase.h (+3/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/bug-1434875-net-client-disconnect
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+354531@code.launchpad.net

Commit message

Adding a dialog for the host when the connection to a client is lost. Allows the host to select whether to replace the client with an AI or to exit.

Description of the change

When a client disconnects (either on purpose or due to network errors), the game is paused, saved, and a dialog is displayed to the host. There, the host can select whether to replace the client with a Normal/Weak/Very Weak/Empty AI or to exit the game.
No dialog is shown, if the leaving player ...
- is only an observer
- has already lost. In that case the player is replaced by the Empty AI
- has won the game. In that case the player is replaced by the Normal AI

So far, this branch seems to work fine in my testcases and could be merged.
[ignore next part, not an issue of this branch]
However, ASAN is complaining about access to already freed memory when the host closes the game with Alt+F4 while the dialog is shown. Strangely, there is no complain if the "really exit game" confirmation dialog has been shown but aborted.
I wasn't able to figure out what is happening there. So if someone has an idea what is causing the problem, please tell me or fix it yourself. Apart from this memory bug, the branch is ready for review and testing.
[/ignore]

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3921. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/426390888.
Appveyor build 3719. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1434875_net_client_disconnect-3719.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3925. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/426559900.
Appveyor build 3723. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1434875_net_client_disconnect-3723.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have not been able to fix the memory leak.

The heart of the problem is that dropdowns break the expected tree hierarchy for panels by necessity - otherwise, we would not be able to show the list outside of the enclosing box. We have continually been struggling with this.

It's also weir that the same problem doesn't happen for the editor player menu, where we also have dropdowns inside a box inside a unique window.

I think we need to change the panel implementation to use shared_ptr instead of raw pointers. I have too many UI branches open at this time to do this now though - the changes will be huge and cause merge conflicts all over the place.

Revision history for this message
Notabilis (notabilis27) wrote :

Thanks for the explanation, it makes much more sense now. I haven't considered that the dropbox needs to access "higher" parents, so it was quite a strange effect. But I guess that means that this branch isn't to blame and can be reviewed and merged.

Would you mind copying your explanation to a new bug report about the memory leak and targeting it for build-21? The leak doesn't seem to cause any immediate harm so I think we can postpone fixing it until your other branches are in.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Bug is up: https://bugs.launchpad.net/widelands/+bug/1792079

I have 1 more idea for the memory leak. I'll try to fix it and then start the code review.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I suggested some improvements for the i18n. There is also some code duplication that we should get rid of.

review: Needs Fixing
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3945. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/427533795.
Appveyor build 3743. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1434875_net_client_disconnect-3743.

Revision history for this message
Notabilis (notabilis27) wrote :

Thanks for the bug report, the local fix and the review. Your remarks are hopefully all fixed now.
Do we need a translator comment for both occurrences of "Replace with %s" or is one comment enough when it is the same string?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3958. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/427902291.
Appveyor build 3756. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1434875_net_client_disconnect-3756.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 3965. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/428324625.
Appveyor build 3763. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1434875_net_client_disconnect-3763.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have tweaked the strings some more - all good now :)

> Do we need a translator comment for both occurrences of "Replace with %s" or is one comment enough when it is the same string?

One comment is enough - Gettext uses the string as a key, so it will be presented for translation only once. We can disambiguate them with pgettext when needed.

@bunnybot merge

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/network/gamehost.cc'
2--- src/network/gamehost.cc 2018-04-07 16:59:00 +0000
3+++ src/network/gamehost.cc 2018-09-14 08:20:24 +0000
4@@ -31,6 +31,7 @@
5 #endif
6
7 #include "ai/computer_player.h"
8+#include "ai/defaultai.h"
9 #include "base/i18n.h"
10 #include "base/md5.h"
11 #include "base/warning.h"
12@@ -560,6 +561,25 @@
13 ->instantiate(*d->game, p));
14 }
15
16+void GameHost::replace_client_with_ai(uint8_t playernumber, const std::string& ai) {
17+ assert(d->game->get_player(playernumber + 1)->get_ai().empty());
18+ assert(d->game->get_player(playernumber + 1)->get_ai()
19+ == d->settings.players.at(playernumber).ai);
20+ // Inform all players about the change
21+ // Has to be done at first in this method since the calls later on overwrite players[].name
22+ send_system_message_code(
23+ "CLIENT_X_REPLACED_WITH",
24+ d->settings.players.at(playernumber).name,
25+ ComputerPlayer::get_implementation(ai)->descname);
26+ set_player_ai(playernumber, ai, false);
27+ d->game->get_player(playernumber + 1)->set_ai(ai);
28+ // Activate the ai
29+ init_computer_player(playernumber + 1);
30+ set_player_state(playernumber, PlayerSettings::State::kComputer);
31+ assert(d->game->get_player(playernumber + 1)->get_ai()
32+ == d->settings.players.at(playernumber).ai);
33+}
34+
35 void GameHost::init_computer_players() {
36 const Widelands::PlayerNumber nr_players = d->game->map().get_nrplayers();
37 iterate_players_existing_novar(p, nr_players, *d->game) {
38@@ -2254,8 +2274,7 @@
39 }
40
41 set_player_state(number, PlayerSettings::State::kOpen);
42- if (d->game)
43- init_computer_player(number + 1);
44+ // Don't replace player with AI, let host choose what to do
45 }
46
47 void GameHost::disconnect_client(uint32_t const number,
48@@ -2266,6 +2285,34 @@
49
50 Client& client = d->clients.at(number);
51
52+ // If the client is linked to a player and it is the client that closes the connection
53+ // and the game has already started ...
54+ if (client.playernum != UserSettings::none() && reason != "SERVER_LEFT" && d->game != nullptr) {
55+ // And the client hasn't lost/won yet ...
56+ if (d->settings.users.at(client.usernum).result == Widelands::PlayerEndResult::kUndefined) {
57+ // If not shown yet, show a window and ask the host player what to do
58+ // with the tribe of the leaving client
59+ if (d->game->get_igbase()->show_game_client_disconnected()) {
60+ // Window has just been opened, pause game and create a save game
61+ if (!forced_pause()) {
62+ force_pause();
63+ }
64+ WLApplication::emergency_save(*d->game);
65+ }
66+ // Client was active but is a winner of the game: Replace with normal AI
67+ } else if (d->settings.users.at(client.usernum).result
68+ == Widelands::PlayerEndResult::kWon) {
69+ replace_client_with_ai(client.playernum, DefaultAI::normal_impl.name);
70+ // Client was active but has lost or gave up: Replace with empty AI
71+ } else {
72+ assert(d->settings.users.at(client.usernum).result
73+ == Widelands::PlayerEndResult::kLost
74+ || d->settings.users.at(client.usernum).result
75+ == Widelands::PlayerEndResult::kResigned);
76+ replace_client_with_ai(client.playernum, "empty");
77+ }
78+ }
79+
80 // If the client was completely connected before the disconnect, free the
81 // user settings and send changes to the clients
82 if (client.usernum >= 0) {
83
84=== modified file 'src/network/gamehost.h'
85--- src/network/gamehost.h 2018-04-07 16:59:00 +0000
86+++ src/network/gamehost.h 2018-09-14 08:20:24 +0000
87@@ -26,6 +26,7 @@
88 #include "logic/widelands.h"
89 #include "network/nethost_interface.h"
90 #include "network/network.h"
91+#include "ui_basic/unique_window.h"
92
93 struct ChatMessage;
94 struct GameHostImpl;
95@@ -79,6 +80,7 @@
96 void set_player_shared(PlayerSlot number, Widelands::PlayerNumber shared);
97 void switch_to_player(uint32_t user, uint8_t number);
98 void set_win_condition_script(const std::string& wc);
99+ void replace_client_with_ai(uint8_t playernumber, const std::string& ai);
100
101 // just visible stuff for the select mapmenu
102 void set_multiplayer_game_settings();
103
104=== modified file 'src/network/network_gaming_messages.cc'
105--- src/network/network_gaming_messages.cc 2018-04-07 16:59:00 +0000
106+++ src/network/network_gaming_messages.cc 2018-09-14 08:20:24 +0000
107@@ -128,6 +128,7 @@
108 ngmessages["MALFORMED_COMMANDS"] = _("Client sent malformed commands: %s");
109 ngmessages["SOMETHING_WRONG"] = _("Something went wrong: %s");
110 ngmessages["CLIENT_X_LEFT_GAME"] = _("%1$s has left the game (%2$s)");
111+ ngmessages["CLIENT_X_REPLACED_WITH"] = _("%1$s has been replaced with %2$s");
112 ngmessages["UNKNOWN_LEFT_GAME"] = _("Unknown user has left the game (%s)");
113 ngmessages["SYNCREQUEST_WO_GAME"] =
114 _("Server sent a SYNCREQUEST even though no game is running.");
115
116=== modified file 'src/ui_basic/unique_window.cc'
117--- src/ui_basic/unique_window.cc 2018-04-27 06:11:05 +0000
118+++ src/ui_basic/unique_window.cc 2018-09-14 08:20:24 +0000
119@@ -37,14 +37,15 @@
120 if (!window) {
121 open_window();
122 } else {
123- if (window->is_minimal())
124+ if (window->is_minimal()) {
125 window->restore();
126+ }
127 window->move_to_top();
128 }
129 }
130
131 /**
132- * Destroys the window, if it eixsts.
133+ * Destroys the window, if it exists.
134 */
135 void UniqueWindow::Registry::destroy() {
136 if (window) {
137
138=== modified file 'src/wui/CMakeLists.txt'
139--- src/wui/CMakeLists.txt 2018-05-16 05:30:22 +0000
140+++ src/wui/CMakeLists.txt 2018-09-14 08:20:24 +0000
141@@ -175,8 +175,12 @@
142 encyclopedia_window.h
143 fieldaction.cc
144 fieldaction.h
145+ game_client_disconnected.cc
146+ game_client_disconnected.h
147 game_debug_ui.cc
148 game_debug_ui.h
149+ game_exit_confirm_box.cc
150+ game_exit_confirm_box.h
151 game_statistics_menu.cc
152 game_statistics_menu.h
153 game_main_menu_save_game.cc
154
155=== added file 'src/wui/game_client_disconnected.cc'
156--- src/wui/game_client_disconnected.cc 1970-01-01 00:00:00 +0000
157+++ src/wui/game_client_disconnected.cc 2018-09-14 08:20:24 +0000
158@@ -0,0 +1,178 @@
159+/*
160+ * Copyright (C) 2002-2018 by the Widelands Development Team
161+ *
162+ * This program is free software; you can redistribute it and/or
163+ * modify it under the terms of the GNU General Public License
164+ * as published by the Free Software Foundation; either version 2
165+ * of the License, or (at your option) any later version.
166+ *
167+ * This program is distributed in the hope that it will be useful,
168+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
169+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
170+ * GNU General Public License for more details.
171+ *
172+ * You should have received a copy of the GNU General Public License
173+ * along with this program; if not, write to the Free Software
174+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
175+ *
176+ */
177+
178+#include "wui/game_client_disconnected.h"
179+
180+#include <boost/bind.hpp>
181+#include <boost/format.hpp>
182+
183+#include "ai/computer_player.h"
184+#include "ai/defaultai.h"
185+#include "base/i18n.h"
186+#include "network/gamehost.h"
187+#include "wui/game_exit_confirm_box.h"
188+#include "wui/interactive_gamebase.h"
189+
190+namespace {
191+
192+constexpr int32_t width = 256;
193+constexpr int32_t margin = 10;
194+constexpr int32_t vspacing = 5;
195+constexpr uint32_t vgap = 3;
196+
197+} // end anonymous namespace
198+
199+GameClientDisconnected::GameClientDisconnected(InteractiveGameBase* gb,
200+ UI::UniqueWindow::Registry& registry,
201+ GameHost* host)
202+ : UI::UniqueWindow(gb, "client_disconnected", &registry, 2 * margin + width, 0,
203+ /** TRANSLATORS: Window label */
204+ _("Client got disconnected")),
205+ igb_(gb),
206+ host_(host),
207+ box_(this, margin, margin, UI::Box::Vertical, width, get_h() - 2 * margin, vspacing),
208+ box_h_(&box_, margin, margin, UI::Box::Horizontal, width, 35, vspacing),
209+ text_(&box_,
210+ 0,
211+ 0,
212+ width,
213+ 10, // automatic height
214+ UI::PanelStyle::kWui,
215+ _("A player disconnected from the game. An automatic save game has been created. "
216+ "Do you want to continue playing?"),
217+ UI::Align::kLeft,
218+ UI::MultilineTextarea::ScrollMode::kNoScrolling),
219+ continue_(&box_h_,
220+ "continue_game",
221+ 0,
222+ 0,
223+ width - 35 - vspacing,
224+ 35,
225+ UI::ButtonStyle::kWuiMenu,
226+ /** TRANSLATORS: Button text */
227+ _("Continue game"),
228+ /** TRANSLATORS: Button tooltip */
229+ _("Replace the disconnected player with the selected AI and continue playing")),
230+ type_dropdown_(&box_h_,
231+ width - 50, // x
232+ 0, // y
233+ 60, // width of selection box
234+ 800, // height of selection box, shrinks automatically
235+ 35, // width/height of button
236+ /** TRANSLATORS: Dropdown tooltip to select the AI difficulty when a player has disconnected from a game */
237+ _("AI for the disconnected player"),
238+ UI::DropdownType::kPictorial,
239+ UI::PanelStyle::kWui),
240+ exit_game_(&box_,
241+ "exit_game",
242+ 0,
243+ 0,
244+ width,
245+ 35,
246+ UI::ButtonStyle::kWuiMenu,
247+ g_gr->images().get("images/wui/menus/menu_exit_game.png"),
248+ /** TRANSLATORS: Button tooltip */
249+ _("Exit Game")) {
250+
251+ box_.add(&text_);
252+ box_.add_space(vgap);
253+ box_h_.add(&continue_);
254+ box_h_.add(&type_dropdown_);
255+ box_.add(&box_h_);
256+ box_.add(&exit_game_);
257+ box_.set_size(width, text_.get_h() + vgap + box_h_.get_h() + exit_game_.get_h() + 3 * vspacing);
258+ set_inner_size(get_inner_w(), box_.get_h() + 2 * margin);
259+
260+ continue_.sigclicked.connect(
261+ boost::bind(&GameClientDisconnected::clicked_continue, boost::ref(*this)));
262+ exit_game_.sigclicked.connect(
263+ boost::bind(&GameClientDisconnected::clicked_exit_game, boost::ref(*this)));
264+
265+ // Add all AI types
266+ for (const auto* impl : ComputerPlayer::get_implementations()) {
267+ type_dropdown_.add(impl->descname,
268+ impl->name,
269+ g_gr->images().get(impl->icon_filename), false,
270+ /** TRANSLATORS: Dropdown selection. Parameter is the name of the AI that will be used as replacement for a disconnected player */
271+ (boost::format(_("Replace player with %s")) % impl->descname).str());
272+ }
273+
274+ // Set default mode to normal AI
275+ type_dropdown_.select(DefaultAI::normal_impl.name.c_str());
276+
277+ if (get_usedefaultpos()) {
278+ center_to_parent();
279+ }
280+}
281+
282+void GameClientDisconnected::die() {
283+
284+ if (host_->forced_pause()) {
285+ host_->end_forced_pause();
286+ }
287+ if (is_visible()) {
288+ // Dialog aborted, default to the old behavior and add a normal AI
289+ set_ai(DefaultAI::normal_impl.name);
290+ }
291+ UI::UniqueWindow::die();
292+}
293+
294+void GameClientDisconnected::clicked_continue() {
295+ assert(type_dropdown_.has_selection());
296+
297+ const std::string selection = type_dropdown_.get_selected();
298+ assert(selection != "");
299+
300+ set_ai(selection);
301+ // Visibility works as a hint that the window was closed by a button click
302+ set_visible(false);
303+
304+ die();
305+}
306+
307+void GameClientDisconnected::clicked_exit_game() {
308+ if (SDL_GetModState() & KMOD_CTRL) {
309+ igb_->end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
310+ } else {
311+ GameExitConfirmBox *gecb = new GameExitConfirmBox(*get_parent(), *igb_);
312+ gecb->cancel.connect(
313+ boost::bind(&GameClientDisconnected::exit_game_aborted,
314+ boost::ref(*this), gecb));
315+
316+ set_visible(false);
317+ }
318+}
319+
320+void GameClientDisconnected::exit_game_aborted(Panel *dialog) {
321+ // Make parent window visible again so player can use another option
322+ set_visible(true);
323+ // Make panel (that is: the dialog box) invisible to avoid both being visible for a moment
324+ dialog->set_visible(false);
325+}
326+
327+void GameClientDisconnected::set_ai(const std::string& ai) {
328+ const std::string ai_descr = ComputerPlayer::get_implementation(ai)->descname;
329+ for (size_t i = 0; i < host_->settings().players.size(); i++) {
330+ if (host_->settings().players.at(i).state != PlayerSettings::State::kOpen
331+ || !igb_->game().get_player(i + 1)->get_ai().empty()) {
332+ continue;
333+ }
334+ host_->replace_client_with_ai(i, ai);
335+ }
336+}
337
338=== added file 'src/wui/game_client_disconnected.h'
339--- src/wui/game_client_disconnected.h 1970-01-01 00:00:00 +0000
340+++ src/wui/game_client_disconnected.h 2018-09-14 08:20:24 +0000
341@@ -0,0 +1,62 @@
342+/*
343+ * Copyright (C) 2002-2018 by the Widelands Development Team
344+ *
345+ * This program is free software; you can redistribute it and/or
346+ * modify it under the terms of the GNU General Public License
347+ * as published by the Free Software Foundation; either version 2
348+ * of the License, or (at your option) any later version.
349+ *
350+ * This program is distributed in the hope that it will be useful,
351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
353+ * GNU General Public License for more details.
354+ *
355+ * You should have received a copy of the GNU General Public License
356+ * along with this program; if not, write to the Free Software
357+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
358+ *
359+ */
360+
361+#ifndef WL_WUI_GAME_CLIENT_DISCONNECTED_H
362+#define WL_WUI_GAME_CLIENT_DISCONNECTED_H
363+
364+#include "ui_basic/box.h"
365+#include "ui_basic/button.h"
366+#include "ui_basic/dropdown.h"
367+#include "ui_basic/multilinetextarea.h"
368+#include "ui_basic/unique_window.h"
369+
370+class GameHost;
371+class InteractiveGameBase;
372+
373+/**
374+ * Dialog that offers to replace a player with an AI or exit the game.
375+ * Even when the text only talks about a single player, actually all recently disconnected
376+ * players will be replaced by the taken choice.
377+ */
378+struct GameClientDisconnected : public UI::UniqueWindow {
379+ GameClientDisconnected(InteractiveGameBase*,
380+ UI::UniqueWindow::Registry&,
381+ GameHost*);
382+
383+ void die() override;
384+
385+private:
386+ void clicked_continue();
387+ void clicked_exit_game();
388+ void exit_game_aborted(UI::Panel*);
389+
390+ void set_ai(const std::string& ai);
391+
392+ InteractiveGameBase* igb_;
393+ GameHost* host_;
394+
395+ UI::Box box_;
396+ UI::Box box_h_;
397+ UI::MultilineTextarea text_;
398+ UI::Button continue_;
399+ UI::Dropdown<std::string> type_dropdown_;
400+ UI::Button exit_game_;
401+};
402+
403+#endif // end of include guard: WL_WUI_GAME_CLIENT_DISCONNECTED_H
404
405=== added file 'src/wui/game_exit_confirm_box.cc'
406--- src/wui/game_exit_confirm_box.cc 1970-01-01 00:00:00 +0000
407+++ src/wui/game_exit_confirm_box.cc 2018-09-14 08:20:24 +0000
408@@ -0,0 +1,43 @@
409+/*
410+ * Copyright (C) 2002-2018 by the Widelands Development Team
411+ *
412+ * This program is free software; you can redistribute it and/or
413+ * modify it under the terms of the GNU General Public License
414+ * as published by the Free Software Foundation; either version 2
415+ * of the License, or (at your option) any later version.
416+ *
417+ * This program is distributed in the hope that it will be useful,
418+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
419+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
420+ * GNU General Public License for more details.
421+ *
422+ * You should have received a copy of the GNU General Public License
423+ * along with this program; if not, write to the Free Software
424+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
425+ *
426+ */
427+
428+#include "wui/game_exit_confirm_box.h"
429+
430+#include "base/i18n.h"
431+
432+// TODO(GunChleoc): Arabic: Buttons need more height for Arabic
433+GameExitConfirmBox::GameExitConfirmBox(UI::Panel& parent, InteractiveGameBase& gb)
434+ : UI::WLMessageBox(&parent,
435+ /** TRANSLATORS: Window label when "Exit game" has been pressed */
436+ _("Exit Game Confirmation"),
437+ _("Are you sure you wish to exit this game?"),
438+ MBoxType::kOkCancel),
439+ igb_(gb) {
440+}
441+
442+void GameExitConfirmBox::clicked_ok() {
443+ // Call method linked to ok button
444+ ok();
445+ igb_.end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
446+}
447+
448+void GameExitConfirmBox::clicked_back() {
449+ cancel();
450+ die();
451+}
452
453=== added file 'src/wui/game_exit_confirm_box.h'
454--- src/wui/game_exit_confirm_box.h 1970-01-01 00:00:00 +0000
455+++ src/wui/game_exit_confirm_box.h 2018-09-14 08:20:24 +0000
456@@ -0,0 +1,41 @@
457+/*
458+ * Copyright (C) 2002-2018 by the Widelands Development Team
459+ *
460+ * This program is free software; you can redistribute it and/or
461+ * modify it under the terms of the GNU General Public License
462+ * as published by the Free Software Foundation; either version 2
463+ * of the License, or (at your option) any later version.
464+ *
465+ * This program is distributed in the hope that it will be useful,
466+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
467+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
468+ * GNU General Public License for more details.
469+ *
470+ * You should have received a copy of the GNU General Public License
471+ * along with this program; if not, write to the Free Software
472+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
473+ *
474+ */
475+
476+#ifndef WL_WUI_GAME_EXIT_CONFIRM_BOX_H
477+#define WL_WUI_GAME_EXIT_CONFIRM_BOX_H
478+
479+#include "ui_basic/messagebox.h"
480+#include "wui/interactive_gamebase.h"
481+
482+/**
483+ Simple ok/abort box asking whether the game really should be exited.
484+ */
485+class GameExitConfirmBox : public UI::WLMessageBox {
486+public:
487+ GameExitConfirmBox(UI::Panel& parent, InteractiveGameBase& gb);
488+
489+ void clicked_ok() override;
490+
491+ void clicked_back() override;
492+
493+private:
494+ InteractiveGameBase& igb_;
495+};
496+
497+#endif // end of include guard: WL_WUI_GAME_EXIT_CONFIRM_BOX_H
498
499=== modified file 'src/wui/game_options_menu.cc'
500--- src/wui/game_options_menu.cc 2018-05-13 07:15:39 +0000
501+++ src/wui/game_options_menu.cc 2018-09-14 08:20:24 +0000
502@@ -27,6 +27,7 @@
503 #include "base/i18n.h"
504 #include "graphic/graphic.h"
505 #include "sound/sound_handler.h"
506+#include "wui/game_exit_confirm_box.h"
507 #include "wui/game_main_menu_save_game.h"
508 #include "wui/game_options_sound_menu.h"
509 #include "wui/unique_window_handler.h"
510@@ -36,30 +37,6 @@
511 #define vspacing 5
512 #define vgap 3
513
514-class GameOptionsMenuExitConfirmBox : public UI::WLMessageBox {
515-public:
516- // TODO(GunChleoc): Arabic: Buttons need more height for Arabic
517- GameOptionsMenuExitConfirmBox(UI::Panel& parent, InteractiveGameBase& gb)
518- : UI::WLMessageBox(&parent,
519- /** TRANSLATORS: Window label when "Exit game" has been pressed */
520- _("Exit Game Confirmation"),
521- _("Are you sure you wish to exit this game?"),
522- MBoxType::kOkCancel),
523- igb_(gb) {
524- }
525-
526- void clicked_ok() override {
527- igb_.end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
528- }
529-
530- void clicked_back() override {
531- die();
532- }
533-
534-private:
535- InteractiveGameBase& igb_;
536-};
537-
538 GameOptionsMenu::GameOptionsMenu(InteractiveGameBase& gb,
539 UI::UniqueWindow::Registry& registry,
540 InteractiveGameBase::GameMainMenuWindows& windows)
541@@ -137,7 +114,7 @@
542 if (SDL_GetModState() & KMOD_CTRL) {
543 igb_.end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
544 } else {
545- new GameOptionsMenuExitConfirmBox(*get_parent(), igb_);
546+ new GameExitConfirmBox(*get_parent(), igb_);
547 die();
548 }
549 }
550
551=== modified file 'src/wui/interactive_gamebase.cc'
552--- src/wui/interactive_gamebase.cc 2018-08-14 13:40:46 +0000
553+++ src/wui/interactive_gamebase.cc 2018-09-14 08:20:24 +0000
554@@ -34,9 +34,11 @@
555 #include "logic/map.h"
556 #include "logic/map_objects/tribes/ship.h"
557 #include "logic/player.h"
558+#include "network/gamehost.h"
559 #include "profile/profile.h"
560 #include "wui/constructionsitewindow.h"
561 #include "wui/dismantlesitewindow.h"
562+#include "wui/game_client_disconnected.h"
563 #include "wui/game_summary.h"
564 #include "wui/militarysitewindow.h"
565 #include "wui/productionsitewindow.h"
566@@ -284,3 +286,14 @@
567 }
568 new GameSummaryScreen(this, &game_summary_);
569 }
570+
571+bool InteractiveGameBase::show_game_client_disconnected() {
572+ assert(is_a(GameHost, get_game()->game_controller()));
573+ if (!client_disconnected_.window) {
574+ if (upcast(GameHost, host, get_game()->game_controller())) {
575+ new GameClientDisconnected(this, client_disconnected_, host);
576+ return true;
577+ }
578+ }
579+ return false;
580+}
581
582=== modified file 'src/wui/interactive_gamebase.h'
583--- src/wui/interactive_gamebase.h 2018-08-14 13:40:46 +0000
584+++ src/wui/interactive_gamebase.h 2018-09-14 08:20:24 +0000
585@@ -95,6 +95,8 @@
586 }
587
588 void show_game_summary();
589+ /// For the game host. Show a window and ask the host player what to do with the tribe of the leaving client.
590+ bool show_game_client_disconnected();
591 void postload() override;
592 void start() override;
593
594@@ -107,6 +109,7 @@
595 PlayerType playertype_;
596 UI::UniqueWindow::Registry fieldaction_;
597 UI::UniqueWindow::Registry game_summary_;
598+ UI::UniqueWindow::Registry client_disconnected_;
599 UI::Button* toggle_buildhelp_;
600 UI::Button* reset_zoom_;
601

Subscribers

People subscribed via source and target branches

to status/vote changes: