Completely rewrote "multiplayer lobby" and "multiplayer game creation" code,
...and modified the associated server code. Added the ability to kick users in the "game creation" screen Added the ability for users to switch sides in the "game creation" screen Added the name of the previous user for each position, on the "game creation" screen, when loading saves. Fixed several bugs. Certainly introduced several more.
This commit is contained in:
parent
4e892cef25
commit
9283ba2423
24 changed files with 2382 additions and 3025 deletions
|
@ -56,6 +56,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
intro.cpp \
|
||||
key.cpp \
|
||||
language.cpp \
|
||||
leader_list.cpp \
|
||||
log.cpp \
|
||||
map.cpp \
|
||||
map_label.cpp \
|
||||
|
@ -63,8 +64,10 @@ wesnoth_SOURCES = about.cpp \
|
|||
mapgen_dialog.cpp \
|
||||
mouse.cpp \
|
||||
multiplayer.cpp \
|
||||
multiplayer_client.cpp \
|
||||
multiplayer_ui.cpp \
|
||||
multiplayer_wait.cpp \
|
||||
multiplayer_connect.cpp \
|
||||
multiplayer_create.cpp \
|
||||
multiplayer_lobby.cpp \
|
||||
network.cpp \
|
||||
network_worker.cpp \
|
||||
|
@ -140,6 +143,7 @@ wesnoth_SOURCES = about.cpp \
|
|||
intro.hpp \
|
||||
key.hpp \
|
||||
language.hpp \
|
||||
leader_list.hpp \
|
||||
log.hpp \
|
||||
map.hpp \
|
||||
map_label.hpp \
|
||||
|
@ -147,8 +151,10 @@ wesnoth_SOURCES = about.cpp \
|
|||
mapgen_dialog.hpp \
|
||||
mouse.hpp \
|
||||
multiplayer.hpp \
|
||||
multiplayer_client.hpp \
|
||||
multiplayer_ui.hpp \
|
||||
multiplayer_wait.hpp \
|
||||
multiplayer_connect.hpp \
|
||||
multiplayer_create.hpp \
|
||||
multiplayer_lobby.hpp \
|
||||
network.hpp \
|
||||
network_worker.hpp \
|
||||
|
|
16
src/game.cpp
16
src/game.cpp
|
@ -35,7 +35,6 @@
|
|||
#include "log.hpp"
|
||||
#include "mapgen.hpp"
|
||||
#include "multiplayer.hpp"
|
||||
#include "multiplayer_client.hpp"
|
||||
#include "network.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "playlevel.hpp"
|
||||
|
@ -1302,25 +1301,20 @@ bool game_controller::play_multiplayer()
|
|||
std::vector<std::string> chat;
|
||||
config game_data;
|
||||
|
||||
const std::string controller = (res == 2 ? "network" : (res == 3 ? "human" : "ai"));
|
||||
const mp::controller cntr = (res == 2 ?
|
||||
mp::CNTR_NETWORK :
|
||||
(res == 3 ? mp::CNTR_LOCAL : mp::CNTR_COMPUTER ));
|
||||
const bool is_server = res == 2;
|
||||
|
||||
multiplayer_game_setup_dialog mp_dialog(disp(),units_data_,game_config_,state_,is_server,controller);
|
||||
lobby::RESULT res = lobby::CONTINUE;
|
||||
while(res == lobby::CONTINUE) {
|
||||
res = lobby::enter(disp(),game_data,game_config_,&mp_dialog,chat);
|
||||
}
|
||||
mp::start_server(disp(), game_config_, units_data_, cntr, is_server);
|
||||
|
||||
if(res == lobby::CREATE) {
|
||||
mp_dialog.start_game();
|
||||
}
|
||||
} else if(res == 0 || res == 1) {
|
||||
std::string host;
|
||||
if(res == 0) {
|
||||
host = preferences::official_network_host();
|
||||
}
|
||||
|
||||
play_multiplayer_client(disp(),units_data_,game_config_,state_,host);
|
||||
mp::start_client(disp(), game_config_, units_data_, host);
|
||||
}
|
||||
} catch(gamestatus::load_game_failed& e) {
|
||||
gui::show_error_message(disp(), _("The game could not be loaded: ") + e.message);
|
||||
|
|
|
@ -128,7 +128,7 @@ struct player_info
|
|||
|
||||
//object which holds all the data needed to start a scenario.
|
||||
//i.e. this is the object serialized to disk when saving/loading a game.
|
||||
//is also the object which needs to be created to start a nwe game
|
||||
//is also the object which needs to be created to start a new game
|
||||
struct game_state
|
||||
{
|
||||
game_state() : difficulty("NORMAL") {}
|
||||
|
|
127
src/leader_list.cpp
Normal file
127
src/leader_list.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "leader_list.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
|
||||
leader_list_manager::leader_list_manager(const config::child_list& side_list,
|
||||
const game_data* data, gui::combo* combo) :
|
||||
side_list_(side_list), data_(data), combo_(combo)
|
||||
{
|
||||
}
|
||||
|
||||
void leader_list_manager::set_combo(gui::combo* combo)
|
||||
{
|
||||
combo_ = combo;
|
||||
|
||||
if (combo_ != NULL) {
|
||||
update_leader_list(0);
|
||||
}
|
||||
}
|
||||
|
||||
void leader_list_manager::update_leader_list(int side_index)
|
||||
{
|
||||
const config& side = *side_list_[side_index];
|
||||
|
||||
leaders_.clear();
|
||||
|
||||
if(side["random_faction"] == "yes") {
|
||||
if(combo_ != NULL) {
|
||||
std::vector<std::string> dummy;
|
||||
dummy.push_back("-");
|
||||
combo_->enable(false);
|
||||
combo_->set_items(dummy);
|
||||
combo_->set_selected(0);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if(combo_ != NULL)
|
||||
combo_->enable(true);
|
||||
}
|
||||
|
||||
if(!side["leader"].empty()) {
|
||||
leaders_ = utils::split(side["leader"]);
|
||||
}
|
||||
|
||||
const std::string default_leader = side["type"];
|
||||
size_t default_index = 0;
|
||||
|
||||
std::vector<std::string>::const_iterator itor;
|
||||
|
||||
for (itor = leaders_.begin(); itor != leaders_.end(); ++itor) {
|
||||
if (*itor == default_leader) {
|
||||
break;
|
||||
}
|
||||
default_index++;
|
||||
}
|
||||
|
||||
if (default_index == leaders_.size()) {
|
||||
leaders_.push_back(default_leader);
|
||||
}
|
||||
|
||||
std::vector<std::string> leader_strings;
|
||||
|
||||
for(itor = leaders_.begin(); itor != leaders_.end(); ++itor) {
|
||||
|
||||
const game_data::unit_type_map& utypes = data_->unit_types;
|
||||
|
||||
//const std::string name = data_->unit_types->find(*itor).language_name();
|
||||
if (utypes.find(*itor) != utypes.end()) {
|
||||
const std::string name = utypes.find(*itor)->second.language_name();
|
||||
const std::string image = utypes.find(*itor)->second.image();
|
||||
|
||||
leader_strings.push_back(IMAGE_PREFIX + image + COLUMN_SEPARATOR + name);
|
||||
} else {
|
||||
leader_strings.push_back("?");
|
||||
}
|
||||
}
|
||||
|
||||
leaders_.push_back("random");
|
||||
// FIXME: Maybe this should not code into the code.
|
||||
leader_strings.push_back(IMAGE_PREFIX + std::string("random-enemy.png") +
|
||||
COLUMN_SEPARATOR + _("Random"));
|
||||
|
||||
if(combo_ != NULL) {
|
||||
combo_->set_items(leader_strings);
|
||||
combo_->set_selected(default_index);
|
||||
}
|
||||
}
|
||||
|
||||
void leader_list_manager::set_leader(const std::string& leader)
|
||||
{
|
||||
if(combo_ == NULL)
|
||||
return;
|
||||
|
||||
int leader_index = 0;
|
||||
for(std::vector<std::string>::const_iterator itor = leaders_.begin();
|
||||
itor != leaders_.end(); ++itor) {
|
||||
if (leader == *itor)
|
||||
combo_->set_selected(leader_index);
|
||||
++leader_index;
|
||||
}
|
||||
}
|
||||
|
||||
std::string leader_list_manager::get_leader() const
|
||||
{
|
||||
if(combo_ == NULL)
|
||||
return _("?");
|
||||
|
||||
if(leaders_.empty())
|
||||
return "random";
|
||||
|
||||
if(size_t(combo_->selected()) >= leaders_.size())
|
||||
return _("?");
|
||||
|
||||
return leaders_[combo_->selected()];
|
||||
}
|
||||
|
43
src/leader_list.hpp
Normal file
43
src/leader_list.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef LEADER_LIST_HPP_INCLUDED
|
||||
#define LEADER_LIST_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include <string>
|
||||
|
||||
class leader_list_manager
|
||||
{
|
||||
public:
|
||||
leader_list_manager(const config::child_list& side_list, const game_data* data,
|
||||
gui::combo* combo = NULL);
|
||||
|
||||
void set_combo(gui::combo* combo);
|
||||
void update_leader_list(int side);
|
||||
std::string get_leader() const;
|
||||
void set_leader(const std::string& leader);
|
||||
bool is_leader_ok(std::string leader);
|
||||
|
||||
private:
|
||||
std::vector<std::string> leaders_;
|
||||
config::child_list side_list_;
|
||||
const game_data* data_;
|
||||
gui::combo* combo_;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
|
@ -11,593 +11,335 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "cursor.hpp"
|
||||
#include "events.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "font.hpp"
|
||||
#include "language.hpp"
|
||||
#include "log.hpp"
|
||||
#include "image.hpp"
|
||||
#include "key.hpp"
|
||||
#include "log.hpp"
|
||||
#include "mapgen.hpp"
|
||||
#include "multiplayer.hpp"
|
||||
#include "multiplayer_ui.hpp"
|
||||
#include "multiplayer_connect.hpp"
|
||||
#include "multiplayer_client.hpp"
|
||||
#include "network.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "multiplayer_wait.hpp"
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "multiplayer_create.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "scoped_resource.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "util.hpp"
|
||||
#include "widgets/textbox.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/slider.hpp"
|
||||
#include "widgets/widget.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "log.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "network.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define LOG_G lg::info(lg::general)
|
||||
#define LOG_NW lg::info(lg::network)
|
||||
#define LOG_DP lg::info(lg::display)
|
||||
#define ERR_CF lg::err(lg::config)
|
||||
|
||||
network_game_manager::~network_game_manager()
|
||||
namespace {
|
||||
|
||||
class network_game_manager
|
||||
{
|
||||
if(network::nconnections() > 0) {
|
||||
LOG_NW << "sending leave_game\n";
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
network::send_data(cfg);
|
||||
LOG_NW << "sent leave_game\n";
|
||||
}
|
||||
}
|
||||
|
||||
multiplayer_game_setup_dialog::multiplayer_game_setup_dialog(
|
||||
display& disp, game_data& units_data,
|
||||
const config& cfg, game_state& state, bool server, const std::string& controller)
|
||||
: disp_(disp), units_data_(units_data), cfg_(cfg), state_(state), server_(server), level_(NULL), map_selection_(-1),
|
||||
maps_menu_(NULL), turns_slider_(NULL), village_gold_slider_(NULL), xp_modifier_slider_(NULL),
|
||||
fog_game_(NULL), shroud_game_(NULL), observers_game_(NULL),
|
||||
cancel_game_(NULL), launch_game_(NULL), regenerate_map_(NULL), generator_settings_(NULL),
|
||||
era_combo_(NULL), vision_combo_(NULL), name_entry_(NULL), generator_(NULL), controller_(controller)
|
||||
{
|
||||
LOG_DP << "setup dialog ctor\n";
|
||||
|
||||
state_.players.clear();
|
||||
state_.variables.clear();
|
||||
|
||||
//build the list of scenarios to play
|
||||
get_files_in_dir(get_user_data_dir() + "/editor/maps",&user_maps_,NULL,FILE_NAME_ONLY);
|
||||
|
||||
map_options_ = user_maps_;
|
||||
|
||||
const config::child_list& levels = cfg.get_children("multiplayer");
|
||||
for(config::child_list::const_iterator j = levels.begin(); j != levels.end(); ++j){
|
||||
map_options_.push_back((**j)["name"]);
|
||||
}
|
||||
|
||||
//add the 'load game' option
|
||||
map_options_.push_back(_("Load Game..."));
|
||||
|
||||
//create the scenarios menu
|
||||
maps_menu_.assign(new gui::menu(disp_,map_options_));
|
||||
maps_menu_->set_numeric_keypress_selection(false);
|
||||
|
||||
turns_slider_.assign(new gui::slider(disp_));
|
||||
turns_slider_->set_min(20);
|
||||
turns_slider_->set_max(100);
|
||||
turns_slider_->set_value(preferences::turns());
|
||||
turns_slider_->set_help_string(_("The maximum turns the game will go for"));
|
||||
|
||||
village_gold_slider_.assign(new gui::slider(disp_));
|
||||
village_gold_slider_->set_min(1);
|
||||
village_gold_slider_->set_max(5);
|
||||
village_gold_slider_->set_value(preferences::village_gold());
|
||||
village_gold_slider_->set_help_string(_("The amount of income each village yields per turn"));
|
||||
|
||||
xp_modifier_slider_.assign(new gui::slider(disp_));
|
||||
xp_modifier_slider_->set_min(25);
|
||||
xp_modifier_slider_->set_max(200);
|
||||
xp_modifier_slider_->set_value(preferences::xp_modifier());
|
||||
xp_modifier_slider_->set_increment(10);
|
||||
xp_modifier_slider_->set_help_string(_("The amount of experience a unit needs to advance"));
|
||||
|
||||
fog_game_.assign(new gui::button(disp_,_("Fog Of War"),gui::button::TYPE_CHECK));
|
||||
fog_game_->set_check(preferences::fog());
|
||||
fog_game_->set_help_string(_("Enemy units cannot be seen unless they are in range of your units"));
|
||||
|
||||
shroud_game_.assign(new gui::button(disp_,_("Shroud"),gui::button::TYPE_CHECK));
|
||||
shroud_game_->set_check(preferences::shroud());
|
||||
shroud_game_->set_help_string(_("The map is unknown until your units explore it"));
|
||||
|
||||
observers_game_.assign(new gui::button(disp_,_("Observers"),gui::button::TYPE_CHECK));
|
||||
observers_game_->set_check(preferences::allow_observers());
|
||||
observers_game_->set_help_string(_("Allow users who are not playing to watch the game"));
|
||||
|
||||
cancel_game_.assign(new gui::button(disp_,_("Cancel")));
|
||||
launch_game_.assign(new gui::button(disp_,_("Ok")));
|
||||
|
||||
regenerate_map_.assign(new gui::button(disp_,_("Regenerate")));
|
||||
|
||||
generator_settings_.assign(new gui::button(disp_,_("Settings...")));
|
||||
|
||||
//The possible vision settings
|
||||
std::vector<std::string> vision_types;
|
||||
vision_types.push_back(_("Share View"));
|
||||
vision_types.push_back(_("Share Maps"));
|
||||
vision_types.push_back(_("Share None"));
|
||||
vision_combo_.assign(new gui::combo(disp_,vision_types));
|
||||
|
||||
//the possible eras to play
|
||||
const config::child_list& era_list = cfg.get_children("era");
|
||||
std::vector<std::string> eras;
|
||||
for(config::child_list::const_iterator er = era_list.begin(); er != era_list.end(); ++er) {
|
||||
eras.push_back((**er)["name"]);
|
||||
}
|
||||
|
||||
if(eras.empty()) {
|
||||
gui::show_dialog(disp_,NULL,"",_("No multiplayer sides."),gui::OK_ONLY);
|
||||
ERR_CF << "no eras found\n";
|
||||
throw config::error("no eras found");
|
||||
}
|
||||
|
||||
era_combo_.assign(new gui::combo(disp_,eras));
|
||||
era_combo_->set_selected(preferences::era());
|
||||
|
||||
LOG_DP << "end setup dialog ctor\n";
|
||||
}
|
||||
|
||||
void multiplayer_game_setup_dialog::set_area(const SDL_Rect& area)
|
||||
{
|
||||
LOG_DP << "setup dialog set_area\n";
|
||||
|
||||
area_ = area;
|
||||
|
||||
map_selection_ = -1;
|
||||
|
||||
// Dialog width and height
|
||||
int width = int(area.w);
|
||||
int height = int(area.h);
|
||||
const int left = area.x;
|
||||
const int top = area.y;
|
||||
const int border_size = 6;
|
||||
const int right = left + width;
|
||||
const int bottom = top + height;
|
||||
|
||||
int xpos = left + border_size;
|
||||
int ypos = top + gui::draw_dialog_title(left,top,&disp_,_("Create Game")).h + border_size;
|
||||
|
||||
//Name Entry
|
||||
ypos += font::draw_text(&disp_,disp_.screen_area(),font::SIZE_SMALL,font::GOOD_COLOUR,
|
||||
_("Name of game:"),xpos,ypos).h + border_size;
|
||||
string_map i18n_symbols;
|
||||
i18n_symbols["login"] = preferences::login();
|
||||
name_entry_.assign(new gui::textbox(disp_,width-20,vgettext("$login's game", i18n_symbols)));
|
||||
name_entry_->set_location(xpos,ypos);
|
||||
name_entry_->set_dirty();
|
||||
|
||||
ypos += name_entry_->location().h + border_size;
|
||||
|
||||
const int minimap_width = 200;
|
||||
|
||||
//the map selection menu goes near the middle of the dialog, to the right of
|
||||
//the minimap
|
||||
const int map_label_height = font::draw_text(&disp_,disp_.screen_area(),font::SIZE_SMALL,font::GOOD_COLOUR,
|
||||
_("Map to play:"),xpos + minimap_width + border_size,ypos).h;
|
||||
|
||||
maps_menu_->set_max_width(area.x + area.w - (xpos + minimap_width) - 250);
|
||||
maps_menu_->set_max_height(area.y + area.h - (ypos + map_label_height));
|
||||
maps_menu_->set_items(map_options_);
|
||||
maps_menu_->set_location(xpos + minimap_width + border_size,ypos + map_label_height + border_size);
|
||||
maps_menu_->move_selection(preferences::map());
|
||||
maps_menu_->set_dirty();
|
||||
|
||||
SDL_Rect rect;
|
||||
|
||||
//the sliders and other options on the right side of the dialog
|
||||
rect.x = xpos + minimap_width + maps_menu_->width() + border_size;
|
||||
rect.y = ypos;
|
||||
rect.w = maximum<int>(0,right - border_size - rect.x);
|
||||
//a font sized "12" isn't necessarily 12 pixel high.
|
||||
rect.h = font::get_max_height(font::SIZE_SMALL);
|
||||
|
||||
turns_restorer_ = surface_restorer(&disp_.video(),rect);
|
||||
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
turns_slider_->set_location(rect);
|
||||
turns_slider_->set_dirty();
|
||||
|
||||
//Village Gold
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
village_gold_restorer_ = surface_restorer(&disp_.video(),rect);
|
||||
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
village_gold_slider_->set_location(rect);
|
||||
village_gold_slider_->set_dirty();
|
||||
|
||||
//Experience Modifier
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
xp_restorer_ = surface_restorer(&disp_.video(),rect);
|
||||
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
xp_modifier_slider_->set_location(rect);
|
||||
xp_modifier_slider_->set_dirty();
|
||||
|
||||
//FOG of war
|
||||
rect.y += rect.h + border_size + 1;
|
||||
|
||||
fog_game_->set_location(rect.x,rect.y);
|
||||
fog_game_->set_dirty();
|
||||
|
||||
rect.y += fog_game_->location().h + border_size + 1;
|
||||
|
||||
//Shroud
|
||||
shroud_game_->set_location(rect.x,rect.y);
|
||||
shroud_game_->set_dirty();
|
||||
|
||||
rect.y += shroud_game_->location().h + border_size;
|
||||
|
||||
//Observers
|
||||
observers_game_->set_location(rect.x,rect.y);
|
||||
observers_game_->set_dirty();
|
||||
|
||||
rect.y += observers_game_->location().h + border_size;
|
||||
|
||||
//Ally shared view settings
|
||||
vision_combo_->set_location(rect.x,rect.y);
|
||||
|
||||
rect.y += vision_combo_->height() + border_size;
|
||||
|
||||
gui::button* left_button = launch_game_;
|
||||
gui::button* right_button = cancel_game_;
|
||||
|
||||
#ifdef OK_BUTTON_ON_RIGHT
|
||||
std::swap(left_button,right_button);
|
||||
#endif
|
||||
|
||||
//Buttons
|
||||
right_button->set_location(right - right_button->width() - gui::ButtonHPadding,bottom - right_button->height() - gui::ButtonVPadding);
|
||||
left_button->set_location(right - right_button->width() - left_button->width() - gui::ButtonHPadding*2,bottom - left_button->height() - gui::ButtonVPadding);
|
||||
|
||||
cancel_game_->set_dirty();
|
||||
launch_game_->set_dirty();
|
||||
|
||||
|
||||
regenerate_map_->set_location(rect.x,rect.y);
|
||||
|
||||
rect.y += regenerate_map_->location().h + border_size;
|
||||
|
||||
generator_settings_->set_location(rect.x,rect.y);
|
||||
|
||||
//player amount number background
|
||||
SDL_Rect player_num_rect = {xpos+minimap_width/2 - 30,ypos+minimap_width,100,25};
|
||||
playernum_restorer_ = surface_restorer(&disp_.video(),player_num_rect);
|
||||
|
||||
SDL_Rect era_rect = {xpos,player_num_rect.y+player_num_rect.h + border_size,50,20};
|
||||
era_rect = font::draw_text(&disp_,era_rect,font::SIZE_SMALL,font::GOOD_COLOUR,_("Era:"),
|
||||
era_rect.x,era_rect.y);
|
||||
|
||||
era_combo_->set_location(era_rect.x+era_rect.w+border_size,era_rect.y);
|
||||
|
||||
SDL_Rect minimap_rect = {xpos,ypos,minimap_width,minimap_width};
|
||||
minimap_restorer_ = surface_restorer(&disp_.video(),minimap_rect);
|
||||
|
||||
name_entry_->hide(false);
|
||||
maps_menu_->hide(false);
|
||||
turns_slider_->hide(false);
|
||||
village_gold_slider_->hide(false);
|
||||
xp_modifier_slider_->hide(false);
|
||||
fog_game_->hide(false);
|
||||
shroud_game_->hide(false);
|
||||
observers_game_->hide(false);
|
||||
vision_combo_->hide(false);
|
||||
right_button->hide(false);
|
||||
left_button->hide(false);
|
||||
regenerate_map_->hide(false);
|
||||
generator_settings_->hide(false);
|
||||
era_combo_->hide(false);
|
||||
|
||||
LOG_DP << "setup dialog end set_area\n";
|
||||
}
|
||||
|
||||
void multiplayer_game_setup_dialog::clear_area()
|
||||
{
|
||||
name_entry_->hide();
|
||||
maps_menu_->hide();
|
||||
turns_slider_->hide();
|
||||
village_gold_slider_->hide();
|
||||
xp_modifier_slider_->hide();
|
||||
fog_game_->hide();
|
||||
shroud_game_->hide();
|
||||
observers_game_->hide();
|
||||
vision_combo_->hide();
|
||||
launch_game_->hide();
|
||||
cancel_game_->hide();
|
||||
regenerate_map_->hide();
|
||||
generator_settings_->hide();
|
||||
era_combo_->hide();
|
||||
}
|
||||
|
||||
lobby::RESULT multiplayer_game_setup_dialog::process()
|
||||
{
|
||||
CKey key;
|
||||
|
||||
name_entry_->process();
|
||||
maps_menu_->process();
|
||||
|
||||
if(cancel_game_->pressed() || key[SDLK_ESCAPE])
|
||||
return lobby::QUIT;
|
||||
|
||||
if(launch_game_->pressed() || maps_menu_->double_clicked()) {
|
||||
if (!name_entry_->text().empty())
|
||||
return lobby::CREATE;
|
||||
else
|
||||
gui::show_dialog(disp_, NULL, "", _("You must enter a name."), gui::OK_ONLY);
|
||||
}
|
||||
|
||||
events::raise_process_event();
|
||||
events::raise_draw_event();
|
||||
SDL_Rect const &screen_area = disp_.screen_area();
|
||||
display *disp = &disp_;
|
||||
|
||||
//Turns per game
|
||||
public:
|
||||
~network_game_manager()
|
||||
{
|
||||
const int cur_turns = turns_slider_->value();
|
||||
turns_restorer_.restore();
|
||||
if(network::nconnections() > 0) {
|
||||
LOG_NW << "sending leave_game\n";
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
network::send_data(cfg);
|
||||
LOG_NW << "sent leave_game\n";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::stringstream turns_str;
|
||||
turns_str << _("Turns: ");
|
||||
if (cur_turns < 100)
|
||||
turns_str << cur_turns;
|
||||
else
|
||||
turns_str << _("unlimited");
|
||||
|
||||
SDL_Rect const &rect = turns_restorer_.area();
|
||||
font::draw_text(disp, screen_area, font::SIZE_SMALL, font::GOOD_COLOUR,
|
||||
turns_str.str(), rect.x, rect.y);
|
||||
void run_lobby_loop(display& disp, mp::ui& ui)
|
||||
{
|
||||
disp.video().modeChanged();
|
||||
bool first = true;
|
||||
|
||||
while (ui.get_result() == mp::ui::CONTINUE) {
|
||||
if (disp.video().modeChanged() || first) {
|
||||
SDL_Rect lobby_pos = { 0, 0, disp.video().getx(), disp.video().gety() };
|
||||
ui.set_location(lobby_pos);
|
||||
first = false;
|
||||
}
|
||||
|
||||
events::raise_process_event();
|
||||
events::raise_draw_event();
|
||||
events::pump();
|
||||
|
||||
ui.process_network();
|
||||
|
||||
disp.video().flip();
|
||||
SDL_Delay(20);
|
||||
}
|
||||
}
|
||||
|
||||
enum server_type {
|
||||
ABORT_SERVER,
|
||||
WESNOTHD_SERVER,
|
||||
SIMPLE_SERVER
|
||||
};
|
||||
|
||||
server_type open_connection(display& disp, const std::string& host)
|
||||
{
|
||||
std::string h = host;
|
||||
|
||||
if(h.empty()) {
|
||||
h = preferences::network_host();
|
||||
const int res = gui::show_dialog(disp, NULL, _("Connect to Host"), "",
|
||||
gui::OK_CANCEL, NULL, NULL,
|
||||
_("Choose host to connect to") + std::string(": "), &h);
|
||||
|
||||
if(res != 0 || h.empty()) {
|
||||
return ABORT_SERVER;
|
||||
}
|
||||
}
|
||||
|
||||
//Villages can produce between 1 and 10 gold a turn
|
||||
{
|
||||
const int village_gold = village_gold_slider_->value();
|
||||
village_gold_restorer_.restore();
|
||||
network::connection sock;
|
||||
|
||||
std::stringstream gold_str;
|
||||
gold_str << _("Village Gold: ") << village_gold;
|
||||
const int pos = h.find_first_of(":");
|
||||
|
||||
if(pos == -1) {
|
||||
sock = network::connect(h);
|
||||
} else {
|
||||
sock = network::connect(h.substr(0,pos),
|
||||
lexical_cast_default<int>(h.substr(pos+1), 15000));
|
||||
}
|
||||
|
||||
SDL_Rect const &rect = village_gold_restorer_.area();
|
||||
font::draw_text(disp, screen_area, font::SIZE_SMALL, font::GOOD_COLOUR,
|
||||
gold_str.str(), rect.x, rect.y);
|
||||
if (!sock) {
|
||||
return ABORT_SERVER;
|
||||
}
|
||||
|
||||
//Experience modifier
|
||||
{
|
||||
const int xpmod = xp_modifier_slider_->value();
|
||||
xp_restorer_.restore();
|
||||
preferences::set_network_host(h);
|
||||
|
||||
config data;
|
||||
network::connection data_res = gui::network_data_dialog(disp,_("Connecting to remote host..."),data);
|
||||
mp::check_response(data_res, data);
|
||||
|
||||
std::stringstream xp_str;
|
||||
xp_str << _("Experience Requirements: ") << xpmod << '%';
|
||||
|
||||
SDL_Rect const &rect = xp_restorer_.area();
|
||||
font::draw_text(disp, screen_area, font::SIZE_SMALL, font::GOOD_COLOUR,
|
||||
xp_str.str(), rect.x, rect.y);
|
||||
const std::string& version = data["version"];
|
||||
if(version.empty() == false && version != game_config::version) {
|
||||
throw network::error("The server requires version '" + version
|
||||
+ "' while you are using version '" + game_config::version + "'");
|
||||
}
|
||||
|
||||
bool map_changed = map_selection_ != maps_menu_->selection();
|
||||
map_selection_ = maps_menu_->selection();
|
||||
//if we got a direction to login
|
||||
if(data.child("mustlogin")) {
|
||||
|
||||
if(map_changed) {
|
||||
generator_.assign(NULL);
|
||||
bool first_time = true;
|
||||
config* error = NULL;
|
||||
|
||||
SDL_Rect minimap_rect = minimap_restorer_.area();
|
||||
tooltips::clear_tooltips(minimap_rect);
|
||||
|
||||
const size_t select = size_t(maps_menu_->selection());
|
||||
|
||||
if(select < user_maps_.size()) {
|
||||
const config* const generic_multiplayer = cfg_.child("generic_multiplayer");
|
||||
if(generic_multiplayer != NULL) {
|
||||
scenario_data_ = *generic_multiplayer;
|
||||
scenario_data_["map_data"] = read_map(user_maps_[select]);
|
||||
level_ = &scenario_data_;
|
||||
do {
|
||||
if(error != NULL) {
|
||||
gui::show_dialog(disp,NULL,"",(*error)["message"],gui::OK_ONLY);
|
||||
}
|
||||
|
||||
} else if(select != maps_menu_->nitems()-1) {
|
||||
const size_t index = select - user_maps_.size();
|
||||
const config::child_list& levels = cfg_.get_children("multiplayer");
|
||||
std::string login = preferences::login();
|
||||
|
||||
if(index < levels.size()) {
|
||||
|
||||
scenario_data_ = *levels[index];
|
||||
level_ = &scenario_data_;
|
||||
|
||||
std::string& map_data = scenario_data_["map_data"];
|
||||
if (map_data.empty() && !scenario_data_["map"].empty()) {
|
||||
map_data = read_map(scenario_data_["map"]);
|
||||
if(!first_time) {
|
||||
const int res = gui::show_dialog(disp, NULL, "",
|
||||
_("You must log in to this server"), gui::OK_CANCEL,
|
||||
NULL, NULL, _("Login") + std::string(": "), &login);
|
||||
if(res != 0 || login.empty()) {
|
||||
return ABORT_SERVER;
|
||||
}
|
||||
|
||||
//if the map should be randomly generated
|
||||
if (!scenario_data_["map_generation"].empty()) {
|
||||
generator_.assign(create_map_generator(scenario_data_["map_generation"],
|
||||
scenario_data_.child("generator")));
|
||||
}
|
||||
|
||||
if (!scenario_data_["description"].empty()) {
|
||||
tooltips::add_tooltip(minimap_rect,scenario_data_["description"]);
|
||||
}
|
||||
preferences::set_login(login);
|
||||
}
|
||||
} else {
|
||||
|
||||
scenario_data_.clear();
|
||||
first_time = false;
|
||||
|
||||
playernum_restorer_.restore();
|
||||
minimap_restorer_.restore();
|
||||
config response;
|
||||
response.add_child("login")["username"] = login;
|
||||
network::send_data(response);
|
||||
|
||||
data_res = network::receive_data(data, 0, 3000);
|
||||
if(!data_res) {
|
||||
throw network::error(_("Connection timed out"));
|
||||
}
|
||||
|
||||
SDL_Rect const &rect = playernum_restorer_.area();
|
||||
font::draw_text(disp, screen_area, font::SIZE_SMALL, font::GOOD_COLOUR,
|
||||
_("Players: ") + std::string(1, '?'), rect.x, rect.y);
|
||||
}
|
||||
LOG_NW << "login response: '" << data.write() << "'\n";
|
||||
|
||||
error = data.child("error");
|
||||
} while(error != NULL);
|
||||
}
|
||||
|
||||
if(generator_ != NULL && generator_->allow_user_config() && generator_settings_->pressed()) {
|
||||
generator_->user_config(disp_);
|
||||
map_changed = true;
|
||||
if (data.child("join_lobby")) {
|
||||
return WESNOTHD_SERVER;
|
||||
} else {
|
||||
return SIMPLE_SERVER;
|
||||
}
|
||||
|
||||
if(generator_ != NULL && level_ != NULL && (map_changed || regenerate_map_->pressed())) {
|
||||
const cursor::setter cursor_setter(cursor::WAIT);
|
||||
}
|
||||
|
||||
//generate the random map
|
||||
scenario_data_ = generator_->create_scenario(std::vector<std::string>());
|
||||
level_ = &scenario_data_;
|
||||
map_changed = true;
|
||||
|
||||
//set the scenario to have placing of sides based on the terrain they prefer
|
||||
(*level_)["modify_placing"] = "true";
|
||||
}
|
||||
// The multiplayer logic consists in 4 screens:
|
||||
//
|
||||
// lobby <-> create <-> connect <-> (game)
|
||||
// <------------> wait <-> (game)
|
||||
//
|
||||
// To each of this screen corresponds a dialog, each dialog being defined in
|
||||
// the multiplayer_(screen) file.
|
||||
//
|
||||
// The functions enter_(screen)_mode are simple functions that take care of
|
||||
// creating the dialogs, then, according to the dialog result, of calling other
|
||||
// of those screen functions.
|
||||
|
||||
//if the map has changed and "Load Map" is not selected
|
||||
if(map_changed && level_ != NULL) {
|
||||
generator_settings_->hide(generator_ == NULL);
|
||||
regenerate_map_->hide(generator_ == NULL);
|
||||
void enter_wait_mode(display& disp, const config& game_config, game_data& data, mp::chat& chat, config& gamelist, bool observe)
|
||||
{
|
||||
mp::ui::result res;
|
||||
config level;
|
||||
game_state state;
|
||||
network_game_manager m;
|
||||
|
||||
const std::string& map_data = (*level_)["map_data"];
|
||||
|
||||
gamemap map(cfg_,map_data);
|
||||
|
||||
//if there are less sides in the configuration than there are starting
|
||||
//positions, then generate the additional sides
|
||||
const int map_positions = map.num_valid_starting_positions();
|
||||
|
||||
for(int pos = level_->get_children("side").size(); pos < map_positions; ++pos) {
|
||||
config& side = level_->add_child("side");
|
||||
side["enemy"] = "1";
|
||||
char buf[50];
|
||||
sprintf(buf,"%d",(pos+1));
|
||||
side["side"] = buf;
|
||||
side["team_name"] = buf;
|
||||
side["canrecruit"] = "1";
|
||||
side["controller"] = "human";
|
||||
}
|
||||
|
||||
//if there are too many sides, remove some
|
||||
while(level_->get_children("side").size() > map_positions) {
|
||||
level_->remove_child("side",level_->get_children("side").size()-1);
|
||||
}
|
||||
|
||||
SDL_Rect minimap_rect = minimap_restorer_.area();
|
||||
const surface mini(image::getMinimap(minimap_rect.w, minimap_rect.h, map, 0));
|
||||
|
||||
if(mini != NULL) {
|
||||
SDL_BlitSurface(mini, NULL, disp_.video().getSurface(), &minimap_rect);
|
||||
update_rect(minimap_rect);
|
||||
{
|
||||
mp::wait ui(disp, game_config, data, chat, gamelist);
|
||||
|
||||
//Display the number of players
|
||||
const int nsides = level_->get_children("side").size();
|
||||
playernum_restorer_.restore();
|
||||
ui.join_game(observe);
|
||||
|
||||
std::stringstream players;
|
||||
players << _("Players: ") << nsides;
|
||||
run_lobby_loop(disp, ui);
|
||||
res = ui.get_result();
|
||||
|
||||
SDL_Rect const &rect = playernum_restorer_.area();
|
||||
font::draw_text(disp, screen_area, font::SIZE_SMALL, font::GOOD_COLOUR,
|
||||
players.str(), rect.x, rect.y);
|
||||
if (res == mp::ui::PLAY) {
|
||||
ui.start_game();
|
||||
level = ui.get_level();
|
||||
state = ui.get_state();
|
||||
}
|
||||
}
|
||||
|
||||
return lobby::CONTINUE;
|
||||
switch (res) {
|
||||
case mp::ui::PLAY:
|
||||
play_level(data, game_config, &level, disp.video(), state, std::vector<config*>());
|
||||
recorder.clear();
|
||||
|
||||
break;
|
||||
case mp::ui::QUIT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void multiplayer_game_setup_dialog::start_game()
|
||||
void enter_connect_mode(display& disp, const config& game_config, game_data& data,
|
||||
mp::chat& chat, config& gamelist, const mp::create::parameters& params,
|
||||
mp::controller default_controller, bool is_server)
|
||||
{
|
||||
LOG_G << "calling start_game()\n";
|
||||
mp::ui::result res;
|
||||
config level;
|
||||
game_state state;
|
||||
const network::manager net_manager;
|
||||
const network::server_manager server_man(15000,server_ ? network::server_manager::TRY_CREATE_SERVER : network::server_manager::NO_SERVER);
|
||||
const network::server_manager serv_manager(15000, is_server ?
|
||||
network::server_manager::TRY_CREATE_SERVER :
|
||||
network::server_manager::NO_SERVER);
|
||||
network_game_manager m;
|
||||
|
||||
if(server_ && server_man.is_running() == false) {
|
||||
gui::show_dialog(disp_,NULL,_("Warning"),_("The game was unable to bind to the port needed to host games over the network. Network players will be unable to connect to this game"),
|
||||
gui::OK_ONLY);
|
||||
{
|
||||
mp::connect ui(disp, game_config, data, chat, gamelist, params, default_controller);
|
||||
run_lobby_loop(disp, ui);
|
||||
|
||||
res = ui.get_result();
|
||||
|
||||
// start_game() updates the parameters to reflect game start,
|
||||
// so it must be called before get_level()
|
||||
if (res == mp::ui::PLAY) {
|
||||
ui.start_game();
|
||||
level = ui.get_level();
|
||||
state = ui.get_state();
|
||||
}
|
||||
}
|
||||
|
||||
turns_restorer_ = surface_restorer();
|
||||
village_gold_restorer_ = surface_restorer();
|
||||
xp_restorer_ = surface_restorer();
|
||||
playernum_restorer_ = surface_restorer();
|
||||
minimap_restorer_ = surface_restorer();
|
||||
switch (res) {
|
||||
case mp::ui::PLAY:
|
||||
play_level(data, game_config, &level, disp.video(), state, std::vector<config*>());
|
||||
recorder.clear();
|
||||
|
||||
std::cerr << "loading connector...\n";
|
||||
//Connector
|
||||
mp_connect connector(disp_, name_entry_->text(), cfg_, units_data_, state_, false, controller_);
|
||||
|
||||
std::cerr << "done loading connector...\n";
|
||||
|
||||
const int turns = turns_slider_->value() < turns_slider_->max_value() ?
|
||||
turns_slider_->value() : -1;
|
||||
|
||||
const config::child_list& era_list = cfg_.get_children("era");
|
||||
|
||||
const int share = vision_combo_->selected();
|
||||
|
||||
//Save values for next game
|
||||
preferences::set_allow_observers(observers_game_->checked());
|
||||
preferences::set_fog(fog_game_->checked());
|
||||
preferences::set_shroud(shroud_game_->checked());
|
||||
preferences::set_turns(turns_slider_->value());
|
||||
preferences::set_village_gold(village_gold_slider_->value());
|
||||
preferences::set_xp_modifier(xp_modifier_slider_->value());
|
||||
preferences::set_era(era_combo_->selected());
|
||||
preferences::set_map(maps_menu_->selection());
|
||||
|
||||
const int res = connector.load_map((*era_list[era_combo_->selected()])["id"],
|
||||
scenario_data_, turns, village_gold_slider_->value(),
|
||||
xp_modifier_slider_->value(), fog_game_->checked(),
|
||||
shroud_game_->checked(), observers_game_->checked(),
|
||||
share == 0, share == 1);
|
||||
if(res == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
name_entry_->set_focus(false);
|
||||
|
||||
//free up widget resources so they're not consumed while the game is on
|
||||
maps_menu_.assign(NULL);
|
||||
turns_slider_.assign(NULL);
|
||||
village_gold_slider_.assign(NULL);
|
||||
xp_modifier_slider_.assign(NULL);
|
||||
fog_game_.assign(NULL);
|
||||
shroud_game_.assign(NULL);
|
||||
observers_game_.assign(NULL);
|
||||
vision_combo_.assign(NULL);
|
||||
cancel_game_.assign(NULL);
|
||||
launch_game_.assign(NULL);
|
||||
regenerate_map_.assign(NULL);
|
||||
generator_settings_.assign(NULL);
|
||||
era_combo_.assign(NULL);
|
||||
name_entry_.assign(NULL);
|
||||
generator_.assign(NULL);
|
||||
|
||||
std::vector<std::string> messages;
|
||||
config game_data;
|
||||
lobby::RESULT result = lobby::CONTINUE;
|
||||
while(result == lobby::CONTINUE) {
|
||||
result = lobby::enter(disp_,game_data,cfg_,&connector,messages);
|
||||
}
|
||||
|
||||
if(result == lobby::CREATE) {
|
||||
connector.start_game();
|
||||
break;
|
||||
case mp::ui::QUIT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void enter_create_mode(display& disp, const config& game_config, game_data& data, mp::chat& chat, config& gamelist, mp::controller default_controller, bool is_server)
|
||||
{
|
||||
mp::ui::result res;
|
||||
mp::create::parameters params;
|
||||
|
||||
{
|
||||
mp::create ui(disp, game_config, chat, gamelist);
|
||||
run_lobby_loop(disp, ui);
|
||||
res = ui.get_result();
|
||||
params = ui.get_parameters();
|
||||
}
|
||||
|
||||
switch (res) {
|
||||
case mp::ui::CREATE:
|
||||
enter_connect_mode(disp, game_config, data, chat, gamelist, params, default_controller, is_server);
|
||||
break;
|
||||
case mp::ui::QUIT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void enter_lobby_mode(display& disp, const config& game_config, game_data& data, mp::chat& chat, config& gamelist)
|
||||
{
|
||||
mp::ui::result res;
|
||||
|
||||
while (true) {
|
||||
{
|
||||
mp::lobby ui(disp, game_config, chat, gamelist);
|
||||
run_lobby_loop(disp, ui);
|
||||
res = ui.get_result();
|
||||
}
|
||||
|
||||
switch (res) {
|
||||
case mp::ui::JOIN:
|
||||
enter_wait_mode(disp, game_config, data, chat, gamelist, false);
|
||||
break;
|
||||
case mp::ui::OBSERVE:
|
||||
enter_wait_mode(disp, game_config, data, chat, gamelist, true);
|
||||
break;
|
||||
case mp::ui::CREATE:
|
||||
try {
|
||||
enter_create_mode(disp, game_config, data, chat, gamelist, mp::CNTR_NETWORK, false);
|
||||
} catch(network::error& error) {
|
||||
if (!error.message.empty())
|
||||
gui::show_error_message(disp, error.message);
|
||||
}
|
||||
break;
|
||||
case mp::ui::QUIT:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace mp {
|
||||
|
||||
void start_server(display& disp, const config& game_config, game_data& data,
|
||||
mp::controller default_controller, bool is_server)
|
||||
{
|
||||
mp::chat chat;
|
||||
config gamelist;
|
||||
preferences::display_manager disp_manager(&disp);
|
||||
|
||||
enter_create_mode(disp, game_config, data, chat, gamelist, default_controller, is_server);
|
||||
}
|
||||
|
||||
void start_client(display& disp, const config& game_config, game_data& data,
|
||||
const std::string host)
|
||||
{
|
||||
const network::manager net_manager;
|
||||
preferences::display_manager disp_manager(&disp);
|
||||
|
||||
mp::chat chat;
|
||||
config gamelist;
|
||||
server_type type = open_connection(disp, host);
|
||||
|
||||
switch(type) {
|
||||
case WESNOTHD_SERVER:
|
||||
enter_lobby_mode(disp, game_config, data, chat, gamelist);
|
||||
break;
|
||||
case SIMPLE_SERVER:
|
||||
enter_wait_mode(disp, game_config, data, chat, gamelist, false);
|
||||
break;
|
||||
case ABORT_SERVER:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
|
@ -13,79 +13,23 @@
|
|||
#ifndef MULTIPLAYER_HPP_INCLUDED
|
||||
#define MULTIPLAYER_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display.hpp"
|
||||
#include "font.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "mapgen.hpp"
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "scoped_resource.hpp"
|
||||
#include "config.hpp"
|
||||
#include "unit_types.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/slider.hpp"
|
||||
#include "widgets/textbox.hpp"
|
||||
namespace mp {
|
||||
|
||||
enum controller { CNTR_NETWORK = 0, CNTR_LOCAL, CNTR_COMPUTER, CNTR_EMPTY, CNTR_LAST };
|
||||
|
||||
//an object which guarantees that when it is destroyed, a 'leave game'
|
||||
//message will be sent to any hosts still connected.
|
||||
struct network_game_manager {
|
||||
network_game_manager() {}
|
||||
~network_game_manager();
|
||||
};
|
||||
/**
|
||||
* This is the main entry points of multiplayer mode.
|
||||
*/
|
||||
void start_server(display& disp, const config& game_config, game_data& data,
|
||||
mp::controller default_controller, bool is_server);
|
||||
|
||||
class multiplayer_game_setup_dialog : public lobby::dialog, public font::floating_label_context
|
||||
{
|
||||
public:
|
||||
multiplayer_game_setup_dialog(display& disp, game_data& units_data,
|
||||
const config& cfg, game_state& state, bool server=false, const std::string& controller="ai");
|
||||
void start_client(display& disp, const config& game_config, game_data& data,
|
||||
const std::string host);
|
||||
|
||||
virtual void set_area(const SDL_Rect& area);
|
||||
virtual void clear_area();
|
||||
|
||||
lobby::RESULT process();
|
||||
|
||||
void start_game();
|
||||
|
||||
private:
|
||||
display& disp_;
|
||||
game_data& units_data_;
|
||||
const config& cfg_;
|
||||
game_state& state_;
|
||||
bool server_;
|
||||
SDL_Rect area_;
|
||||
|
||||
config* level_;
|
||||
|
||||
int map_selection_;
|
||||
|
||||
std::vector<std::string> user_maps_, map_options_;
|
||||
config scenario_data_;
|
||||
|
||||
util::scoped_ptr<gui::menu> maps_menu_;
|
||||
util::scoped_ptr<gui::slider> turns_slider_, village_gold_slider_, xp_modifier_slider_;
|
||||
util::scoped_ptr<gui::button> fog_game_, shroud_game_, observers_game_,
|
||||
cancel_game_, launch_game_,
|
||||
regenerate_map_, generator_settings_;
|
||||
|
||||
util::scoped_ptr<gui::combo> era_combo_, vision_combo_;
|
||||
util::scoped_ptr<gui::textbox> name_entry_;
|
||||
|
||||
util::scoped_ptr<map_generator> generator_;
|
||||
|
||||
surface_restorer turns_restorer_, village_gold_restorer_, xp_restorer_, playernum_restorer_,
|
||||
minimap_restorer_;
|
||||
|
||||
const std::string controller_;
|
||||
};
|
||||
|
||||
//function to host a multiplayer game. If server is true, then the
|
||||
//game will accept connections from foreign hosts. Otherwise it'll
|
||||
//just use existing network connections for players, or the game
|
||||
//is an entirely local game
|
||||
int play_multiplayer(display& disp, game_data& units_data,
|
||||
const config& cfg, game_state& state, bool server=true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,819 +0,0 @@
|
|||
#include "global.hpp"
|
||||
|
||||
#include "language.hpp"
|
||||
#include "log.hpp"
|
||||
#include "multiplayer.hpp"
|
||||
#include "multiplayer_client.hpp"
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "scoped_resource.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "util.hpp"
|
||||
#include "wassert.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define LOG_NW lg::info(lg::network)
|
||||
#define ERR_NW lg::err(lg::network)
|
||||
|
||||
namespace {
|
||||
|
||||
class gamelist_manager : public gui::dialog_action
|
||||
{
|
||||
public:
|
||||
int do_action() {
|
||||
const network::connection res = network::receive_data(cfg);
|
||||
if(res != 0 && get_gamelist() != NULL) {
|
||||
return UPDATED_GAMELIST;
|
||||
} else {
|
||||
return CONTINUE_DIALOG;
|
||||
}
|
||||
}
|
||||
|
||||
const config* get_gamelist() const {
|
||||
return cfg.child("gamelist");
|
||||
}
|
||||
|
||||
~gamelist_manager() {} // Workaround for IRIX Mips CC
|
||||
|
||||
enum { UPDATED_GAMELIST = -3 };
|
||||
|
||||
private:
|
||||
config cfg;
|
||||
};
|
||||
|
||||
void check_response(network::connection res, const config& data)
|
||||
{
|
||||
if(!res) {
|
||||
throw network::error(_("Connection timed out"));
|
||||
}
|
||||
|
||||
const config* err = data.child("error");
|
||||
if(err != NULL) {
|
||||
throw network::error((*err)["message"]);
|
||||
}
|
||||
}
|
||||
|
||||
void receive_gamelist(display& disp, config& data)
|
||||
{
|
||||
for(;;) {
|
||||
|
||||
if(data.child("gamelist")) {
|
||||
break;
|
||||
}
|
||||
|
||||
const network::connection res = gui::network_data_dialog(disp,_("Receiving game list..."),data);
|
||||
check_response(res,data);
|
||||
}
|
||||
}
|
||||
|
||||
class wait_for_start : public lobby::dialog
|
||||
{
|
||||
public:
|
||||
wait_for_start(display& disp, const config& game_config, config& cfg,
|
||||
const game_data& data, int team_num, const std::string& team_name,
|
||||
const std::string& team_leader)
|
||||
: status(START_GAME), got_side(false), team(team_num),
|
||||
name(team_name), leader(team_leader), disp_(disp),
|
||||
sides_(cfg), units_data_(data), game_config_(game_config),
|
||||
cancel_button_(NULL), menu_(NULL)
|
||||
{
|
||||
SDL_Rect empty_rect = {0,0,0,0};
|
||||
area_ = empty_rect;
|
||||
}
|
||||
|
||||
void generate_menu(bool first)
|
||||
{
|
||||
if(area_.h == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> details;
|
||||
const config* const era_cfg = game_config_.find_child("era","id",sides_["era"]);
|
||||
|
||||
const config::child_list& sides = sides_.get_children("side");
|
||||
for(config::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
||||
const config& sd = **s;
|
||||
|
||||
std::string description = sd["description"];
|
||||
std::string side_name = (*(era_cfg->find_child("multiplayer_side", "id", sd["id"])))["name"];
|
||||
std::string leader_type = sd["type"];
|
||||
|
||||
if(first && (s - sides.begin() == size_t(team-1))) {
|
||||
description = preferences::login();
|
||||
side_name = name;
|
||||
leader_type = leader;
|
||||
}
|
||||
|
||||
std::string leader_name;
|
||||
std::string leader_image;
|
||||
const game_data::unit_type_map& utypes = units_data_.unit_types;
|
||||
if (utypes.find(leader_type) != utypes.end()) {
|
||||
leader_name = utypes.find(leader_type)->second.language_name();
|
||||
leader_image = utypes.find(leader_type)->second.image();
|
||||
}
|
||||
if (!leader_image.empty()) {
|
||||
// Dumps the "image" part of the faction name, if any,
|
||||
// to replace it by a picture of the actual leader
|
||||
if(side_name[0] == font::IMAGE) {
|
||||
int p = side_name.find_first_of(COLUMN_SEPARATOR);
|
||||
if(p != std::string::npos && p < side_name.size()) {
|
||||
side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.substr(p+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << description << COLUMN_SEPARATOR << side_name << COLUMN_SEPARATOR;
|
||||
if(!leader_name.empty())
|
||||
str << "(" << leader_name << ")";
|
||||
str << COLUMN_SEPARATOR << sd["gold"] << ' ' << sgettext("unit^Gold")
|
||||
<< COLUMN_SEPARATOR << sd["team_name"];
|
||||
details.push_back(str.str());
|
||||
}
|
||||
|
||||
SDL_Rect rect = area_;
|
||||
rect.h -= 2 * gui::ButtonVPadding;
|
||||
if (cancel_button_)
|
||||
rect.h -= cancel_button_->height();
|
||||
SDL_FillRect(disp_.video().getSurface(),&rect,SDL_MapRGB(disp_.video().getSurface()->format,0,0,0));
|
||||
|
||||
menu_.assign(new gui::menu(disp_, details, false, rect.h));
|
||||
menu_->set_location(rect);
|
||||
}
|
||||
|
||||
void set_area(const SDL_Rect& area) {
|
||||
area_ = area;
|
||||
|
||||
cancel_button_.assign(new gui::button(disp_,_("Cancel")));
|
||||
int y = area.y + area.h - cancel_button_->height() - gui::ButtonVPadding;
|
||||
cancel_button_->set_location(area.x + area.w - cancel_button_->width() - gui::ButtonHPadding, y);
|
||||
|
||||
std::string const text = _("Waiting for game to start...");
|
||||
SDL_Rect rect = font::draw_text(NULL, disp_.screen_area(), font::SIZE_NORMAL,
|
||||
font::NORMAL_COLOUR, text, 0, 0);
|
||||
rect.x = area_.x + gui::ButtonHPadding;
|
||||
rect.y = y + 4;
|
||||
font::draw_text(&disp_, rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, text, rect.x, rect.y);
|
||||
|
||||
generate_menu(true);
|
||||
events::raise_draw_event();
|
||||
}
|
||||
|
||||
void clear_widgets() {
|
||||
cancel_button_.assign(NULL);
|
||||
menu_.assign(NULL);
|
||||
}
|
||||
|
||||
lobby::RESULT process() {
|
||||
if (cancel_button_->pressed())
|
||||
return lobby::QUIT;
|
||||
|
||||
if(menu_ != NULL) {
|
||||
menu_->process();
|
||||
}
|
||||
|
||||
config reply;
|
||||
const network::connection res = network::receive_data(reply);
|
||||
if(res) {
|
||||
LOG_NW << "received data while waiting: " << reply.write() << "\n";
|
||||
const config::child_list& assigns = reply.get_children("reassign_side");
|
||||
for(config::child_list::const_iterator a = assigns.begin(); a != assigns.end(); ++a) {
|
||||
if(lexical_cast_default<int>((**a)["from"]) == team) {
|
||||
team = lexical_cast_default<int>((**a)["to"]);
|
||||
generate_menu(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(reply.values["failed"] == "yes") {
|
||||
status = SIDE_UNAVAILABLE;
|
||||
return lobby::QUIT;
|
||||
} else if(reply["side_secured"].empty() == false) {
|
||||
got_side = true;
|
||||
LOG_NW << "received side secured message\n";
|
||||
} else if(reply.child("start_game")) {
|
||||
LOG_NW << "received start_game message\n";
|
||||
//break out of dialog
|
||||
status = START_GAME;
|
||||
return lobby::CREATE;
|
||||
} else if(reply.child("leave_game")) {
|
||||
status = GAME_CANCELLED;
|
||||
return lobby::QUIT;
|
||||
} else if(reply.child("scenario_diff")) {
|
||||
LOG_NW << "received diff for scenario....applying...\n";
|
||||
sides_.apply_diff(*reply.child("scenario_diff"));
|
||||
generate_menu(false);
|
||||
} else if(reply.child("side")) {
|
||||
sides_ = reply;
|
||||
LOG_NW << "got some sides. Current number of sides = "
|
||||
<< sides_.get_children("side").size() << ","
|
||||
<< reply.get_children("side").size() << "\n";
|
||||
} else {
|
||||
data_.push_back(reply);
|
||||
}
|
||||
}
|
||||
|
||||
return lobby::CONTINUE;
|
||||
}
|
||||
|
||||
enum { START_GAME, GAME_CANCELLED, SIDE_UNAVAILABLE } status;
|
||||
|
||||
bool got_side;
|
||||
int team;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string leader;
|
||||
display& disp_;
|
||||
config& sides_;
|
||||
const game_data& units_data_;
|
||||
const config& game_config_;
|
||||
util::scoped_ptr<gui::button> cancel_button_;
|
||||
util::scoped_ptr<gui::menu> menu_;
|
||||
std::deque<config> data_;
|
||||
|
||||
SDL_Rect area_;
|
||||
|
||||
bool manages_network() const { return true; }
|
||||
bool get_network_data(config& out) {
|
||||
if(data_.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
out = data_.front();
|
||||
data_.pop_front();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
|
||||
game_state& state, std::string& host)
|
||||
{
|
||||
log_scope2(network, "playing multiplayer client");
|
||||
|
||||
const network::manager net_manager;
|
||||
|
||||
if(host.empty()) {
|
||||
host = preferences::network_host();
|
||||
const int res = gui::show_dialog(disp,NULL,_("Connect to Host"),"",
|
||||
gui::OK_CANCEL,NULL,NULL,
|
||||
_("Choose host to connect to") + std::string(": "),&host);
|
||||
if(res != 0 || host.empty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
network::connection sock;
|
||||
|
||||
const int pos = host.find_first_of(":");
|
||||
|
||||
if(pos == -1) {
|
||||
sock = network::connect(host);
|
||||
} else {
|
||||
sock = network::connect(host.substr(0,pos),atoi(host.substr(pos+1).c_str()));
|
||||
}
|
||||
|
||||
config sides, data;
|
||||
|
||||
network::connection data_res = gui::network_data_dialog(disp,_("Connecting to remote host..."),data);
|
||||
check_response(data_res,data);
|
||||
|
||||
preferences::set_network_host(host);
|
||||
|
||||
//if response contained a version number
|
||||
const std::string& version = data["version"];
|
||||
if(version.empty() == false && version != game_config::version) {
|
||||
throw network::error("The server requires version '" + version
|
||||
+ "' while you are using version '" + game_config::version + "'");
|
||||
}
|
||||
|
||||
bool logged_in = false;
|
||||
|
||||
//if we got a direction to login
|
||||
if(data.child("mustlogin")) {
|
||||
|
||||
bool first_time = true;
|
||||
config* error = NULL;
|
||||
|
||||
do {
|
||||
if(error != NULL) {
|
||||
gui::show_dialog(disp,NULL,"",(*error)["message"],gui::OK_ONLY);
|
||||
}
|
||||
|
||||
std::string login = preferences::login();
|
||||
|
||||
if(!first_time) {
|
||||
const int res = gui::show_dialog(disp,NULL,"",
|
||||
_("You must log in to this server"),gui::OK_CANCEL,
|
||||
NULL,NULL,_("Login") + std::string(": "),&login);
|
||||
if(res != 0 || login.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
preferences::set_login(login);
|
||||
}
|
||||
|
||||
first_time = false;
|
||||
|
||||
config response;
|
||||
response.add_child("login")["username"] = login;
|
||||
network::send_data(response);
|
||||
|
||||
data_res = network::receive_data(data,0,3000);
|
||||
if(!data_res) {
|
||||
throw network::error(_("Connection timed out"));
|
||||
}
|
||||
|
||||
LOG_NW << "login response: '" << data.write() << "'\n";
|
||||
|
||||
error = data.child("error");
|
||||
} while(error != NULL);
|
||||
|
||||
logged_in = true;
|
||||
}
|
||||
|
||||
for(bool first_time = true; (first_time || logged_in) && network::nconnections() > 0;
|
||||
first_time = false) {
|
||||
|
||||
if(!first_time) {
|
||||
receive_gamelist(disp,data);
|
||||
}
|
||||
|
||||
LOG_NW << "when receiving gamelist got '" << data.write() << "'\n";
|
||||
|
||||
bool observer = false;
|
||||
|
||||
//if we got a gamelist back - otherwise we have
|
||||
//got a description of the game back
|
||||
const config* const gamelist = data.child("gamelist");
|
||||
if(gamelist != NULL) {
|
||||
config game_data = data;
|
||||
int status = -1;
|
||||
|
||||
std::vector<std::string> chat_messages;
|
||||
while(status == -1) {
|
||||
const lobby::RESULT res = lobby::enter(disp,game_data,cfg,NULL,chat_messages);
|
||||
switch(res) {
|
||||
case lobby::QUIT: {
|
||||
status = 1;
|
||||
return;
|
||||
}
|
||||
case lobby::CREATE: {
|
||||
multiplayer_game_setup_dialog mp_dialog(disp,units_data,cfg,state,false);
|
||||
lobby::RESULT res = lobby::CONTINUE;
|
||||
while(res == lobby::CONTINUE) {
|
||||
res = lobby::enter(disp,game_data,cfg,&mp_dialog,chat_messages);
|
||||
}
|
||||
|
||||
if(res == lobby::CREATE) {
|
||||
mp_dialog.start_game();
|
||||
}
|
||||
|
||||
status = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case lobby::OBSERVE:
|
||||
observer = true;
|
||||
case lobby::JOIN: {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
data_res = gui::network_data_dialog(disp,_("Getting game data..."),sides);
|
||||
if(data_res && sides.child("error")) {
|
||||
gui::show_dialog(disp,NULL,"",(*sides.child("error"))["message"]);
|
||||
break;
|
||||
}
|
||||
|
||||
check_response(data_res,sides);
|
||||
|
||||
//if we have got valid side data
|
||||
if(sides.child("gamelist") == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(sides.child("error")) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
sides = data;
|
||||
}
|
||||
|
||||
//ensure we send a close game message to the server when we are done
|
||||
network_game_manager game_manager;
|
||||
|
||||
const config::child_list& sides_list = sides.get_children("side");
|
||||
|
||||
int team_num = 0;
|
||||
std::string team_name;
|
||||
std::string team_leader;
|
||||
|
||||
if(!observer) {
|
||||
//search for an appropriate vacant slot. If a description is set
|
||||
//(i.e. we're loading from a saved game), then prefer to get the side
|
||||
//with the same description as our login. Otherwise just choose the first
|
||||
//available side.
|
||||
config::child_list::const_iterator side_choice = sides_list.end();
|
||||
int nchoice = -1, n = 1;
|
||||
bool allow_changes = false;
|
||||
std::string default_race;
|
||||
for(config::child_list::const_iterator s = sides_list.begin(); s != sides_list.end(); ++s, ++n) {
|
||||
if((**s)["controller"] == "network" &&
|
||||
(**s)["taken"] != "yes") {
|
||||
if(side_choice == sides_list.end() || (**s)["description"] == preferences::login()) {
|
||||
side_choice = s;
|
||||
nchoice = n;
|
||||
allow_changes = (**s)["allow_changes"] != "no";
|
||||
default_race = (**s)["name"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(side_choice == sides_list.end()) {
|
||||
gui::show_dialog(disp,NULL,"",_("There are no available sides in this game."),gui::OK_ONLY);
|
||||
continue;
|
||||
}
|
||||
|
||||
team_num = nchoice;
|
||||
|
||||
config response;
|
||||
response["side"] = lexical_cast<std::string>(nchoice);
|
||||
response["description"] = preferences::login();
|
||||
|
||||
const std::string& era = sides["era"];
|
||||
|
||||
const config* const era_cfg = cfg.find_child("era","id",era);
|
||||
|
||||
if(era_cfg == NULL) {
|
||||
ERR_NW << "era '" << era << "' not found\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const config::child_list& possible_sides = era_cfg->get_children("multiplayer_side");
|
||||
|
||||
if(possible_sides.empty()) {
|
||||
ERR_NW << "no multiplayer sides found\n";
|
||||
return;
|
||||
}
|
||||
|
||||
size_t choice = 0;
|
||||
|
||||
std::vector<std::string> choices;
|
||||
for(config::child_list::const_iterator side =
|
||||
possible_sides.begin(); side != possible_sides.end(); ++side) {
|
||||
choices.push_back((**side)["name"]);
|
||||
|
||||
if(choices.back() == default_race) {
|
||||
choice = side - possible_sides.begin();
|
||||
}
|
||||
}
|
||||
|
||||
//if the client is allowed to choose their team, instead of having
|
||||
//it set by the server, do that here.
|
||||
std::string leader;
|
||||
|
||||
if(allow_changes) {
|
||||
std::vector<gui::preview_pane* > preview_panes;
|
||||
leader_preview_pane leader_selector(disp, &units_data, possible_sides);
|
||||
preview_panes.push_back(&leader_selector);
|
||||
|
||||
choice = size_t(gui::show_dialog(disp,NULL,"",
|
||||
_("Choose your side:"),gui::OK_ONLY,&choices,&preview_panes));
|
||||
leader = leader_selector.get_selected_leader();
|
||||
}
|
||||
|
||||
wassert(choice < possible_sides.size());
|
||||
|
||||
const config& chosen_side = *possible_sides[choice];
|
||||
response["id"] = chosen_side["id"];
|
||||
team_name = response["name"] = chosen_side["name"];
|
||||
if(leader.empty()) {
|
||||
team_leader = response["type"] = chosen_side["type"];
|
||||
} else {
|
||||
team_leader = response["type"] = leader;
|
||||
}
|
||||
response["random_faction"] = chosen_side["random_faction"];
|
||||
response["recruit"] = chosen_side["recruit"];
|
||||
response["music"] = chosen_side["music"];
|
||||
|
||||
network::send_data(response);
|
||||
}
|
||||
|
||||
wait_for_start waiter(disp,cfg,sides,units_data,team_num,team_name,team_leader);
|
||||
std::vector<std::string> messages;
|
||||
config game_data;
|
||||
lobby::RESULT dialog_res = lobby::CONTINUE;
|
||||
while(dialog_res == lobby::CONTINUE) {
|
||||
dialog_res = lobby::enter(disp,game_data,cfg,&waiter,messages);
|
||||
}
|
||||
|
||||
waiter.clear_widgets();
|
||||
|
||||
if(waiter.status == wait_for_start::GAME_CANCELLED) {
|
||||
gui::show_dialog(disp,NULL,"",_("The game has been cancelled"),
|
||||
gui::OK_ONLY);
|
||||
continue;
|
||||
} else if(waiter.status == wait_for_start::SIDE_UNAVAILABLE) {
|
||||
gui::show_dialog(disp,NULL,"",_("The side you have chosen is no longer available"),
|
||||
gui::OK_ONLY);
|
||||
continue;
|
||||
} else if(dialog_res != lobby::CREATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!observer && !waiter.got_side) {
|
||||
throw network::error("Choice of team unavailable.");
|
||||
}
|
||||
|
||||
team_num = waiter.team;
|
||||
|
||||
//we want to make the network/human players look right from our
|
||||
//perspective
|
||||
{
|
||||
const config::child_list& sides_list = sides.get_children("side");
|
||||
for(config::child_list::const_iterator side = sides_list.begin(); side != sides_list.end(); ++side) {
|
||||
if(team_num-1 == side - sides_list.begin()) {
|
||||
(**side)["controller"] = preferences::client_type();
|
||||
} else if((**side)["controller"] != "null") {
|
||||
(**side)["controller"] = "network";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//any replay data is only temporary and should be removed from
|
||||
//the level data in case we want to save the game later
|
||||
config* const replay_data = sides.child("replay");
|
||||
config replay_data_store;
|
||||
if(replay_data != NULL) {
|
||||
replay_data_store = *replay_data;
|
||||
LOG_NW << "setting replay\n";
|
||||
recorder = replay(replay_data_store);
|
||||
if(!recorder.empty()) {
|
||||
const int res = gui::show_dialog(disp,NULL,
|
||||
"", _("Show replay of game up to save point?"),
|
||||
gui::YES_NO);
|
||||
//if yes, then show the replay, otherwise
|
||||
//skip showing the replay
|
||||
if(res == 0) {
|
||||
sides = state.starting_pos;
|
||||
recorder.set_skip(0);
|
||||
} else {
|
||||
LOG_NW << "skipping...\n";
|
||||
recorder.set_skip(-1);
|
||||
}
|
||||
}
|
||||
|
||||
sides.clear_children("replay");
|
||||
}
|
||||
|
||||
LOG_NW << "starting game\n";
|
||||
|
||||
state.starting_pos = sides;
|
||||
state.snapshot = sides;
|
||||
state.players.clear();
|
||||
|
||||
recorder.set_save_info(state);
|
||||
|
||||
std::vector<config*> story;
|
||||
play_level(units_data,cfg,&sides,disp.video(),state,story);
|
||||
recorder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
leader_list_manager::leader_list_manager(const config::child_list& side_list,
|
||||
const game_data* data, gui::combo* combo) :
|
||||
side_list_(side_list), data_(data), combo_(combo)
|
||||
{
|
||||
#if 0
|
||||
for(config::const_child_iterator itor = side_list.begin(); itor != side_list.end(); ++itor) {
|
||||
std::cerr << "Child: " << *itor << "\n";
|
||||
std::cerr << (**itor).write();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void leader_list_manager::set_combo(gui::combo* combo)
|
||||
{
|
||||
combo_ = combo;
|
||||
|
||||
if (combo_ != NULL) {
|
||||
update_leader_list(0);
|
||||
}
|
||||
}
|
||||
|
||||
void leader_list_manager::update_leader_list(int side_index)
|
||||
{
|
||||
const config& side = *side_list_[side_index];
|
||||
|
||||
leaders_.clear();
|
||||
|
||||
if(side["random_faction"] == "yes") {
|
||||
if(combo_ != NULL) {
|
||||
std::vector<std::string> dummy;
|
||||
dummy.push_back("-");
|
||||
combo_->enable(false);
|
||||
combo_->set_items(dummy);
|
||||
combo_->set_selected(0);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if(combo_ != NULL)
|
||||
combo_->enable(true);
|
||||
}
|
||||
|
||||
if(!side["leader"].empty()) {
|
||||
leaders_ = utils::split(side["leader"]);
|
||||
}
|
||||
|
||||
const std::string default_leader = side["type"];
|
||||
int default_index = 0;
|
||||
|
||||
std::vector<std::string>::const_iterator itor;
|
||||
|
||||
for (itor = leaders_.begin(); itor != leaders_.end(); ++itor) {
|
||||
if (*itor == default_leader) {
|
||||
break;
|
||||
}
|
||||
default_index++;
|
||||
}
|
||||
|
||||
if (default_index == leaders_.size()) {
|
||||
leaders_.push_back(default_leader);
|
||||
}
|
||||
|
||||
std::vector<std::string> leader_strings;
|
||||
|
||||
for(itor = leaders_.begin(); itor != leaders_.end(); ++itor) {
|
||||
|
||||
const game_data::unit_type_map& utypes = data_->unit_types;
|
||||
|
||||
//const std::string name = data_->unit_types->find(*itor).language_name();
|
||||
if (utypes.find(*itor) != utypes.end()) {
|
||||
const std::string name = utypes.find(*itor)->second.language_name();
|
||||
const std::string image = utypes.find(*itor)->second.image();
|
||||
|
||||
leader_strings.push_back(IMAGE_PREFIX + image + COLUMN_SEPARATOR + name);
|
||||
} else {
|
||||
leader_strings.push_back("?");
|
||||
}
|
||||
}
|
||||
|
||||
leaders_.push_back("random");
|
||||
// FIXME: Maybe this should not code into the code.
|
||||
leader_strings.push_back(IMAGE_PREFIX + std::string("random-enemy.png") +
|
||||
COLUMN_SEPARATOR + _("Random"));
|
||||
|
||||
if(combo_ != NULL) {
|
||||
combo_->set_items(leader_strings);
|
||||
combo_->set_selected(default_index);
|
||||
}
|
||||
}
|
||||
|
||||
void leader_list_manager::set_leader(const std::string& leader)
|
||||
{
|
||||
if(combo_ == NULL)
|
||||
return;
|
||||
|
||||
int leader_index = 0;
|
||||
for(std::vector<std::string>::const_iterator itor = leaders_.begin();
|
||||
itor != leaders_.end(); ++itor) {
|
||||
if (leader == *itor) {
|
||||
combo_->set_selected(leader_index);
|
||||
return;
|
||||
}
|
||||
++leader_index;
|
||||
}
|
||||
}
|
||||
|
||||
std::string leader_list_manager::get_leader()
|
||||
{
|
||||
if(combo_ == NULL)
|
||||
return _("?");
|
||||
|
||||
if(combo_->selected() >= leaders_.size())
|
||||
return _("?");
|
||||
|
||||
return leaders_[combo_->selected()];
|
||||
}
|
||||
|
||||
namespace {
|
||||
static const SDL_Rect leader_pane_position = {-260,-370,260,370};
|
||||
static const int leader_pane_border = 10;
|
||||
}
|
||||
|
||||
leader_preview_pane::leader_preview_pane(display& disp, const game_data* data,
|
||||
const config::child_list& side_list) :
|
||||
gui::preview_pane(disp),
|
||||
side_list_(side_list),
|
||||
leader_combo_(disp, std::vector<std::string>()),
|
||||
leaders_(side_list, data, &leader_combo_),
|
||||
selection_(0), data_(data)
|
||||
{
|
||||
|
||||
set_location(leader_pane_position);
|
||||
}
|
||||
|
||||
void leader_preview_pane::process_event()
|
||||
{
|
||||
if (leader_combo_.changed()) {
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void leader_preview_pane::draw_contents()
|
||||
{
|
||||
bg_restore();
|
||||
|
||||
surface const screen = disp().video().getSurface();
|
||||
|
||||
SDL_Rect const &loc = location();
|
||||
const SDL_Rect area = { loc.x + leader_pane_border, loc.y + leader_pane_border,
|
||||
loc.w - leader_pane_border * 2, loc.h - leader_pane_border * 2 };
|
||||
SDL_Rect clip_area = area;
|
||||
const clip_rect_setter clipper(screen,clip_area);
|
||||
|
||||
if(selection_ < side_list_.size()) {
|
||||
const config& side = *side_list_[selection_];
|
||||
std::string faction = side["name"];
|
||||
const std::string recruits = side["recruit"];
|
||||
const std::vector<std::string> recruit_list = utils::split(recruits);
|
||||
std::ostringstream recruit_string;
|
||||
|
||||
if(faction[0] == font::IMAGE) {
|
||||
int p = faction.find_first_of(COLUMN_SEPARATOR);
|
||||
if(p != std::string::npos && p < faction.size())
|
||||
faction = faction.substr(p+1);
|
||||
}
|
||||
std::string leader = leaders_.get_leader();
|
||||
|
||||
const game_data::unit_type_map& utypes = data_->unit_types;
|
||||
std::string leader_name;
|
||||
std::string image;
|
||||
|
||||
if (utypes.find(leader) != utypes.end()) {
|
||||
leader_name = utypes.find(leader)->second.language_name();
|
||||
image = utypes.find(leader)->second.image();
|
||||
}
|
||||
for(std::vector<std::string>::const_iterator itor = recruit_list.begin();
|
||||
itor != recruit_list.end(); ++itor) {
|
||||
|
||||
if (utypes.find(*itor) != utypes.end()) {
|
||||
if(itor != recruit_list.begin())
|
||||
recruit_string << ", ";
|
||||
recruit_string << utypes.find(*itor)->second.language_name();
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect image_rect = {area.x,area.y,0,0};
|
||||
|
||||
surface unit_image(image::get_image(image, image::UNSCALED));
|
||||
|
||||
if(!unit_image.null()) {
|
||||
image_rect.w = unit_image->w;
|
||||
image_rect.h = unit_image->h;
|
||||
SDL_BlitSurface(unit_image,NULL,screen,&image_rect);
|
||||
}
|
||||
|
||||
font::draw_text(&disp(),area,font::SIZE_PLUS,font::NORMAL_COLOUR,faction,area.x + 80, area.y + 30);
|
||||
const SDL_Rect leader_rect = font::draw_text(&disp(),area,font::SIZE_SMALL,font::NORMAL_COLOUR,
|
||||
_("Leader: "),area.x, area.y + 80);
|
||||
font::draw_wrapped_text(&disp(),area,font::SIZE_SMALL,font::NORMAL_COLOUR,
|
||||
_("Recruits: ") + recruit_string.str(),area.x, area.y + 102,
|
||||
area.w);
|
||||
|
||||
leader_combo_.set_location(leader_rect.x + leader_rect.w + 10, leader_rect.y + (leader_rect.h - leader_combo_.height()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool leader_preview_pane::show_above() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool leader_preview_pane::left_side() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void leader_preview_pane::set_selection(int selection)
|
||||
{
|
||||
selection_ = selection;
|
||||
leaders_.update_leader_list(selection_);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
std::string leader_preview_pane::get_selected_leader()
|
||||
{
|
||||
return leaders_.get_leader();
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#ifndef MULTIPLAYER_CLIENT_HPP_INCLUDED
|
||||
#define MULTIPLAYER_CLIENT_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "network.hpp"
|
||||
#include "unit_types.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
|
||||
//function to play a game as a client, joining either another player who is
|
||||
//hosting a game, or connecting to a server.
|
||||
void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
|
||||
game_state& state, std::string& host);
|
||||
|
||||
|
||||
class leader_list_manager
|
||||
{
|
||||
public:
|
||||
leader_list_manager(const config::child_list& side_list, const game_data* data,
|
||||
gui::combo* combo = NULL);
|
||||
|
||||
void set_combo(gui::combo* combo);
|
||||
void update_leader_list(int side);
|
||||
std::string get_leader();
|
||||
void set_leader(const std::string& leader);
|
||||
bool is_leader_ok(std::string leader);
|
||||
|
||||
private:
|
||||
std::vector<std::string> leaders_;
|
||||
config::child_list side_list_;
|
||||
const game_data* data_;
|
||||
gui::combo* combo_;
|
||||
|
||||
};
|
||||
|
||||
class leader_preview_pane : public gui::preview_pane
|
||||
{
|
||||
public:
|
||||
leader_preview_pane(display& disp, const game_data* data,
|
||||
const config::child_list& side_list);
|
||||
|
||||
bool show_above() const;
|
||||
bool left_side() const;
|
||||
void set_selection(int index);
|
||||
std::string get_selected_leader();
|
||||
|
||||
private:
|
||||
virtual void draw_contents();
|
||||
virtual void process_event();
|
||||
|
||||
const config::child_list side_list_;
|
||||
gui::combo leader_combo_; // Must appear before the leader_list_manager
|
||||
leader_list_manager leaders_;
|
||||
int selection_;
|
||||
const game_data* data_;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
|
@ -13,111 +13,223 @@
|
|||
#ifndef MULTIPLAYER_CONNECT_H_INCLUDED
|
||||
#define MULTIPLAYER_CONNECT_H_INCLUDED
|
||||
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "multiplayer_client.hpp"
|
||||
#include "network.hpp"
|
||||
#include "widgets/textbox.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/slider.hpp"
|
||||
#include "widgets/scrollpane.hpp"
|
||||
#include "widgets/label.hpp"
|
||||
#include "multiplayer_ui.hpp"
|
||||
#include "multiplayer_create.hpp"
|
||||
#include "config.hpp"
|
||||
#include "network.hpp"
|
||||
#include "leader_list.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class mp_connect : public lobby::dialog
|
||||
namespace mp {
|
||||
|
||||
class connect : public mp::ui
|
||||
{
|
||||
|
||||
public:
|
||||
mp_connect(display& disp, std::string game_name,
|
||||
const config &cfg, game_data& units_data,
|
||||
game_state& state, bool join = false,
|
||||
const std::string& default_controller="ai");
|
||||
~mp_connect();
|
||||
struct connected_user {
|
||||
connected_user(const std::string& name, mp::controller controller,
|
||||
network::connection connection) :
|
||||
name(name), controller(controller), connection(connection)
|
||||
{};
|
||||
std::string name;
|
||||
mp::controller controller;
|
||||
network::connection connection;
|
||||
};
|
||||
|
||||
int load_map(const std::string& era, config& scenario, int num_turns, int village_gold, int xpmodifier,
|
||||
bool fog_game, bool shroud_game, bool allow_observers, bool share_view, bool share_maps);
|
||||
typedef std::vector<connected_user> connected_user_list;
|
||||
|
||||
class side {
|
||||
public:
|
||||
side(connect& parent, const config& cfg,
|
||||
int index, int default_gold,
|
||||
bool enabled = true);
|
||||
|
||||
side(const side& a);
|
||||
|
||||
void add_widgets_to_scrollpane(gui::scrollpane& pane, int pos);
|
||||
|
||||
void process_event();
|
||||
|
||||
// Returns true if this side changed since last call to changed()
|
||||
bool changed();
|
||||
|
||||
// Gets a config object representing this side. If
|
||||
// include_leader is set to true, the config objects include
|
||||
// the "type=" defining the leader type, else it does not.
|
||||
config get_config() const;
|
||||
|
||||
// Returns true if this side is waiting for a network player.
|
||||
bool available() const;
|
||||
|
||||
// Sets the controller of a side.
|
||||
void set_controller(mp::controller controller);
|
||||
mp::controller get_controller() const;
|
||||
|
||||
// Adds an user to the user list combo
|
||||
void update_user_list();
|
||||
|
||||
// Returns the username of this side
|
||||
const std::string& get_id() const;
|
||||
// Sets the username of this side
|
||||
void set_id(const std::string& id);
|
||||
|
||||
const std::string& get_original_description() const;
|
||||
|
||||
// Imports data from the network into this side, and updates
|
||||
// the UI accordingly.
|
||||
void import_network_user(const config& data);
|
||||
|
||||
// Resets this side to its default state, and updates the UI
|
||||
// accordingly.
|
||||
void reset(mp::controller controller);
|
||||
|
||||
// Resolves the random leader / factions.
|
||||
void resolve_random();
|
||||
|
||||
private:
|
||||
void update_controller_ui();
|
||||
void update_ui();
|
||||
|
||||
// The mp::connect widget owning this mp::connect::side. Used
|
||||
// in the constructor, must be first.
|
||||
connect* parent_;
|
||||
|
||||
config cfg_;
|
||||
|
||||
// Configurable variables
|
||||
int index_;
|
||||
std::string id_;
|
||||
std::string original_description_;
|
||||
mp::controller controller_;
|
||||
int faction_;
|
||||
int team_;
|
||||
int colour_;
|
||||
int gold_;
|
||||
std::string leader_;
|
||||
//bool taken_;
|
||||
|
||||
// Widgets for this side
|
||||
gui::label player_number_;
|
||||
gui::combo combo_controller_;
|
||||
gui::label orig_controller_;
|
||||
gui::combo combo_faction_;
|
||||
gui::combo combo_leader_;
|
||||
gui::combo combo_team_;
|
||||
gui::combo combo_colour_;
|
||||
gui::slider slider_gold_;
|
||||
gui::label label_gold_;
|
||||
|
||||
leader_list_manager llm_;
|
||||
|
||||
bool enabled_;
|
||||
bool changed_;
|
||||
};
|
||||
typedef std::vector<side> side_list;
|
||||
|
||||
|
||||
connect(display& disp, const config& game_config, const game_data& data,
|
||||
chat& c, config& gamelist, const create::parameters& params,
|
||||
mp::controller default_controller);
|
||||
|
||||
virtual void process_event();
|
||||
|
||||
// Returns the level data useful to play the game
|
||||
const config& get_level();
|
||||
const game_state& get_state();
|
||||
|
||||
// Updates the current level, resolves random factions, and sends a
|
||||
// "start game" message to the network, with the current level
|
||||
void start_game();
|
||||
|
||||
protected:
|
||||
virtual void layout_children(const SDL_Rect& rect);
|
||||
|
||||
virtual void process_network_data(const config& data, const network::connection sock);
|
||||
virtual void process_network_error(network::error& error);
|
||||
virtual bool accept_connections();
|
||||
virtual void process_network_connection(const network::connection sock);
|
||||
|
||||
virtual void hide_children(bool hide=true);
|
||||
|
||||
virtual void gamelist_updated();
|
||||
private:
|
||||
virtual void set_area(const SDL_Rect& rect);
|
||||
virtual void clear_area();
|
||||
// Those 2 functions are actually the steps of the (complex)
|
||||
// construction of this class. First, initialize default lists (for
|
||||
// colours, sides, etc), then, load the game set the widgets values to
|
||||
// the values given a the game creation step.
|
||||
void lists_init(bool changes_allowes);
|
||||
void load_game(const create::parameters& params);
|
||||
|
||||
lobby::RESULT process();
|
||||
bool manages_network() const { return true; }
|
||||
bool get_network_data(config& cfg);
|
||||
// Updates the level_ variable to reflect the sides in the sides_ vector
|
||||
void update_level();
|
||||
|
||||
void lists_init();
|
||||
void gui_update();
|
||||
void add_player(const std::string& name);
|
||||
void remove_player(const std::string& name);
|
||||
void update_positions();
|
||||
void update_network();
|
||||
bool is_full();
|
||||
// Returns true if there still are sides available for this game
|
||||
bool sides_available();
|
||||
|
||||
// Updates the state of the player list, the launch button and of the
|
||||
// start game label, to reflect the actual state.
|
||||
void update_playerlist_state();
|
||||
|
||||
size_t combo_index_to_team(size_t index) const;
|
||||
// Returns the index of a player, from its id, or -1 if the player was not found
|
||||
connected_user_list::iterator find_player(const std::string& id);
|
||||
|
||||
display *disp_;
|
||||
// Returns the side which is taken by a given player, or -1 if none was found.
|
||||
int find_player_side(const std::string& id) const;
|
||||
|
||||
// Adds a player
|
||||
void update_user_combos();
|
||||
|
||||
// Removes a player and kicks it from the game
|
||||
void kick_player(const std::string& name);
|
||||
|
||||
// This is the main, and global, game data.
|
||||
const game_data& game_data_;
|
||||
|
||||
// This is the configuration object which represents the level which
|
||||
// will be generated by configuring this multiplayer game.
|
||||
config level_;
|
||||
|
||||
// This is the "game state" object, which is constructed along with the
|
||||
// "level" object
|
||||
game_state state_;
|
||||
|
||||
// The era used for factions
|
||||
std::string era_;
|
||||
|
||||
const config *cfg_;
|
||||
game_data *data_;
|
||||
game_state *state_;
|
||||
config *level_;
|
||||
|
||||
//the state the scenario is in before changes,
|
||||
//so that we can generate a diff to send to clients
|
||||
config old_level_;
|
||||
std::map<config*,network::connection> positions_;
|
||||
|
||||
bool show_replay_;
|
||||
bool save_;
|
||||
int status_;
|
||||
bool join_;
|
||||
|
||||
SDL_Rect rect_;
|
||||
// The list of available sides for the current era
|
||||
config::child_list era_sides_;
|
||||
|
||||
// Lists used for combos
|
||||
std::vector<std::string> player_types_;
|
||||
std::vector<std::string> player_races_;
|
||||
std::vector<std::string> player_factions_;
|
||||
std::vector<std::string> player_teams_;
|
||||
std::vector<std::string> player_colors_;
|
||||
std::vector<std::string> player_colours_;
|
||||
|
||||
//std::vector<std::vector<std::string> > player_leaders_;
|
||||
std::vector<leader_list_manager> player_leaders_;
|
||||
std::vector<std::string> possible_faction_ids_;
|
||||
side_list sides_;
|
||||
connected_user_list users_;
|
||||
|
||||
std::vector<std::string> team_names_;
|
||||
std::vector<int> team_indices_;
|
||||
gui::label waiting_label_;
|
||||
bool message_full_;
|
||||
|
||||
controller default_controller_;
|
||||
|
||||
// Widgets
|
||||
gui::scrollpane scroll_pane_;
|
||||
|
||||
std::vector<gui::label> player_numbers_;
|
||||
std::vector<gui::combo> combos_type_;
|
||||
std::vector<gui::combo> combos_race_;
|
||||
std::vector<gui::combo> combos_leader_;
|
||||
std::vector<gui::combo> combos_team_;
|
||||
std::vector<gui::combo> combos_color_;
|
||||
std::vector<gui::slider> sliders_gold_;
|
||||
std::vector<gui::label> labels_gold_;
|
||||
gui::label type_title_label_;
|
||||
gui::label faction_title_label_;
|
||||
gui::label team_title_label_;
|
||||
gui::label colour_title_label_;
|
||||
gui::label gold_title_label_;
|
||||
|
||||
gui::button ai_;
|
||||
gui::button launch_;
|
||||
gui::button cancel_;
|
||||
|
||||
gui::label waiting_label_;
|
||||
bool message_full_;
|
||||
|
||||
std::deque<config> network_data_;
|
||||
|
||||
const std::string default_controller_;
|
||||
const std::string team_prefix_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,453 +1,196 @@
|
|||
#include "global.hpp"
|
||||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
#include "events.hpp"
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "font.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "image.hpp"
|
||||
#include "key.hpp"
|
||||
#include "language.hpp"
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "network.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "wassert.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/textbox.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
namespace mp {
|
||||
|
||||
#include <sstream>
|
||||
lobby::lobby(display& disp, const config& cfg, chat& c, config& gamelist) :
|
||||
mp::ui(disp, cfg, c, gamelist),
|
||||
|
||||
namespace {
|
||||
int xscale(display& disp, int x)
|
||||
observe_game_(disp, _("Observe Game")),
|
||||
join_game_(disp, _("Join Game")),
|
||||
create_game_(disp, _("Create Game")),
|
||||
quit_game_(disp, _("Quit")),
|
||||
games_menu_(disp, std::vector<std::string>()),
|
||||
current_game_(0)
|
||||
{
|
||||
return (x*disp.x())/1024;
|
||||
gamelist_updated();
|
||||
}
|
||||
|
||||
int yscale(display& disp, int y)
|
||||
void lobby::hide_children(bool hide)
|
||||
{
|
||||
return (y*disp.y())/768;
|
||||
mp::ui::hide_children(hide);
|
||||
|
||||
games_menu_.hide(hide);
|
||||
observe_game_.hide(hide);
|
||||
join_game_.hide(hide);
|
||||
create_game_.hide(hide);
|
||||
quit_game_.hide(hide);
|
||||
}
|
||||
|
||||
const config* generate_game_options(const config& terrain_data, const config& game_data, int& game_selection,
|
||||
std::vector<std::string>& options, std::vector<bool>& game_vacant_slots, std::vector<bool>& game_observers)
|
||||
void lobby::layout_children(const SDL_Rect& rect)
|
||||
{
|
||||
const config* const gamelist = game_data.child("gamelist");
|
||||
mp::ui::layout_children(rect);
|
||||
|
||||
// Game List GUI
|
||||
if(gamelist == NULL) {
|
||||
return gamelist;
|
||||
join_game_.set_location(xscale(12),yscale(7));
|
||||
observe_game_.set_location(join_game_.location().x + join_game_.location().w + 5,yscale(7));
|
||||
create_game_.set_location(observe_game_.location().x + observe_game_.location().w + 5,yscale(7));
|
||||
quit_game_.set_location(create_game_.location().x + create_game_.location().w + 5,yscale(7));
|
||||
|
||||
games_menu_.set_width(xscale(832));
|
||||
games_menu_.set_location(xscale(12),yscale(42));
|
||||
}
|
||||
|
||||
void lobby::gamelist_updated()
|
||||
{
|
||||
ui::gamelist_updated();
|
||||
|
||||
std::vector<std::string> game_strings;
|
||||
const config* list = gamelist().child("gamelist");
|
||||
if(list == NULL) {
|
||||
// No gamelist yet. Do not update anything.
|
||||
return;
|
||||
}
|
||||
config::child_list games = list->get_children("game");
|
||||
config::child_iterator game;
|
||||
game_observers_.clear();
|
||||
game_vacant_slots_.clear();
|
||||
|
||||
options.clear();
|
||||
game_vacant_slots.clear();
|
||||
game_observers.clear();
|
||||
|
||||
config::const_child_itors i;
|
||||
for(i = gamelist->child_range("game"); i.first != i.second; ++i.first) {
|
||||
|
||||
std::cerr << "game data here:" << (**i.first).write() << "end game data here\n";
|
||||
for(game = games.begin(); game != games.end(); ++game) {
|
||||
|
||||
std::stringstream str;
|
||||
|
||||
//if this is the item that should be selected, make it selected by default
|
||||
if(game_selection-- == 0) {
|
||||
str << DEFAULT_ITEM;
|
||||
}
|
||||
|
||||
std::string map_data = (**i.first)["map_data"];
|
||||
std::string map_data = (**game)["map_data"];
|
||||
if(map_data == "") {
|
||||
map_data = read_map((**i.first)["map"]);
|
||||
map_data = read_map((**game)["map"]);
|
||||
}
|
||||
|
||||
if(map_data != "") {
|
||||
try {
|
||||
gamemap map(terrain_data,map_data);
|
||||
gamemap map(game_config(), map_data);
|
||||
const surface mini(image::getMinimap(100,100,map,0));
|
||||
|
||||
//generate a unique id to show the map as
|
||||
char buf[50];
|
||||
sprintf(buf,"addr %p", (SDL_Surface*)mini);
|
||||
sprintf(buf,"addr %d",(int)(SDL_Surface*)mini);
|
||||
|
||||
image::register_image(buf,mini);
|
||||
|
||||
str << IMAGE_PREFIX << buf << COLUMN_SEPARATOR;
|
||||
str << "&" << buf << COLUMN_SEPARATOR;
|
||||
} catch(gamemap::incorrect_format_exception& e) {
|
||||
std::cerr << "illegal map: " << e.msg_ << "\n";
|
||||
}
|
||||
} else {
|
||||
str << '(' << _("Shroud") << ')' << COLUMN_SEPARATOR;
|
||||
str << "(" << _("Shroud") << ")" << COLUMN_SEPARATOR;
|
||||
}
|
||||
|
||||
std::string name = (**i.first)["name"];
|
||||
if(name.size() > 30)
|
||||
name.resize(30);
|
||||
std::string name = (**game)["name"];
|
||||
|
||||
str << name;
|
||||
str << font::make_text_ellipsis(name, font::SIZE_NORMAL, xscale(300));
|
||||
|
||||
const std::string& turn = (**i.first)["turn"];
|
||||
const std::string& slots = (**i.first)["slots"];
|
||||
if (!turn.empty()) {
|
||||
str << COLUMN_SEPARATOR << _("Turn") << ' ' << turn;
|
||||
} else if(!slots.empty()) {
|
||||
str << COLUMN_SEPARATOR << slots << ' '
|
||||
<< gettext(slots == "1" ? N_("Vacant Slot") : N_("Vacant Slots"));
|
||||
const std::string& turn = (**game)["turn"];
|
||||
const std::string& slots = (**game)["slots"];
|
||||
int nslots = lexical_cast_default<int>(slots, 0);
|
||||
|
||||
if(turn != "") {
|
||||
str << COLUMN_SEPARATOR << _("Turn") << " " << turn;
|
||||
} else if(slots != "") {
|
||||
str << COLUMN_SEPARATOR << slots << " " <<
|
||||
ngettext(_("Vacant Slot"), _("Vacant Slots"), nslots);
|
||||
}
|
||||
|
||||
options.push_back(str.str());
|
||||
game_vacant_slots.push_back(turn.empty() && slots != "" && slots != "0");
|
||||
game_observers.push_back((**i.first)["observer"] != "no");
|
||||
game_strings.push_back(str.str());
|
||||
|
||||
game_vacant_slots_.push_back(slots != "" && slots != "0");
|
||||
game_observers_.push_back((**game)["observer"] != "no");
|
||||
}
|
||||
|
||||
if(game_strings.empty()) {
|
||||
game_strings.push_back("<no games open>");
|
||||
}
|
||||
|
||||
games_menu_.set_items(game_strings);
|
||||
|
||||
if(games_menu_.selection() >= 0 && games_menu_.selection() < int(game_vacant_slots_.size())) {
|
||||
wassert(game_vacant_slots_.size() == game_observers_.size());
|
||||
|
||||
observe_game_.enable(true);
|
||||
join_game_.enable(true);
|
||||
join_game_.hide(!game_vacant_slots_[games_menu_.selection()]);
|
||||
observe_game_.hide(!game_observers_[games_menu_.selection()]);
|
||||
} else {
|
||||
observe_game_.enable(false);
|
||||
join_game_.enable(false);
|
||||
}
|
||||
|
||||
return gamelist;
|
||||
}
|
||||
|
||||
void generate_user_list(const config& game_data, std::vector<std::string>& users, int& user_selection)
|
||||
void lobby::process_event()
|
||||
{
|
||||
users.clear();
|
||||
games_menu_.process();
|
||||
|
||||
for(config::const_child_itors i = game_data.child_range("user"); i.first != i.second; ++i.first) {
|
||||
std::string name = (**i.first)["name"];
|
||||
if(name.size() > 30)
|
||||
name.resize(30);
|
||||
|
||||
const std::string avail = (**i.first)["available"];
|
||||
|
||||
//display unavailable players in red
|
||||
if(avail == "no") {
|
||||
name.insert(name.begin(),'#');
|
||||
}
|
||||
|
||||
//if this user should be selected
|
||||
if(user_selection-- == 0) {
|
||||
name.insert(name.begin(), DEFAULT_ITEM);
|
||||
}
|
||||
|
||||
users.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace lobby {
|
||||
|
||||
RESULT enter(display& disp, config& game_data, const config& terrain_data, dialog* dlg,
|
||||
std::vector<std::string>& messages)
|
||||
{
|
||||
statistics::fresh_stats();
|
||||
|
||||
std::cerr << "entered multiplayer lobby...\n";
|
||||
const preferences::display_manager disp_manager(&disp);
|
||||
const hotkey::basic_handler key_handler(&disp);
|
||||
const tooltips::manager tooltips_manager(disp);
|
||||
disp.video().modeChanged(); // resets modeChanged value
|
||||
|
||||
CKey key;
|
||||
|
||||
surface background(image::get_image("misc/lobby.png",image::UNSCALED));
|
||||
background.assign(scale_surface(background,disp.x(),disp.y()));
|
||||
|
||||
if(background == NULL) {
|
||||
return QUIT;
|
||||
int selection = games_menu_.selection();
|
||||
if(selection != current_game_ && selection >= 0 && selection < int(game_vacant_slots_.size())) {
|
||||
current_game_ = selection;
|
||||
join_game_.hide(!game_vacant_slots_[selection]);
|
||||
observe_game_.hide(!game_observers_[selection]);
|
||||
}
|
||||
|
||||
SDL_BlitSurface(background, NULL, disp.video().getSurface(), NULL);
|
||||
update_whole_screen();
|
||||
const bool games_available = game_vacant_slots_.empty() == false;
|
||||
const bool double_click = games_menu_.double_clicked();
|
||||
const bool observe = observe_game_.pressed() || !games_available && double_click;
|
||||
|
||||
gui::textbox message_entry(disp,500);
|
||||
if(games_available && (observe || join_game_.pressed() || double_click)) {
|
||||
const size_t index = size_t(games_menu_.selection());
|
||||
const config* game = gamelist().child("gamelist");
|
||||
if (game != NULL) {
|
||||
const config::const_child_itors i = game->child_range("game");
|
||||
wassert(index < size_t(i.second - i.first));
|
||||
const std::string& id = (**(i.first+index))["id"];
|
||||
|
||||
bool last_escape = true;
|
||||
config response;
|
||||
config& join = response.add_child("join");
|
||||
join["id"] = id;
|
||||
network::send_data(response);
|
||||
|
||||
int game_selection = 0, user_selection = 0;
|
||||
|
||||
for(bool first_time = true; ; first_time = false) {
|
||||
message_entry.set_focus(true);
|
||||
|
||||
const SDL_Rect dlg_rect = {xscale(disp,12),yscale(disp,42),xscale(disp,832),yscale(disp,518)};
|
||||
|
||||
//if the dialog is present, back it up before we repaint the entire screen
|
||||
surface_restorer dlg_restorer;
|
||||
if(dlg != NULL && first_time == false) {
|
||||
dlg_restorer = surface_restorer(&disp.video(),dlg_rect);
|
||||
}
|
||||
|
||||
SDL_BlitSurface(background, NULL, disp.video().getSurface(), NULL);
|
||||
|
||||
dlg_restorer.restore();
|
||||
dlg_restorer = surface_restorer();
|
||||
|
||||
const SDL_Rect chat_area = { xscale(disp,12), yscale(disp,576), xscale(disp,832), yscale(disp,142) };
|
||||
|
||||
gui::textbox chat_textbox(disp,chat_area.w,"",false);
|
||||
|
||||
std::vector<std::string> options;
|
||||
std::vector<bool> game_vacant_slots, game_observers;
|
||||
|
||||
const config* gamelist = NULL;
|
||||
|
||||
if(dlg == NULL) {
|
||||
gamelist = generate_game_options(terrain_data,game_data,game_selection,options,game_vacant_slots,game_observers);
|
||||
if(gamelist == NULL) {
|
||||
std::cerr << "ERROR: could not find gamelist\n";
|
||||
return QUIT;
|
||||
}
|
||||
}
|
||||
|
||||
if(dlg == NULL && options.empty()) {
|
||||
options.push_back(_("<no games open>"));
|
||||
}
|
||||
|
||||
gui::menu games_menu(disp,options);
|
||||
gui::button observe_game(disp,_("Observe Game"));
|
||||
gui::button join_game(disp,_("Join Game"));
|
||||
gui::button new_game(disp,_("Create Game"));
|
||||
gui::button quit_game(disp,_("Quit"));
|
||||
|
||||
if(dlg != NULL) {
|
||||
observe_game.hide();
|
||||
join_game.hide();
|
||||
new_game.hide();
|
||||
quit_game.hide();
|
||||
}
|
||||
|
||||
if(games_menu.selection() >= 0 && games_menu.selection() < int(game_vacant_slots.size())) {
|
||||
wassert(game_vacant_slots.size() == game_observers.size());
|
||||
|
||||
join_game.hide(!game_vacant_slots[games_menu.selection()]);
|
||||
observe_game.hide(!game_observers[games_menu.selection()]);
|
||||
} else {
|
||||
observe_game.hide();
|
||||
join_game.hide();
|
||||
}
|
||||
|
||||
std::vector<std::string> users;
|
||||
|
||||
generate_user_list(game_data,users,user_selection);
|
||||
|
||||
if(users.empty()) {
|
||||
users.push_back(preferences::login());
|
||||
}
|
||||
|
||||
std::cerr << "have " << users.size() << " users\n";
|
||||
|
||||
if(users.empty()) {
|
||||
std::cerr << "ERROR: empty user list received\n";
|
||||
users.push_back("error");
|
||||
}
|
||||
|
||||
gui::menu users_menu(disp,users);
|
||||
|
||||
games_menu.set_numeric_keypress_selection(false);
|
||||
users_menu.set_numeric_keypress_selection(false);
|
||||
|
||||
// Set GUI locations
|
||||
users_menu.set_width(xscale(disp,156));
|
||||
users_menu.set_location(xscale(disp,856),yscale(disp,42));
|
||||
|
||||
update_rect(xscale(disp,856),yscale(disp,42),xscale(disp,156),yscale(disp,708));
|
||||
|
||||
if(dlg != NULL) {
|
||||
if(first_time) {
|
||||
dlg->set_area(dlg_rect);
|
||||
}
|
||||
} else {
|
||||
games_menu.set_width(xscale(disp,832));
|
||||
games_menu.set_location(xscale(disp,12),yscale(disp,42));
|
||||
}
|
||||
|
||||
update_rect(xscale(disp,12),yscale(disp,42),xscale(disp,832),yscale(disp,518));
|
||||
join_game.set_location(xscale(disp,12),yscale(disp,7));
|
||||
observe_game.set_location(join_game.location().x + join_game.location().w + 5,yscale(disp,7));
|
||||
new_game.set_location(observe_game.location().x + observe_game.location().w + 5,yscale(disp,7));
|
||||
quit_game.set_location(new_game.location().x + new_game.location().w + 5,yscale(disp,7));
|
||||
message_entry.set_location(xscale(disp,14),yscale(disp,732));
|
||||
message_entry.set_width(xscale(disp,830));
|
||||
|
||||
update_whole_screen();
|
||||
|
||||
bool old_enter = true;
|
||||
|
||||
size_t last_message = messages.size() < 50 ? 0 : messages.size() - 50;
|
||||
|
||||
for(;;) {
|
||||
|
||||
if(last_message < messages.size()) {
|
||||
// Display Chats
|
||||
std::stringstream text;
|
||||
for(; last_message != messages.size(); ++last_message) {
|
||||
text << messages[last_message];
|
||||
if(last_message+1 != messages.size()) {
|
||||
text << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
chat_textbox.append_text(text.str());
|
||||
|
||||
chat_textbox.set_location(chat_area);
|
||||
chat_textbox.set_wrap(true);
|
||||
chat_textbox.scroll_to_bottom();
|
||||
chat_textbox.set_dirty();
|
||||
}
|
||||
|
||||
int mousex, mousey;
|
||||
SDL_GetMouseState(&mousex,&mousey);
|
||||
tooltips::process(mousex, mousey);
|
||||
|
||||
if(dlg != NULL) {
|
||||
const RESULT res = dlg->process();
|
||||
if(res != CONTINUE) {
|
||||
return res;
|
||||
}
|
||||
if (observe) {
|
||||
set_result(OBSERVE);
|
||||
} else {
|
||||
games_menu.process();
|
||||
|
||||
if(games_menu.selection() >= 0 && games_menu.selection() < int(game_vacant_slots.size())) {
|
||||
join_game.hide(!game_vacant_slots[games_menu.selection()]);
|
||||
observe_game.hide(!game_observers[games_menu.selection()]);
|
||||
}
|
||||
set_result(JOIN);
|
||||
}
|
||||
|
||||
users_menu.process();
|
||||
|
||||
const bool games_available = game_vacant_slots.empty() == false;
|
||||
const bool double_click = games_menu.double_clicked();
|
||||
const bool observe = observe_game.pressed() || games_available && double_click && join_game.hidden();
|
||||
if(games_available && (observe || join_game.pressed() || double_click)) {
|
||||
const size_t index = size_t(games_menu.selection());
|
||||
const config::const_child_itors i = gamelist->child_range("game");
|
||||
wassert(index < size_t(i.second - i.first));
|
||||
const std::string& id = (**(i.first+index))["id"];
|
||||
|
||||
config response;
|
||||
config& join = response.add_child("join");
|
||||
join["id"] = id;
|
||||
network::send_data(response);
|
||||
return observe ? OBSERVE : JOIN;
|
||||
}
|
||||
|
||||
if(dlg == NULL && new_game.pressed()) {
|
||||
return CREATE;
|
||||
break;
|
||||
}
|
||||
|
||||
const bool enter = key[SDLK_RETURN] && !old_enter;
|
||||
old_enter = key[SDLK_RETURN] != 0;
|
||||
if(enter && message_entry.text().empty() == false) {
|
||||
const std::string& text = message_entry.text();
|
||||
|
||||
static const std::string query = "/query ";
|
||||
if(text.size() >= query.size() && std::equal(query.begin(),query.end(),text.begin())) {
|
||||
const std::string args = text.substr(query.size());
|
||||
|
||||
config cfg;
|
||||
cfg.add_child("query")["type"] = args;
|
||||
network::send_data(cfg);
|
||||
} else {
|
||||
|
||||
config msg;
|
||||
config& child = msg.add_child("message");
|
||||
child["message"] = text;
|
||||
child["sender"] = preferences::login();
|
||||
network::send_data(msg);
|
||||
|
||||
std::stringstream message;
|
||||
message << "<" << child["sender"] << "> " << child["message"];
|
||||
messages.push_back(message.str());
|
||||
}
|
||||
|
||||
message_entry.clear();
|
||||
}
|
||||
|
||||
if(last_escape == false && key[SDLK_ESCAPE] || dlg == NULL && quit_game.pressed()){
|
||||
return QUIT;
|
||||
}
|
||||
|
||||
last_escape = key[SDLK_ESCAPE] != 0;
|
||||
|
||||
events::raise_process_event();
|
||||
events::raise_draw_event();
|
||||
|
||||
chat_textbox.process();
|
||||
|
||||
user_selection = users_menu.selection();
|
||||
game_selection = games_menu.selection();
|
||||
|
||||
//if the list is refreshed, we want to redraw the entire screen
|
||||
config data;
|
||||
bool got_data = false;
|
||||
if(dlg == NULL || dlg->manages_network() == false) {
|
||||
const network::connection res = network::receive_data(data);
|
||||
if(res) {
|
||||
got_data = true;
|
||||
}
|
||||
} else if(dlg != NULL && dlg->manages_network()) {
|
||||
got_data = dlg->get_network_data(data);
|
||||
}
|
||||
|
||||
if(got_data) {
|
||||
if(data.child("gamelist")) {
|
||||
game_data = data;
|
||||
break;
|
||||
} else if(data.child("gamelist_diff")) {
|
||||
const config old_gamelist = game_data.child("gamelist") != NULL ? *game_data.child("gamelist") : config();
|
||||
|
||||
const int old_users = game_data.get_children("user").size();
|
||||
game_data.apply_diff(*data.child("gamelist_diff"));
|
||||
const int new_users = game_data.get_children("user").size();
|
||||
|
||||
const config new_gamelist = game_data.child("gamelist") != NULL ? *game_data.child("gamelist") : config();
|
||||
if(new_users < old_users) {
|
||||
sound::play_sound(game_config::sounds::user_leave);
|
||||
} else if(new_users > old_users) {
|
||||
sound::play_sound(game_config::sounds::user_arrive);
|
||||
}
|
||||
|
||||
generate_user_list(game_data,users,user_selection);
|
||||
users_menu.set_items(users);
|
||||
|
||||
gamelist = game_data.child("gamelist");
|
||||
if(gamelist == NULL) {
|
||||
std::cerr << "ERROR: could not find game list\n";
|
||||
return QUIT;
|
||||
}
|
||||
|
||||
if(dlg == NULL && *gamelist != old_gamelist) {
|
||||
generate_game_options(terrain_data,game_data,game_selection,options,game_vacant_slots,game_observers);
|
||||
}
|
||||
|
||||
if(dlg == NULL && options.empty()) {
|
||||
options.push_back(_("<no games open>"));
|
||||
}
|
||||
|
||||
games_menu.set_items(options);
|
||||
} else if(data.child("error")) {
|
||||
throw network::error((*data.child("error"))["message"]);
|
||||
} else if(data.child("message")) {
|
||||
sound::play_sound(game_config::sounds::receive_message);
|
||||
|
||||
const config& msg = *data.child("message");
|
||||
std::stringstream message;
|
||||
message << "<" << msg["sender"] << "> " << msg["message"];
|
||||
messages.push_back(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
if(disp.video().modeChanged()) {
|
||||
if (dlg != NULL)
|
||||
dlg->clear_area();
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
events::pump();
|
||||
disp.video().flip();
|
||||
SDL_Delay(20);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(create_game_.pressed()) {
|
||||
set_result(CREATE);
|
||||
return;
|
||||
}
|
||||
|
||||
if(quit_game_.pressed()) {
|
||||
set_result(QUIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lobby::process_network_data(const config& data, const network::connection sock)
|
||||
{
|
||||
ui::process_network_data(data, sock);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,32 +1,55 @@
|
|||
#ifndef MULTIPLAYER_LOBBY_INCLUDED
|
||||
#define MULTIPLAYER_LOBBY_INCLUDED
|
||||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef MULTIPLAYER_LOBBY_HPP_INCLUDED
|
||||
#define MULTIPLAYER_LOBBY_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display.hpp"
|
||||
#include "multiplayer_ui.hpp"
|
||||
|
||||
///this module controls the multiplayer lobby. A section on the server which
|
||||
///allows players to chat, create games, and join games.
|
||||
namespace lobby {
|
||||
#include "widgets/menu.hpp"
|
||||
|
||||
enum RESULT { QUIT, CREATE, JOIN, OBSERVE, CONTINUE };
|
||||
// This module controls the multiplayer lobby. A section on the server which
|
||||
// allows players to chat, create games, and join games.
|
||||
namespace mp {
|
||||
|
||||
///interface for an interactive dialog that is displayed while lobby user lists
|
||||
///and lobby chat continue
|
||||
class dialog
|
||||
class lobby : public ui
|
||||
{
|
||||
public:
|
||||
virtual void set_area(const SDL_Rect& area) = 0;
|
||||
virtual void clear_area() {};
|
||||
virtual RESULT process() = 0;
|
||||
virtual bool manages_network() const { return false; }
|
||||
virtual bool get_network_data(config& out) { return false; }
|
||||
virtual ~dialog() {}
|
||||
};
|
||||
lobby(display& d, const config& cfg, chat& c, config& gamelist);
|
||||
|
||||
///function which controls the lobby, and will result in the player creating
|
||||
///a game, joining a game, or quitting the lobby.
|
||||
RESULT enter(display& disp, config& data, const config& terrain_data, dialog* dlg,
|
||||
std::vector<std::string>& messages);
|
||||
virtual void process_event();
|
||||
|
||||
protected:
|
||||
virtual void hide_children(bool hide=true);
|
||||
virtual void layout_children(const SDL_Rect& rect);
|
||||
virtual void process_network_data(const config& data, const network::connection sock);
|
||||
|
||||
virtual void gamelist_updated();
|
||||
private:
|
||||
|
||||
std::vector<bool> game_vacant_slots_;
|
||||
std::vector<bool> game_observers_;
|
||||
|
||||
gui::button observe_game_;
|
||||
gui::button join_game_;
|
||||
gui::button create_game_;
|
||||
gui::button quit_game_;
|
||||
|
||||
gui::menu games_menu_;
|
||||
int current_game_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
395
src/multiplayer_wait.cpp
Normal file
395
src/multiplayer_wait.cpp
Normal file
|
@ -0,0 +1,395 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "multiplayer_wait.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "log.hpp"
|
||||
#include "wassert.hpp"
|
||||
#include "util.hpp"
|
||||
#include "replay.hpp"
|
||||
|
||||
#define LOG_NW lg::info(lg::network)
|
||||
#define ERR_NW lg::err(lg::network)
|
||||
|
||||
namespace {
|
||||
const SDL_Rect leader_pane_position = {-260,-370,260,370};
|
||||
const int leader_pane_border = 10;
|
||||
}
|
||||
|
||||
namespace mp {
|
||||
|
||||
wait::leader_preview_pane::leader_preview_pane(display& disp, const game_data* data,
|
||||
const config::child_list& side_list) :
|
||||
gui::preview_pane(disp),
|
||||
side_list_(side_list),
|
||||
leader_combo_(disp, std::vector<std::string>()),
|
||||
leaders_(side_list, data, &leader_combo_),
|
||||
selection_(0), data_(data)
|
||||
{
|
||||
set_location(leader_pane_position);
|
||||
}
|
||||
|
||||
void wait::leader_preview_pane::process_event()
|
||||
{
|
||||
if (leader_combo_.changed()) {
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void wait::leader_preview_pane::draw_contents()
|
||||
{
|
||||
bg_restore();
|
||||
|
||||
surface const screen = disp().video().getSurface();
|
||||
|
||||
SDL_Rect const &loc = location();
|
||||
const SDL_Rect area = { loc.x + leader_pane_border, loc.y + leader_pane_border,
|
||||
loc.w - leader_pane_border * 2, loc.h - leader_pane_border * 2 };
|
||||
SDL_Rect clip_area = area;
|
||||
const clip_rect_setter clipper(screen,clip_area);
|
||||
|
||||
if(selection_ < side_list_.size()) {
|
||||
const config& side = *side_list_[selection_];
|
||||
std::string faction = side["name"];
|
||||
const std::string recruits = side["recruit"];
|
||||
const std::vector<std::string> recruit_list = utils::split(recruits);
|
||||
std::ostringstream recruit_string;
|
||||
|
||||
if(faction[0] == font::IMAGE) {
|
||||
std::string::size_type p = faction.find_first_of(COLUMN_SEPARATOR);
|
||||
if(p != std::string::npos && p < faction.size())
|
||||
faction = faction.substr(p+1);
|
||||
}
|
||||
std::string leader = leaders_.get_leader();
|
||||
|
||||
const game_data::unit_type_map& utypes = data_->unit_types;
|
||||
std::string leader_name;
|
||||
std::string image;
|
||||
|
||||
if (utypes.find(leader) != utypes.end()) {
|
||||
leader_name = utypes.find(leader)->second.language_name();
|
||||
image = utypes.find(leader)->second.image();
|
||||
}
|
||||
for(std::vector<std::string>::const_iterator itor = recruit_list.begin();
|
||||
itor != recruit_list.end(); ++itor) {
|
||||
|
||||
if (utypes.find(*itor) != utypes.end()) {
|
||||
if(itor != recruit_list.begin())
|
||||
recruit_string << ", ";
|
||||
recruit_string << utypes.find(*itor)->second.language_name();
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect image_rect = {area.x,area.y,0,0};
|
||||
|
||||
surface unit_image(image::get_image(image, image::UNSCALED));
|
||||
|
||||
if(!unit_image.null()) {
|
||||
image_rect.w = unit_image->w;
|
||||
image_rect.h = unit_image->h;
|
||||
SDL_BlitSurface(unit_image,NULL,screen,&image_rect);
|
||||
}
|
||||
|
||||
font::draw_text(&disp(),area,font::SIZE_PLUS,font::NORMAL_COLOUR,faction,area.x + 80, area.y + 30);
|
||||
const SDL_Rect leader_rect = font::draw_text(&disp(),area,font::SIZE_SMALL,font::NORMAL_COLOUR,
|
||||
_("Leader: "),area.x, area.y + 80);
|
||||
font::draw_wrapped_text(&disp(),area,font::SIZE_SMALL,font::NORMAL_COLOUR,
|
||||
_("Recruits: ") + recruit_string.str(),area.x, area.y + 102,
|
||||
area.w);
|
||||
|
||||
leader_combo_.set_location(leader_rect.x + leader_rect.w + 10, leader_rect.y + (leader_rect.h - leader_combo_.height()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool wait::leader_preview_pane::show_above() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wait::leader_preview_pane::left_side() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void wait::leader_preview_pane::set_selection(int selection)
|
||||
{
|
||||
selection_ = selection;
|
||||
leaders_.update_leader_list(selection_);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
std::string wait::leader_preview_pane::get_selected_leader()
|
||||
{
|
||||
return leaders_.get_leader();
|
||||
}
|
||||
|
||||
wait::wait(display& disp, const config& cfg, const game_data& data, mp::chat& c, config& gamelist) :
|
||||
ui(disp, cfg, c, gamelist),
|
||||
|
||||
cancel_button_(disp, _("Cancel")),
|
||||
start_label_(disp, _("Waiting for game to start...")),
|
||||
game_menu_(disp, std::vector<std::string>()),
|
||||
|
||||
game_data_(data),
|
||||
stop_updates_(false)
|
||||
{
|
||||
gamelist_updated();
|
||||
}
|
||||
|
||||
void wait::process_event()
|
||||
{
|
||||
if (cancel_button_.pressed())
|
||||
set_result(QUIT);
|
||||
}
|
||||
|
||||
void wait::join_game(bool observe)
|
||||
{
|
||||
for(;;) {
|
||||
network::connection data_res = gui::network_data_dialog(disp(),
|
||||
_("Getting game data..."), level_);
|
||||
check_response(data_res, level_);
|
||||
|
||||
//if we have got valid side data
|
||||
if(level_.child("gamelist") == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!observe) {
|
||||
const config::child_list& sides_list = level_.get_children("side");
|
||||
|
||||
if(sides_list.empty()) {
|
||||
set_result(QUIT);
|
||||
throw network::error(_("No multiplayer sides available in this game"));
|
||||
return;
|
||||
}
|
||||
|
||||
//search for an appropriate vacant slot. If a description is set
|
||||
//(i.e. we're loading from a saved game), then prefer to get the side
|
||||
//with the same description as our login. Otherwise just choose the first
|
||||
//available side.
|
||||
int side_choice = 0;
|
||||
for(config::child_list::const_iterator s = sides_list.begin(); s != sides_list.end(); ++s) {
|
||||
if((**s)["controller"] == "network" && (**s)["id"].empty()) {
|
||||
if((**s)["original_description"] == preferences::login()) {
|
||||
side_choice = s - sides_list.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool allow_changes = (*sides_list[side_choice])["allow_changes"] != "no";
|
||||
|
||||
const std::string& era = level_["era"];
|
||||
const config* const era_cfg = game_config().find_child("era","id",era);
|
||||
if(era_cfg == NULL) {
|
||||
set_result(QUIT);
|
||||
throw network::error(_("Era not available"));
|
||||
return;
|
||||
}
|
||||
|
||||
const config::child_list& possible_sides = era_cfg->get_children("multiplayer_side");
|
||||
if(possible_sides.empty()) {
|
||||
set_result(QUIT);
|
||||
throw network::error(_("No multiplayer sides found"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> choices;
|
||||
for(config::child_list::const_iterator side =
|
||||
possible_sides.begin(); side != possible_sides.end(); ++side) {
|
||||
choices.push_back((**side)["name"]);
|
||||
}
|
||||
|
||||
//if the client is allowed to choose their team, instead of having
|
||||
//it set by the server, do that here.
|
||||
std::string leader_choice;
|
||||
size_t faction_choice = 0;
|
||||
|
||||
if(allow_changes) {
|
||||
std::vector<gui::preview_pane* > preview_panes;
|
||||
leader_preview_pane leader_selector(disp(), &game_data_, possible_sides);
|
||||
preview_panes.push_back(&leader_selector);
|
||||
|
||||
faction_choice = size_t(gui::show_dialog(disp(), NULL, "", _("Choose your side:"),
|
||||
gui::OK_ONLY, &choices, &preview_panes));
|
||||
leader_choice = leader_selector.get_selected_leader();
|
||||
}
|
||||
|
||||
wassert(faction_choice < possible_sides.size());
|
||||
//team_ = faction_choice;
|
||||
|
||||
config response;
|
||||
response["side"] = lexical_cast<std::string>(side_choice);
|
||||
response["name"] = preferences::login();
|
||||
response["faction"] = lexical_cast<std::string>(faction_choice);
|
||||
response["leader"] = leader_choice;
|
||||
|
||||
network::send_data(response);
|
||||
}
|
||||
|
||||
generate_menu();
|
||||
}
|
||||
|
||||
const game_state& wait::get_state()
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
const config& wait::get_level()
|
||||
{
|
||||
return level_;
|
||||
}
|
||||
|
||||
void wait::start_game()
|
||||
{
|
||||
const config::child_list& sides_list = level_.get_children("side");
|
||||
for(config::child_list::const_iterator side = sides_list.begin();
|
||||
side != sides_list.end(); ++side) {
|
||||
if((**side)["controller"] == "network" && (**side)["id"] == preferences::login()) {
|
||||
(**side)["controller"] = preferences::client_type();
|
||||
} else if((**side)["controller"] != "null") {
|
||||
(**side)["controller"] = "network";
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << level_.write() << "\n";
|
||||
|
||||
// FIXME: To be reviewed
|
||||
|
||||
//any replay data is only temporary and should be removed from
|
||||
//the level data in case we want to save the game later
|
||||
config* const replay_data = level_.child("replay");
|
||||
config replay_data_store;
|
||||
if(replay_data != NULL) {
|
||||
replay_data_store = *replay_data;
|
||||
LOG_NW << "setting replay\n";
|
||||
recorder = replay(replay_data_store);
|
||||
if(!recorder.empty()) {
|
||||
recorder.set_skip(-1);
|
||||
}
|
||||
|
||||
level_.clear_children("replay");
|
||||
}
|
||||
|
||||
LOG_NW << "starting game\n";
|
||||
state_.campaign_type = "multiplayer";
|
||||
state_.starting_pos = level_;
|
||||
state_.snapshot = level_;
|
||||
state_.players.clear();
|
||||
recorder.set_save_info(state_);
|
||||
}
|
||||
|
||||
void wait::layout_children(const SDL_Rect& rect)
|
||||
{
|
||||
ui::layout_children(rect);
|
||||
|
||||
const SDL_Rect ca = client_area();
|
||||
int y = ca.y + ca.h - cancel_button_.height() - gui::ButtonVPadding;
|
||||
|
||||
game_menu_.set_location(ca.x, ca.y);
|
||||
game_menu_.set_measurements(ca.x + ca.w, y - ca.y - gui::ButtonVPadding);
|
||||
cancel_button_.set_location(ca.x + ca.w - cancel_button_.width() - gui::ButtonHPadding, y);
|
||||
start_label_.set_location(ca.x + gui::ButtonHPadding, y + 4);
|
||||
}
|
||||
|
||||
void wait::hide_children(bool hide)
|
||||
{
|
||||
ui::hide_children(hide);
|
||||
|
||||
cancel_button_.hide(hide);
|
||||
game_menu_.hide(hide);
|
||||
}
|
||||
|
||||
void wait::process_network_data(const config& data, const network::connection sock)
|
||||
{
|
||||
ui::process_network_data(data, sock);
|
||||
|
||||
if(data["failed"] == "yes") {
|
||||
set_result(QUIT);
|
||||
return;
|
||||
} else if(data.child("stop_updates")) {
|
||||
stop_updates_ = true;
|
||||
} else if(data.child("start_game")) {
|
||||
LOG_NW << "received start_game message\n";
|
||||
set_result(PLAY);
|
||||
return;
|
||||
} else if(data.child("leave_game")) {
|
||||
set_result(QUIT);
|
||||
return;
|
||||
} else if(data.child("scenario_diff")) {
|
||||
LOG_NW << "received diff for scenario....applying...\n";
|
||||
level_.apply_diff(*data.child("scenario_diff"));
|
||||
generate_menu();
|
||||
} else if(data.child("side")) {
|
||||
level_ = data;
|
||||
LOG_NW << "got some sides. Current number of sides = "
|
||||
<< level_.get_children("side").size() << ","
|
||||
<< data.get_children("side").size() << "\n";
|
||||
generate_menu();
|
||||
}
|
||||
}
|
||||
|
||||
void wait::generate_menu()
|
||||
{
|
||||
if (stop_updates_)
|
||||
return;
|
||||
|
||||
std::vector<std::string> details;
|
||||
std::vector<std::string> playerlist;
|
||||
|
||||
const config::child_list& sides = level_.get_children("side");
|
||||
for(config::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
||||
const config& sd = **s;
|
||||
|
||||
std::string description = sd["description"];
|
||||
std::string side_name = sd["name"];
|
||||
std::string leader_type = sd["type"];
|
||||
|
||||
if(!sd["id"].empty())
|
||||
playerlist.push_back(sd["id"]);
|
||||
|
||||
std::string leader_name;
|
||||
std::string leader_image;
|
||||
const game_data::unit_type_map& utypes = game_data_.unit_types;
|
||||
if (utypes.find(leader_type) != utypes.end()) {
|
||||
leader_name = utypes.find(leader_type)->second.language_name();
|
||||
leader_image = utypes.find(leader_type)->second.image();
|
||||
}
|
||||
if (!leader_image.empty()) {
|
||||
// Dumps the "image" part of the faction name, if any,
|
||||
// to replace it by a picture of the actual leader
|
||||
if(side_name[0] == font::IMAGE) {
|
||||
std::string::size_type p = side_name.find_first_of(COLUMN_SEPARATOR);
|
||||
if(p != std::string::npos && p < side_name.size()) {
|
||||
side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.substr(p+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << description << COLUMN_SEPARATOR << side_name << COLUMN_SEPARATOR;
|
||||
if(!leader_name.empty())
|
||||
str << "(" << leader_name << ")";
|
||||
str << COLUMN_SEPARATOR << sd["gold"] << ' ' << sgettext("unit^Gold")
|
||||
<< COLUMN_SEPARATOR << sd["team_name"];
|
||||
details.push_back(str.str());
|
||||
}
|
||||
|
||||
game_menu_.set_items(details);
|
||||
|
||||
// Uses the actual connected player list if we do not have any
|
||||
// "gamelist" user data
|
||||
if (gamelist().child("user") != NULL)
|
||||
set_user_list(playerlist);
|
||||
}
|
||||
|
||||
}
|
||||
|
84
src/multiplayer_wait.hpp
Normal file
84
src/multiplayer_wait.hpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C)
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef MULTIPLAYER_WAIT_HPP_INCLUDED
|
||||
#define MULTIPLAYER_WAIT_HPP_INCLUDED
|
||||
|
||||
#include "widgets/label.hpp"
|
||||
#include "widgets/button.hpp"
|
||||
#include "widgets/menu.hpp"
|
||||
#include "widgets/combo.hpp"
|
||||
|
||||
#include "show_dialog.hpp"
|
||||
#include "multiplayer_ui.hpp"
|
||||
#include "leader_list.hpp"
|
||||
|
||||
namespace mp {
|
||||
|
||||
class wait : public ui
|
||||
{
|
||||
public:
|
||||
wait(display& disp, const config& cfg, const game_data& data, chat& c, config& gamelist);
|
||||
virtual void process_event();
|
||||
|
||||
void join_game(bool observe);
|
||||
|
||||
const game_state& get_state();
|
||||
const config& get_level();
|
||||
|
||||
void start_game();
|
||||
|
||||
protected:
|
||||
virtual void layout_children(const SDL_Rect& rect);
|
||||
virtual void hide_children(bool hide=true);
|
||||
virtual void process_network_data(const config& data, const network::connection sock);
|
||||
private:
|
||||
class leader_preview_pane : public gui::preview_pane
|
||||
{
|
||||
public:
|
||||
leader_preview_pane(display& disp, const game_data* data,
|
||||
const config::child_list& side_list);
|
||||
|
||||
bool show_above() const;
|
||||
bool left_side() const;
|
||||
void set_selection(int index);
|
||||
std::string get_selected_leader();
|
||||
|
||||
private:
|
||||
virtual void draw_contents();
|
||||
virtual void process_event();
|
||||
|
||||
const config::child_list side_list_;
|
||||
gui::combo leader_combo_; // Must appear before the leader_list_manager
|
||||
leader_list_manager leaders_;
|
||||
size_t selection_;
|
||||
const game_data* data_;
|
||||
};
|
||||
|
||||
void generate_menu();
|
||||
|
||||
gui::button cancel_button_;
|
||||
gui::label start_label_;
|
||||
gui::menu game_menu_;
|
||||
|
||||
// int team_;
|
||||
const game_data& game_data_;
|
||||
|
||||
config level_;
|
||||
game_state state_;
|
||||
|
||||
bool stop_updates_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -136,6 +136,7 @@ bool game::take_side(network::connection player, const config& cfg)
|
|||
|
||||
const bool res = take_side(player,new_cfg);
|
||||
|
||||
#if 0
|
||||
//if there's another side available, then tell the player that they've
|
||||
//had their side reassigned
|
||||
if(res) {
|
||||
|
@ -145,6 +146,7 @@ bool game::take_side(network::connection player, const config& cfg)
|
|||
reassign["to"] = new_cfg["side"];
|
||||
network::queue_data(response,player);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -162,6 +164,40 @@ bool game::take_side(network::connection player, const config& cfg)
|
|||
return true;
|
||||
}
|
||||
|
||||
void game::update_side_data()
|
||||
{
|
||||
sides_taken_.clear();
|
||||
sides_.clear();
|
||||
|
||||
const config::child_itors level_sides = level_.child_range("side");
|
||||
|
||||
//for each player:
|
||||
// * Find the player name
|
||||
// * Find the side this player name corresponds to
|
||||
for(std::vector<network::connection>::const_iterator player = players_.begin();
|
||||
player != players_.end(); ++player) {
|
||||
|
||||
player_map::const_iterator info = player_info_->find(*player);
|
||||
if (info == player_info_->end()) {
|
||||
std::cerr << "Error: unable to find player info for connection " << *player << "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
config::child_iterator sd;
|
||||
for(sd = level_sides.first; sd != level_sides.second; ++sd) {
|
||||
if ((**sd)["id"] == info->second.name()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sd != level_sides.second) {
|
||||
//this user has a side. Updates the variables accordingly.
|
||||
sides_taken_.insert((**sd)["side"]);
|
||||
sides_[*player] = (**sd)["side"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& game::transfer_side_control(const config& cfg)
|
||||
{
|
||||
const std::string& player = cfg["player"];
|
||||
|
@ -231,16 +267,16 @@ const std::string& game::transfer_side_control(const config& cfg)
|
|||
|
||||
size_t game::available_slots() const
|
||||
{
|
||||
size_t total_slots = 0;
|
||||
size_t available_slots = 0;
|
||||
const config::child_list& sides = level_.get_children("side");
|
||||
for(config::child_list::const_iterator i = sides.begin(); i != sides.end(); ++i) {
|
||||
std::cerr << "side controller: '" << (**i)["controller"] << "'\n";
|
||||
if((**i)["controller"] == "network") {
|
||||
++total_slots;
|
||||
if((**i)["controller"] == "network" && (**i)["id"].empty()) {
|
||||
++available_slots;
|
||||
}
|
||||
}
|
||||
|
||||
return total_slots - sides_taken_.size();
|
||||
return available_slots;
|
||||
}
|
||||
|
||||
bool game::describe_slots()
|
||||
|
@ -378,7 +414,7 @@ void game::add_player(network::connection player)
|
|||
network::queue_data(history_,player);
|
||||
}
|
||||
|
||||
void game::remove_player(network::connection player)
|
||||
void game::remove_player(network::connection player, bool notify_creator)
|
||||
{
|
||||
const std::vector<network::connection>::iterator itor =
|
||||
std::find(players_.begin(),players_.end(),player);
|
||||
|
@ -389,7 +425,7 @@ void game::remove_player(network::connection player)
|
|||
std::map<network::connection,std::string>::iterator side = sides_.find(player);
|
||||
if(side != sides_.end()) {
|
||||
//send the host a notification of removal of this side
|
||||
if(players_.empty() == false) {
|
||||
if(notify_creator && players_.empty() == false) {
|
||||
config drop;
|
||||
drop["side_drop"] = side->second;
|
||||
network::queue_data(drop,players_.front());
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
|
||||
bool take_side(network::connection player, const config& cfg);
|
||||
|
||||
void update_side_data();
|
||||
|
||||
const std::string& transfer_side_control(const config& cfg);
|
||||
|
||||
size_t available_slots() const;
|
||||
|
@ -47,7 +49,7 @@ public:
|
|||
void ban_player(network::connection player);
|
||||
|
||||
void add_player(network::connection player);
|
||||
void remove_player(network::connection player);
|
||||
void remove_player(network::connection player, bool notify_creator=true);
|
||||
|
||||
int id() const;
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ private:
|
|||
const network::server_manager server_;
|
||||
|
||||
config login_response_;
|
||||
config join_lobby_response_;
|
||||
|
||||
config initial_response_;
|
||||
config old_initial_response_;
|
||||
|
@ -109,6 +110,8 @@ server::server(int port, input_stream& input) : net_manager_(5), server_(port),
|
|||
{
|
||||
login_response_.add_child("mustlogin");
|
||||
login_response_["version"] = game_config::version;
|
||||
|
||||
join_lobby_response_.add_child("join_lobby");
|
||||
}
|
||||
|
||||
bool server::is_ip_banned(const std::string& ip)
|
||||
|
@ -285,7 +288,6 @@ void server::run()
|
|||
|
||||
config data;
|
||||
while((sock = network::receive_data(data)) != network::null_connection) {
|
||||
|
||||
metrics_.service_request();
|
||||
process_data(sock,data,gamelist);
|
||||
}
|
||||
|
@ -403,6 +405,8 @@ void server::process_login(const network::connection sock, const config& data, c
|
|||
return;
|
||||
}
|
||||
|
||||
network::send_data(join_lobby_response_, sock);
|
||||
|
||||
config* const player_cfg = &initial_response_.add_child("user");
|
||||
|
||||
const player new_player(username,*player_cfg);
|
||||
|
@ -565,9 +569,19 @@ void server::process_data_from_player_in_game(const network::connection sock, co
|
|||
}
|
||||
|
||||
//if the owner is banning someone from the game
|
||||
if(g->is_owner(sock) && data.child("ban") != NULL) {
|
||||
const config& ban = *data.child("ban");
|
||||
const std::string& name = ban["username"];
|
||||
if(g->is_owner(sock) && (data.child("ban") != NULL || data.child("kick") != NULL)) {
|
||||
std::string name;
|
||||
bool ban;
|
||||
if (data.child("ban") != NULL) {
|
||||
const config& u = *data.child("ban");
|
||||
name = u["username"];
|
||||
ban = true;
|
||||
} else if (data.child("kick") != NULL) {
|
||||
const config& u = *data.child("kick");
|
||||
name = u["username"];
|
||||
ban = false;
|
||||
}
|
||||
|
||||
player_map::iterator pl;
|
||||
for(pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if(pl->second.name() == name) {
|
||||
|
@ -576,9 +590,14 @@ void server::process_data_from_player_in_game(const network::connection sock, co
|
|||
}
|
||||
|
||||
if(pl->first != sock && pl != players_.end()) {
|
||||
g->ban_player(pl->first);
|
||||
const config& msg = construct_server_message("You have been banned",*g);
|
||||
network::send_data(msg,pl->first);
|
||||
if (ban) {
|
||||
g->ban_player(pl->first);
|
||||
const config& msg = construct_server_message("You have been banned",*g);
|
||||
network::send_data(msg, pl->first);
|
||||
} else {
|
||||
g->remove_player(pl->first, false);
|
||||
}
|
||||
|
||||
config leave_game;
|
||||
leave_game.add_child("leave_game");
|
||||
network::send_data(leave_game,pl->first);
|
||||
|
@ -608,6 +627,7 @@ void server::process_data_from_player_in_game(const network::connection sock, co
|
|||
//if this is data describing changes to a game.
|
||||
else if(g->is_owner(sock) && data.child("scenario_diff")) {
|
||||
g->level().apply_diff(*data.child("scenario_diff"));
|
||||
g->update_side_data();
|
||||
g->send_data(data,sock);
|
||||
|
||||
const bool lobby_changes = g->describe_slots();
|
||||
|
@ -643,6 +663,7 @@ void server::process_data_from_player_in_game(const network::connection sock, co
|
|||
|
||||
//record the full description of the scenario in g->level()
|
||||
g->level() = data;
|
||||
g->update_side_data();
|
||||
g->describe_slots();
|
||||
|
||||
//send all players in the lobby the update to the list of games
|
||||
|
@ -652,6 +673,7 @@ void server::process_data_from_player_in_game(const network::connection sock, co
|
|||
//we've already initialized this scenario, but clobber its old
|
||||
//contents with the new ones given here
|
||||
g->level() = data;
|
||||
g->update_side_data();
|
||||
}
|
||||
|
||||
//send all players in the level, except the sender, the new data
|
||||
|
@ -863,7 +885,10 @@ int main(int argc, char** argv)
|
|||
} else if(val == "--help" || val == "-h") {
|
||||
std::cout << "usage: " << argv[0]
|
||||
<< " [options]\n"
|
||||
<< " -p, --port Binds the server to the specified port\n";
|
||||
<< " --fifo file Sets the path for the FIFO used to communicate with the server\n"
|
||||
<< " -m, --max_packet_size n Sets the maximal packet size to n\n"
|
||||
<< " -p, --port Binds the server to the specified port\n"
|
||||
<< " -v, --version Returns the server version\n";
|
||||
return 0;
|
||||
} else if(val == "--version" || val == "-v") {
|
||||
std::cout << "Battle for Wesnoth server " << game_config::version
|
||||
|
|
|
@ -50,6 +50,11 @@ void combo::set_items(const std::vector<std::string>& items)
|
|||
selected_ = -1;
|
||||
}
|
||||
|
||||
size_t combo::items_size() const
|
||||
{
|
||||
return items_.size();
|
||||
}
|
||||
|
||||
void combo::set_selected_internal(int val)
|
||||
{
|
||||
const size_t index = size_t(val);
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
void set_selected(int val);
|
||||
void set_items(const std::vector<std::string>& items);
|
||||
size_t items_size() const;
|
||||
int selected() const;
|
||||
bool changed();
|
||||
|
||||
|
|
|
@ -46,10 +46,23 @@ scrollpane::scrollpane(display& d) : scrollarea(d), border_(5)
|
|||
void scrollpane::clear()
|
||||
{
|
||||
content_.clear();
|
||||
|
||||
update_content_size();
|
||||
}
|
||||
|
||||
void scrollpane::set_location(SDL_Rect const& rect)
|
||||
{
|
||||
scrollarea::set_location(rect);
|
||||
set_shown_size(client_area().h);
|
||||
update_widget_positions();
|
||||
}
|
||||
|
||||
void scrollpane::hide(bool value)
|
||||
{
|
||||
for(widget_map::iterator itor = content_.begin(); itor != content_.end(); ++itor) {
|
||||
itor->second.w->hide(value);
|
||||
}
|
||||
}
|
||||
|
||||
void scrollpane::add_widget(widget* w, int x, int y, int z_order)
|
||||
{
|
||||
if (w == NULL)
|
||||
|
@ -98,10 +111,14 @@ void scrollpane::scroll(int pos)
|
|||
return;
|
||||
|
||||
content_pos_.y = pos;
|
||||
update_widget_positions();
|
||||
}
|
||||
|
||||
void scrollpane::update_widget_positions()
|
||||
{
|
||||
|
||||
widget_map::iterator itor;
|
||||
|
||||
std::vector<bool> hidden(content_.size());
|
||||
|
||||
int i = 0;
|
||||
for(itor = content_.begin(); itor != content_.end(); ++itor) {
|
||||
hidden[i++] = itor->second.w->hidden();
|
||||
|
|
|
@ -43,6 +43,11 @@ public:
|
|||
/// \param callback a callback interface for warning that the grip has been moved
|
||||
scrollpane(display &d);
|
||||
|
||||
virtual void set_location(SDL_Rect const &rect);
|
||||
using widget::set_location;
|
||||
|
||||
virtual void hide(bool value=true);
|
||||
|
||||
void add_widget(widget* w, int x, int y, int z_order = 0);
|
||||
void remove_widget(widget* w);
|
||||
void clear();
|
||||
|
@ -55,6 +60,7 @@ protected:
|
|||
virtual void scroll(int pos);
|
||||
|
||||
private:
|
||||
void update_widget_positions();
|
||||
void position_widget(scrollpane_widget& spw);
|
||||
SDL_Rect client_area() const;
|
||||
void update_content_size();
|
||||
|
|
|
@ -214,6 +214,13 @@ void textbox::set_wrap(bool val)
|
|||
}
|
||||
}
|
||||
|
||||
void textbox::set_location(const SDL_Rect& rect)
|
||||
{
|
||||
text_pos_ = 0;
|
||||
|
||||
scrollarea::set_location(rect);
|
||||
}
|
||||
|
||||
void textbox::scroll(int pos)
|
||||
{
|
||||
yscroll_ = pos;
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
|
||||
void set_wrap(bool val);
|
||||
|
||||
void set_location(const SDL_Rect& rect);
|
||||
using scrollarea::set_location;
|
||||
|
||||
protected:
|
||||
virtual void draw_contents();
|
||||
virtual void set_inner_location(SDL_Rect const &);
|
||||
|
|
Loading…
Add table
Reference in a new issue