#include #include #include #include #include //--------------- // Chat functions void chat_to_all(int player, std::string message, ServerState &ss) { std::ostringstream msg(""); msg << "chat " << player << " " << message; int i; for (i = 0; i < ss.gs.players.size(); i++) { if (ss.gs.players.at(i).state != DISCONNECTED) { ss.gs.players.at(i).socket->putline(msg.str()); } } } void error_to_one(int player, std::string message, ServerState &ss) { std::ostringstream msg(""); msg << "error " << message; ss.gs.players.at(player).socket->putline(msg.str()); } void status_to_all(std::string line, ServerState &ss) { int i; for (i = 0; i < ss.gs.players.size(); i++) { if (ss.gs.players.at(i).state != DISCONNECTED) { ss.gs.players.at(i).socket->putline(line); } } } void send_players(ServerState &ss) { int i; for (i = 0; i < ss.gs.players.size(); i++) { std::ostringstream msg(""); msg << "player " << i << " "; switch (ss.gs.players.at(i).state) { case PREGAME: case INGAME: msg << "active"; break; case OBSERVER: msg << "observer"; break; case DISCONNECTED: msg << "disconnected"; break; default: debug(CRITICAL, "Invalid player state"); } if (ss.gs.players.at(i).let_go_over) { msg << " over "; } else { msg << " notover "; } msg << ss.gs.players.at(i).hand.size() << " "; msg << ss.gs.players.at(i).name; status_to_all(msg.str(), ss); } } void send_table(ServerState &ss) { std::ostringstream msg(""); msg << "table"; int i; for (i = 0; i < ss.gs.table.size(); i++) { msg << " " << card_to_string(ss.gs.table.at(i).tobeat) << ":"; if (is_card(ss.gs.table.at(i).beater)) { msg << card_to_string(ss.gs.table.at(i).beater); } } status_to_all(msg.str(), ss); } void send_hand(Player &player) { std::ostringstream msg(""); int i; msg << "hand"; for (i = 0; i < player.hand.size(); i++) { msg << " " << card_to_string(player.hand.at(i)); } player.socket->putline(msg.str()); } void send_gamestatus(ServerState &ss) { std::ostringstream msg(""); send_table(ss); msg << "deck " << ss.gs.deck.size(); status_to_all(msg.str(), ss); msg.str(""); msg << "trump "; msg << card_to_string(ss.gs.deck.front()); status_to_all(msg.str(), ss); status_to_all(msg.str(), ss); int i; for (i = 0; i < ss.gs.players.size(); i++) { if (ss.gs.players.at(i).state == INGAME) { send_hand(ss.gs.players.at(i)); } } msg.str(""); msg << "turn " << ss.gs.turn; status_to_all(msg.str(), ss); } //----------------------------- // Command processing functions Card string_to_card(std::string description) { Card result = {NOSUIT, NORANK}; std::istringstream stream(description); char suitchar = 0; stream >> suitchar; switch (std::tolower(suitchar)) { case 'c': result.suit = CLUBS; break; case 's': result.suit = SPADES; break; case 'h': result.suit = HEARTS; break; case 'd': result.suit = DIAMONDS; break; } int rankno = 0; stream >> rankno; if (rankno >= 1 && rankno <= 13) { result.rank = static_cast(rankno); } return result; } std::string card_to_string(Card card) { std::ostringstream description(""); switch (card.suit) { case HEARTS: description << "H"; break; case SPADES: description << "S"; break; case DIAMONDS: description << "D"; break; case CLUBS: description << "C"; break; default: debug(CRITICAL, "Invalid conversion from card to string"); } description << card.rank; return description.str(); } void process_command(std::string line, Player &player, ServerState &ss) { std::istringstream stream(line); std::string command; stream >> command; stream.get(); // Space after command if (player.name == "") { if (command == "name") { std::getline(stream, player.name); } else { error_to_one(player.id, "You need name first", ss); } return; } if (command == "chat") { std::string argstring; std::getline(stream, argstring); chat_to_all(player.id, argstring, ss); return; } std::vector args; while (stream.good()) { std::string part; stream >> part; args.push_back(part); } if (ss.state == SERVER_PREGAME && command == "start") { ss.state = SERVER_INGAME; return; } if (ss.state == SERVER_AFTERGAME && command == "restart") { ss.state = SERVER_PREGAME; return; } if (ss.state == SERVER_INGAME && player.state == INGAME) { process_ingame_command(command, args, player, ss); return; } error_to_one(player.id, "Unknown command or wrong arguments", ss); } void process_ingame_command(std::string command, std::vector args, Player &player, ServerState &ss) { Move move = {0}; move.player = player.id; if (command == "beat" && args.size() == 2) { move.movetype = BEAT; move.owncard = string_to_card(args.at(1)); move.othercard = string_to_card(args.at(0)); } else if (command == "hit" && args.size() == 1) { move.movetype = HIT; move.owncard = string_to_card(args.at(0)); } else if (command == "move" && args.size() == 1) { move.movetype = MOVE; move.owncard = string_to_card(args.at(0)); } else if (command == "pickup" && args.size() == 0) { move.movetype = PICKUP; } else if (command == "over" && args.size() == 0) { player.let_go_over = true; return; } else { error_to_one(player.id, "Unknown command or wrong arguments", ss); } if (legal_move(ss.gs, move)) { apply_move(ss.gs, move); } else { error_to_one(player.id, "Invalid move", ss); } } //-------------------- // Game loop functions void new_player(ServerState &ss, ClientSocket *socket, PlayerState state) { Player player; player.id = ss.gs.players.size(); player.name = std::string(""); player.socket = socket; player.state = state; player.let_go_over = false; ss.gs.players.push_back(player); std::ostringstream msg(""); msg << "hello " << player.id; player.socket->putline(msg.str()); } void handle_dataflow(ServerState &ss) { int i; std::string line; for (i = 0; i < ss.gs.players.size(); i++) { if (ss.gs.players.at(i).state == DISCONNECTED) { continue; } if (ss.gs.players.at(i).socket->getline(line)) { process_command(line, ss.gs.players.at(i), ss); } if (!ss.gs.players.at(i).socket->good()) { if (ss.gs.turn == i) { ss.gs.turn = next_turn(ss.gs); } ss.gs.players.at(i).state = DISCONNECTED; ss.socket->release_client(ss.gs.players.at(i).socket); ss.gs.players.at(i).socket = 0; debug(INFO, "Disconnected player"); } } } void do_pregame(ServerState &ss) { debug(INFO, "Entering pregame"); status_to_all("pregame", ss); ClientSocket *client; while (ss.state == SERVER_PREGAME) { ss.socket->wait_for_event(-1); client = ss.socket->get_client(); if (client != 0) { new_player(ss, client, PREGAME); } handle_dataflow(ss); send_players(ss); } } void do_ingame(ServerState &ss) { debug(INFO, "Starting game"); start_game(ss.gs); status_to_all("gamestart", ss); while (ss.state == SERVER_INGAME) { send_players(ss); send_gamestatus(ss); if (ss.gs.time_to_go_over != 0) { int timeout = ss.gs.time_to_go_over - time(NULL); ss.socket->wait_for_event(timeout * 1000); } else { ss.socket->wait_for_event(-1); } handle_dataflow(ss); if (all_beaten(ss.gs.table) && ss.gs.table.size() != 0 && can_go_over(ss.gs)) { put_over(ss.gs); debug(INFO, "Table over"); } if (game_ended(ss.gs)) { ss.state = SERVER_AFTERGAME; } } }