summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/SConstruct32
-rw-r--r--server/client.cpp213
-rw-r--r--server/client.h132
-rw-r--r--server/connection.cpp67
-rw-r--r--server/connection.h53
-rw-r--r--server/game.cpp456
-rw-r--r--server/game.h75
-rw-r--r--server/hand.cpp252
-rw-r--r--server/hand.h43
-rw-r--r--server/lobby.cpp81
-rw-r--r--server/lobby.h33
-rw-r--r--server/main.cpp13
-rw-r--r--server/player.cpp595
-rw-r--r--server/player.h118
-rw-r--r--server/score.cpp65
-rw-r--r--server/score.h40
-rw-r--r--server/tcpserver.cpp32
-rw-r--r--server/tcpserver.h27
-rw-r--r--server/tests/SConscript11
-rw-r--r--server/tests/calculate_score.cpp47
-rw-r--r--server/tests/test_framework.cpp1
-rw-r--r--server/wall.cpp42
-rw-r--r--server/wall.h30
23 files changed, 0 insertions, 2458 deletions
diff --git a/server/SConstruct b/server/SConstruct
deleted file mode 100644
index 09f34c7..0000000
--- a/server/SConstruct
+++ /dev/null
@@ -1,32 +0,0 @@
-import os
-
-env = Environment(
- ENV = os.environ,
-)
-
-AddOption('--release', action = 'store_true')
-AddOption('--profiling', action = 'store_true')
-
-if env['PLATFORM'] == 'win32':
- env.Append(CPPFLAGS = ['-D _WIN32_WINNT'])
- env.Append(LINKFLAGS = ['-Wl,--enable-auto-import'])
- env.Append(LIBS = ['libboost_system-mgw44-mt-1_44.a', 'wsock32', 'ws2_32'])
-else:
- env.Append(LIBS = ['boost_system', 'boost_serialization', 'pthread'])
-
-if not GetOption('release'):
- env.Append(CPPFLAGS = ['-Wall', '-g', '-D DEBUG'])
-
-if GetOption('profiling'):
- env.Append(CPPFLAGS = ['-pg'])
- env.Append(LINKFLAGS = ['-pg'])
-
-Export('env')
-
-env.Program('aotenjoud', Glob('*.cpp') + Glob('../common/*.cpp'))
-
-env.SConscript('tests/SConscript')
-
-Default('aotenjoud')
-
-# vim: syn=python
diff --git a/server/client.cpp b/server/client.cpp
deleted file mode 100644
index afec605..0000000
--- a/server/client.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-#include "client.h"
-
-#include <boost/bind.hpp>
-
-std::map<uint64_t, boost::weak_ptr<Client> > Client::client_list;
-
-Client::p Client::create(Connection::p c, std::string n) {
- Client::p p(new Client(c, n));
- p->start();
- return p;
-}
-
-Client::p Client::exists(Message::Login::p login_msg) {
- if(client_list.count(login_msg->cookie)) {
- // Obtain shared pointer to existing client.
- Client::p client = client_list[login_msg->cookie].lock();
-
- // TODO: Check if game still is ongoing?
- // A client should only be reconnected to if still participating in an active game.
- // It may however be proven that a client is always destructed if the game has ended, so this test might not be neccessary.
-
- return client;
-
- } else {
- // No or invalid existing client found.
- return Client::p();
- }
-}
-
-Client::Client(Connection::p c, std::string n) : connection(c), timer(c->socket.get_io_service()), nick_(n) {
-
-}
-
-Client::~Client() {
- client_list.erase(cookie);
-}
-
-void Client::reconnect(Connection::p c) {
- connection = c;
- // TODO: Resync client.
-}
-
-void Client::start() {
- // Create a unique cookie for this client.
- cookie = uint64_t(this); // TODO: Use a better cookie source.
-
- // Store a weak pointer to this client.
- client_list[cookie] = shared_from_this();
-
- // Logged in to lobby.
- connection->send(make_shared<Message::LoginResponse>(Message::LoginResponse::Lobby));
-}
-
-void Client::handle_login(Message::p msg, boost::function<void (Client::p)> login_callback) {
- if(msg->type != Message::Types::Login) {
- return;
- }
-
- Message::Login::p login_msg = dynamic_pointer_cast<Message::Login>(msg);
-
- // Check if nick is invalid.
- if(login_msg->nick.size() == 0) {
- connection->send(make_shared<Message::LoginResponse>(Message::LoginResponse::Invalid));
- connection->recv(boost::bind(&Client::handle_login, shared_from_this(), _1, login_callback));
- return;
- }
-
- connection->send(make_shared<Message::LoginResponse>(Message::LoginResponse::Lobby));
-
- nick_ = login_msg->nick;
-
- login_callback(shared_from_this());
-}
-
-void Client::handle_lobby(Message::p msg, boost::function<void (int)> lobby_callback) {
- if(msg->type != Message::Types::LobbyAction) {
- return;
- }
-
- Message::LobbyAction::p lobby_msg = dynamic_pointer_cast<Message::LobbyAction>(msg);
-
- lobby_callback(lobby_msg->index);
-}
-
-void Client::handle_ready(Message::p msg, boost::function<void ()> ready_callback) {
- if(msg->type != Message::Types::Ready) {
- // Wait for another message.
- connection->recv(boost::bind(&Client::handle_ready, shared_from_this(), _1, ready_callback));
- return;
- }
-
- ready_callback();
-}
-
-void Client::handle_action(Message::p msg, boost::function<void (Action)> action_callback, Actions possible_actions) {
- if(msg->type != Message::Types::RoundAction) {
- return;
- }
-
- Message::RoundAction::p action_msg = dynamic_pointer_cast<Message::RoundAction>(msg);
-
- // Check if the action is valid.
- if(possible_actions.contains(action_msg->action)) {
- action_callback(action_msg->action);
- } else {
- action_callback(possible_actions.back());
- }
-}
-
-void Client::lobby_status(const std::vector<std::string>& game_modes, boost::function<void (int)> callback) {
- Message::LobbyStatus::p msg = make_shared<Message::LobbyStatus>();
-
- msg->game_modes = game_modes;
-
- connection->send(msg);
-
- connection->recv(boost::bind(&Client::handle_lobby, shared_from_this(), _1, callback));
-}
-
-std::string Client::nick() {
- return nick_;
-}
-
-void Client::game_start(boost::function<void ()> callback, std::vector<std::string> players) {
- if(connection) {
- Message::GameStart::p msg = make_shared<Message::GameStart>();
-
- msg->players = players;
-
- connection->send(msg);
-
- connection->recv(boost::bind(&Client::handle_ready, shared_from_this(), _1, callback));
- } else {
- callback();
- }
-}
-
-void Client::round_start() {
- if(connection) {
- connection->send(make_shared<Message::RoundStart>());
- }
-}
-
-void Client::round_state(const PlayerState& pl_d, const PlayerState& pl_r, const PlayerState& pl_u, const PlayerState& pl_l, const GameState& g, const Actions& a) {
- if(connection) {
- connection->send(make_shared<Message::RoundState>(pl_d, pl_r, pl_u, pl_l, g, a));
- }
-}
-
-void Client::round_end(Message::RoundEnd::p msg, boost::function<void ()> callback) {
- if(connection) {
- connection->send(msg);
-
- // Check if we're waiting for ready.
- if(callback) {
- connection->recv(boost::bind(&Client::handle_ready, shared_from_this(), _1, callback));
- }
- } else {
- if(callback){callback();}
- }
-}
-
-void Client::get_action(boost::function<void (Action)> callback, Actions expected_actions) {
- if(connection) {
- connection->recv(boost::bind(&Client::handle_action, shared_from_this(), _1, callback, expected_actions));
- } else {
- callback(expected_actions.back());
- }
-}
-
-void Client::game_end(Message::GameEnd::p msg, boost::function<void ()> callback) {
- if(connection) {
- connection->send(msg);
-
- if(callback) {
- connection->recv(boost::bind(&Client::handle_ready, shared_from_this(), _1, callback));
- }
- } else {
- callback();
- }
-}
-
-unsigned int ClientDumb::id() {
- return 0;
-}
-
-std::string ClientDumb::nick() {
- return "CPU";
-}
-
-void ClientDumb::game_start(boost::function<void ()> callback, std::vector<std::string> players) {
- callback();
-}
-
-void ClientDumb::round_start() {
-
-}
-
-void ClientDumb::round_state(const PlayerState& pl_d, const PlayerState& pl_r, const PlayerState& pl_u, const PlayerState& pl_l, const GameState& g, const Actions& a) {
-
-}
-
-void ClientDumb::round_end(Message::RoundEnd::p msg, boost::function<void ()> callback) {
- if(callback) callback();
-}
-
-void ClientDumb::get_action(boost::function<void (Action)> callback, Actions expected_actions) {
- callback(expected_actions.back());
-}
-
-void ClientDumb::game_end(Message::GameEnd::p msg, boost::function<void ()> callback) {
- callback();
-}
diff --git a/server/client.h b/server/client.h
deleted file mode 100644
index e6cfcc7..0000000
--- a/server/client.h
+++ /dev/null
@@ -1,132 +0,0 @@
-#ifndef CLIENT_H
-#define CLIENT_H
-
-#include <string>
-#include <map>
-#include <boost/shared_ptr.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/function.hpp>
-#include <boost/asio.hpp>
-
-#include "connection.h"
-
-//! Abstract client base class.
-class ClientBase {
- public:
- typedef boost::shared_ptr<ClientBase> p;
-
- virtual ~ClientBase() {}
-
- //! Return client's nick.
- virtual std::string nick() = 0;
-
- //! Notify client of a game start.
- virtual void game_start(boost::function<void ()> callback, std::vector<std::string> players) = 0;
-
- //! Notify client of a round start.
- virtual void round_start() = 0;
-
- //! Send round state.
- virtual void round_state(const PlayerState& pl_d, const PlayerState& pl_r, const PlayerState& pl_u, const PlayerState& pl_l, const GameState& g, const Actions& a) = 0;
-
- //! Send round end.
- virtual void round_end(Message::RoundEnd::p msg, boost::function<void ()> callback) = 0;
-
- //! Get action. Upon connection error, last element of expected_actions will be provided.
- virtual void get_action(boost::function<void (Action)> callback, Actions expected_actions) = 0;
-
- virtual void game_end(Message::GameEnd::p msg, boost::function<void ()> callback) = 0;
-
-};
-
-//! Class implementing ClientBase for real clients.
-class Client : public ClientBase, public boost::enable_shared_from_this<Client> {
- public:
- typedef boost::shared_ptr<Client> p;
-
- //! Construct and return a new Client instance.
- static p create(Connection::p c, std::string n);
-
- //! Check if a Client instance of the user already exists and return it.
- static p exists(Message::Login::p login_msg);
-
- ~Client();
-
- private:
- uint64_t cookie;
-
- static std::map<uint64_t, boost::weak_ptr<Client> > client_list;
-
- Connection::p connection;
-
- boost::asio::deadline_timer timer;
-
- std::string nick_;
-
- Client(Connection::p c, std::string n);
-
- //! Start communicating.
- void start();
-
- //! Handle Login-message.
- void handle_login(Message::p msg, boost::function<void (Client::p)> login_callback);
-
- //! Handle LobbyAction-message.
- void handle_lobby(Message::p msg, boost::function<void (int)> lobby_callback);
-
- //! Handle Ready-message.
- void handle_ready(Message::p msg, boost::function<void ()> ready_callback);
-
- //! Handle Action-message.
- void handle_action(Message::p msg, boost::function<void (Action)> action_callback, Actions expected_actions);
-
- public:
- //! Inform client of lobby status (available game modes).
- void lobby_status(const std::vector<std::string>& game_modes, boost::function<void (int)> callback);
-
- //! Return client's nick.
- virtual std::string nick();
-
- //! Notify client of a game start.
- virtual void game_start(boost::function<void ()> callback, std::vector<std::string> players);
-
- //! Notify client of a round start.
- virtual void round_start();
-
- //! Send round state.
- virtual void round_state(const PlayerState& pl_d, const PlayerState& pl_r, const PlayerState& pl_u, const PlayerState& pl_l, const GameState& g, const Actions& a);
-
- //! Send round end.
- virtual void round_end(Message::RoundEnd::p msg, boost::function<void ()> callback);
-
- //! Get action. Upon connection error, last element of expected_actions will be provided.
- virtual void get_action(boost::function<void (Action)> callback, Actions expected_actions);
-
- //! Reconnect a player.
- virtual void reconnect(Connection::p c);
-
- virtual void game_end(Message::GameEnd::p msg, boost::function<void ()> callback);
-};
-
-typedef std::vector<Client> Clients;
-
-class ClientDumb : public ClientBase {
- public:
- virtual unsigned int id();
-
- virtual std::string nick();
-
- virtual void game_start(boost::function<void ()> callback, std::vector<std::string> players);
-
- virtual void round_start();
-
- virtual void round_state(const PlayerState& pl_d, const PlayerState& pl_r, const PlayerState& pl_u, const PlayerState& pl_l, const GameState& g, const Actions& a);
-
- virtual void round_end(Message::RoundEnd::p msg, boost::function<void ()> callback);
-
- virtual void get_action(boost::function<void (Action)> callback, Actions expected_actions);
-
- virtual void game_end(Message::GameEnd::p msg, boost::function<void ()> callback);
-};
-
-#endif
diff --git a/server/connection.cpp b/server/connection.cpp
deleted file mode 100644
index 1e38f7b..0000000
--- a/server/connection.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "connection.h"
-
-#include <boost/bind.hpp>
-
-Connection::Connection(boost::asio::io_service& io_service) : socket(io_service) {
-
-}
-
-void Connection::handle_read(uint8_t* data, std::size_t bytes, const boost::system::error_code& error_code) {
- if(error_code) {
- error("Read error.");
- return;
- }
-
- got_data(data, bytes);
-
- delete[] data;
-}
-
-void Connection::handle_write() {
-
-}
-
-void Connection::request_data(std::size_t bytes) {
- uint8_t* buf = new uint8_t[bytes];
-
- boost::asio::async_read(socket, boost::asio::buffer(buf, bytes),
- boost::bind(&Connection::handle_read, shared_from_this(), buf, bytes, boost::asio::placeholders::error));
-}
-
-void Connection::got_message(const Message::p& msg) {
- // TODO: Implement message queueing. For now, unexpected messages will cause an error.
-
- boost::function<void (Message::p)> f = recv_callback;
- recv_callback.clear();
- error_callback.clear();
-
- f(msg);
-}
-
-void Connection::error(const std::string& msg) {
- if(error_callback) {
- boost::function<void (const std::string&)> f = error_callback;
- recv_callback.clear();
- error_callback.clear();
-
- f(msg);
- } else {
- recv_callback.clear();
- }
-}
-
-void Connection::write_data(uint8_t* data, std::size_t bytes) {
- boost::asio::async_write(socket, boost::asio::buffer(data, bytes),
- boost::bind(&Connection::handle_write, shared_from_this()));
-}
-
-Connection::p Connection::create(boost::asio::io_service& io_service) {
- return Connection::p(new Connection(io_service));
-}
-
-void Connection::recv(boost::function<void (Message::p)> callback, boost::function<void (const std::string&)> error) {
- recv_callback = callback;
- error_callback = error;
-
- start_recv();
-} \ No newline at end of file
diff --git a/server/connection.h b/server/connection.h
deleted file mode 100644
index b51a67f..0000000
--- a/server/connection.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef CONNECTION_H
-#define CONNECTION_H
-
-#include <boost/asio.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/function.hpp>
-
-#include "../common/connectionbase.h"
-
-class Connection : public ConnectionBase, public boost::enable_shared_from_this<Connection> {
- private:
- friend class TCPServer;
- friend class Client;
-
- boost::asio::ip::tcp::socket socket;
-
- boost::function<void (Message::p)> recv_callback;
- boost::function<void (const std::string&)> error_callback;
-
- Connection(boost::asio::io_service& io_service);
-
- //! Callback for when data is read.
- void handle_read(uint8_t* data, std::size_t bytes, const boost::system::error_code& error_code);
-
- //! Callback for when data is written.
- void handle_write();
-
- protected:
- //! Implements request_data().
- virtual void request_data(std::size_t bytes);
-
- //! Implements write_data().
- virtual void write_data(uint8_t* data, std::size_t bytes);
-
- //! Implements got_message().
- virtual void got_message(const Message::p& msg);
-
- //! Implements error().
- virtual void error(const std::string& msg);
-
- public:
- typedef boost::shared_ptr<Connection> p;
-
- //! Constructs a new instance and returns a shared pointer.
- static p create(boost::asio::io_service& io_service);
-
- //! Initiates an asynchronous message receive.
- //! \param callback Callback for received message.
- //! \param error Callback for error.
- void recv(boost::function<void (Message::p)> callback, boost::function<void (const std::string&)> error = 0);
-};
-
-#endif
diff --git a/server/game.cpp b/server/game.cpp
deleted file mode 100644
index e791379..0000000
--- a/server/game.cpp
+++ /dev/null
@@ -1,456 +0,0 @@
-#include "game.h"
-
-#include <boost/bind.hpp>
-
-#include <iostream>
-#include <algorithm>
-#include <map>
-
-#ifdef DEBUG
-#include <ctime>
-#endif
-
-Game::p Game::create(ClientBase::p player_1, ClientBase::p player_2, ClientBase::p player_3, ClientBase::p player_4) {
- Game::p p(new Game(player_1, player_2, player_3, player_4));
- p->start();
- return p;
-}
-
-Game::~Game() {
- std::cout << "Game destroyed." << std::endl;
-}
-
-Game::Game(ClientBase::p player_1, ClientBase::p player_2, ClientBase::p player_3, ClientBase::p player_4) {
- players[0].client = player_1;
- players[1].client = player_2;
- players[2].client = player_3;
- players[3].client = player_4;
-}
-
-void Game::handle_ready() {
- if(--awaiting_players) {
- return;
- }
-
- std::cout << "All ready!" << std::endl;
-
- round_start();
-}
-
-void Game::start() {
- std::cout << "Started a game with "
- << players[0].client->nick() << ", "
- << players[1].client->nick() << ", "
- << players[2].client->nick() << " and "
- << players[3].client->nick() << "." << std::endl;
-
- round_wind = 0;
- round_num = 0;
-
- awaiting_players = 4;
-
- std::vector<std::string> pl;
-
- pl.push_back(players[0].client->nick());
- pl.push_back(players[1].client->nick());
- pl.push_back(players[2].client->nick());
- pl.push_back(players[3].client->nick());
-
- players[0].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl);
-
- pl.erase(pl.begin());
- pl.push_back(players[0].client->nick());
- players[1].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl);
-
- pl.erase(pl.begin());
- pl.push_back(players[1].client->nick());
- players[2].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl);
-
- pl.erase(pl.begin());
- pl.push_back(players[2].client->nick());
- players[3].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl);
-}
-
-void Game::round_start() {
- std::cout << "Starting a new round." << std::endl;
-
- // Build a new wall.
- wall.build();
-
- // Clear previous dora and draw a new.
- dora.clear();
- dora.push_back(wall.take_one());
-
- kan_dora_pending = false;
-
- // Notify players of round start.
- // TODO: Tell them where wall is broken.
- players[0].round_start(-round_num);
- players[1].round_start(-round_num + 1);
- players[2].round_start(-round_num + 2);
- players[3].round_start(-round_num + 3);
-
- // Draw hands.
- for(int i = 0; i < 13; i++) {
- players[0].draw(wall.take_one());
- players[1].draw(wall.take_one());
- players[2].draw(wall.take_one());
- players[3].draw(wall.take_one());
- }
-
- // Sort hands.
- players[0].hand.sort();
- players[1].hand.sort();
- players[2].hand.sort();
- players[3].hand.sort();
-
- // Give east an extra tile.
- players[0].draw(wall.take_one());
-
- current_player = 0;
-
- round_update_draw();
-}
-
-void Game::round_update_draw() {
- // Get possible actions for current player.
- Actions possible_actions = players[current_player].get_actions_draw();
-
- // Construct and send state to each client.
- PlayerNum player = 0;
- do {
- PlayerState state[4];
-
- state[0] = players[player].get_state();
- state[1] = players[player + 1].get_state_filtered();
- state[2] = players[player + 2].get_state_filtered();
- state[3] = players[player + 3].get_state_filtered();
-
- Actions a;
- if(player == current_player) {
- a = possible_actions;
- }
-
- GameState gstate = {dora, current_player - player, round_wind, round_num, 0, 0};
-
- players[player].client->round_state(state[0], state[1], state[2], state[3], gstate, a);
- } while(++player);
-
- // Await action from client.
- players[current_player].client->get_action(boost::bind(&Game::handle_action_draw, shared_from_this(), _1), possible_actions);
-}
-
-void Game::round_update_discard() {
- Tile discarded_tile = players[current_player].last_discard();
-
- std::map<int, Actions> possible_actions;
-
- // Construct and send state to each client.
- PlayerNum player = 0;
- do {
- PlayerState state[4];
-
- state[0] = players[player].get_state();
- state[1] = players[player + 1].get_state_filtered();
- state[2] = players[player + 2].get_state_filtered();
- state[3] = players[player + 3].get_state_filtered();
-
- Actions a;
- if(player != current_player) {
- if(a = players[player].get_actions_discard(discarded_tile, current_player - player)) {
- possible_actions[player] = a;
- }
- }
-
- GameState gstate = {dora, current_player - player, round_wind, round_num, 0, 0};
-
- players[player].client->round_state(state[0], state[1], state[2], state[3], gstate, a);
- } while(++player);
-
- preceding_action = Action::Pass;
- if(possible_actions.empty()) {
- awaiting_players = 1;
- handle_action_discard(Action::Pass, 0);
- } else {
- awaiting_players = possible_actions.size();
- for(std::map<int, Actions>::iterator it = possible_actions.begin(); it != possible_actions.end(); it++) {
- players[it->first].client->get_action(boost::bind(&Game::handle_action_discard, shared_from_this(), _1, it->first), it->second);
- }
- }
-}
-
-void Game::handle_action_draw(Action action) {
- switch(action.type) {
- case Action::Discard: {
- players[current_player].discard(action.target_offset);
- if(kan_dora_pending) {
- kan_dora_pending = false;
- dora.push_back(wall.take_one());
- }
- round_update_discard();
- } break;
-
- case Action::Tsumo: {
- players[current_player].declare_tsumo();
- round_end(Tsumo);
- } break;
-
- case Action::Riichi: {
- players[current_player].declare_riichi();
- round_update_draw();
- } break;
-
- case Action::Kan: {
- if(action.target_type == Action::Index) {
- players[current_player].make_kan(action.target_offset - 2);
- dora.push_back(wall.take_one());
- } else {
- players[current_player].make_kan_extend(action.target_offset);
- kan_dora_pending = true;
- }
- players[current_player].draw(wall.take_one());
- round_update_draw();
- } break;
-
- case Action::Draw:
- // Not implemented yet.
-
- // Will never occur on draw:
- case Action::Pass:
- case Action::Chi:
- case Action::Pon:
- case Action::Ron:
- break;
- }
-}
-
-void Game::handle_action_discard(Action action, int player) {
- switch(preceding_action.type) {
- case Action::Pass:
-
- case Action::Chi:
- if(action.type == Action::Chi) {
- preceding_action = action;
- preceding_action_owner = player;
- break;
- }
-
- case Action::Kan:
- if(action.type == Action::Kan) {
- preceding_action = action;
- preceding_action_owner = player;
- break;
- }
-
- case Action::Pon:
- if(action.type == Action::Pon) {
- preceding_action = action;
- preceding_action_owner = player;
- break;
- }
-
- // First ron.
- if(action.type == Action::Ron) {
- preceding_action = action;
- preceding_action_owner = player;
- break;
- }
-
- // Multiple ron.
- case Action::Ron:
- if(action.type == Action::Ron && current_player - player > current_player - preceding_action_owner) {
- preceding_action = action;
- preceding_action_owner = player;
- break;
- }
- break;
-
- // Will never occur on discard:
- case Action::Discard:
- case Action::Riichi:
- case Action::Tsumo:
- case Action::Draw:
- break;
- }
-
- if(--awaiting_players) {
- return;
- }
-
- switch(preceding_action.type) {
- case Action::Pass: {
- // Tile not claimed, next player draws.
-
- // Check if the wall has run out.
- if(wall.remaining() <= 14) {
- round_end(Draw);
- break;
- }
-
- players[++current_player].draw(wall.take_one());
- round_update_draw();
- } break;
-
- case Action::Chi: {
- players[preceding_action_owner].make_chi(players[current_player].claim(), preceding_action.target_offset);
- current_player = preceding_action_owner;
- round_update_draw();
- } break;
-
- case Action::Pon: {
- players[preceding_action_owner].make_pon(players[current_player].claim(), preceding_action.target_offset - 1, current_player - preceding_action_owner);
- current_player = preceding_action_owner;
- round_update_draw();
- } break;
-
- case Action::Kan: {
- players[preceding_action_owner].make_kan(players[current_player].claim(), preceding_action.target_offset - 2, current_player - preceding_action_owner);
- current_player = preceding_action_owner;
- kan_dora_pending = true;
- players[current_player].draw(wall.take_one());
- round_update_draw();
- } break;
-
- case Action::Ron: {
- players[preceding_action_owner].declare_ron(players[current_player].claim());
- round_end(Ron);
- } break;
-
- // Will never occur on discard:
- case Action::Discard:
- case Action::Riichi:
- case Action::Tsumo:
- case Action::Draw:
- break;
- }
-}
-
-Tiles merge_hand_open(const Tiles& hand, const Sets& open) {
- Tiles tiles = hand;
- for(Sets::const_iterator it = open.begin(); it != open.end(); it++) {
- tiles.insert(tiles.end(), it->tiles.begin(), it->tiles.end());
- }
- return tiles;
-}
-
-void Game::round_end(Endcondition end) {
-
- Message::RoundEnd::p msg[4] = make_shared<Message::RoundEnd>();
-
- for(CyclicInt<4> count_player = 0; count_player < 4; count_player++) {
- msg[count_player]->total_han = msg[count_player]->total_fu = msg[count_player]->won = msg[count_player]->score[count_player].won = 0;
- for(int i = 0; i < 4; i++) {
- msg[count_player]->score[count_player + i].score = players[count_player + i].get_state().score;
- msg[count_player]->score[count_player + i].won = 0;
- }
-
- switch(end) {
- case Draw:
- //msg->total_han = msg->total_fu = msg->won = 0;
- break;
-
- case Tsumo: {
- Player& player = players[current_player];
-
- msg[count_player]->hand = merge_hand_open(player.hand, player.open);
-
- msg[count_player]->yakus = player.won_han;
-
- msg[count_player]->total_han = player.won_value.han();
- msg[count_player]->total_fu = player.won_value.fu_rounded();
-
- if(current_player == round_num) {
- msg[count_player]->won = 3 * player.won_value.tsumo_east();
-
- for(int y = 0; y < 4; y++) {
- msg[count_player]->score[current_player + y].won = -(msg[count_player]->won / 3);
- }
- msg[count_player]->score[count_player + current_player ].won = msg[count_player]->won;
-
- } else {
- msg[count_player]->won = player.won_value.tsumo_east() + 2 * player.won_value.tsumo();
-
- for(int y = 0; y < 4; y++) {
- if(count_player + y == current_player) {
- msg[count_player]->score[y].won = msg[count_player]->won;
- } else if (count_player + y == round_num){
- msg[count_player]->score[y].won = -(player.won_value.tsumo_east());
- } else {
- msg[count_player]->score[y].won = -(player.won_value.tsumo());
- }
- }
- }
-
- } break;
-
- case Ron:
- for(int i = 0; i < 4; i++) {
- if(players[current_player + i].won) {
- Player& player = players[current_player + i];
-
- msg[count_player]->hand = merge_hand_open(player.hand, player.open);
-
- msg[count_player]->yakus = player.won_han;
-
- msg[count_player]->total_han = player.won_value.han();
- msg[count_player]->total_fu = player.won_value.fu_rounded();
-
- if(current_player + i == round_num) {
- msg[count_player]->won = player.won_value.ron_east();
- msg[count_player]->score[current_player + i].won = player.won_value.ron_east();
- msg[count_player]->score[current_player].won = -(player.won_value.ron_east());
-
- } else {
- msg[count_player]->won = player.won_value.ron();
- msg[count_player]->score[current_player + i].won = player.won_value.ron();
- msg[count_player]->score[current_player].won = -(player.won_value.ron());
- }
-
- // TODO: Support multiple wins.
- break;
- }
- }
- break;
- }
-
- //Send med score
- msg[count_player]->game_end = false;
- if(count_player == 3) break;
- }
-
- round_num++;
-
- if(!round_num) {
- round_wind++;
- //Do a 4-round game for now
- for(int i = 0; i < 4; ++i) {
- msg[i]->game_end = true;
- players[i].client->round_end(msg[i], 0);
- }
- game_end();
- } else {
- awaiting_players = 4;
- players[0].client->round_end(msg[0], boost::bind(&Game::handle_ready, shared_from_this()));
- players[1