refactor of server code
This commit is contained in:
parent
6eb0df334a
commit
caf6d9cf5f
1 changed files with 491 additions and 462 deletions
|
@ -64,6 +64,13 @@ public:
|
|||
server(int port, input_stream& input);
|
||||
void run();
|
||||
private:
|
||||
void process_data(network::connection sock, config& data, config& gamelist);
|
||||
|
||||
void process_login(network::connection sock, const config& data, config& gamelist);
|
||||
void process_query(network::connection sock, const config& query, config& gamelist);
|
||||
void process_data_from_player_in_lobby(network::connection sock, config& data, config& gamelist);
|
||||
void process_data_from_player_in_game(network::connection sock, config& data, config& gamelist);
|
||||
|
||||
void process_command(const std::string& cmd);
|
||||
|
||||
void delete_game(std::vector<game>::iterator i);
|
||||
|
@ -279,468 +286,7 @@ void server::run()
|
|||
while((sock = network::receive_data(data)) != network::null_connection) {
|
||||
|
||||
metrics_.service_request();
|
||||
|
||||
//if someone who is not yet logged in is sending
|
||||
//login details
|
||||
if(not_logged_in_.is_member(sock)) {
|
||||
const config* const login = data.child("login");
|
||||
|
||||
//client must send a login first.
|
||||
if(login == NULL) {
|
||||
network::send_data(construct_error(
|
||||
"You must login first"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the username is valid (all alpha-numeric or space)
|
||||
std::string username = (*login)["username"];
|
||||
config::strip(username);
|
||||
const int alnum = std::count_if(username.begin(),username.end(),isalnum);
|
||||
const int spaces = std::count(username.begin(),username.end(),' ');
|
||||
if((alnum + spaces != username.size()) || spaces == username.size() || username.empty()) {
|
||||
network::send_data(construct_error(
|
||||
"This username is not valid"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(username.size() > 32) {
|
||||
network::send_data(construct_error(
|
||||
"This username is too long"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(username == "server") {
|
||||
network::send_data(construct_error(
|
||||
"The nick 'server' is reserved and can not be used by players"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
//check the username isn't already taken
|
||||
player_map::const_iterator p;
|
||||
for(p = players_.begin(); p != players_.end(); ++p) {
|
||||
if(p->second.name() == username) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(p != players_.end()) {
|
||||
network::send_data(construct_error(
|
||||
"This username is already taken"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
config* const player_cfg = &initial_response_.add_child("user");
|
||||
|
||||
const player new_player(username,*player_cfg);
|
||||
|
||||
players_.insert(std::pair<network::connection,player>(sock,new_player));
|
||||
|
||||
//remove player from the not-logged-in list and place
|
||||
//the player in the lobby
|
||||
not_logged_in_.remove_player(sock);
|
||||
lobby_players_.add_player(sock);
|
||||
|
||||
//send the new player the entire list of games and players
|
||||
network::send_data(initial_response_,sock);
|
||||
|
||||
//send other players in the lobby the update that the player has joined
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
|
||||
std::cerr << "'" << username << "' (" << network::ip_address(sock) << ") has logged on\n";
|
||||
|
||||
for(std::vector<game>::iterator g = games_.begin(); g != games_.end(); ++g) {
|
||||
g->send_data_observers(construct_server_message(username + " has logged into the lobby",*g));
|
||||
}
|
||||
|
||||
} else if(const config* query = data.child("query")) {
|
||||
|
||||
//process queries from clients in here
|
||||
std::ostringstream response;
|
||||
if((*query)["type"] == "metrics") {
|
||||
//a query for server data from a player
|
||||
response << metrics_;
|
||||
} else {
|
||||
response << "Error: unrecognized query";
|
||||
}
|
||||
|
||||
network::send_data(construct_server_message(response.str(),lobby_players_),sock);
|
||||
} else if(lobby_players_.is_member(sock)) {
|
||||
const config* const create_game = data.child("create_game");
|
||||
if(create_game != NULL) {
|
||||
|
||||
std::cerr << "creating game...\n";
|
||||
|
||||
//create the new game, remove the player from the
|
||||
//lobby and put him/her in the game they have created
|
||||
games_.push_back(game(players_));
|
||||
lobby_players_.remove_player(sock);
|
||||
games_.back().add_player(sock);
|
||||
|
||||
//store the game data here at the moment
|
||||
games_.back().level() = *create_game;
|
||||
std::stringstream converter;
|
||||
converter << games_.back().id();
|
||||
games_.back().level()["id"] = converter.str();
|
||||
|
||||
//mark the player as unavailable in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(false);
|
||||
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//see if the player is joining a game
|
||||
const config* const join = data.child("join");
|
||||
if(join != NULL) {
|
||||
const std::string& id = (*join)["id"];
|
||||
const int nid = atoi(id.c_str());
|
||||
const std::vector<game>::iterator it =
|
||||
std::find_if(games_.begin(),games_.end(),
|
||||
game_id_matches(nid));
|
||||
if(it == games_.end()) {
|
||||
//send a response saying the game has been cancelled
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
network::send_data(cfg,sock);
|
||||
|
||||
std::cerr << "attempt to join unknown game\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if(it->player_is_banned(sock)) {
|
||||
network::send_data(construct_error("You are banned from this game"),sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
lobby_players_.remove_player(sock);
|
||||
|
||||
//send them the game data
|
||||
network::send_data(it->level(),sock);
|
||||
|
||||
it->add_player(sock);
|
||||
|
||||
//mark the player as unavailable in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(false);
|
||||
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
}
|
||||
|
||||
//see if it's a message, in which case we add the name
|
||||
//of the sender, and forward it to all players in the lobby
|
||||
config* const message = data.child("message");
|
||||
if(message != NULL) {
|
||||
const player_map::const_iterator p = players_.find(sock);
|
||||
wassert(p != players_.end());
|
||||
(*message)["sender"] = p->second.name();
|
||||
|
||||
truncate_message((*message)["message"]);
|
||||
|
||||
lobby_players_.send_data(data,sock);
|
||||
}
|
||||
} else {
|
||||
std::vector<game>::iterator g;
|
||||
for(g = games_.begin(); g != games_.end(); ++g) {
|
||||
if(g->is_member(sock))
|
||||
break;
|
||||
}
|
||||
|
||||
if(g == games_.end()) {
|
||||
std::cerr << "ERROR: unknown socket " << games_.size() << "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
//if info is being provided about the game state
|
||||
if(data.child("info") != NULL) {
|
||||
const config& info = *data.child("info");
|
||||
if(info["type"] == "termination") {
|
||||
g->set_termination_reason(info["condition"]);
|
||||
}
|
||||
}
|
||||
|
||||
//if the owner is changing the controller for a side
|
||||
if(g->is_owner(sock) && data.child("change_controller") != NULL) {
|
||||
const config& change = *data.child("change_controller");
|
||||
const std::string& result = g->transfer_side_control(change);
|
||||
if(result == "") {
|
||||
const config& msg = construct_server_message(change["player"] + " takes control of side " + change["side"],*g);
|
||||
g->send_data(msg);
|
||||
} else {
|
||||
const config& msg = construct_server_message(result,*g);
|
||||
network::send_data(msg,sock);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//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"];
|
||||
player_map::iterator pl;
|
||||
for(pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if(pl->second.name() == name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
config leave_game;
|
||||
leave_game.add_child("leave_game");
|
||||
network::send_data(leave_game,pl->first);
|
||||
|
||||
g->describe_slots();
|
||||
lobby_players_.add_player(pl->first);
|
||||
|
||||
//mark the player as available in the lobby
|
||||
pl->second.mark_available(true);
|
||||
|
||||
//send the player who was banned the lobby game list
|
||||
network::send_data(initial_response_,pl->first);
|
||||
|
||||
//send all other players in the lobby the update to the lobby
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
} else if(pl == players_.end()) {
|
||||
const config& response = construct_server_message(
|
||||
"Ban failed: user '" + name + "' not found",*g);
|
||||
network::send_data(response,sock);
|
||||
}
|
||||
} else if(data.child("ban")) {
|
||||
const config& response = construct_server_message(
|
||||
"You cannot ban: not the game creator",*g);
|
||||
network::send_data(response,sock);
|
||||
}
|
||||
|
||||
//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->send_data(data,sock);
|
||||
|
||||
const bool lobby_changes = g->describe_slots();
|
||||
if(lobby_changes) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
}
|
||||
|
||||
//if this is data describing the level for a game.
|
||||
else if(g->is_owner(sock) && data.child("side") != NULL) {
|
||||
|
||||
const bool is_init = g->level_init();
|
||||
|
||||
//if this game is having its level data initialized
|
||||
//for the first time, and is ready for players to join.
|
||||
//We should currently have a summary of the game in g->level().
|
||||
//we want to move this summary to the initial_response_, and
|
||||
//place a pointer to that summary in the game's description.
|
||||
//g->level() should then receive the full data for the game.
|
||||
if(!is_init) {
|
||||
|
||||
//if there is no shroud, then tell players in the lobby what the map looks like
|
||||
if((*data.child("side"))["shroud"] != "yes") {
|
||||
g->level().values["map_data"] = data["map_data"];
|
||||
g->level().values["map"] = data["map"];
|
||||
}
|
||||
|
||||
//update our config object which describes the
|
||||
//open games, and notifies the game of where its description
|
||||
//is located at
|
||||
config& desc = gamelist.add_child("game",g->level());
|
||||
g->set_description(&desc);
|
||||
|
||||
//record the full description of the scenario in g->level()
|
||||
g->level() = data;
|
||||
g->describe_slots();
|
||||
|
||||
//send all players in the lobby the update to the list of games
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
|
||||
//we've already initialized this scenario, but clobber its old
|
||||
//contents with the new ones given here
|
||||
g->level() = data;
|
||||
}
|
||||
|
||||
//send all players in the level, except the sender, the new data
|
||||
g->send_data(data,sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
const string_map::const_iterator side = data.values.find("side");
|
||||
if(side != data.values.end()) {
|
||||
const bool res = g->take_side(sock,data);
|
||||
config response;
|
||||
if(res) {
|
||||
std::cerr << "played joined side\n";
|
||||
response["side_secured"] = side->second;
|
||||
|
||||
//update the number of available slots
|
||||
const bool res = g->describe_slots();
|
||||
if(res) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
} else {
|
||||
response["failed"] = "yes";
|
||||
}
|
||||
|
||||
network::send_data(response,sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(data.child("start_game")) {
|
||||
//send notification of the game starting immediately.
|
||||
//g->start_game() will send data that assumes the [start_game]
|
||||
//message has been sent
|
||||
g->send_data(data,sock);
|
||||
|
||||
g->start_game();
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
continue;
|
||||
} else if(data.child("leave_game")) {
|
||||
const bool needed = g->is_needed(sock);
|
||||
|
||||
if(needed) {
|
||||
|
||||
//tell all other players the game is over,
|
||||
//because a needed player has left
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
g->send_data(cfg);
|
||||
|
||||
//delete the game's description
|
||||
config* const gamelist = initial_response_.child("gamelist");
|
||||
wassert(gamelist != NULL);
|
||||
const config::child_itors vg = gamelist->child_range("game");
|
||||
|
||||
const config::child_iterator desc = std::find(vg.first,vg.second,g->description());
|
||||
if(desc != vg.second) {
|
||||
gamelist->remove_child("game",desc - vg.first);
|
||||
}
|
||||
|
||||
//update the state of the lobby to players in it.
|
||||
//We have to sync the state of the lobby so we can
|
||||
//send it to the players leaving the game
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
|
||||
//set the availability status for all quitting players
|
||||
for(player_map::iterator pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if(g->is_member(pl->first)) {
|
||||
pl->second.mark_available(true);
|
||||
}
|
||||
}
|
||||
|
||||
//put the players back in the lobby and send
|
||||
//them the game list and user list again
|
||||
g->send_data(initial_response_);
|
||||
metrics_.game_terminated(g->termination_reason());
|
||||
lobby_players_.add_players(*g);
|
||||
games_.erase(g);
|
||||
|
||||
//now sync players in the lobby again, to remove the game
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
|
||||
g->remove_player(sock);
|
||||
g->describe_slots();
|
||||
lobby_players_.add_player(sock);
|
||||
|
||||
//mark the player as available in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(true);
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
|
||||
//send the player who has quit the game list
|
||||
network::send_data(initial_response_,sock);
|
||||
|
||||
//send all other players in the lobby the update to the lobby
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if(data["side_secured"].empty() == false) {
|
||||
continue;
|
||||
} else if(data["failed"].empty() == false) {
|
||||
std::cerr << "ERROR: failure to get side\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
config* const turn = data.child("turn");
|
||||
if(turn != NULL) {
|
||||
g->filter_commands(sock,*turn);
|
||||
|
||||
//notify the game of the commands, and if it changes
|
||||
//the description, then sync the new description
|
||||
//to players in the lobby
|
||||
const bool res = g->process_commands(*turn);
|
||||
if(res) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
|
||||
//any private 'speak' commands must be repackaged seperate
|
||||
//to other commands, and re-sent, since they should only go
|
||||
//to some clients.
|
||||
const config::child_itors speaks = turn->child_range("command");
|
||||
int npublic = 0, nprivate = 0, nother = 0;
|
||||
std::string team_name;
|
||||
for(config::child_iterator i = speaks.first; i != speaks.second; ++i) {
|
||||
config* const speak = (*i)->child("speak");
|
||||
if(speak == NULL) {
|
||||
++nother;
|
||||
continue;
|
||||
}
|
||||
|
||||
truncate_message((*speak)["message"]);
|
||||
|
||||
//force the description to be correct to prevent
|
||||
//spoofing of messages
|
||||
const player_map::const_iterator pl = players_.find(sock);
|
||||
wassert(pl != players_.end());
|
||||
(*speak)["description"] = pl->second.name();
|
||||
|
||||
if((*speak)["team_name"] == "") {
|
||||
++npublic;
|
||||
} else {
|
||||
++nprivate;
|
||||
team_name = (*speak)["team_name"];
|
||||
}
|
||||
}
|
||||
|
||||
//if all there are are messages and they're all private, then
|
||||
//just forward them on to the client that should receive them.
|
||||
if(nprivate > 0 && npublic == 0 && nother == 0) {
|
||||
g->send_data_team(data,team_name,sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
//at the moment, if private messages are mixed in with other
|
||||
//data, then let them go through. It's exceedingly unlikely that
|
||||
//this will happen anyway, and if it does, the client should
|
||||
//respect not displaying the message.
|
||||
}
|
||||
|
||||
//forward data to all players who are in the game,
|
||||
//except for the original data sender
|
||||
g->send_data(data,sock);
|
||||
|
||||
if(g->started()) {
|
||||
g->record_data(data);
|
||||
}
|
||||
}
|
||||
process_data(sock,data,gamelist);
|
||||
}
|
||||
|
||||
metrics_.no_requests();
|
||||
|
@ -793,6 +339,489 @@ void server::run()
|
|||
}
|
||||
}
|
||||
|
||||
void server::process_data(const network::connection sock, config& data, config& gamelist)
|
||||
{
|
||||
//if someone who is not yet logged in is sending
|
||||
//login details
|
||||
if(not_logged_in_.is_member(sock)) {
|
||||
process_login(sock,data,gamelist);
|
||||
} else if(const config* query = data.child("query")) {
|
||||
process_query(sock,*query,gamelist);
|
||||
} else if(lobby_players_.is_member(sock)) {
|
||||
process_data_from_player_in_lobby(sock,data,gamelist);
|
||||
} else {
|
||||
process_data_from_player_in_game(sock,data,gamelist);
|
||||
}
|
||||
}
|
||||
|
||||
void server::process_login(const network::connection sock, const config& data, config& gamelist)
|
||||
{
|
||||
const config* const login = data.child("login");
|
||||
|
||||
//client must send a login first.
|
||||
if(login == NULL) {
|
||||
network::send_data(construct_error(
|
||||
"You must login first"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
//check the username is valid (all alpha-numeric or space)
|
||||
std::string username = (*login)["username"];
|
||||
config::strip(username);
|
||||
const int alnum = std::count_if(username.begin(),username.end(),isalnum);
|
||||
const int spaces = std::count(username.begin(),username.end(),' ');
|
||||
if((alnum + spaces != username.size()) || spaces == username.size() || username.empty()) {
|
||||
network::send_data(construct_error(
|
||||
"This username is not valid"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(username.size() > 32) {
|
||||
network::send_data(construct_error(
|
||||
"This username is too long"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(username == "server") {
|
||||
network::send_data(construct_error(
|
||||
"The nick 'server' is reserved and can not be used by players"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
//check the username isn't already taken
|
||||
player_map::const_iterator p;
|
||||
for(p = players_.begin(); p != players_.end(); ++p) {
|
||||
if(p->second.name() == username) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(p != players_.end()) {
|
||||
network::send_data(construct_error(
|
||||
"This username is already taken"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
config* const player_cfg = &initial_response_.add_child("user");
|
||||
|
||||
const player new_player(username,*player_cfg);
|
||||
|
||||
players_.insert(std::pair<network::connection,player>(sock,new_player));
|
||||
|
||||
//remove player from the not-logged-in list and place
|
||||
//the player in the lobby
|
||||
not_logged_in_.remove_player(sock);
|
||||
lobby_players_.add_player(sock);
|
||||
|
||||
//send the new player the entire list of games and players
|
||||
network::send_data(initial_response_,sock);
|
||||
|
||||
//send other players in the lobby the update that the player has joined
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
|
||||
std::cerr << "'" << username << "' (" << network::ip_address(sock) << ") has logged on\n";
|
||||
|
||||
for(std::vector<game>::iterator g = games_.begin(); g != games_.end(); ++g) {
|
||||
g->send_data_observers(construct_server_message(username + " has logged into the lobby",*g));
|
||||
}
|
||||
}
|
||||
|
||||
void server::process_query(const network::connection sock, const config& query, config& gamelist)
|
||||
{
|
||||
//process queries from clients in here
|
||||
std::ostringstream response;
|
||||
if(query["type"] == "metrics") {
|
||||
//a query for server data from a player
|
||||
response << metrics_;
|
||||
} else {
|
||||
response << "Error: unrecognized query";
|
||||
}
|
||||
|
||||
network::send_data(construct_server_message(response.str(),lobby_players_),sock);
|
||||
}
|
||||
|
||||
void server::process_data_from_player_in_lobby(const network::connection sock, config& data, config& gamelist)
|
||||
{
|
||||
const config* const create_game = data.child("create_game");
|
||||
if(create_game != NULL) {
|
||||
|
||||
std::cerr << "creating game...\n";
|
||||
|
||||
//create the new game, remove the player from the
|
||||
//lobby and put him/her in the game they have created
|
||||
games_.push_back(game(players_));
|
||||
lobby_players_.remove_player(sock);
|
||||
games_.back().add_player(sock);
|
||||
|
||||
//store the game data here at the moment
|
||||
games_.back().level() = *create_game;
|
||||
std::stringstream converter;
|
||||
converter << games_.back().id();
|
||||
games_.back().level()["id"] = converter.str();
|
||||
|
||||
//mark the player as unavailable in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(false);
|
||||
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//see if the player is joining a game
|
||||
const config* const join = data.child("join");
|
||||
if(join != NULL) {
|
||||
const std::string& id = (*join)["id"];
|
||||
const int nid = atoi(id.c_str());
|
||||
const std::vector<game>::iterator it =
|
||||
std::find_if(games_.begin(),games_.end(),
|
||||
game_id_matches(nid));
|
||||
if(it == games_.end()) {
|
||||
//send a response saying the game has been cancelled
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
network::send_data(cfg,sock);
|
||||
|
||||
std::cerr << "attempt to join unknown game\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if(it->player_is_banned(sock)) {
|
||||
network::send_data(construct_error("You are banned from this game"),sock);
|
||||
return;
|
||||
}
|
||||
|
||||
lobby_players_.remove_player(sock);
|
||||
|
||||
//send them the game data
|
||||
network::send_data(it->level(),sock);
|
||||
|
||||
it->add_player(sock);
|
||||
|
||||
//mark the player as unavailable in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(false);
|
||||
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
}
|
||||
|
||||
//see if it's a message, in which case we add the name
|
||||
//of the sender, and forward it to all players in the lobby
|
||||
config* const message = data.child("message");
|
||||
if(message != NULL) {
|
||||
const player_map::const_iterator p = players_.find(sock);
|
||||
wassert(p != players_.end());
|
||||
(*message)["sender"] = p->second.name();
|
||||
|
||||
truncate_message((*message)["message"]);
|
||||
|
||||
lobby_players_.send_data(data,sock);
|
||||
}
|
||||
}
|
||||
|
||||
void server::process_data_from_player_in_game(const network::connection sock, config& data, config& gamelist)
|
||||
{
|
||||
std::vector<game>::iterator g;
|
||||
for(g = games_.begin(); g != games_.end(); ++g) {
|
||||
if(g->is_member(sock))
|
||||
break;
|
||||
}
|
||||
|
||||
if(g == games_.end()) {
|
||||
std::cerr << "ERROR: unknown socket " << games_.size() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
//if info is being provided about the game state
|
||||
if(data.child("info") != NULL) {
|
||||
const config& info = *data.child("info");
|
||||
if(info["type"] == "termination") {
|
||||
g->set_termination_reason(info["condition"]);
|
||||
}
|
||||
}
|
||||
|
||||
//if the owner is changing the controller for a side
|
||||
if(g->is_owner(sock) && data.child("change_controller") != NULL) {
|
||||
const config& change = *data.child("change_controller");
|
||||
const std::string& result = g->transfer_side_control(change);
|
||||
if(result == "") {
|
||||
const config& msg = construct_server_message(change["player"] + " takes control of side " + change["side"],*g);
|
||||
g->send_data(msg);
|
||||
} else {
|
||||
const config& msg = construct_server_message(result,*g);
|
||||
network::send_data(msg,sock);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//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"];
|
||||
player_map::iterator pl;
|
||||
for(pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if(pl->second.name() == name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
config leave_game;
|
||||
leave_game.add_child("leave_game");
|
||||
network::send_data(leave_game,pl->first);
|
||||
|
||||
g->describe_slots();
|
||||
lobby_players_.add_player(pl->first);
|
||||
|
||||
//mark the player as available in the lobby
|
||||
pl->second.mark_available(true);
|
||||
|
||||
//send the player who was banned the lobby game list
|
||||
network::send_data(initial_response_,pl->first);
|
||||
|
||||
//send all other players in the lobby the update to the lobby
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
} else if(pl == players_.end()) {
|
||||
const config& response = construct_server_message(
|
||||
"Ban failed: user '" + name + "' not found",*g);
|
||||
network::send_data(response,sock);
|
||||
}
|
||||
} else if(data.child("ban")) {
|
||||
const config& response = construct_server_message(
|
||||
"You cannot ban: not the game creator",*g);
|
||||
network::send_data(response,sock);
|
||||
}
|
||||
|
||||
//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->send_data(data,sock);
|
||||
|
||||
const bool lobby_changes = g->describe_slots();
|
||||
if(lobby_changes) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
}
|
||||
|
||||
//if this is data describing the level for a game.
|
||||
else if(g->is_owner(sock) && data.child("side") != NULL) {
|
||||
|
||||
const bool is_init = g->level_init();
|
||||
|
||||
//if this game is having its level data initialized
|
||||
//for the first time, and is ready for players to join.
|
||||
//We should currently have a summary of the game in g->level().
|
||||
//we want to move this summary to the initial_response_, and
|
||||
//place a pointer to that summary in the game's description.
|
||||
//g->level() should then receive the full data for the game.
|
||||
if(!is_init) {
|
||||
|
||||
//if there is no shroud, then tell players in the lobby what the map looks like
|
||||
if((*data.child("side"))["shroud"] != "yes") {
|
||||
g->level().values["map_data"] = data["map_data"];
|
||||
g->level().values["map"] = data["map"];
|
||||
}
|
||||
|
||||
//update our config object which describes the
|
||||
//open games, and notifies the game of where its description
|
||||
//is located at
|
||||
config& desc = gamelist.add_child("game",g->level());
|
||||
g->set_description(&desc);
|
||||
|
||||
//record the full description of the scenario in g->level()
|
||||
g->level() = data;
|
||||
g->describe_slots();
|
||||
|
||||
//send all players in the lobby the update to the list of games
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
|
||||
//we've already initialized this scenario, but clobber its old
|
||||
//contents with the new ones given here
|
||||
g->level() = data;
|
||||
}
|
||||
|
||||
//send all players in the level, except the sender, the new data
|
||||
g->send_data(data,sock);
|
||||
return;
|
||||
}
|
||||
|
||||
const string_map::const_iterator side = data.values.find("side");
|
||||
if(side != data.values.end()) {
|
||||
const bool res = g->take_side(sock,data);
|
||||
config response;
|
||||
if(res) {
|
||||
std::cerr << "played joined side\n";
|
||||
response["side_secured"] = side->second;
|
||||
|
||||
//update the number of available slots
|
||||
const bool res = g->describe_slots();
|
||||
if(res) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
} else {
|
||||
response["failed"] = "yes";
|
||||
}
|
||||
|
||||
network::send_data(response,sock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(data.child("start_game")) {
|
||||
//send notification of the game starting immediately.
|
||||
//g->start_game() will send data that assumes the [start_game]
|
||||
//message has been sent
|
||||
g->send_data(data,sock);
|
||||
|
||||
g->start_game();
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
return;
|
||||
} else if(data.child("leave_game")) {
|
||||
const bool needed = g->is_needed(sock);
|
||||
|
||||
if(needed) {
|
||||
|
||||
//tell all other players the game is over,
|
||||
//because a needed player has left
|
||||
config cfg;
|
||||
cfg.add_child("leave_game");
|
||||
g->send_data(cfg);
|
||||
|
||||
//delete the game's description
|
||||
config* const gamelist = initial_response_.child("gamelist");
|
||||
wassert(gamelist != NULL);
|
||||
const config::child_itors vg = gamelist->child_range("game");
|
||||
|
||||
const config::child_iterator desc = std::find(vg.first,vg.second,g->description());
|
||||
if(desc != vg.second) {
|
||||
gamelist->remove_child("game",desc - vg.first);
|
||||
}
|
||||
|
||||
//update the state of the lobby to players in it.
|
||||
//We have to sync the state of the lobby so we can
|
||||
//send it to the players leaving the game
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
|
||||
//set the availability status for all quitting players
|
||||
for(player_map::iterator pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if(g->is_member(pl->first)) {
|
||||
pl->second.mark_available(true);
|
||||
}
|
||||
}
|
||||
|
||||
//put the players back in the lobby and send
|
||||
//them the game list and user list again
|
||||
g->send_data(initial_response_);
|
||||
metrics_.game_terminated(g->termination_reason());
|
||||
lobby_players_.add_players(*g);
|
||||
games_.erase(g);
|
||||
|
||||
//now sync players in the lobby again, to remove the game
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
} else {
|
||||
|
||||
g->remove_player(sock);
|
||||
g->describe_slots();
|
||||
lobby_players_.add_player(sock);
|
||||
|
||||
//mark the player as available in the lobby
|
||||
const player_map::iterator pl = players_.find(sock);
|
||||
if(pl != players_.end()) {
|
||||
pl->second.mark_available(true);
|
||||
} else {
|
||||
std::cerr << "ERROR: Could not find player in map\n";
|
||||
}
|
||||
|
||||
//send the player who has quit the game list
|
||||
network::send_data(initial_response_,sock);
|
||||
|
||||
//send all other players in the lobby the update to the lobby
|
||||
lobby_players_.send_data(sync_initial_response(),sock);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if(data["side_secured"].empty() == false) {
|
||||
return;
|
||||
} else if(data["failed"].empty() == false) {
|
||||
std::cerr << "ERROR: failure to get side\n";
|
||||
return;
|
||||
}
|
||||
|
||||
config* const turn = data.child("turn");
|
||||
if(turn != NULL) {
|
||||
g->filter_commands(sock,*turn);
|
||||
|
||||
//notify the game of the commands, and if it changes
|
||||
//the description, then sync the new description
|
||||
//to players in the lobby
|
||||
const bool res = g->process_commands(*turn);
|
||||
if(res) {
|
||||
lobby_players_.send_data(sync_initial_response());
|
||||
}
|
||||
|
||||
//any private 'speak' commands must be repackaged seperate
|
||||
//to other commands, and re-sent, since they should only go
|
||||
//to some clients.
|
||||
const config::child_itors speaks = turn->child_range("command");
|
||||
int npublic = 0, nprivate = 0, nother = 0;
|
||||
std::string team_name;
|
||||
for(config::child_iterator i = speaks.first; i != speaks.second; ++i) {
|
||||
config* const speak = (*i)->child("speak");
|
||||
if(speak == NULL) {
|
||||
++nother;
|
||||
continue;
|
||||
}
|
||||
|
||||
truncate_message((*speak)["message"]);
|
||||
|
||||
//force the description to be correct to prevent
|
||||
//spoofing of messages
|
||||
const player_map::const_iterator pl = players_.find(sock);
|
||||
wassert(pl != players_.end());
|
||||
(*speak)["description"] = pl->second.name();
|
||||
|
||||
if((*speak)["team_name"] == "") {
|
||||
++npublic;
|
||||
} else {
|
||||
++nprivate;
|
||||
team_name = (*speak)["team_name"];
|
||||
}
|
||||
}
|
||||
|
||||
//if all there are are messages and they're all private, then
|
||||
//just forward them on to the client that should receive them.
|
||||
if(nprivate > 0 && npublic == 0 && nother == 0) {
|
||||
g->send_data_team(data,team_name,sock);
|
||||
return;
|
||||
}
|
||||
|
||||
//at the moment, if private messages are mixed in with other
|
||||
//data, then let them go through. It's exceedingly unlikely that
|
||||
//this will happen anyway, and if it does, the client should
|
||||
//respect not displaying the message.
|
||||
}
|
||||
|
||||
//forward data to all players who are in the game,
|
||||
//except for the original data sender
|
||||
g->send_data(data,sock);
|
||||
|
||||
if(g->started()) {
|
||||
g->record_data(data);
|
||||
}
|
||||
}
|
||||
|
||||
void server::delete_game(std::vector<game>::iterator i)
|
||||
{
|
||||
metrics_.game_terminated(i->termination_reason());
|
||||
|
|
Loading…
Add table
Reference in a new issue