gt:             final Game game = games.byId(arena.game_id());
context: package tc.oc.commons.bukkit.ticket;
  
  import java.util.Comparator;
  import java.util.Objects;
  import java.util.Optional;
  import java.util.Set;
  import javax.annotation.Nullable;
  import javax.inject.Inject;
  import javax.inject.Singleton;
  
  import com.google.common.collect.Sets;
  import com.google.common.util.concurrent.Futures;
  import com.google.common.util.concurrent.ListenableFuture;
  import net.md_5.bungee.api.chat.TranslatableComponent;
  import org.bukkit.command.CommandSender;
  import org.bukkit.entity.Player;
  import tc.oc.api.bukkit.users.BukkitUserStore;
  import tc.oc.api.docs.Arena;
  import tc.oc.api.docs.Game;
  import tc.oc.api.docs.PlayerId;
  import tc.oc.api.docs.Server;
  import tc.oc.api.docs.Ticket;
  import tc.oc.api.docs.virtual.MatchDoc;
  import tc.oc.api.docs.virtual.ServerDoc;
  import tc.oc.api.games.ArenaStore;
  import tc.oc.api.games.GameStore;
  import tc.oc.api.games.TicketService;
  import tc.oc.api.games.TicketStore;
  import tc.oc.api.message.types.PlayGameRequest;
  import tc.oc.api.message.types.Reply;
  import tc.oc.minecraft.scheduler.SyncExecutor;
  import tc.oc.api.servers.ServerStore;
  import tc.oc.commons.bukkit.chat.Audiences;
  import tc.oc.commons.bukkit.chat.WarningComponent;
  import tc.oc.commons.bukkit.format.GameFormatter;
  import tc.oc.commons.bukkit.teleport.PlayerServerChanger;
  import tc.oc.commons.bukkit.util.PlayerStates;
  import tc.oc.commons.core.chat.Audience;
  import tc.oc.commons.core.commands.CommandFutureCallback;
  
  /**
   * User actions for querying, joining, and leaving {@link Game}s
   */
  @Singleton
  public class TicketBooth {
  
      private final SyncExecutor syncExecutor;
      private final BukkitUserStore userStore;
      private final PlayerStates playerStates;
      private final Audiences audiences;
      private final TicketService ticketService;
      private final GameFormatter gameFormatter;
      private final GameStore games;
      private final ArenaStore arenas;
      private final TicketStore tickets;
      private final ServerStore servers;
      private final PlayerServerChanger serverChanger;
      private final Server localServer;
  
      private @Nullable PlayHandler playHandler;
  
      @Inject TicketBooth(SyncExecutor syncExecutor,
                          BukkitUserStore userStore,
                          PlayerStates playerStates,
                          Audiences audiences,
                          TicketService ticketService,
                          GameFormatter gameFormatter,
                          GameStore games,
                          ArenaStore arenas,
                          TicketStore tickets,
                          ServerStore servers,
                          PlayerServerChanger serverChanger,
                          Server localServer) {
  
          this.syncExecutor = syncExecutor;
          this.userStore = userStore;
          this.playerStates = playerStates;
          this.audiences = audiences;
          this.ticketService = ticketService;
          this.gameFormatter = gameFormatter;
          this.games = games;
          this.arenas = arenas;
          this.tickets = tickets;
          this.servers = servers;
          this.serverChanger = serverChanger;
          this.localServer = localServer;
      }
  
      @FunctionalInterface
      public interface PlayHandler {
          boolean requestPlay(Player player);
      }
  
      public PlayHandler playHandler() {
          return playHandler;
      }
  
      public void setPlayHandler(PlayHandler handler) {
          playHandler = handler;
      }
  
      public void removePlayHandler(PlayHandler handler) {
          if(handler.equals(playHandler)) {
              playHandler = null;
          }
      }
  
      public Set<Game> allGames(CommandSender viewer) {
          return Sets.filter(games.set(), game -> game.visibility() == ServerDoc.Visibility.PUBLIC);
      }
  
      public void showGames(CommandSender sender) {
          gameFormatter.sendList(audiences.get(sender), allGames(sender));
      }
  
      public @Nullable Arena localArena() {
          final String id = localServer.arena_id();
          return id == null ? null : arenas.byId(id);
      }
  
      public @Nullable Game localGame() {
          final String id = localServer.game_id();
          return id == null ? null : games.byId(id);
      }
  
      public @Nullable Arena currentArena(Player player) {
          return currentArena(userStore.playerId(player));
      }
  
      public @Nullable Game currentGame(Player player) {
          return currentGame(userStore.playerId(player));
      }
  
      public @Nullable Arena currentArena(PlayerId playerId) {
          final Ticket ticket = tickets.tryUser(playerId);
          return ticket == null ? null : arenas.byId(ticket.arena_id());
      }
  
      public @Nullable Game currentGame(PlayerId playerId) {
          final Arena arena = currentArena(playerId);
          return arena == null ? null : games.byId(arena.game_id());
      }
  
      public @Nullable Game findGame(CommandSender sender, String name) {
          name = name.trim().toLowerCase();
          for(Game game : games.set()) {
              if(game.visibility() != ServerDoc.Visibility.PRIVATE &&
                 name.equals(game.name().toLowerCase())) {
                  return game;
              }
          }
          audiences.get(sender).sendMessage(new WarningComponent("game.unknown", name));
          showGames(sender);
          return null;
      }
  
      public @Nullable Arena findArena(CommandSender sender, @Nullable String name) {
          final Arena arena;
          if(name == null || name.length() == 0) {
              arena = localArena();
              if(arena == null) {
                  showGames(sender);
              }
          } else {
              final Game game = findGame(sender, name);
              if(game == null) return null;
  
              arena = arenas.tryDatacenterAndGameId(localServer.datacenter(), game._id());
              if(arena == null) {
                  audiences.get(sender).sendMessage(new WarningComponent("game.offline",  gameFormatter.name(game)));
              }
          }
          return arena;
      }
  
      private ListenableFuture<Reply> sendPlayRequest(Player player, @Nullable Arena arena) {
          return sendPlayRequest(userStore.playerId(player), arena);
      }
  
      private ListenableFuture<Reply> sendPlayRequest(PlayerId playerId, @Nullable Arena arena) {
          return sendPlayRequest(playerId, arena, false);
      }
  
      private ListenableFuture<Reply> sendPlayRequest(PlayerId playerId, @Nullable Arena arena, boolean force) {
          final Arena playing = currentArena(playerId);
          if(!force && Objects.equals(playing, arena)) {
              return Futures.immediateFuture(Reply.SUCCESS);
          } else {
              return ticketService.requestPlay(new PlayGameRequest() {
                  @Override public String user_id() { return playerId._id(); }
                  @Override public @Nullable String arena_id() { return arena == null ? null : arena._id(); }
              });
          }
      }
  
      public void leaveGame(Player player, boolean returnToLobby) {
          final Audience audience = audiences.get(player);
  
          final PlayerId playerId = userStore.playerId(player);
          final Game game = currentGame(playerId);
          if(game == null) {
              audience.sendMessage(gameFormatter.notPlaying());
              return;
          }
  
          syncExecutor.callback(
              sendPlayRequest(playerId, null),
              CommandFutureCallback.onSuccess(player, reply -> {
                  audience.sendMessage(gameFormatter.left(game));
                  if(returnToLobby) {
                      serverChanger.sendPlayerToLobby(player, true);
                  }
              })
          );
      }
  
      public void playLocalGame(Player player) {
          final Game game = localGame();
          if(game != null) {
              final Arena arena = arenas.tryDatacenterAndGameId(localServer.datacenter(), game._id());
              if(arena != null) {
                  playGame(player, arena);
              }
          } else if(playHandler != null) {
              playHandler.requestPlay(player);
          } else {
              showGames(player);
          }
      }
  
      public void playGame(Player player, @Nullable String name) {
          final Arena arena = findArena(player, name);
          if(arena != null) {
              playGame(player, arena);
          }
      }
  
      public void playGame(Player player, Arena arena) {
          final PlayerId playerId = userStore.playerId(player);
          boolean forceRequest = false;
          if(arena.equals(currentArena(playerId))) {
  