improvements to the way network games are set up

This commit is contained in:
Dave White 2004-05-11 18:32:37 +00:00
parent 540c2e8345
commit 0946c4b945
9 changed files with 207 additions and 148 deletions

View file

@ -341,6 +341,7 @@ vacant_slots="Vacant Slots"
host_game="Host Multiplayer Game"
join_game="Join Game"
observe_game="Observe Game"
load_game="Load Game"
create_new_game="Create Game"
name_of_game="Name of game"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 875 B

View file

@ -8,6 +8,7 @@
#include "replay.hpp"
#include "scoped_resource.hpp"
#include "show_dialog.hpp"
#include "util.hpp"
#include <sstream>
@ -35,59 +36,6 @@ private:
config cfg;
};
enum GAME_LIST_RESULT { QUIT_GAME, CREATE_GAME, JOIN_GAME };
GAME_LIST_RESULT manage_game_list(display& disp, const config* gamelist)
{
gamelist_manager manager;
for(;;) {
std::vector<std::string> options;
options.push_back(string_table["create_new_game"]);
for(config::const_child_itors i = gamelist->child_range("game");
i.first != i.second; ++i.first) {
options.push_back((**i.first)["name"]);
}
options.push_back(string_table["quit_button"]);
const int res = gui::show_dialog(disp,NULL,"","Choose game to join",
gui::MESSAGE,&options,NULL,"",NULL,&manager);
if(res == gamelist_manager::UPDATED_GAMELIST) {
gamelist = manager.get_gamelist();
} else if(res == 0) {
std::string name;
const int res = gui::show_dialog(disp,NULL,"","Name your game:",
gui::OK_CANCEL,NULL,NULL,"Name:",&name);
if(res == 0) {
config response;
config& create_game = response.add_child("create_game");;
create_game["name"] = name;
network::send_data(response);
return CREATE_GAME;
}
} else if(size_t(res) == options.size()-1) {
return QUIT_GAME;
} else if(res > 0 && size_t(res) < options.size()) {
const config::const_child_itors i = gamelist->child_range("game");
const size_t index = size_t(res)-1;
assert(i.second - i.first > int(index));
const std::string& id = (**(i.first+index))["id"];
config response;
config& join = response.add_child("join");
join["id"] = id;
network::send_data(response);
return JOIN_GAME;
}
}
return QUIT_GAME;
}
void check_response(network::connection res, const config& data)
{
if(!res) {
@ -116,7 +64,7 @@ void receive_gamelist(display& disp, config& data)
class wait_for_start : public lobby::dialog
{
public:
wait_for_start(display& disp, config& cfg) : got_side(false), status(START_GAME), disp_(disp), cancel_button_(NULL), sides_(cfg) {}
wait_for_start(display& disp, config& cfg, int team_num) : got_side(false), team(team_num), status(START_GAME), disp_(disp), cancel_button_(NULL), sides_(cfg) {}
void set_area(const SDL_Rect& area) {
const std::string text = string_table["waiting_start"];
@ -146,6 +94,13 @@ public:
const network::connection res = network::receive_data(reply);
if(res) {
std::cerr << "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"]);
}
}
if(reply.values["failed"] == "yes") {
status = SIDE_UNAVAILABLE;
return lobby::QUIT;
@ -175,6 +130,7 @@ public:
}
bool got_side;
int team;
enum { START_GAME, GAME_CANCELLED, SIDE_UNAVAILABLE } status;
private:
@ -275,8 +231,7 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
logged_in = true;
}
for(bool first_time = true;
(first_time || logged_in) && network::nconnections() > 0;
for(bool first_time = true; (first_time || logged_in) && network::nconnections() > 0;
first_time = false) {
if(!first_time) {
@ -285,6 +240,8 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
std::cerr << "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");
@ -310,6 +267,9 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
break;
}
case lobby::OBSERVE:
observer = true;
case lobby::JOIN: {
status = 1;
break;
@ -332,56 +292,41 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
//ensure we send a close game message to the server when we are done
network_game_manager game_manager;
std::map<int,int> choice_map;
std::vector<std::string> choices, race_names;
std::vector<bool> changes_allowed;
const bool allow_observer = sides["observer"] != "no";
if(allow_observer) {
choices.push_back(string_table["observer"]);
}
const config::child_list& sides_list = sides.get_children("side");
for(config::child_list::const_iterator s = sides_list.begin(); s != sides_list.end(); ++s) {
if((**s)["controller"] == "network" &&
(**s)["taken"] != "yes") {
choice_map[choices.size()] = 1 + s - sides_list.begin();
choices.push_back((**s)["name"] + "," + (**s)["type"] + "," + (**s)["description"]);
int team_num = 0;
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)["changes_allowed"] != "no";
default_race = (**s)["name"];
}
}
}
race_names.push_back((**s)["name"]);
changes_allowed.push_back((**s)["allow_changes"] != "no");
}
if(side_choice == sides_list.end()) {
gui::show_dialog(disp,NULL,"",string_table["no_sides_available"],gui::OK_ONLY);
continue;
}
if(choices.empty()) {
gui::show_dialog(disp,NULL,"",string_table["no_sides_available"],gui::OK_ONLY);
continue;
}
int choice = gui::show_dialog(disp,NULL,"",string_table["client_choose_side"],
gui::OK_CANCEL,&choices);
if((choice != 0 || allow_observer == false) && choice_map.count(choice) == 0) {
continue;
}
const bool observer = allow_observer && choice == 0;
const int team_num = observer ? -1 : choice_map[choice];
//send our choice of team to the server
if(!observer) {
assert(team_num >= 1 && team_num <= race_names.size());
const std::string& default_race = race_names[team_num-1];
const bool allow_changes = changes_allowed[team_num-1];
team_num = nchoice;
config response;
std::stringstream stream;
stream << team_num;
response["side"] = stream.str();
response["side"] = lexical_cast<std::string>(nchoice);
response["description"] = preferences::login();
const std::string& era = sides["era"];
@ -407,8 +352,9 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
possible_sides.begin(); side != possible_sides.end(); ++side) {
choices.push_back(translate_string_default((**side)["id"],(**side)["name"]));
if(choices.back() == default_race)
if(choices.back() == default_race) {
choice = side - possible_sides.begin();
}
}
//if the client is allowed to choose their team, instead of having
@ -429,7 +375,7 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
network::send_data(response);
}
wait_for_start waiter(disp,sides);
wait_for_start waiter(disp,sides,team_num);
std::vector<std::string> messages;
config game_data;
const lobby::RESULT dialog_res = lobby::enter(disp,game_data,cfg,&waiter,messages);
@ -450,6 +396,8 @@ void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
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

View file

@ -451,7 +451,7 @@ void mp_connect::gui_update()
if (side["controller"] == "network") {
if (side["description"] == "") {
combos_type_[n].set_selected(0);
} else if (side["description"] == "Computer Player") {
} else if (side["description"] == string_table["ai_controlled"]) {
//When loading a game you did not create AI players are marked
//as network players, set back to AI
combos_type_[n].set_selected(2);
@ -855,47 +855,84 @@ void mp_connect::update_network()
}
}
const int side_taken = atoi(cfg["side"].c_str())-1;
int side_taken = atoi(cfg["side"].c_str())-1;
if(side_taken >= 0 && side_taken < int(sides.size())) {
std::map<config*,network::connection>::iterator pos = positions_.find(sides[side_taken]);
if(pos != positions_.end()) {
if(!pos->second || pos->second == sock) {
std::cerr << "client has taken a valid position\n";
//see if we can reassign the player to a different position
if(pos->second && pos->second != sock) {
side_taken = 0;
for(pos = positions_.begin(); pos != positions_.end(); ++pos, ++side_taken) {
if(pos->first->values["controller"] == "network" &&
pos->first->values["taken"] != "yes") {
break;
}
}
//does the client already own the side, and is just updating
//it, or is it taking a vacant slot?
const bool update_only = pos->second == sock;
if(pos == positions_.end()) {
config response;
response.values["failed"] = "yes";
network::send_data(response,sock);
return;
}
//broadcast to everyone the new game status
pos->first->values["controller"] = "network";
pos->first->values["taken"] = "yes";
pos->first->values["description"] = cfg["description"];
pos->first->values["name"] = cfg["name"];
pos->first->values["type"] = cfg["type"];
pos->first->values["recruit"] = cfg["recruit"];
pos->first->values["music"] = cfg["music"];
pos->first->values["terrain_liked"] = cfg["terrain_liked"];
pos->second = sock;
network::send_data(*level_);
std::cerr << "sent player data\n";
//send a reply telling the client they have secured
//the side they asked for
std::stringstream side;
side << (side_taken+1);
config reply;
reply.values["side_secured"] = side.str();
std::cerr << "going to send data...\n";
network::send_data(reply,sock);
// Add to combo list
add_player(cfg["description"]);
} else {
config response;
response.values["failed"] = "yes";
network::send_data(response,sock);
config reassign;
config& cfg = reassign.add_child("reassign_side");
cfg["from"] = cfg["side"];
cfg["to"] = lexical_cast<std::string>(side_taken+1);
network::send_data(reassign,sock);
}
std::cerr << "client has taken a valid position\n";
//does the client already own the side, and is just updating
//it, or is it taking a vacant slot?
const bool update_only = pos->second == sock;
//broadcast to everyone the new game status
pos->first->values["controller"] = "network";
pos->first->values["taken"] = "yes";
if(cfg["description"].empty() == false) {
pos->first->values["description"] = cfg["description"];
}
if(cfg["name"].empty() == false) {
pos->first->values["name"] = cfg["name"];
}
if(cfg["type"].empty() == false) {
pos->first->values["type"] = cfg["type"];
}
if(cfg["recruit"].empty() == false) {
pos->first->values["recruit"] = cfg["recruit"];
}
if(cfg["music"].empty() == false) {
pos->first->values["music"] = cfg["music"];
}
if(cfg["terrain_liked"].empty() == false) {
pos->first->values["terrain_liked"] = cfg["terrain_liked"];
}
pos->second = sock;
network::send_data(*level_);
std::cerr << "sent player data\n";
//send a reply telling the client they have secured
//the side they asked for
std::stringstream side;
side << (side_taken+1);
config reply;
reply.values["side_secured"] = side.str();
std::cerr << "going to send data...\n";
network::send_data(reply,sock);
// Add to combo list
add_player(cfg["description"]);
} else {
std::cerr << "tried to take illegal side: " << side_taken << "\n";
}

View file

@ -83,6 +83,7 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
chat_textbox.scroll_to_bottom();
std::vector<std::string> options;
std::vector<bool> game_vacant_slots, game_observers;
const config* const gamelist = game_data.child("gamelist");
@ -144,6 +145,8 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
}
options.push_back(str.str());
game_vacant_slots.push_back(slots != "" && slots != "0");
game_observers.push_back((**i.first)["observer"] != "no");
}
}
@ -153,17 +156,25 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
}
gui::menu games_menu(disp,options);
gui::button observe_game(disp,string_table["observe_game"]);
gui::button join_game(disp,string_table["join_game"]);
gui::button new_game(disp,string_table["create_new_game"]);
gui::button quit_game(disp,string_table["quit_button"]);
if(dlg != NULL) {
observe_game.hide();
join_game.hide();
new_game.hide();
quit_game.hide();
}
if(!games_available) {
if(games_menu.selection() >= 0 && games_menu.selection() < int(game_vacant_slots.size())) {
assert(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();
}
@ -217,8 +228,9 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
update_rect(xscale(disp,19),yscale(disp,23),xscale(disp,832),yscale(disp,520));
join_game.set_location(xscale(disp,19),yscale(disp,545));
new_game.set_location(xscale(disp,19)+join_game.width()+5,yscale(disp,545));
quit_game.set_location(xscale(disp,19)+join_game.width()+5+new_game.width()+5,yscale(disp,545));
observe_game.set_location(join_game.location().x + join_game.location().w + 5,yscale(disp,545));
new_game.set_location(observe_game.location().x + observe_game.location().w + 5,yscale(disp,545));
quit_game.set_location(new_game.location().x + new_game.location().w + 5,yscale(disp,545));
message_entry.set_location(xscale(disp,19),yscale(disp,725));
message_entry.set_width(xscale(disp,832));
@ -241,14 +253,19 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
games_menu.process(mousex,mousey,left_button,
key[SDLK_UP],key[SDLK_DOWN],
key[SDLK_PAGEUP],key[SDLK_PAGEDOWN]);
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()]);
}
}
users_menu.process(mousex,mousey,left_button,
key[SDLK_UP],key[SDLK_DOWN],
key[SDLK_PAGEUP],key[SDLK_PAGEDOWN]);
if(games_available &&
(join_game.process(mousex,mousey,left_button) || games_menu.double_clicked())) {
const bool observe = observe_game.pressed();
if(games_available && (observe || join_game.pressed() || games_menu.double_clicked())) {
const size_t index = size_t(games_menu.selection());
const config::const_child_itors i = gamelist->child_range("game");
assert(index < size_t(i.second - i.first));
@ -258,10 +275,10 @@ RESULT enter(display& disp, config& game_data, const config& terrain_data, dialo
config& join = response.add_child("join");
join["id"] = id;
network::send_data(response);
return JOIN;
return observe ? OBSERVE : JOIN;
}
if(dlg == NULL && new_game.process(mousex,mousey,left_button)) {
if(dlg == NULL && new_game.pressed()) {
return CREATE;
break;
}

View file

@ -8,7 +8,7 @@
///allows players to chat, create games, and join games.
namespace lobby {
enum RESULT { QUIT, CREATE, JOIN, CONTINUE };
enum RESULT { QUIT, CREATE, JOIN, OBSERVE, CONTINUE };
///interface for an interactive dialog that is displayed while lobby user lists
///and lobby chat continue

View file

@ -112,6 +112,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
game_state& state_of_game,
const std::vector<config*>& story)
{
const int ticks = SDL_GetTicks();
std::cerr << "in play_level()...\n";
//if the entire scenario should be randomly generated
@ -146,6 +147,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
state_of_game.starting_pos = new_level;
}
std::cerr << "generated map " << (SDL_GetTicks() - ticks) << "\n";
const statistics::scenario_context statistics_context(translate_string_default((*level)["id"],(*level)["name"]));
const int num_turns = atoi(level->values["turns"].c_str());
@ -153,6 +156,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
gamemap map(game_config,map_data);
std::cerr << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
CKey key;
unit_map units;
@ -175,6 +180,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
}
std::cerr << "initializing teams..." << unit_cfg.size() << "\n";;
std::cerr << (SDL_GetTicks() - ticks) << "\n";
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::cerr << "initializing team...\n";
@ -268,6 +274,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
}
}
std::cerr << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
const config* theme_cfg = NULL;
if((*level)["theme"] != "") {
theme_cfg = game_config.find_child("theme","name",(*level)["theme"]);
@ -277,14 +285,19 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
theme_cfg = game_config.find_child("theme","name",preferences::theme());
}
std::cerr << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
const config dummy_cfg;
display gui(units,video,map,status,teams,theme_cfg != NULL ? *theme_cfg : dummy_cfg, game_config);
std::cerr << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
//object that will make sure that labels are removed at the end of the scenario
const font::floating_label_manager labels_manager;
gui.labels().read(*level);
std::cerr << "a... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
gui.set_team(first_human_team);
}
@ -292,6 +305,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
const preferences::display_manager prefs_disp_manager(&gui);
const tooltips::manager tooltips_manager(gui);
std::cerr << "b... " << (SDL_GetTicks() - ticks) << "\n";
if(recorder.skipping() == false) {
for(std::vector<config*>::const_iterator story_i = story.begin();
story_i != story.end(); ++story_i) {
@ -301,14 +316,20 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
show_map_scene(gui,*level);
}
std::cerr << "c... " << (SDL_GetTicks() - ticks) << "\n";
const std::string& music = level->values["music"];
if(music != "") {
sound::play_music(music);
}
std::cerr << "d... " << (SDL_GetTicks() - ticks) << "\n";
victory_conditions::set_victory_when_enemies_defeated(
(*level)["victory_when_enemies_defeated"] != "no");
std::cerr << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
game_events::manager events_manager(*level,gui,map,units,teams,
state_of_game,status,gameinfo);
@ -322,17 +343,23 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
turn_info::floating_textbox textbox_info;
std::cerr << "entering try... " << (SDL_GetTicks() - ticks) << "\n";
try {
gui.begin_game();
gui.adjust_colours(0,0,0);
game_events::fire("prestart");
std::cerr << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
clear_shroud(gui,status,map,gameinfo,units,teams,first_human_team);
std::cerr << "b " << (SDL_GetTicks() - ticks) << "\n";
gui.scroll_to_tile(map.starting_position(first_human_team+1).x,map.starting_position(first_human_team+1).y,display::WARP);
std::cerr << "c " << (SDL_GetTicks() - ticks) << "\n";
}
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
std::cerr << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
bool replaying = (recorder.at_end() == false);
@ -345,6 +372,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
}
std::cerr << "starting main loop\n";
std::cerr << (SDL_GetTicks() - ticks) << "\n";
std::deque<config> data_backlog;

View file

@ -242,7 +242,8 @@ report generate_report(TYPE type, const gamemap& map, const unit_map& units,
break;
}
case UPKEEP: {
str << team_upkeep(units,current_side);
const team_data data = calculate_team_data(current_team,current_side,units);
str << data.expenses << " (" << data.upkeep << ")";
break;
}
case EXPENSES: {

View file

@ -111,10 +111,37 @@ bool game::take_side(network::connection player, const config& cfg)
const std::string& side = cfg["side"];
//if the player is already on a side or the side is already taken
if(sides_.count(player) || sides_taken_.count(side))
//if the player is already on a side
if(sides_.count(player))
return false;
//if the side is already taken, see if we can give the player
//another side instead
if(sides_taken_.count(side)) {
std::cerr << "side '" << side << "' taken, searching for alternative side\n";
const config::child_list& sides = level_.get_children("side");
for(config::child_list::const_iterator i = sides.begin(); i != sides.end(); ++i) {
if((**i)["controller"] == "network" && sides_taken_.count((**i)["side"]) == 0) {
config new_cfg = cfg;
new_cfg["side"] = (**i)["side"];
const bool res = take_side(player,new_cfg);
//if there's another side available, then tell the player that they've
//had their side reassigned
if(res) {
config response;
config& reassign = response.add_child("reassign_side");
reassign["from"] = cfg["side"];
reassign["to"] = new_cfg["side"];
network::send_data(response,player);
}
return res;
}
}
}
sides_[player] = side;
sides_taken_.insert(side);