// This file is part of Timmi. // // Timmi is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. #include #include #include #include bool Player::operator==(const Player &other) const { return id == other.id && name == other.name && handsize == other.handsize && state == other.state && let_go_over == other.let_go_over; } Player parse_player(std::string line) { Player player = {0, "", 0, DISCONNECTED, false}; bool error = false; std::istringstream stream(line); std::string part; stream >> part; if (part != "player") { error = true; } stream >> player.id; stream >> part; if (part == "active") { player.state = ACTIVE; } else if (part == "observer") { player.state = OBSERVER; } else if (part == "disconnected") { player.state = DISCONNECTED; } else { error = true; } stream >> player.handsize; stream >> part; if (part == "over") { player.let_go_over = true; } else if (part == "notover") { player.let_go_over = false; } else { error = true; } stream.get(); // Remove space std::getline(stream, player.name); if (error) { std::ostringstream msg(""); msg << "Parsing player line failed: " << line; debug(WARNING, msg.str()); } return player; } Event parse_event(std::string line) { Event event = {NOEVENT, 0, {NOJOKER, NOSUIT, NORANK}, {NOJOKER, NOSUIT, NORANK}}; std::istringstream stream(line); std::string part; stream >> part; if (part == "over") { stream >> part; if (part == "timeout") { event.eventtype = OVER_TIMEOUT; } else if (part == "cantadd") { event.eventtype = OVER_CANTADD; } else if (part == "letgoover") { event.eventtype = OVER_LETGOOVER; } } else if (part == "move") { stream >> event.player; stream >> part; if (part == "pickup") { event.eventtype = MOVE_PICKUP; } else if (part == "hit") { event.eventtype = MOVE_HIT; stream >> part; event.owncard = string_to_card(part); } else if (part == "beat") { event.eventtype = MOVE_BEAT; stream >> part; event.othercard = string_to_card(part); stream >> part; event.owncard = string_to_card(part); } else if (part == "move") { event.eventtype = MOVE_MOVE; stream >> part; event.owncard = string_to_card(part); } } else if (part == "player") { stream >> event.player; stream >> part; if (part == "join") { event.eventtype = STATUS_JOIN; } else if (part == "disconnected") { event.eventtype = STATUS_DISCONNECTED; } else if (part == "won") { event.eventtype = STATUS_WON; } else if (part == "observer") { event.eventtype = STATUS_OBSERVER; } else if (part == "active") { event.eventtype = STATUS_ACTIVE; } } return event; } TimmiClient::TimmiClient(std::string name): name_(name), myid_(0), turn_(0), decksize_(0), trump_(), ingame_(false), status_valid_(false), c_cards_in_hand_(5), c_over_delay_(30), c_deck_count_(1), c_allow_jokers_(0) {} void TimmiClient::handle_line(std::string line) { TCPClient::handle_line(line); std::istringstream stream(line); std::string part; stream >> part; if (part == "status_begin") { get_status(); } else if (part == "hello") { stream >> myid_; std::ostringstream msg(""); msg << "name " << name_; send_line(msg.str()); } else if (part == "chat") { int who = 0; stream >> who; stream.get(); // Remove space part = ""; getline(stream, part); handle_chat(who, part); } else if (part == "error") { stream >> part; handle_error(part); } else if (part == "winners") { std::vector winners; int who; while (stream >> who) { winners.push_back(who); } handle_winners(winners); status_valid_ = false; } else if (part != "") { Event event = parse_event(line); if (event.eventtype != NOEVENT) { handle_event(event); status_valid_ = false; } else { std::ostringstream msg(""); msg << "Unknown command from server: " << line; debug(WARNING, msg.str()); } } } void TimmiClient::get_status() { players_.clear(); std::string line(""); while (true) { while (!socket_->getline(line)) { socket_->wait_for_event(); } TCPClient::handle_line(line); std::istringstream stream(line); std::string part(""); stream >> part; if (part == "player") { players_.push_back(parse_player(line)); } else if (part == "table") { table_ = string_to_table(line); } else if (part == "hand") { hand_ = string_to_hand(line); } else if (part == "deck") { stream >> decksize_ >> part; trump_ = string_to_card(part); } else if (part == "turn") { stream >> turn_; } else if (part == "gamestate") { stream >> part; if (part == "pregame") { ingame_ = false; } else if (part == "ingame") { ingame_ = true; } else { std::ostringstream msg(""); msg << "Unknown gamestate " << part; debug(WARNING, msg.str()); } } else if (part == "setting") { stream >> part; if (part == "CARDS_IN_HAND") { stream >> c_cards_in_hand_; } else if (part == "OVER_DELAY") { stream >> c_over_delay_; } else if (part == "DECK_COUNT") { stream >> c_deck_count_; } else if (part == "ALLOW_JOKERS") { stream >> c_allow_jokers_; } else { std::ostringstream msg(""); msg << "Unknown setting " << part; debug(INFO, msg.str()); } } else if (part == "status_end") { break; } else if (part != "") { std::ostringstream msg(""); msg << "Unknown status command: " << line; debug(WARNING, msg.str()); } } status_valid_ = true; } Player TimmiClient::find_player(int playerid) const { int i; for (i = 0; i < players_.size(); i++) { if (players_.at(i).id == playerid) { return players_.at(i); } } debug(WARNING, "Player not found"); Player player = {0, "", 0, DISCONNECTED, false}; return player; } Player &TimmiClient::find_player_mutable(int playerid) { int i; for (i = 0; i < players_.size(); i++) { if (players_.at(i).id == playerid) { return players_.at(i); } } debug(CRITICAL, "Player not found for mutating"); return players_.at(0); } int TimmiClient::next_turn_id() const { int i; // Find current turn for (i = 0; i < players_.size(); i++) { if (players_.at(i).id == turn_) { break; } } int origturnpos = i; for (i = origturnpos + 1; i != origturnpos; i++) { if (i >= players_.size()) { i = 0; } if (players_.at(i).state == ACTIVE) { return players_.at(i).id; } } debug(WARNING, "No next turn possible"); return turn_; } int TimmiClient::next_turn_cardcount() const { return find_player(next_turn_id()).handsize; } std::string TimmiClient::describe_player(int playerid) const { return find_player(playerid).name; } void TimmiClient::handle_event(const Event &event) { std::ostringstream msg(""); switch (event.eventtype) { case MOVE_PICKUP: msg << "Player " << describe_player(event.player); msg << " picked up table: " << table_to_string(table_); break; case MOVE_HIT: msg << "Player " << describe_player(event.player); msg << " hit card " << card_to_string(event.owncard); break; case MOVE_BEAT: msg << "Player " << describe_player(event.player); msg << " beat card " << card_to_string(event.othercard); msg << " with card " << card_to_string(event.owncard); break; case MOVE_MOVE: msg << "Player " << describe_player(event.player); msg << " moved cards by adding " << card_to_string(event.owncard); break; case OVER_TIMEOUT: msg << "Table went over: Timeouted"; break; case OVER_CANTADD: msg << "Table went over: No player could add"; break; case OVER_LETGOOVER: msg << "Table went over: Players allowed"; break; case STATUS_DISCONNECTED: msg << "Player " << describe_player(event.player) << " disconnected"; break; case STATUS_WON: msg << "Player " << describe_player(event.player) << " ran out of cards and won"; break; case STATUS_OBSERVER: msg << "Player " << describe_player(event.player) << " moved to observer mode"; break; case STATUS_ACTIVE: msg << "Player " << describe_player(event.player) << " is active again"; break; case STATUS_JOIN: msg << "Player " << describe_player(event.player) << " joined the game"; break; default: debug(WARNING, "Invalid event"); } debug(INFO, msg.str()); } void TimmiClient::handle_chat(int player, std::string message) { std::ostringstream msg(""); msg << "<" << describe_player(player) << "> " << message; debug(INFO, msg.str()); } void TimmiClient::handle_error(std::string errorcode) { std::ostringstream msg(""); msg << "Server reported error: " << errorcode; debug(WARNING, msg.str()); } void TimmiClient::handle_winners(std::vector winners) { std::ostringstream msg(""); msg << "Game ended, winners: "; int i; for (i = 0; i < winners.size(); i++) { if (i != 0) { msg << ", "; } msg << describe_player(winners.at(i)); } debug(INFO, msg.str()); }