Contact us for your own Poker & Casino license now!

info@cubeia.com
Stora Nygatan 44, 111 27 Stockholm
  • No products in the cart.

Cubeia Poker, Casino & White Label

Games & Aynchronous Integrations

When a game network attaches external integrations, such as remote wallets, asynchronous communications becomes important. This can be any kind of integration, but in this blog post we’ll assume it’s a wallet we’re talking with. So, what’s our problem?

  • If the network has several operators no operator must block players from another operator. If the wallet operation is synchronous, one call to an integration may lock the entire table. It is more acceptable if there is only one operator, but when there’s several, we must make sure no-one is penalized by another players operator.
  • Similarly, we may have other integrations that takes time even for a single operator and don’t want to block game play during it’s operation. This might include interactions with national gambling authorities, etc.

So here’ a wallet example: A player needs to buy in after having lost all money at the table. The game server asks the player if he wants to buy in and if the player accepts the buy in he’s placed in a “buy-in in progress” state while the game server sorts out the actual money transfer. Note that the game play may well start at the table with the player in a sit-out state if the buy-in takes a long time.

  1. Send buy-in information to player
  2. On buy-in request from player, set player state to “buy-in in progress”
  3. Hand-off buy-in operation from the game to a wallet service
  4. When buy in is complete, wallet service notifies game
  5. Game sets player as “in game” and notifies the same

Easy, huh? Now let’s do some coding. A word of warning though, I’ll write it down off the cuff so you’d better off treating the following as pseudo code, but it should give you an idea on how it’s done.

We need some messages to communicate with the client. This is normally covered anyway in the game design, so I won’t go into too much details here.

{ "action":"buy-in-info","max":1000,"min":10 }

When receiving a buy-in information action the client notifiers the player (often via a buy-in dialogue) of the available buy-in option. When the player decides to buy in it sends an action to the server.

{ "action":"buy-in-request","amount":50 }

We don’t need any correlation ID on this request if the table only allows for one buy-in at the time. Which is kind of reasonable anyway.

Now the player should be marked as having a buy-in in progress. A new round may start at the table while we wait for the buy-in if the game rules allows, but we should remember when the buy-in started so we can cancel it from within the game.

setPlayerState(player, new BuyinInProgress(50, System.currentTimeMillis());

Now, let’s tell the wallet service that we want a buy-in transfer for the player.

WalletService serv = serviceRegistry.getServiceInstance(WalletService.class);
serv.handle(new BuyInRequest(playerId, tableId, 50));

Not shown above is any correlation ID: if the game and services must handle multiple requests for the same player and game, the request object above probably should have a unique ID for the game to match up responses on,

At this point it should be emphasized that the wallet service must handle different operators with different resources. This might happen automatically in later system components, but the service must also not block the game thread, so an immediate thread pool might be a good idea.

private Map<Integer, ExecutorService> pools; 

public void handle(final BuyInRequest req) {
  final int operatorId = lookupOperator(req.getPlayerId());
  ExecutorService exec = pools.get(operatorId);
  exec.execute(new Runnable() {
    public void run() {
      doBuyIn(operatorId, req);
    }
  });
}

The above code keeps thread pools for each operator and looks up the operator ID for every given player before handing over the execution. This way, if operator X stops responding we might hang all players for that particular operator, but other operators will be left unaffected.

private void doBuyIn(int operatorId, BuyInRequest req) {
  BuyInHandler handler = lookupBuyInHandler(operatorId);
  BuyInResponse resp = handler.perform(req);
  sendBuyInResponse(resp);
}

When the buy in has finished the service sends a buy-in response back to the game. This response will arrive as a game object action to the game’s normal processor. The reason for doing this is to maintain the single-threaded status of a game: if delivered as an action to the game, Firebase guarantees that only one thread at the time executes for a particular table.

private void sendBuyInresponse(BuyInResponse resp) {
  GameObjectAction action = new GameObjectAction(resp.getTableId());
  action.setAttachment(resp);
  serviceRouter.dispatchToGame(gameId, action);
}

And now, when the game receives the above action it can set the players state to “in-game” again and add the buy-in amount to the table. Finally we’ll send an message to the player, notifying him that the buy-in was successful (if it was).

{ "action":"buy-in-response","status":"OK","amount":50 }

After which everything is back to normal and everyone is happy. So what are we missing here?

  • How do you handle partial amounts?
  • If buy-in fails or times out, do you kick the player from the table?
  • If an operator is slow or failing, should we use a circuit breaker to stop pushing requests at them for a while?
  • Any flow-control needed to manage operator traffic?
  • Etc…

So, there, I’m sure you can fill in a lot of missing details as well. But hopefully this little post have given you some ideas on how to handle asynchronous integration in games in Firebase.