Commit 7f24d557 authored by Jonathan Michalon's avatar Jonathan Michalon

Fix game state confusion and "table" naming mess

There was a confusion between a "game" and a "table". "tables" was just a list
of Game's. Also a table was "open" as long as it was created but a Game in
state "open" meant that it was open to new players.
This commit removes the "table" terminology, merges TablesFilters into GameState
and implements filtering in the enumeration method.
parent 8b95f39a
......@@ -13,7 +13,7 @@ public class Bot : OVCCClient.Client
throws Error
{
uint player_suffix = 7;
int table_to_join = -1;
int game_to_join = -1;
/* connect to the server */
yield bind_to (server);
......@@ -36,29 +36,29 @@ public class Bot : OVCCClient.Client
}
debug ("Logged in with player_suffix = %u", player_suffix);
/* join an open table */
/* join an open game */
var list = yield server.enumerate_tables(OVCCClient.TablesFilter.OPEN);
print ("List of open tables:\n");
var list = yield server.enumerate_games (OVCC.GameState.NEW | OVCC.GameState.PLAYER_WAITING);
print ("List of open games:\n");
var idx = 0;
foreach (var d in list) {
print (" - %02i: %s\n", idx, d.to_string());
idx++;
}
if (list.length == 0) {
print ("(currently no table)\n");
print ("(currently no game)\n");
}
print ("What table index to join? (-1 or empty for any open table) ");
print ("What game index to join? (-1 or empty for any open game) ");
string? line = interactive ? stdin.read_line () : "";
if (line != null && line != "") {
table_to_join = int.parse(line);
game_to_join = int.parse (line);
}
debug ("Trying to join table %i...", table_to_join);
debug ("Trying to join game %i...", game_to_join);
try {
yield join_table (table_to_join);
yield join_game (game_to_join);
} catch (Error e4) {
leave ();
throw e4;
......
......@@ -284,7 +284,7 @@ G_MODULE_EXPORT void
game_start_activate_handler (GtkAction *action,
App *app)
{
if (app->game && ovcc_game_get_state (app->game) == OVCC_GAME_STATE_STOPPED) {
if (app->game && ovcc_game_state_can_start (ovcc_game_get_state (app->game))) {
GError *err = NULL;
if (! ovcc_game_start (app->game, &err)) {
......@@ -406,8 +406,8 @@ on_game_state_changed (OVCCGame *game,
started = TRUE;
break;
case OVCC_GAME_STATE_STOPPED:
app_set_status (app, _("Game stopped"));
case OVCC_GAME_STATE_PLAYER_WAITING:
app_set_status (app, _("Game now waiting player"));
can_start = TRUE;
break;
}
......
......@@ -141,16 +141,16 @@ ovccgtk_client_new (const gchar *name)
static void
ovccgtk_client_join_table_ready (GObject *object,
GAsyncResult *result,
gpointer data)
ovccgtk_client_join_game_ready (GObject *object,
GAsyncResult *result,
gpointer data)
{
GTask *task = data;
GError *error = NULL;
g_debug ("ovccclient_client_join_table_finish()");
if (! ovccclient_client_join_table_finish (OVCCCLIENT_CLIENT (object),
result, &error)) {
g_debug ("ovccclient_client_join_game_finish()");
if (! ovccclient_client_join_game_finish (OVCCCLIENT_CLIENT (object),
result, &error)) {
ovccclient_client_leave (OVCCCLIENT_CLIENT (object), NULL);
g_task_return_error (task, error);
g_object_unref (task);
......@@ -176,10 +176,10 @@ ovccgtk_client_login_ready (GObject *object,
g_task_return_error (task, error);
g_object_unref (task);
} else {
g_debug ("ovccclient_client_join_table()");
ovccclient_client_join_table (OVCCCLIENT_CLIENT (object), -1,
g_task_get_cancellable (task),
ovccgtk_client_join_table_ready, task);
g_debug ("ovccclient_client_join_game()");
ovccclient_client_join_game (OVCCCLIENT_CLIENT (object), -1,
g_task_get_cancellable (task),
ovccgtk_client_join_game_ready, task);
}
}
......
......@@ -33,7 +33,7 @@ libovcc_la_SOURCES = ovcc-board.vala \
network/ovcc-network-disconnect-message.vala \
network/ovcc-network-welcome-message.vala \
network/ovcc-network-error-message.vala \
network/ovcc-network-list-tables-message.vala
network/ovcc-network-list-games-message.vala
ovccinclude_HEADERS = ovcc.h
vapi_DATA = ovcc.vapi
......
......@@ -25,7 +25,7 @@ public class OVCC.Network.JoinMessage : VariantMessage
{
public override MessageType message_type { get { return MessageType.JOIN; } }
public int table_index { get; set; default = -1; }
public int game_index { get; set; default = -1; }
public State status { get; set; default = State.QUERY; }
public enum State
......@@ -38,12 +38,12 @@ public class OVCC.Network.JoinMessage : VariantMessage
public JoinMessage (int idx)
{
Object (table_index: idx);
Object (game_index: idx);
}
protected override Variant build_variant ()
{
return new Variant ("(ii)", status, table_index);
return new Variant ("(ii)", status, game_index);
}
protected override void parse_variant (uint8[] data)
{
......@@ -51,6 +51,6 @@ public class OVCC.Network.JoinMessage : VariantMessage
int idx;
parse_data (data, "(ii)", out s, out idx);
status = s;
table_index = idx;
game_index = idx;
}
}
......@@ -18,13 +18,13 @@
*
*/
public class OVCC.Network.ListTablesMessage : VariantMessage
public class OVCC.Network.ListGamesMessage : VariantMessage
{
public override MessageType message_type { get { return MessageType.LIST_TABLES; } }
public override MessageType message_type { get { return MessageType.LIST_GAMES; } }
public GameDescription[] descriptions;
public ListTablesMessage (GameDescription[] descriptions = {})
public ListGamesMessage (GameDescription[] descriptions = {})
{
this.descriptions = descriptions;
}
......
......@@ -46,7 +46,7 @@ namespace OVCC.Network
GAMEDATA,
SIGNAL,
DISCONNECT,
LIST_TABLES,
LIST_GAMES,
INVALID
}
......@@ -61,7 +61,7 @@ namespace OVCC.Network
typeof (GamedataMessage),
typeof (SignalMessage),
typeof (DisconnectMessage),
typeof (ListTablesMessage)
typeof (ListGamesMessage)
};
public abstract MessageType message_type { get; }
......
......@@ -166,7 +166,6 @@ namespace OVCC.Network
}
break;
case OVCC.GameState.FINISHED:
case OVCC.GameState.STOPPED:
break; /* should be automatic */
case OVCC.GameState.ABORTED:
game_object.abort();
......
......@@ -38,18 +38,35 @@ namespace OVCC
/**
* Possible states of a game.
* Only one state at a time. "Flags" type is used for filtering only.
*
* @param STOPPED The game is stopped (not started or finished)
* @param NEW The game is just created but no player joined
* @param PLAYER_WAITING The game has players but is not started yet
* @param STARTED The game is started
* @param FINISHED The game is finished
* @param ABORTED The game is stopped, but not finished
*/
[Flags]
public enum GameState
{
STOPPED,
NEW,
PLAYER_WAITING,
STARTED,
FINISHED,
ABORTED
ABORTED;
public bool is_open()
{
return NEW in this || PLAYER_WAITING in this;
}
public bool is_done()
{
return FINISHED in this || ABORTED in this;
}
public bool can_start()
{
return PLAYER_WAITING in this;
}
}
/**
......@@ -83,7 +100,7 @@ namespace OVCC
{
private SList<Player> _players = null;
public GameState state { get; private set; default = GameState.STOPPED; }
public GameState state { get; private set; default = GameState.NEW; }
public Stack stack { private get; construct; }
public Board board { get; construct; }
public unowned SList<Player> players { get { return this._players; } }
......@@ -130,9 +147,15 @@ namespace OVCC
});
this.player_added.connect ((p) => {
this.notify_property ("players");
if (players.length() == 1 && this.state == GameState.NEW) {
this.state = GameState.PLAYER_WAITING;
}
});
this.player_removed.connect ((p) => {
this.notify_property ("players");
if (players.length() < 1 && this.state == GameState.PLAYER_WAITING) {
this.state = GameState.NEW;
}
});
this.turn_finished.connect (() => next_player ());
......@@ -519,7 +542,7 @@ namespace OVCC
*/
public GameDescription describe ()
{
/* FIXME table name */
/* FIXME game name */
return new GameDescription ("Unnamed Game", state, get_player_nicks());
}
}
......
......@@ -147,17 +147,17 @@ namespace OVCCClient
}
/**
* Joins a table asynchronously
* Joins a game asynchronously
*
* @param index The index of the table to join, -1 means any open table
* @param index The index of the game to join, -1 means any open game
* @param cancellable a Cancellable object or null
* @return The game corresponding to that table
* @return The joined Game
*/
public async Game join_table (int index,
Cancellable? cancellable = null)
public async Game join_game (int index,
Cancellable? cancellable = null)
throws ServerError, IOError
{
game = yield server.join_table (index, player, cancellable);
game = yield server.join_game (index, player, cancellable);
sigqueue.add (game, game.notify["current-player"].connect (() => {
debug ("Current player changed to \"%s\"", game.current_player.nick);
......
......@@ -63,9 +63,9 @@ namespace OVCCClient
*/
MISSING_AUTHENTICATION,
/**
* No open table available
* No open game available
*/
NO_OPEN_TABLE,
NO_OPEN_GAME,
/**
* Data could not be reached where it was searched for
*/
......@@ -75,32 +75,7 @@ namespace OVCCClient
*/
FAILED
}
/**
* Flags for filtering table list
*/
[Flags]
public enum TablesFilter
{
/**
* All tables
*/
ALL,
/**
* Only open tables
*/
OPEN,
/**
* Only tables with people waiting
*/
PEOPLE_WAITING,
/**
* Only tables where people ask for other players
*/
ASKING_FOR_PEOPLE
}
/**
* A class representing the server side of the network clients
*
......@@ -438,44 +413,54 @@ namespace OVCCClient
}
/**
* Enumerate some tables available on the server
* Enumerate some games available on the server
*
* This method retrieves a list of available tables currently on the server
* This method retrieves a list of available games currently on the server
* matching the given filter
*
* @param filter the {@link TablesFilter} to use
* @param a mask of {@link GameState}s to filter in
* @return A list of {@link GameDescription} on the remote server matching //filter//
*/
public async GameDescription[]? enumerate_tables (TablesFilter filter,
Cancellable? cancellable = null)
public async GameDescription[]? enumerate_games (GameState? filter = null,
Cancellable? cancellable = null)
throws ServerError, IOError
requires (connection != null)
{
/* ask the server to send the list */
yield send_message (new ListTablesMessage (), cancellable);
yield send_message (new ListGamesMessage (), cancellable);
/* wait for answer */
Message msg = yield receive_type (MessageType.LIST_TABLES, cancellable);
ListTablesMessage list = msg as ListTablesMessage;
Message msg = yield receive_type (MessageType.LIST_GAMES, cancellable);
ListGamesMessage list = msg as ListGamesMessage;
/* FIXME implement filter */
/* filter the list */
GameDescription[] filtered = {};
if (filter != null) {
foreach (var d in list.descriptions) {
if (d.state in filter) {
filtered += d;
}
}
} else {
filtered = list.descriptions;
}
return list.descriptions;
return filtered;
}
/**
* Join a table, index -1 means any open table
* Join a game, index -1 means any open game
*
* The index is typically within the list returned by enumerate_tables()
* The index is typically within the list returned by enumerate_games()
*
* @param index The table index
* @param index The game index
* @param player The player to make join
* @param cancellable a Cancellable object or null
* @return The game instance corresponding to the one joint on the remote server
*/
public async Game join_table (int index,
Player player,
Cancellable? cancellable = null)
public async Game join_game (int index,
Player player,
Cancellable? cancellable = null)
throws ServerError, IOError
requires (connection != null)
{
......@@ -491,9 +476,9 @@ namespace OVCCClient
JoinMessage join = msg as JoinMessage;
if (join.status != JoinMessage.State.OK) {
if (index == -1) {
throw new ServerError.NO_OPEN_TABLE ("Server said that no open table is available");
throw new ServerError.NO_OPEN_GAME ("Server said that no open game is available");
} else {
throw new ServerError.NO_OPEN_TABLE ("Server said that it is not an open table");
throw new ServerError.NO_OPEN_GAME ("Server said that it is not an open game");
}
}
......
......@@ -29,7 +29,7 @@ static const string TILESET_FILE = "tileset.xml";
public class Server: ThreadedSocketService
{
private List<Client> clients = null;
private List<Game> tables = null;
private List<Game> games = null;
private TilesDef tiles = new TilesDef ();
private TileSet tileset = new TileSet ();
private string tiles_data;
......@@ -37,42 +37,42 @@ public class Server: ThreadedSocketService
public string name { get; construct; }
public uint port { get; construct; default = 0xdead; }
public uint max_tables { get; set; default = 1; }
public uint max_games { get; set; default = 5; }
public signal void stopped ();
/* removes old tables */
private void cleanup_tables ()
/* removes old games */
private void cleanup_games ()
{
lock (tables) {
foreach (var game in tables) {
if (game.state != GameState.STOPPED && game.players.length() < 1) {
tables.remove (game);
debug ("Cleaned up table %p", game);
lock (games) {
foreach (var game in games) {
if (game.players.length () < 1 && !game.state.is_open ()) {
games.remove (game);
debug ("Cleaned up game %p", game);
}
}
}
}
/* get the most populated open table available, maybe creating one */
private Game? pick_open_table ()
/* get the most populated open game available, maybe creating one */
private Game? pick_open_game ()
{
Game? candidate = null;
uint n_open_tables = 0;
uint n_open_games = 0;
lock (tables) {
foreach (var game in tables) {
if (game.state == GameState.STOPPED &&
lock (games) {
foreach (var game in games) {
if (game.state.is_open() &&
(candidate == null || candidate.players.length() < game.players.length())) {
candidate = game;
}
n_open_tables++;
n_open_games++;
}
/* if there is no open tables but room for more, create a new one */
if (candidate == null && n_open_tables < max_tables) {
/* if there is no open games but room for more, create a new one */
if (candidate == null && n_open_games < max_games) {
candidate = new Game (tileset, null);
tables.prepend (candidate);
debug ("Added table %p, which describes as '%s'", candidate, candidate.describe().to_string());
games.prepend (candidate);
debug ("Added game %p, which describes as '%s'", candidate, candidate.describe().to_string());
}
}
......@@ -137,27 +137,27 @@ public class Server: ThreadedSocketService
private bool handle_join (JoinMessage msg, Client client)
throws Error
{
Game? table = null;
if (msg.table_index == -1) {
table = pick_open_table ();
Game? game = null;
if (msg.game_index == -1) {
game = pick_open_game ();
} else {
table = tables.nth_data (msg.table_index);
game = games.nth_data (msg.game_index);
}
if (table == null || table.state != GameState.STOPPED) {
debug ("Bad table %d, sending fail join message", msg.table_index);
var join = new JoinMessage (msg.table_index);
if (game == null || !game.state.is_open()) {
debug ("Bad game %d, sending fail join message", msg.game_index);
var join = new JoinMessage (msg.game_index);
join.status = JoinMessage.State.FAILED;
client.send (join);
return false;
}
debug ("Accepting table join");
var join = new JoinMessage (msg.table_index);
debug ("Accepting game join");
var join = new JoinMessage (msg.game_index);
join.status = JoinMessage.State.OK;
client.send (join);
client.join (table);
client.join (game);
debug ("Sending game data");
var data = new GamedataMessage (client.game.get_stack_ids(),
......@@ -167,14 +167,14 @@ public class Server: ThreadedSocketService
return true;
}
private bool handle_list_tables (ListTablesMessage msg, Client client)
private bool handle_list_games (ListGamesMessage msg, Client client)
throws Error
{
GameDescription[] list = {};
foreach (var t in tables) {
foreach (var t in games) {
list += t.describe ();
}
var data = new ListTablesMessage (list);
var data = new ListGamesMessage (list);
client.send (data);
return true;
......@@ -239,12 +239,12 @@ public class Server: ThreadedSocketService
warning ("Problems handling a client join... left in unknown state");
}
break;
case MessageType.LIST_TABLES:
case MessageType.LIST_GAMES:
try {
handle_list_tables (msg as ListTablesMessage, client);
handle_list_games (msg as ListGamesMessage, client);
} catch (Error e) {
/* FIXME send error message */
warning ("Problems handling a list table action...");
warning ("Problems handling a list game action...");
}
break;
case MessageType.SIGNAL:
......@@ -257,7 +257,7 @@ public class Server: ThreadedSocketService
this.clients.remove (client);
}
try { client.disconnect (); } catch { /* we don't care if disconnection failed */ }
cleanup_tables ();
cleanup_games ();
debug ("Closed connection");
return true;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment