00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "global.hpp"
00016
00017 #include "construct_dialog.hpp"
00018 #include "dialogs.hpp"
00019 #include "game_config.hpp"
00020 #include "gettext.hpp"
00021 #include "log.hpp"
00022 #include "multiplayer.hpp"
00023 #include "multiplayer_ui.hpp"
00024 #include "multiplayer_connect.hpp"
00025 #include "multiplayer_wait.hpp"
00026 #include "multiplayer_lobby.hpp"
00027 #include "multiplayer_create.hpp"
00028 #include "playmp_controller.hpp"
00029 #include "network.hpp"
00030 #include "playcampaign.hpp"
00031 #include "game_preferences.hpp"
00032 #include "preferences_display.hpp"
00033 #include "random.hpp"
00034 #include "replay.hpp"
00035 #include "video.hpp"
00036 #include "statistics.hpp"
00037 #include "serialization/string_utils.hpp"
00038 #include "upload_log.hpp"
00039 #include "wml_separators.hpp"
00040
00041 #define LOG_NW LOG_STREAM(info, network)
00042
00043 namespace {
00044
00045 class network_game_manager
00046 {
00047 public:
00048
00049 network_game_manager() {};
00050
00051 ~network_game_manager()
00052 {
00053 if(network::nconnections() > 0) {
00054 LOG_NW << "sending leave_game\n";
00055 config cfg;
00056 cfg.add_child("leave_game");
00057 network::send_data(cfg, 0, true);
00058 LOG_NW << "sent leave_game\n";
00059 }
00060 };
00061 };
00062
00063 }
00064
00065 static void run_lobby_loop(display& disp, mp::ui& ui)
00066 {
00067 disp.video().modeChanged();
00068 bool first = true;
00069 font::cache_mode(font::CACHE_LOBBY);
00070 while (ui.get_result() == mp::ui::CONTINUE) {
00071 if (disp.video().modeChanged() || first) {
00072 SDL_Rect lobby_pos = { 0, 0, disp.video().getx(), disp.video().gety() };
00073 ui.set_location(lobby_pos);
00074 first = false;
00075 }
00076
00077 events::raise_process_event();
00078 events::raise_draw_event();
00079 events::pump();
00080
00081 ui.process_network();
00082
00083 disp.flip();
00084 disp.delay(20);
00085 }
00086 font::cache_mode(font::CACHE_GAME);
00087 }
00088
00089 namespace {
00090
00091 enum server_type {
00092 ABORT_SERVER,
00093 WESNOTHD_SERVER,
00094 SIMPLE_SERVER
00095 };
00096
00097 class server_button : public gui::dialog_button
00098 {
00099 public:
00100 server_button(CVideo &vid): dialog_button(vid, _("View List"))
00101 {}
00102 protected:
00103 int action(gui::dialog_process_info &dp_info)
00104 {
00105
00106 gui::dialog server_dialog(dialog()->get_display(), _("List of Servers"),
00107 _("Choose a known server from the list"), gui::OK_CANCEL);
00108 std::vector<std::string> servers;
00109 std::ostringstream menu_heading;
00110 menu_heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Address");
00111 servers.push_back(menu_heading.str());
00112 const std::vector<game_config::server_info>& pref_servers = preferences::server_list();
00113 std::vector<game_config::server_info>::const_iterator server;
00114 for(server = pref_servers.begin(); server != pref_servers.end(); ++server) {
00115 servers.push_back(server->name + COLUMN_SEPARATOR + server->address);
00116 }
00117 server_dialog.set_menu(servers);
00118 gui::menu::basic_sorter server_sorter;
00119 server_sorter.set_alpha_sort(0).set_id_sort(1);
00120 server_dialog.get_menu().set_sorter(&server_sorter);
00121 if(server_dialog.show() >= 0) {
00122
00123 dialog()->get_textbox().set_text(preferences::server_list()[server_dialog.result()].address);
00124 }
00125
00126 dp_info.clear_buttons();
00127 return gui::CONTINUE_DIALOG;
00128 }
00129 };
00130 }
00131
00132 static server_type open_connection(game_display& disp, const std::string& original_host)
00133 {
00134 std::string h = original_host;
00135
00136 if(h.empty()) {
00137 gui::dialog d(disp, _("Connect to Host"), "", gui::OK_CANCEL);
00138 d.set_textbox(_("Choose host to connect to: "), preferences::network_host());
00139 d.add_button( new server_button(disp.video()), gui::dialog::BUTTON_EXTRA);
00140 if(d.show() || d.textbox_text().empty()) {
00141 return ABORT_SERVER;
00142 }
00143 h = d.textbox_text();
00144 }
00145
00146 network::connection sock;
00147
00148 const int pos = h.find_first_of(":");
00149 std::string host;
00150 unsigned int port;
00151
00152 if(pos == -1) {
00153 host = h;
00154 port = 15000;
00155 } else {
00156 host = h.substr(0, pos);
00157 port = lexical_cast_default<unsigned int>(h.substr(pos + 1), 15000);
00158 }
00159
00160
00161
00162 typedef std::pair<std::string, int> hostpair;
00163 std::set<hostpair> shown_hosts;
00164 shown_hosts.insert(hostpair(host, port));
00165
00166 config::child_list redirects;
00167 config data;
00168 sock = dialogs::network_connect_dialog(disp,_("Connecting to Server..."),host,port);
00169
00170 do {
00171
00172 if (!sock) {
00173 return ABORT_SERVER;
00174 }
00175
00176 data.clear();
00177 network::connection data_res = dialogs::network_receive_dialog(
00178 disp,_("Reading from Server..."),data);
00179 if (!data_res) return ABORT_SERVER;
00180 mp::check_response(data_res, data);
00181
00182
00183 const std::string& version = data["version"];
00184 if(version.empty() == false && version != game_config::version) {
00185 utils::string_map i18n_symbols;
00186 i18n_symbols["version1"] = version;
00187 i18n_symbols["version2"] = game_config::version;
00188 const std::string errorstring = vgettext("The server requires version '$version1' while you are using version '$version2'", i18n_symbols);
00189 throw network::error(errorstring);
00190 }
00191
00192
00193 if(data.child("redirect")) {
00194 config* redirect = data.child("redirect");
00195
00196 host = (*redirect)["host"];
00197 port = lexical_cast_default<unsigned int>((*redirect)["port"], 15000);
00198
00199 if(shown_hosts.find(hostpair(host,port)) != shown_hosts.end()) {
00200 throw network::error(_("Server-side redirect loop"));
00201 }
00202 shown_hosts.insert(hostpair(host, port));
00203
00204 if(network::nconnections() > 0)
00205 network::disconnect();
00206 sock = dialogs::network_connect_dialog(disp,_("Connecting to Server..."),host,port);
00207 continue;
00208 }
00209
00210 if(data.child("version")) {
00211 config cfg;
00212 config res;
00213 cfg["version"] = game_config::version;
00214 res.add_child("version", cfg);
00215 network::send_data(res, 0, true);
00216 }
00217
00218
00219 if(data.child("mustlogin")) {
00220 bool first_time = true;
00221 config* error = NULL;
00222
00223 do {
00224 if(error != NULL) {
00225 gui::dialog(disp,"",(*error)["message"],gui::OK_ONLY).show();
00226 }
00227
00228 std::string login = preferences::login();
00229
00230 if(!first_time) {
00231 const int res = gui::show_dialog(disp, NULL, "",
00232 _("You must log in to this server"), gui::OK_CANCEL,
00233 NULL, NULL, _("Login: "), &login, mp::max_login_size);
00234 if(res != 0 || login.empty()) {
00235 return ABORT_SERVER;
00236 }
00237 preferences::set_login(login);
00238 }
00239
00240 first_time = false;
00241
00242 config response;
00243 response.add_child("login")["username"] = login;
00244 network::send_data(response, 0, true);
00245
00246 network::connection data_res = network::receive_data(data, 0, 3000);
00247 if(!data_res) {
00248 throw network::error(_("Connection timed out"));
00249 }
00250
00251 error = data.child("error");
00252 } while(error != NULL);
00253 }
00254 } while(!(data.child("join_lobby") || data.child("join_game")));
00255
00256 if (h != preferences::server_list().front().address)
00257 preferences::set_network_host(h);
00258
00259 if (data.child("join_lobby")) {
00260 return WESNOTHD_SERVER;
00261 } else {
00262 return SIMPLE_SERVER;
00263 }
00264
00265 }
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 static void enter_wait_mode(game_display& disp, const config& game_config, mp::chat& chat, config& gamelist, bool observe)
00281 {
00282 mp::ui::result res;
00283 game_state state;
00284 network_game_manager m;
00285 upload_log nolog(false);
00286
00287 gamelist.clear();
00288 statistics::fresh_stats();
00289
00290 {
00291 mp::wait ui(disp, game_config, chat, gamelist);
00292
00293 ui.join_game(observe);
00294
00295 run_lobby_loop(disp, ui);
00296 res = ui.get_result();
00297
00298 if (res == mp::ui::PLAY) {
00299 ui.start_game();
00300
00301
00302
00303
00304
00305
00306
00307 state = ui.get_state();
00308
00309 }
00310 }
00311
00312 switch (res) {
00313 case mp::ui::PLAY:
00314 play_game(disp, state, game_config, nolog, IO_CLIENT,
00315 preferences::skip_mp_replay() && observe);
00316 recorder.clear();
00317
00318 break;
00319 case mp::ui::QUIT:
00320 default:
00321 break;
00322 }
00323 }
00324
00325 static void enter_create_mode(game_display& disp, const config& game_config, mp::chat& chat, config& gamelist, mp::controller default_controller, bool is_server);
00326
00327 static void enter_connect_mode(game_display& disp, const config& game_config,
00328 mp::chat& chat, config& gamelist, const mp::create::parameters& params,
00329 mp::controller default_controller, bool is_server)
00330 {
00331 mp::ui::result res;
00332 game_state state;
00333 const network::manager net_manager(1,1);
00334 const network::server_manager serv_manager(15000, is_server ?
00335 network::server_manager::TRY_CREATE_SERVER :
00336 network::server_manager::NO_SERVER);
00337 network_game_manager m;
00338 upload_log nolog(false);
00339
00340 gamelist.clear();
00341 statistics::fresh_stats();
00342
00343 {
00344 mp::connect ui(disp, game_config, chat, gamelist, params, default_controller);
00345 run_lobby_loop(disp, ui);
00346
00347 res = ui.get_result();
00348
00349
00350
00351 if (res == mp::ui::PLAY) {
00352 ui.start_game();
00353 state = ui.get_state();
00354 }
00355 }
00356
00357 switch (res) {
00358 case mp::ui::PLAY:
00359 play_game(disp, state, game_config, nolog, IO_SERVER);
00360 recorder.clear();
00361
00362 break;
00363 case mp::ui::CREATE:
00364 enter_create_mode(disp, game_config, chat, gamelist, default_controller, is_server);
00365 break;
00366 case mp::ui::QUIT:
00367 default:
00368 network::send_data(config("refresh_lobby"), 0, true);
00369 break;
00370 }
00371 }
00372
00373 static void enter_create_mode(game_display& disp, const config& game_config, mp::chat& chat, config& gamelist, mp::controller default_controller, bool is_server)
00374 {
00375 mp::ui::result res;
00376 mp::create::parameters params;
00377
00378 {
00379 mp::create ui(disp, game_config, chat, gamelist);
00380 run_lobby_loop(disp, ui);
00381 res = ui.get_result();
00382 params = ui.get_parameters();
00383 }
00384
00385 switch (res) {
00386 case mp::ui::CREATE:
00387 enter_connect_mode(disp, game_config, chat, gamelist, params, default_controller, is_server);
00388 break;
00389 case mp::ui::QUIT:
00390 default:
00391
00392 network::send_data(config("refresh_lobby"), 0, true);
00393 break;
00394 }
00395 }
00396
00397 static void enter_lobby_mode(game_display& disp, const config& game_config, mp::chat& chat, config& gamelist)
00398 {
00399 mp::ui::result res;
00400
00401 while (true) {
00402 {
00403 mp::lobby ui(disp, game_config, chat, gamelist);
00404 run_lobby_loop(disp, ui);
00405 res = ui.get_result();
00406 }
00407
00408 switch (res) {
00409 case mp::ui::JOIN:
00410 try {
00411 enter_wait_mode(disp, game_config, chat, gamelist, false);
00412 } catch(config::error& error) {
00413 if(!error.message.empty()) {
00414 gui::show_error_message(disp, error.message);
00415 }
00416
00417 network::send_data(config("refresh_lobby"), 0, true);
00418 }
00419 break;
00420 case mp::ui::OBSERVE:
00421 try {
00422 enter_wait_mode(disp, game_config, chat, gamelist, true);
00423 } catch(config::error& error) {
00424 if(!error.message.empty()) {
00425 gui::show_error_message(disp, error.message);
00426 }
00427 }
00428
00429 network::send_data(config("refresh_lobby"), 0, true);
00430 break;
00431 case mp::ui::CREATE:
00432 try {
00433 enter_create_mode(disp, game_config, chat, gamelist, mp::CNTR_NETWORK, false);
00434 } catch(config::error& error) {
00435 if (!error.message.empty())
00436 gui::show_error_message(disp, error.message);
00437
00438 network::send_data(config("refresh_lobby"), 0, true);
00439 }
00440 break;
00441 case mp::ui::QUIT:
00442 return;
00443 case mp::ui::PREFERENCES:
00444 {
00445 const preferences::display_manager disp_manager(&disp);
00446 preferences::show_preferences_dialog(disp,game_config);
00447
00448 network::send_data(config("refresh_lobby"), 0, true);
00449 }
00450 break;
00451 default:
00452 return;
00453 }
00454 }
00455 }
00456
00457 namespace mp {
00458
00459 void start_server(game_display& disp, const config& game_config,
00460 mp::controller default_controller, bool is_server)
00461 {
00462 const set_random_generator generator_setter(&recorder);
00463 mp::chat chat;
00464 config gamelist;
00465 playmp_controller::set_replay_last_turn(0);
00466 preferences::set_message_private(false);
00467 enter_create_mode(disp, game_config, chat, gamelist, default_controller, is_server);
00468 }
00469
00470 void start_client(game_display& disp, const config& game_config,
00471 const std::string host)
00472 {
00473 const set_random_generator generator_setter(&recorder);
00474 const network::manager net_manager(1,1);
00475
00476 mp::chat chat;
00477 config gamelist;
00478 server_type type = open_connection(disp, host);
00479
00480 switch(type) {
00481 case WESNOTHD_SERVER:
00482 enter_lobby_mode(disp, game_config, chat, gamelist);
00483 break;
00484 case SIMPLE_SERVER:
00485 playmp_controller::set_replay_last_turn(0);
00486 preferences::set_message_private(false);
00487 enter_wait_mode(disp, game_config, chat, gamelist, false);
00488 break;
00489 case ABORT_SERVER:
00490 break;
00491 }
00492 }
00493
00494 }
00495