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 ...@@ -13,7 +13,7 @@ public class Bot : OVCCClient.Client
throws Error throws Error
{ {
uint player_suffix = 7; uint player_suffix = 7;
int table_to_join = -1; int game_to_join = -1;
/* connect to the server */ /* connect to the server */
yield bind_to (server); yield bind_to (server);
...@@ -36,29 +36,29 @@ public class Bot : OVCCClient.Client ...@@ -36,29 +36,29 @@ public class Bot : OVCCClient.Client
} }
debug ("Logged in with player_suffix = %u", player_suffix); 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); var list = yield server.enumerate_games (OVCC.GameState.NEW | OVCC.GameState.PLAYER_WAITING);
print ("List of open tables:\n"); print ("List of open games:\n");
var idx = 0; var idx = 0;
foreach (var d in list) { foreach (var d in list) {
print (" - %02i: %s\n", idx, d.to_string()); print (" - %02i: %s\n", idx, d.to_string());
idx++; idx++;
} }
if (list.length == 0) { 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 () : ""; string? line = interactive ? stdin.read_line () : "";
if (line != null && 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 { try {
yield join_table (table_to_join); yield join_game (game_to_join);
} catch (Error e4) { } catch (Error e4) {
leave (); leave ();
throw e4; throw e4;
......
...@@ -284,7 +284,7 @@ G_MODULE_EXPORT void ...@@ -284,7 +284,7 @@ G_MODULE_EXPORT void
game_start_activate_handler (GtkAction *action, game_start_activate_handler (GtkAction *action,
App *app) 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; GError *err = NULL;
if (! ovcc_game_start (app->game, &err)) { if (! ovcc_game_start (app->game, &err)) {
...@@ -406,8 +406,8 @@ on_game_state_changed (OVCCGame *game, ...@@ -406,8 +406,8 @@ on_game_state_changed (OVCCGame *game,
started = TRUE; started = TRUE;
break; break;
case OVCC_GAME_STATE_STOPPED: case OVCC_GAME_STATE_PLAYER_WAITING:
app_set_status (app, _("Game stopped")); app_set_status (app, _("Game now waiting player"));
can_start = TRUE; can_start = TRUE;
break; break;
} }
......
...@@ -141,15 +141,15 @@ ovccgtk_client_new (const gchar *name) ...@@ -141,15 +141,15 @@ ovccgtk_client_new (const gchar *name)
static void static void
ovccgtk_client_join_table_ready (GObject *object, ovccgtk_client_join_game_ready (GObject *object,
GAsyncResult *result, GAsyncResult *result,
gpointer data) gpointer data)
{ {
GTask *task = data; GTask *task = data;
GError *error = NULL; GError *error = NULL;
g_debug ("ovccclient_client_join_table_finish()"); g_debug ("ovccclient_client_join_game_finish()");
if (! ovccclient_client_join_table_finish (OVCCCLIENT_CLIENT (object), if (! ovccclient_client_join_game_finish (OVCCCLIENT_CLIENT (object),
result, &error)) { result, &error)) {
ovccclient_client_leave (OVCCCLIENT_CLIENT (object), NULL); ovccclient_client_leave (OVCCCLIENT_CLIENT (object), NULL);
g_task_return_error (task, error); g_task_return_error (task, error);
...@@ -176,10 +176,10 @@ ovccgtk_client_login_ready (GObject *object, ...@@ -176,10 +176,10 @@ ovccgtk_client_login_ready (GObject *object,
g_task_return_error (task, error); g_task_return_error (task, error);
g_object_unref (task); g_object_unref (task);
} else { } else {
g_debug ("ovccclient_client_join_table()"); g_debug ("ovccclient_client_join_game()");
ovccclient_client_join_table (OVCCCLIENT_CLIENT (object), -1, ovccclient_client_join_game (OVCCCLIENT_CLIENT (object), -1,
g_task_get_cancellable (task), g_task_get_cancellable (task),
ovccgtk_client_join_table_ready, task); ovccgtk_client_join_game_ready, task);
} }
} }
......
...@@ -33,7 +33,7 @@ libovcc_la_SOURCES = ovcc-board.vala \ ...@@ -33,7 +33,7 @@ libovcc_la_SOURCES = ovcc-board.vala \
network/ovcc-network-disconnect-message.vala \ network/ovcc-network-disconnect-message.vala \
network/ovcc-network-welcome-message.vala \ network/ovcc-network-welcome-message.vala \
network/ovcc-network-error-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 ovccinclude_HEADERS = ovcc.h
vapi_DATA = ovcc.vapi vapi_DATA = ovcc.vapi
......
...@@ -25,7 +25,7 @@ public class OVCC.Network.JoinMessage : VariantMessage ...@@ -25,7 +25,7 @@ public class OVCC.Network.JoinMessage : VariantMessage
{ {
public override MessageType message_type { get { return MessageType.JOIN; } } 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 State status { get; set; default = State.QUERY; }
public enum State public enum State
...@@ -38,12 +38,12 @@ public class OVCC.Network.JoinMessage : VariantMessage ...@@ -38,12 +38,12 @@ public class OVCC.Network.JoinMessage : VariantMessage
public JoinMessage (int idx) public JoinMessage (int idx)
{ {
Object (table_index: idx); Object (game_index: idx);
} }
protected override Variant build_variant () 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) protected override void parse_variant (uint8[] data)
{ {
...@@ -51,6 +51,6 @@ public class OVCC.Network.JoinMessage : VariantMessage ...@@ -51,6 +51,6 @@ public class OVCC.Network.JoinMessage : VariantMessage
int idx; int idx;
parse_data (data, "(ii)", out s, out idx); parse_data (data, "(ii)", out s, out idx);
status = s; status = s;
table_index = idx; game_index = idx;
} }
} }
...@@ -18,13 +18,13 @@ ...@@ -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 GameDescription[] descriptions;
public ListTablesMessage (GameDescription[] descriptions = {}) public ListGamesMessage (GameDescription[] descriptions = {})
{ {
this.descriptions = descriptions; this.descriptions = descriptions;
} }
......
...@@ -46,7 +46,7 @@ namespace OVCC.Network ...@@ -46,7 +46,7 @@ namespace OVCC.Network
GAMEDATA, GAMEDATA,
SIGNAL, SIGNAL,
DISCONNECT, DISCONNECT,
LIST_TABLES, LIST_GAMES,
INVALID INVALID
} }
...@@ -61,7 +61,7 @@ namespace OVCC.Network ...@@ -61,7 +61,7 @@ namespace OVCC.Network
typeof (GamedataMessage), typeof (GamedataMessage),
typeof (SignalMessage), typeof (SignalMessage),
typeof (DisconnectMessage), typeof (DisconnectMessage),
typeof (ListTablesMessage) typeof (ListGamesMessage)
}; };
public abstract MessageType message_type { get; } public abstract MessageType message_type { get; }
......
...@@ -166,7 +166,6 @@ namespace OVCC.Network ...@@ -166,7 +166,6 @@ namespace OVCC.Network
} }
break; break;
case OVCC.GameState.FINISHED: case OVCC.GameState.FINISHED:
case OVCC.GameState.STOPPED:
break; /* should be automatic */ break; /* should be automatic */
case OVCC.GameState.ABORTED: case OVCC.GameState.ABORTED:
game_object.abort(); game_object.abort();
......
...@@ -38,18 +38,35 @@ namespace OVCC ...@@ -38,18 +38,35 @@ namespace OVCC
/** /**
* Possible states of a game. * 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 STARTED The game is started
* @param FINISHED The game is finished * @param FINISHED The game is finished
* @param ABORTED The game is stopped, but not finished * @param ABORTED The game is stopped, but not finished
*/ */
[Flags]
public enum GameState public enum GameState
{ {
STOPPED, NEW,
PLAYER_WAITING,
STARTED, STARTED,
FINISHED, 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 ...@@ -83,7 +100,7 @@ namespace OVCC
{ {
private SList<Player> _players = null; 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 Stack stack { private get; construct; }
public Board board { get; construct; } public Board board { get; construct; }
public unowned SList<Player> players { get { return this._players; } } public unowned SList<Player> players { get { return this._players; } }
...@@ -130,9 +147,15 @@ namespace OVCC ...@@ -130,9 +147,15 @@ namespace OVCC
}); });
this.player_added.connect ((p) => { this.player_added.connect ((p) => {
this.notify_property ("players"); this.notify_property ("players");
if (players.length() == 1 && this.state == GameState.NEW) {
this.state = GameState.PLAYER_WAITING;
}
}); });
this.player_removed.connect ((p) => { this.player_removed.connect ((p) => {
this.notify_property ("players"); this.notify_property ("players");
if (players.length() < 1 && this.state == GameState.PLAYER_WAITING) {
this.state = GameState.NEW;
}
}); });
this.turn_finished.connect (() => next_player ()); this.turn_finished.connect (() => next_player ());
...@@ -519,7 +542,7 @@ namespace OVCC ...@@ -519,7 +542,7 @@ namespace OVCC
*/ */
public GameDescription describe () public GameDescription describe ()
{ {
/* FIXME table name */ /* FIXME game name */
return new GameDescription ("Unnamed Game", state, get_player_nicks()); return new GameDescription ("Unnamed Game", state, get_player_nicks());
} }
} }
......
...@@ -147,17 +147,17 @@ namespace OVCCClient ...@@ -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 * @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, public async Game join_game (int index,
Cancellable? cancellable = null) Cancellable? cancellable = null)
throws ServerError, IOError 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 (() => { sigqueue.add (game, game.notify["current-player"].connect (() => {
debug ("Current player changed to \"%s\"", game.current_player.nick); debug ("Current player changed to \"%s\"", game.current_player.nick);
......
...@@ -63,9 +63,9 @@ namespace OVCCClient ...@@ -63,9 +63,9 @@ namespace OVCCClient
*/ */
MISSING_AUTHENTICATION, 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 * Data could not be reached where it was searched for
*/ */
...@@ -76,31 +76,6 @@ namespace OVCCClient ...@@ -76,31 +76,6 @@ namespace OVCCClient
FAILED 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 * A class representing the server side of the network clients
* *
...@@ -438,42 +413,52 @@ namespace OVCCClient ...@@ -438,42 +413,52 @@ 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 * 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// * @return A list of {@link GameDescription} on the remote server matching //filter//
*/ */
public async GameDescription[]? enumerate_tables (TablesFilter filter, public async GameDescription[]? enumerate_games (GameState? filter = null,
Cancellable? cancellable = null) Cancellable? cancellable = null)
throws ServerError, IOError throws ServerError, IOError
requires (connection != null) requires (connection != null)
{ {
/* ask the server to send the list */ /* ask the server to send the list */
yield send_message (new ListTablesMessage (), cancellable); yield send_message (new ListGamesMessage (), cancellable);
/* wait for answer */ /* wait for answer */
Message msg = yield receive_type (MessageType.LIST_TABLES, cancellable); Message msg = yield receive_type (MessageType.LIST_GAMES, cancellable);
ListTablesMessage list = msg as ListTablesMessage; 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 player The player to make join
* @param cancellable a Cancellable object or null * @param cancellable a Cancellable object or null
* @return The game instance corresponding to the one joint on the remote server * @return The game instance corresponding to the one joint on the remote server
*/ */
public async Game join_table (int index, public async Game join_game (int index,
Player player, Player player,
Cancellable? cancellable = null) Cancellable? cancellable = null)
throws ServerError, IOError throws ServerError, IOError
...@@ -491,9 +476,9 @@ namespace OVCCClient ...@@ -491,9 +476,9 @@ namespace OVCCClient
JoinMessage join = msg as JoinMessage; JoinMessage join = msg as JoinMessage;
if (join.status != JoinMessage.State.OK) { if (join.status != JoinMessage.State.OK) {
if (index == -1) { 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 { } 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"; ...@@ -29,7 +29,7 @@ static const string TILESET_FILE = "tileset.xml";
public class Server: ThreadedSocketService public class Server: ThreadedSocketService
{ {
private List<Client> clients = null; private List<Client> clients = null;
private List<Game> tables = null; private List<Game> games = null;
private TilesDef tiles = new TilesDef (); private TilesDef tiles = new TilesDef ();
private TileSet tileset = new TileSet (); private TileSet tileset = new TileSet ();
private string tiles_data; private string tiles_data;
...@@ -37,42 +37,42 @@ public class Server: ThreadedSocketService ...@@ -37,42 +37,42 @@ public class Server: ThreadedSocketService
public string name { get; construct; } public string name { get; construct; }
public uint port { get; construct; default = 0xdead; } 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 (); public signal void stopped ();
/* removes old tables */ /* removes old games */
private void cleanup_tables () private void cleanup_games ()
{ {
lock (tables) { lock (games) {
foreach (var game in tables) { foreach (var game in games) {
if (game.state != GameState.STOPPED && game.players.length() < 1) { if (game.players.length () < 1 && !game.state.is_open ()) {
tables.remove (game); games.remove (game);
debug ("Cleaned up table %p", game); debug ("Cleaned up game %p", game);
} }
} }