TBR croupier: Only Creator has Game Start button.
- use watch update to look for an updated game status
- Remove Start Game buttons from the other devices
- Style arrange players a little more flexibly.
Change-Id: Ie2dfdb510c15dc4534d663c7340c723cd4903b88
diff --git a/lib/components/croupier.dart b/lib/components/croupier.dart
index b164dd1..8c151fb 100644
--- a/lib/components/croupier.dart
+++ b/lib/components/croupier.dart
@@ -108,13 +108,7 @@
case logic_croupier.CroupierState.ArrangePlayers:
return new Container(
padding: new EdgeDims.only(top: ui.window.padding.top),
- child: new Column([
- _buildArrangePlayers(),
- new FlatButton(
- child: new Text('Back'),
- onPressed: makeSetStateCallback(
- logic_croupier.CroupierState.Welcome))
- ]));
+ child: _buildArrangePlayers());
case logic_croupier.CroupierState.PlayGame:
return new Container(
padding: new EdgeDims.only(top: ui.window.padding.top),
@@ -134,16 +128,23 @@
// shown if the person has not sat down yet.
Widget _buildPlayerProfiles(bool needsArrangement) {
List<Widget> profileWidgets = new List<Widget>();
+ double size = 125.0;
config.croupier.players_found.forEach((int userID, int playerNumber) {
if (!needsArrangement || playerNumber == null) {
CroupierSettings cs = config.croupier.settings_everyone[userID];
// cs could be null if this settings data hasn't synced yet.
// If so, a placeholder is shown instead.
- profileWidgets.add(new CroupierProfileComponent(settings: cs));
+ profileWidgets.add(new CroupierProfileComponent(
+ settings: cs, height: size, width: size));
}
});
- return new Grid(profileWidgets, maxChildExtent: 120.0);
+ if (needsArrangement) {
+ return new ScrollableViewport(
+ child: new Row(profileWidgets),
+ scrollDirection: ScrollDirection.horizontal);
+ }
+ return new Grid(profileWidgets, maxChildExtent: size);
}
Widget _buildArrangePlayers() {
@@ -152,34 +153,60 @@
logic_game.GameArrangeData gad = config.croupier.game.gameArrangeData;
Iterable<int> playerNumbers = config.croupier.players_found.values;
+ allWidgets.add(new Flexible(
+ flex: 0,
+ child: new Row([
+ new Text("${config.croupier.game.gameTypeName}",
+ style: style.Text.hugeStyle)
+ ], justifyContent: FlexJustifyContent.spaceAround)));
+
+ // Then show the profile widgets of those who have joined the game.
+ allWidgets.add(new Flexible(flex: 0, child: new Text("Player List")));
+ allWidgets.add(new Flexible(
+ flex: 1, child: _buildPlayerProfiles(gad.needsArrangement)));
+
+ if (gad.needsArrangement) {
+ // Games that need arrangement can show their game arrange component.
+ allWidgets.add(component_game.createGameArrangeComponent(config.croupier,
+ width: ui.window.size.width, height: ui.window.size.height / 2));
+ }
+
// Allow games that can start with these players to begin.
// Debug Mode should also go through.
- NoArgCb onPressed;
+ NoArgCb startCb;
if (gad.canStart(playerNumbers) || config.croupier.debugMode) {
- onPressed = () {
- makeSetStateCallback(logic_croupier.CroupierState.PlayGame)();
+ startCb = () {
+ config.croupier.settings_manager
+ .setGameStatus(config.croupier.game.gameID, "RUNNING");
// Since playerNumber starts out as null, we should set it to -1 if
// the person pressed Start Game without sitting.
if (config.croupier.game.playerNumber == null) {
config.croupier.game.playerNumber = -1;
}
- config.croupier.game.startGameSignal();
};
}
-
- // Always include the Start Game Button.
- allWidgets.add(
- new FlatButton(child: new Text('Start Game'), onPressed: onPressed));
-
- // Games that need arrangement can show their game arrange component.
- if (gad.needsArrangement) {
- allWidgets.add(component_game.createGameArrangeComponent(config.croupier,
- width: ui.window.size.width, height: ui.window.size.height / 2));
+ if (config.croupier.game.isCreator) {
+ allWidgets.add(new Flexible(
+ flex: 0,
+ child: new Row([
+ new Container(
+ decoration: new BoxDecoration(
+ backgroundColor: startCb != null
+ ? style.theme.accentColor
+ : Colors.grey[300]),
+ padding: new EdgeDims.all(10.0),
+ child: new FlatButton(
+ child: new Text("Start Game", style: style.Text.hugeStyle),
+ onPressed: startCb))
+ ], justifyContent: FlexJustifyContent.spaceAround)));
}
-
- // Then show the profile widgets of those who have joined the game.
- allWidgets.add(_buildPlayerProfiles(gad.needsArrangement));
+ allWidgets.add(new Flexible(
+ flex: 0,
+ child: new FlatButton(
+ child: new Text('Back'),
+ onPressed:
+ makeSetStateCallback(logic_croupier.CroupierState.Welcome))));
return new Column(allWidgets);
}
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index c3f03e5..20ed05f 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -612,6 +612,9 @@
Widget _buildSlot(String name, int index) {
NoArgCb onTap = () {
croupier.settings_manager.setPlayerNumber(croupier.game.gameID, index);
+ HeartsGame game = croupier.game;
+ game.playerNumber = index;
+ game.setReadyUI();
};
Widget slotWidget = new Text(name, style: style.Text.hugeStyle);
@@ -632,7 +635,7 @@
color: croupier.game.playerNumber == index
? style.theme.accentColor
: null,
- child: new Center(child: slotWidget)),
+ child: slotWidget),
onTap: onTap));
}
}
diff --git a/lib/logic/croupier.dart b/lib/logic/croupier.dart
index 98dc9a6..70747ac 100644
--- a/lib/logic/croupier.dart
+++ b/lib/logic/croupier.dart
@@ -38,8 +38,12 @@
settings_everyone = new Map<int, CroupierSettings>();
games_found = new Map<String, GameStartData>();
players_found = new Map<int, int>();
- settings_manager = new SettingsManager(appSettings,
- _updateSettingsEveryoneCb, _updateGamesFoundCb, _updatePlayerFoundCb);
+ settings_manager = new SettingsManager(
+ appSettings,
+ _updateSettingsEveryoneCb,
+ _updateGamesFoundCb,
+ _updatePlayerFoundCb,
+ _updateGameStatusCb);
settings_manager.load().then((String csString) {
settings = new CroupierSettings.fromJSONString(csString);
@@ -107,6 +111,22 @@
}
}
+ void _updateGameStatusCb(String statusKey, String newStatus) {
+ switch (newStatus) {
+ case "RUNNING":
+ if (state == CroupierState.ArrangePlayers) {
+ game.startGameSignal();
+ state = CroupierState.PlayGame;
+ }
+ break;
+ default:
+ print("Ignoring new status: ${newStatus}");
+ }
+ if (this.informUICb != null) {
+ this.informUICb();
+ }
+ }
+
// Sets the next part of croupier state.
// Depending on the originating state, data can contain extra information that we need.
void setState(CroupierState nextState, var data) {
@@ -190,6 +210,7 @@
if (nextState == CroupierState.Welcome) {
games_found.clear();
players_found.clear();
+ game = null;
} else if (nextState == CroupierState.JoinGame) {
// Start scanning for games since that's what's next for you.
_scanFuture =
diff --git a/lib/logic/croupier_settings.dart b/lib/logic/croupier_settings.dart
index 14fc3a6..c83f3c9 100644
--- a/lib/logic/croupier_settings.dart
+++ b/lib/logic/croupier_settings.dart
@@ -127,7 +127,7 @@
// Return a random user id.
static int get userID {
- return new math.Random().nextInt(0xffffffff);
+ return new math.Random().nextInt(0xffffff);
}
// Return a random image name.
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index 9461a46..b33606e 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -275,7 +275,6 @@
if (this.debugMode && this.playerNumber < 0) {
this.playerNumber = 0;
}
- setReadyUI();
if (!this.isPlayer) {
this.viewType = HeartsType.Board;
}
diff --git a/lib/src/mocks/settings_manager.dart b/lib/src/mocks/settings_manager.dart
index f96ab8c..533fead 100644
--- a/lib/src/mocks/settings_manager.dart
+++ b/lib/src/mocks/settings_manager.dart
@@ -13,9 +13,14 @@
final util.keyValueCallback updateCallback;
final util.keyValueCallback updateGamesCallback;
final util.keyValueCallback updatePlayerFoundCallback;
+ final util.keyValueCallback updateGameStatusCallback;
- SettingsManager(settings_client.AppSettings _, this.updateCallback,
- this.updateGamesCallback, this.updatePlayerFoundCallback);
+ SettingsManager(
+ settings_client.AppSettings _,
+ this.updateCallback,
+ this.updateGamesCallback,
+ this.updatePlayerFoundCallback,
+ this.updateGameStatusCallback);
Map<String, String> _data = new Map<String, String>();
@@ -68,4 +73,8 @@
Future setPlayerNumber(int gameID, int playerNumber) async {
return new Future(() => null);
}
+
+ Future setGameStatus(int gameID, String status) async {
+ return new Future(() => null);
+ }
}
diff --git a/lib/src/syncbase/settings_manager.dart b/lib/src/syncbase/settings_manager.dart
index 64b419b..790b43c 100644
--- a/lib/src/syncbase/settings_manager.dart
+++ b/lib/src/syncbase/settings_manager.dart
@@ -31,6 +31,7 @@
final util.keyValueCallback updateSettingsCallback;
final util.keyValueCallback updateGamesCallback;
final util.keyValueCallback updatePlayerFoundCallback;
+ final util.keyValueCallback updateGameStatusCallback;
final CroupierClient _cc;
sc.SyncbaseTable tb;
@@ -42,7 +43,8 @@
settings_client.AppSettings appSettings,
this.updateSettingsCallback,
this.updateGamesCallback,
- this.updatePlayerFoundCallback)
+ this.updatePlayerFoundCallback,
+ this.updateGameStatusCallback)
: _cc = new CroupierClient(appSettings);
String _settingsDataKey(int userID) {
@@ -150,8 +152,9 @@
// This watch method ensures that any changes are propagated to the caller.
// In this case, we're forwarding any player changes to the Croupier logic.
- Future _startWatchPlayers(Stream<sc.WatchChange> watchStream) async {
- util.log('Players watching for changes...');
+ // We also catch the game status signals.
+ Future _startWatchGame(Stream<sc.WatchChange> watchStream) async {
+ util.log('Game watching for changes...');
// This stream never really ends, so I guess we'll watch forever.
await for (sc.WatchChange wc in watchStream) {
assert(wc.tableName == util.tableNameGames);
@@ -170,24 +173,30 @@
assert(false);
}
- if (this.updatePlayerFoundCallback != null) {
- String playerID = _getPartFromBack(key, "/", 1);
- String type = _getPartFromBack(key, "/", 0);
- switch (type) {
- case "player_number":
- // Update the player number for this player.
- this.updatePlayerFoundCallback(playerID, value);
- break;
- case "settings_sg":
- // Join this player's settings syncgroup.
- _cc.joinSyncgroup(value);
+ if (key.indexOf("/players") != -1) {
+ if (this.updatePlayerFoundCallback != null) {
+ String playerID = _getPartFromBack(key, "/", 1);
+ String type = _getPartFromBack(key, "/", 0);
+ switch (type) {
+ case "player_number":
+ // Update the player number for this player.
+ this.updatePlayerFoundCallback(playerID, value);
+ break;
+ case "settings_sg":
+ // Join this player's settings syncgroup.
+ _cc.joinSyncgroup(value);
- // Also, signal that this player has been found.
- this.updatePlayerFoundCallback(playerID, null);
- break;
- default:
- print("Unexpected key: ${key} with value ${value}");
- assert(false);
+ // Also, signal that this player has been found.
+ this.updatePlayerFoundCallback(playerID, null);
+ break;
+ default:
+ print("Unexpected key: ${key} with value ${value}");
+ assert(false);
+ }
+ }
+ } else if (key.indexOf("/status") != -1) {
+ if (this.updateGameStatusCallback != null) {
+ this.updateGameStatusCallback(key, value);
}
}
}
@@ -200,9 +209,9 @@
sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
// Watch for the players in the game.
- Stream<sc.WatchChange> watchStream = db.watch(util.tableNameGames,
- util.syncgamePrefix(gameID) + "/players", UTF8.encode("now"));
- _startWatchPlayers(watchStream); // Don't wait for this future.
+ Stream<sc.WatchChange> watchStream = db.watch(
+ util.tableNameGames, util.syncgamePrefix(gameID), UTF8.encode("now"));
+ _startWatchGame(watchStream); // Don't wait for this future.
print("Now writing to some rows of ${gameID}");
// Start up the table and write yourself as player 0.
@@ -228,14 +237,15 @@
Future joinGameSyncgroup(String sgName, int gameID) async {
print("Now joining game syncgroup at ${sgName} and ${gameID}");
- await _cc.joinSyncgroup(sgName);
sc.SyncbaseDatabase db = await _cc.createDatabase();
sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
// Watch for the players in the game.
- Stream<sc.WatchChange> watchStream = db.watch(util.tableNameGames,
- util.syncgamePrefix(gameID) + "/players", UTF8.encode("now"));
- _startWatchPlayers(watchStream); // Don't wait for this future.
+ Stream<sc.WatchChange> watchStream = db.watch(
+ util.tableNameGames, util.syncgamePrefix(gameID), UTF8.encode("now"));
+ _startWatchGame(watchStream); // Don't wait for this future.
+
+ await _cc.joinSyncgroup(sgName);
int id = await _getUserID();
await gameTable
@@ -253,6 +263,13 @@
.put(UTF8.encode("${playerNumber}"));
}
+ Future setGameStatus(int gameID, String status) async {
+ sc.SyncbaseDatabase db = await _cc.createDatabase();
+ sc.SyncbaseTable gameTable = await _cc.createTable(db, util.tableNameGames);
+
+ await gameTable.row("${gameID}/status").put(UTF8.encode(status));
+ }
+
// When starting the settings manager, there may be settings already in the
// store. Make sure to load those.
Future _loadSettings(sc.SyncbaseTable tb) async {
diff --git a/manifest.yaml b/manifest.yaml
index ec47bfb..bfa1022 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -3,6 +3,7 @@
- name: action/build
- name: action/help
- name: action/settings
+ - name: av/play_arrow
- name: navigation/arrow_back
- name: navigation/menu
assets:
diff --git a/schema.md b/schema.md
index d5aafcd..8c01264 100644
--- a/schema.md
+++ b/schema.md
@@ -13,6 +13,7 @@
For advertising and setting up the game:
<game_id>/type = <game_type>
<game_id>/owner = <user_id>
+<game_id>/status = [null|RUNNING]
<game_id>/players/<user_id>/player_number = <player_number>
<game_id>/players/<user_id>/settings_sg = <settings_syncgroup_name>