Exemple #1
0
/** @author BetaSteward_at_googlemail.com */
public class TableController {

  private static final Logger logger = Logger.getLogger(TableController.class);

  private final UUID userId;
  private final UUID chatId;
  private final String controllerName;
  private final Table table;
  private final ConcurrentHashMap<UUID, UUID> userPlayerMap = new ConcurrentHashMap<>();

  private Match match;
  private MatchOptions options;
  private Tournament tournament;

  private ScheduledFuture<?> futureTimeout;
  protected static ScheduledExecutorService timeoutExecutor =
      ThreadExecutor.getInstance().getTimeoutExecutor();

  public TableController(UUID roomId, UUID userId, MatchOptions options) {
    this.userId = userId;
    this.options = options;
    match = GameFactory.getInstance().createMatch(options.getGameType(), options);
    if (userId != null) {
      User user = UserManager.getInstance().getUser(userId);
      // TODO: Handle if user == null
      controllerName = user.getName();
    } else {
      controllerName = "System";
    }
    table =
        new Table(
            roomId,
            options.getGameType(),
            options.getName(),
            controllerName,
            DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()),
            options.getPlayerTypes(),
            TableRecorderImpl.getInstance(),
            match);
    chatId = ChatManager.getInstance().createChatSession("Match Table " + table.getId());
    init();
  }

  public TableController(UUID roomId, UUID userId, TournamentOptions options) {
    this.userId = userId;
    tournament =
        TournamentFactory.getInstance().createTournament(options.getTournamentType(), options);
    if (userId != null) {
      User user = UserManager.getInstance().getUser(userId);
      if (user == null) {
        logger.fatal(
            new StringBuilder("User for userId ")
                .append(userId)
                .append(" could not be retrieved from UserManager")
                .toString());
        controllerName = "[unknown]";
      } else {
        controllerName = user.getName();
      }
    } else {
      controllerName = "System";
    }
    table =
        new Table(
            roomId,
            options.getTournamentType(),
            options.getName(),
            controllerName,
            DeckValidatorFactory.getInstance()
                .createDeckValidator(options.getMatchOptions().getDeckType()),
            options.getPlayerTypes(),
            TableRecorderImpl.getInstance(),
            tournament);
    chatId = ChatManager.getInstance().createChatSession("Tourn. table " + table.getId());
  }

  private void init() {
    match.addTableEventListener(
        new Listener<TableEvent>() {
          @Override
          public void event(TableEvent event) {
            try {
              switch (event.getEventType()) {
                case SIDEBOARD:
                  sideboard(event.getPlayerId(), event.getDeck());
                  break;
              }
            } catch (MageException ex) {
              logger.fatal("Table event listener error", ex);
            }
          }
        });
  }

  public synchronized boolean joinTournament(
      UUID userId,
      String name,
      String playerType,
      int skill,
      DeckCardLists deckList,
      String password)
      throws GameException {
    if (table.getState() != TableState.WAITING) {
      return false;
    }

    Seat seat = table.getNextAvailableSeat(playerType);
    if (seat == null) {
      throw new GameException("No available seats.");
    }
    User user = UserManager.getInstance().getUser(userId);
    if (user == null) {
      logger.fatal(
          new StringBuilder("couldn't get user ")
              .append(name)
              .append(" for join tournament userId = ")
              .append(userId)
              .toString());
      return false;
    }
    // check password
    if (!table.getTournament().getOptions().getPassword().isEmpty() && playerType.equals("Human")) {
      if (!table.getTournament().getOptions().getPassword().equals(password)) {
        user.showUserMessage("Join Table", "Wrong password.");
        return false;
      }
    }
    if (userPlayerMap.containsKey(userId) && playerType.equals("Human")) {
      user.showUserMessage("Join Table", "You can join a table only one time.");
      return false;
    }
    Deck deck = null;
    if (!table.getTournament().getTournamentType().isLimited()) {
      if (deckList != null) {
        deck = Deck.load(deckList, false, false);
      } else {
        user.showUserMessage("Join Table", "No valid deck selected!");
        return false;
      }
      if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
        StringBuilder sb =
            new StringBuilder("You (")
                .append(name)
                .append(") have an invalid deck for the selected ")
                .append(table.getValidator().getName())
                .append(" Format. \n\n");
        for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
          sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
        }
        sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!");
        user.showUserMessage("Join Table", sb.toString());
        if (isOwner(userId)) {
          logger.debug(
              "New table removed because owner submitted invalid deck tableId " + table.getId());
          TableManager.getInstance().removeTable(table.getId());
        }
        return false;
      }
    }
    // Check quit ratio.
    int quitRatio = table.getTournament().getOptions().getQuitRatio();
    if (quitRatio < user.getTourneyQuitRatio()) {
      String message =
          new StringBuilder("Your quit ratio ")
              .append(user.getTourneyQuitRatio())
              .append("% is higher than the table requirement ")
              .append(quitRatio)
              .append("%")
              .toString();
      user.showUserMessage("Join Table", message);
      return false;
    }

    Player player = createPlayer(name, seat.getPlayerType(), skill);
    if (player != null) {
      if (!player.canJoinTable(table)) {
        user.showUserMessage(
            "Join Table",
            new StringBuilder("A ")
                .append(seat.getPlayerType())
                .append(" player can't join this table.")
                .toString());
        return false;
      }
      tournament.addPlayer(player, seat.getPlayerType());
      TournamentPlayer tournamentPlayer = tournament.getPlayer(player.getId());
      if (deck != null && tournamentPlayer != null) {
        tournamentPlayer.submitDeck(deck);
      }
      table.joinTable(player, seat);
      logger.debug(
          "Player "
              + player.getName()
              + " id: "
              + player.getId()
              + " joined tableId: "
              + table.getId());
      // only inform human players and add them to sessionPlayerMap
      if (seat.getPlayer().isHuman()) {
        seat.getPlayer().setUserData(user.getUserData());
        user.addTable(player.getId(), table);
        user.ccJoinedTable(table.getRoomId(), table.getId(), true);
        userPlayerMap.put(userId, player.getId());
      }

      return true;
    } else {
      throw new GameException("Playertype " + seat.getPlayerType() + " could not be created.");
    }
  }

  public boolean hasPlayer(UUID userId) {
    return userPlayerMap.containsKey(userId);
  }

  public synchronized boolean replaceDraftPlayer(
      Player oldPlayer, String name, String playerType, int skill) {
    Player newPlayer = createPlayer(name, playerType, skill);
    if (newPlayer == null || table.getState() != TableState.DRAFTING) {
      return false;
    }
    TournamentPlayer oldTournamentPlayer = tournament.getPlayer(oldPlayer.getId());
    tournament.removePlayer(oldPlayer.getId());
    tournament.addPlayer(newPlayer, playerType);

    TournamentPlayer newTournamentPlayer = tournament.getPlayer(newPlayer.getId());
    newTournamentPlayer.setState(oldTournamentPlayer.getState());
    newTournamentPlayer.setReplacedTournamentPlayer(oldTournamentPlayer);

    DraftManager.getInstance().getController(table.getId()).replacePlayer(oldPlayer, newPlayer);
    return true;
  }

  public synchronized boolean joinTable(
      UUID userId,
      String name,
      String playerType,
      int skill,
      DeckCardLists deckList,
      String password)
      throws MageException {
    User user = UserManager.getInstance().getUser(userId);
    if (user == null) {
      return false;
    }
    if (userPlayerMap.containsKey(userId) && playerType.equals("Human")) {
      user.showUserMessage(
          "Join Table", new StringBuilder("You can join a table only one time.").toString());
      return false;
    }
    if (table.getState() != TableState.WAITING) {
      user.showUserMessage("Join Table", "No available seats.");
      return false;
    }
    // check password
    if (!table.getMatch().getOptions().getPassword().isEmpty() && playerType.equals("Human")) {
      if (!table.getMatch().getOptions().getPassword().equals(password)) {
        user.showUserMessage("Join Table", "Wrong password.");
        return false;
      }
    }
    Seat seat = table.getNextAvailableSeat(playerType);
    if (seat == null) {
      user.showUserMessage("Join Table", "No available seats.");
      return false;
    }
    Deck deck = Deck.load(deckList, false, false);

    if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
      StringBuilder sb =
          new StringBuilder("You (")
              .append(name)
              .append(") have an invalid deck for the selected ")
              .append(table.getValidator().getName())
              .append(" Format. \n\n");
      for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
        sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
      }
      sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!");
      user.showUserMessage("Join Table", sb.toString());
      if (isOwner(userId)) {
        logger.debug(
            "New table removed because owner submitted invalid deck tableId " + table.getId());
        TableManager.getInstance().removeTable(table.getId());
      }
      return false;
    }
    // Check quit ratio.
    int quitRatio = table.getMatch().getOptions().getQuitRatio();
    if (quitRatio < user.getMatchQuitRatio()) {
      String message =
          new StringBuilder("Your quit ratio ")
              .append(user.getMatchQuitRatio())
              .append("% is higher than the table requirement ")
              .append(quitRatio)
              .append("%")
              .toString();
      user.showUserMessage("Join Table", message);
      return false;
    }

    // Check power level for table (currently only used for EDH/Commander table)
    int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel();
    if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase().equals("commander")) {
      int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck);
      if (deckEdhPowerLevel > edhPowerLevel) {
        String message =
            new StringBuilder(
                    "Your deck appears to be too powerful for this table.\n\nReduce the number of extra turn cards, infect, counters, fogs, reconsider your commander. ")
                .append("\nThe table requirement has a maximum power level of ")
                .append(edhPowerLevel)
                .append(" whilst your deck has a calculated power level of ")
                .append(deckEdhPowerLevel)
                .toString();
        user.showUserMessage("Join Table", message);
        return false;
      }
    }

    Player player = createPlayer(name, seat.getPlayerType(), skill);
    if (player == null) {
      String message =
          new StringBuilder("Could not create player ")
              .append(name)
              .append(" of type ")
              .append(seat.getPlayerType())
              .toString();
      logger.warn(
          new StringBuilder("User: "******" => ")
              .append(message)
              .toString());
      user.showUserMessage("Join Table", message);
      return false;
    }
    logger.debug(
        "DECK validated: "
            + table.getValidator().getName()
            + " "
            + player.getName()
            + " "
            + deck.getName());
    if (!player.canJoinTable(table)) {
      user.showUserMessage(
          "Join Table",
          new StringBuilder("A ")
              .append(seat.getPlayerType())
              .append(" player can't join this table.")
              .toString());
      return false;
    }
    match.addPlayer(player, deck);
    table.joinTable(player, seat);
    logger.trace(player.getName() + " joined tableId: " + table.getId());
    // only inform human players and add them to sessionPlayerMap
    if (seat.getPlayer().isHuman()) {
      seat.getPlayer().setUserData(user.getUserData());
      if (!table.isTournamentSubTable()) {
        user.addTable(player.getId(), table);
      }
      user.ccJoinedTable(table.getRoomId(), table.getId(), false);
      userPlayerMap.put(userId, player.getId());
    }
    return true;
  }

  public void addPlayer(UUID userId, Player player, String playerType, Deck deck)
      throws GameException {
    if (table.getState() != TableState.WAITING) {
      return;
    }
    Seat seat = table.getNextAvailableSeat(playerType);
    if (seat == null) {
      throw new GameException("No available seats.");
    }
    match.addPlayer(player, deck);
    table.joinTable(player, seat);
    if (player.isHuman()) {
      userPlayerMap.put(userId, player.getId());
    }
  }

  public synchronized boolean submitDeck(UUID userId, DeckCardLists deckList) throws MageException {
    UUID playerId = userPlayerMap.get(userId);
    if (table.isTournament()) {
      TournamentPlayer player = tournament.getPlayer(playerId);
      if (player == null || player.hasQuit()) {
        return true; // so the construct panel closes after submit
      }
    } else if (table.getMatch() != null) {
      MatchPlayer mPlayer = table.getMatch().getPlayer(playerId);
      if (mPlayer == null || mPlayer.hasQuit()) {
        return true; // so the construct panel closes after submit
      }
      if (table.isTournamentSubTable()) {
        TournamentPlayer tournamentPlayer =
            table.getTournament().getPlayer(mPlayer.getPlayer().getId());
        if (tournamentPlayer != null) {
          tournamentPlayer.setStateInfo(""); // reset sideboarding state
        }
      }
    }
    if (table.getState() != TableState.SIDEBOARDING
        && table.getState() != TableState.CONSTRUCTING) {
      return false;
    }
    Deck deck = Deck.load(deckList, false, false);
    if (table.getState() == TableState.SIDEBOARDING && table.getMatch() != null) {
      MatchPlayer mPlayer = table.getMatch().getPlayer(playerId);
      if (mPlayer != null) {
        deck.setName(mPlayer.getDeck().getName());
      }
    }
    if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
      throw new InvalidDeckException(
          "Invalid deck for this format", table.getValidator().getInvalid());
    }
    submitDeck(userId, playerId, deck);
    return true;
  }

  public void updateDeck(UUID userId, DeckCardLists deckList) throws MageException {
    UUID playerId = userPlayerMap.get(userId);
    if (table.getState() != TableState.SIDEBOARDING
        && table.getState() != TableState.CONSTRUCTING) {
      return;
    }
    Deck deck = Deck.load(deckList, false, false);
    updateDeck(userId, playerId, deck);
  }

  private void submitDeck(UUID userId, UUID playerId, Deck deck) {
    if (table.getState() == TableState.SIDEBOARDING) {
      match.submitDeck(playerId, deck);
      UserManager.getInstance().getUser(userId).removeSideboarding(table.getId());
    } else {
      TournamentManager.getInstance().submitDeck(tournament.getId(), playerId, deck);
      UserManager.getInstance().getUser(userId).removeConstructing(playerId);
    }
  }

  private void updateDeck(UUID userId, UUID playerId, Deck deck) {
    if (table.isTournament()) {
      if (tournament != null) {
        TournamentManager.getInstance().updateDeck(tournament.getId(), playerId, deck);
      } else {
        logger.fatal("Tournament == null  table: " + table.getId() + " userId: " + userId);
      }
    } else if (TableState.SIDEBOARDING.equals(table.getState())) {
      match.updateDeck(playerId, deck);
    } else {
      // deck was meanwhile submitted so the autoupdate can be ignored
    }
  }

  public boolean watchTable(UUID userId) {
    if (table.isTournament()) {
      UserManager.getInstance().getUser(userId).ccShowTournament(table.getTournament().getId());
      return true;
    } else {
      if (table.isTournamentSubTable() && !table.getTournament().getOptions().isWatchingAllowed()) {
        return false;
      }
      if (table.getState() != TableState.DUELING) {
        return false;
      }
      // you can't watch your own game
      if (userPlayerMap.get(userId) != null) {
        return false;
      }
      return UserManager.getInstance().getUser(userId).ccWatchGame(match.getGame().getId());
    }
  }

  //    public boolean replayTable(UUID userId) {
  //        if (table.getState() != TableState.FINISHED) {
  //            return false;
  //        }
  //        ReplayManager.getInstance().replayGame(table.getId(), userId);
  //        return true;
  //    }
  private Player createPlayer(String name, String playerType, int skill) {
    Player player;
    if (options == null) {
      player =
          PlayerFactory.getInstance().createPlayer(playerType, name, RangeOfInfluence.ALL, skill);
    } else {
      player =
          PlayerFactory.getInstance().createPlayer(playerType, name, options.getRange(), skill);
    }
    if (player != null) {
      logger.trace("Player " + player.getName() + " created id: " + player.getId());
    }
    return player;
  }

  public void leaveTableAll() {
    for (UUID leavingUserId : userPlayerMap.keySet()) {
      leaveTable(leavingUserId);
    }
    closeTable();
  }

  public synchronized void leaveTable(UUID userId) {
    if (table == null) {
      logger.error("No table object - userId: " + userId);
      return;
    }
    if (table.isTournament() && tournament == null) {
      logger.error("No tournament object - userId: " + userId + "  table: " + table.getId());
      return;
    }
    if (table != null
        && this.userId != null
        && this.userId.equals(userId) // tourn. sub tables have no creator user
        && (table.getState().equals(TableState.WAITING)
            || table.getState().equals(TableState.READY_TO_START))) {
      // table not started yet and user is the owner, remove the table
      TableManager.getInstance().removeTable(table.getId());
    } else {
      UUID playerId = userPlayerMap.get(userId);
      if (playerId != null) {
        if (table.getState() == TableState.WAITING
            || table.getState() == TableState.READY_TO_START) {
          table.leaveNotStartedTable(playerId);
          if (table.isTournament()) {
            tournament.removePlayer(playerId);
          } else {
            match.quitMatch(playerId);
          }
          User user = UserManager.getInstance().getUser(userId);
          if (user != null) {
            ChatManager.getInstance()
                .broadcast(
                    chatId,
                    user.getName(),
                    "has left the table",
                    ChatMessage.MessageColor.BLUE,
                    true,
                    ChatMessage.MessageType.STATUS,
                    ChatMessage.SoundToPlay.PlayerLeft);
            if (!table.isTournamentSubTable()) {
              user.removeTable(playerId);
            }
          } else {
            logger.debug("User not found - userId: " + userId + " tableId:" + table.getId());
          }
          userPlayerMap.remove(userId);
        } else if (!table.getState().equals(TableState.FINISHED)) {
          if (table.isTournament()) {
            logger.debug("Quit tournament sub tables for userId: " + userId);
            TableManager.getInstance().userQuitTournamentSubTables(tournament.getId(), userId);
            logger.debug(
                "Quit tournament  Id: "
                    + table.getTournament().getId()
                    + "("
                    + table.getTournament().getTournamentState()
                    + ")");
            TournamentManager.getInstance().quit(tournament.getId(), userId);
          } else {
            MatchPlayer matchPlayer = match.getPlayer(playerId);
            if (matchPlayer != null && !match.hasEnded() && !matchPlayer.hasQuit()) {
              Game game = match.getGame();
              if (game != null && !game.hasEnded()) {
                Player player = match.getPlayer(playerId).getPlayer();
                if (player != null && player.isInGame()) {
                  GameManager.getInstance().quitMatch(game.getId(), userId);
                }
                match.quitMatch(playerId);
              } else {
                if (table.getState().equals(TableState.SIDEBOARDING)) {
                  if (!matchPlayer.isDoneSideboarding()) {
                    // submit deck to finish sideboarding and trigger match start / end
                    matchPlayer.submitDeck(matchPlayer.getDeck());
                  }
                }
                match.quitMatch(playerId);
              }
            }
          }
        }
      } else {
        logger.error("No playerId found for userId: " + userId);
      }
    }
  }

  /**
   * Used from non tournament match to start
   *
   * @param userId owner of the tabel
   */
  public synchronized void startMatch(UUID userId) {
    if (isOwner(userId)) {
      startMatch();
    }
  }

  public synchronized void startMatch() {
    if (table.getState().equals(TableState.STARTING)) {
      try {
        if (table.isTournamentSubTable()) {
          logger.info(
              "Tourn. match started id:"
                  + match.getId()
                  + " tournId: "
                  + table.getTournament().getId());
        } else {
          User user = UserManager.getInstance().getUser(userId);
          logger.info(
              "MATCH started ["
                  + match.getName()
                  + "] "
                  + match.getId()
                  + "("
                  + user.getName()
                  + ")");
          logger.debug(
              "- " + match.getOptions().getGameType() + " - " + match.getOptions().getDeckType());
        }
        match.startMatch();
        startGame(null);
      } catch (GameException ex) {
        logger.fatal("Error starting match ", ex);
        match.endGame();
      }
    }
  }

  private void startGame(UUID choosingPlayerId) throws GameException {
    try {
      match.startGame();
      table.initGame();
      GameOptions gameOptions = new GameOptions();
      gameOptions.rollbackTurnsAllowed = match.getOptions().isRollbackTurnsAllowed();
      match.getGame().setGameOptions(gameOptions);
      GameManager.getInstance()
          .createGameSession(
              match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions);
      String creator = null;
      StringBuilder opponent = new StringBuilder();
      for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) { // no AI players
        if (match.getPlayer(entry.getValue()) != null
            && !match.getPlayer(entry.getValue()).hasQuit()) {
          User user = UserManager.getInstance().getUser(entry.getKey());
          if (user != null) {
            user.ccGameStarted(match.getGame().getId(), entry.getValue());

            if (creator == null) {
              creator = user.getName();
            } else {
              if (opponent.length() > 0) {
                opponent.append(" - ");
              }
              opponent.append(user.getName());
            }
          } else {
            logger.error(
                "Unable to find user: "******"  playerId: " + entry.getValue());
            MatchPlayer matchPlayer = match.getPlayer(entry.getValue());
            if (matchPlayer != null && !matchPlayer.hasQuit()) {
              matchPlayer.setQuit(true);
            }
          }
        }
      }
      // Append AI opponents to the log file
      for (MatchPlayer mPlayer : match.getPlayers()) {
        if (!mPlayer.getPlayer().isHuman()) {
          if (opponent.length() > 0) {
            opponent.append(" - ");
          }
          opponent.append(mPlayer.getName());
        }
      }
      ServerMessagesUtil.getInstance().incGamesStarted();

      // log about game started
      logger.info(
          "GAME started "
              + (match.getGame() != null ? match.getGame().getId() : "no Game")
              + " ["
              + match.getName()
              + "] "
              + creator
              + " - "
              + opponent.toString());
      logger.debug("- matchId: " + match.getId() + " [" + match.getName() + "]");
      if (match.getGame() != null) {
        logger.debug("- chatId:  " + GameManager.getInstance().getChatId(match.getGame().getId()));
      }
    } catch (Exception ex) {
      logger.fatal("Error starting game table: " + table.getId(), ex);
      if (table != null) {
        TableManager.getInstance().removeTable(table.getId());
      }
      if (match != null) {
        Game game = match.getGame();
        if (game != null) {
          GameManager.getInstance().removeGame(game.getId());
        }
      }
    }
  }

  public synchronized void startTournament(UUID userId) {
    try {
      if (userId.equals(this.userId) && table.getState().equals(TableState.STARTING)) {
        tournament.setStartTime();
        TournamentManager.getInstance()
            .createTournamentSession(tournament, userPlayerMap, table.getId());
        for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
          User user = UserManager.getInstance().getUser(entry.getKey());
          if (user != null) {
            logger.info(
                new StringBuilder("User ")
                    .append(user.getName())
                    .append(" tournament started: ")
                    .append(tournament.getId())
                    .append(" userId: ")
                    .append(user.getId()));
            user.ccTournamentStarted(tournament.getId(), entry.getValue());
          }
        }
        ServerMessagesUtil.getInstance().incTournamentsStarted();
      }
    } catch (Exception ex) {
      logger.fatal("Error starting tournament", ex);
      TableManager.getInstance().removeTable(table.getId());
      TournamentManager.getInstance().quit(tournament.getId(), userId);
    }
  }

  public void startDraft(Draft draft) {
    table.initDraft();
    DraftManager.getInstance().createDraftSession(draft, userPlayerMap, table.getId());
    for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
      User user = UserManager.getInstance().getUser(entry.getKey());
      if (user != null) {
        logger.info(
            new StringBuilder("User ")
                .append(user.getName())
                .append(" draft started: ")
                .append(draft.getId())
                .append(" userId: ")
                .append(user.getId()));
        user.ccDraftStarted(draft.getId(), entry.getValue());
      } else {
        logger.fatal(
            new StringBuilder("Start draft user not found userId: ").append(entry.getKey()));
      }
    }
  }

  private void sideboard(UUID playerId, Deck deck) throws MageException {
    for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
      if (entry.getValue().equals(playerId)) {
        User user = UserManager.getInstance().getUser(entry.getKey());
        int remaining = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
        if (user != null) {
          user.ccSideboard(deck, table.getId(), remaining, options.isLimited());
        }
        break;
      }
    }
  }

  public int getRemainingTime() {
    return (int) futureTimeout.getDelay(TimeUnit.SECONDS);
  }

  public void construct() {
    table.construct();
  }

  public void initTournament() {
    table.initTournament();
  }

  public void endTournament(Tournament tournament) {
    table.endTournament();
  }

  public MatchOptions getOptions() {
    return options;
  }

  /**
   * Ends the current game and starts if neccessary the next game
   *
   * @return true if table can be closed
   */
  public boolean endGameAndStartNextGame() {
    // get player that chooses who goes first
    Game game = match.getGame();
    if (game == null) {
      return true;
    }
    UUID choosingPlayerId = match.getChooser();
    match.endGame();
    if (ConfigSettings.getInstance().isSaveGameActivated() && !game.isSimulation()) {
      if (GameManager.getInstance().saveGame(game.getId())) {
        match.setReplayAvailable(true);
      }
    }
    GameManager.getInstance().removeGame(game.getId());
    try {
      if (!match.hasEnded()) {
        if (match.getGame() != null && match.getGame().getGameType().isSideboardingAllowed()) {
          sideboard();
        }
        if (!match.hasEnded()) {
          startGame(choosingPlayerId);
        } else {
          closeTable();
        }
      } else {
        closeTable();
      }
    } catch (GameException ex) {
      logger.fatal(null, ex);
    }
    return match.hasEnded();
  }

  private void sideboard() {
    table.sideboard();
    setupTimeout(Match.SIDEBOARD_TIME);
    if (table.isTournamentSubTable()) {
      for (MatchPlayer matchPlayer : match.getPlayers()) {
        if (!matchPlayer.hasQuit()) {
          TournamentPlayer tournamentPlayer =
              table.getTournament().getPlayer(matchPlayer.getPlayer().getId());
          if (tournamentPlayer != null) {
            tournamentPlayer.setStateInfo("sideboarding");
          }
        }
      }
    }
    match.sideboard();
    cancelTimeout();
    if (table.isTournamentSubTable()) {
      for (MatchPlayer matchPlayer : match.getPlayers()) {
        TournamentPlayer tournamentPlayer =
            table.getTournament().getPlayer(matchPlayer.getPlayer().getId());
        if (tournamentPlayer != null && tournamentPlayer.getStateInfo().equals("sideboarding")) {
          tournamentPlayer.setStateInfo("");
        }
      }
    }
  }

  /** Tables of normal matches or tournament sub tables are no longer needed, if the match ends. */
  private void closeTable() {
    this.matchEnd();
    table.closeTable();
  }

  private void matchEnd() {
    if (match != null) {
      for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
        MatchPlayer matchPlayer = match.getPlayer(entry.getValue());
        // opponent(s) left during sideboarding
        if (matchPlayer != null) {
          if (!matchPlayer.hasQuit()) {
            User user = UserManager.getInstance().getUser(entry.getKey());
            if (user != null) {
              if (table.getState().equals(TableState.SIDEBOARDING)) {
                StringBuilder sb = new StringBuilder();
                if (table.isTournamentSubTable()) {
                  sb.append("Your tournament match of round ");
                  sb.append(table.getTournament().getRounds().size());
                  sb.append(" is over. ");
                } else {
                  sb.append("Match [").append(match.getName()).append("] is over. ");
                }
                if (match.getPlayers().size() > 2) {
                  sb.append("All your opponents have lost or quit the match.");
                } else {
                  sb.append("Your opponent has quit the match.");
                }
                user.showUserMessage("Match info", sb.toString());
              }
              // remove table from user - table manager holds table for display of finished matches
              if (!table.isTournamentSubTable()) {
                user.removeTable(entry.getValue());
              }
            }
          }
        }
      }
      // free resources no longer needed
      match.cleanUpOnMatchEnd(
          ConfigSettings.getInstance().isSaveGameActivated(), table.isTournament());
    }
  }

  private synchronized void setupTimeout(int seconds) {
    cancelTimeout();
    if (seconds > 0) {
      futureTimeout =
          timeoutExecutor.schedule(
              new Runnable() {
                @Override
                public void run() {
                  autoSideboard();
                }
              },
              seconds,
              TimeUnit.SECONDS);
    }
  }

  private synchronized void cancelTimeout() {
    if (futureTimeout != null) {
      futureTimeout.cancel(false);
    }
  }

  private void autoSideboard() {
    for (MatchPlayer player : match.getPlayers()) {
      if (!player.isDoneSideboarding()) {
        match.submitDeck(player.getPlayer().getId(), player.generateDeck());
      }
    }
  }

  public void endDraft(Draft draft) {
    if (!draft.isAbort()) {
      for (DraftPlayer player : draft.getPlayers()) {
        player.prepareDeck();
        tournament.getPlayer(player.getPlayer().getId()).setDeck(player.getDeck());
      }
    }
    tournament.clearDraft(); // free the draft resources after draft step has ended
    tournament.nextStep();
  }

  public void swapSeats(int seatNum1, int seatNum2) {
    if (table.getState().equals(TableState.READY_TO_START)) {
      if (seatNum1 >= 0
          && seatNum2 >= 0
          && seatNum1 < table.getSeats().length
          && seatNum2 < table.getSeats().length) {
        Player swapPlayer = table.getSeats()[seatNum1].getPlayer();
        String swapType = table.getSeats()[seatNum1].getPlayerType();
        table.getSeats()[seatNum1].setPlayer(table.getSeats()[seatNum2].getPlayer());
        table.getSeats()[seatNum1].setPlayerType(table.getSeats()[seatNum2].getPlayerType());
        table.getSeats()[seatNum2].setPlayer(swapPlayer);
        table.getSeats()[seatNum2].setPlayerType(swapType);
      }
    }
  }

  public boolean isOwner(UUID userId) {
    if (userId == null) {
      return false;
    }
    return userId.equals(this.userId);
  }

  public Table getTable() {
    return table;
  }

  public UUID getChatId() {
    return chatId;
  }

  public Match getMatch() {
    return match;
  }

  public boolean isTournamentStillValid() {
    if (table.getTournament() != null) {
      if (!table.getState().equals(TableState.WAITING)
          && !table.getState().equals(TableState.READY_TO_START)
          && !table.getState().equals(TableState.STARTING)) {
        TournamentController tournamentController =
            TournamentManager.getInstance().getTournamentController(table.getTournament().getId());
        if (tournamentController != null) {
          return tournamentController.isTournamentStillValid(table.getState());
        } else {
          return false;
        }
      } else {
        // check if table creator is still a valid user, if not remove table
        User user = UserManager.getInstance().getUser(userId);
        return user != null;
      }
    }
    return false;
  }

  public UUID getUserId(UUID playerId) {
    for (Map.Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
      if (entry.getValue().equals(playerId)) {
        return entry.getKey();
      }
    }
    return null;
  }

  public boolean isUserStillActive(UUID userId) {
    UUID playerId = userPlayerMap.get(userId);
    if (playerId != null) {
      if (tournament != null) {
        TournamentPlayer tournamentPlayer = tournament.getPlayer(playerId);
        if (tournamentPlayer != null) {
          return tournamentPlayer.isInTournament();
        }
      } else if (match != null) {
        MatchPlayer matchPlayer = match.getPlayer(playerId);
        return matchPlayer != null && !matchPlayer.hasQuit();
      }
    }
    return false;
  }

  public boolean isMatchTableStillValid() {
    // check only normal match table with state != Finished
    if (!table.isTournament()) {
      int humanPlayers = 0;
      int aiPlayers = 0;
      int validHumanPlayers = 0;
      if (!(table.getState().equals(TableState.WAITING)
          || table.getState().equals(TableState.STARTING)
          || table.getState().equals(TableState.READY_TO_START))) {
        if (match == null) {
          logger.debug("- Match table with no match:");
          logger.debug("-- matchId:" + match.getId() + " [" + match.getName() + "]");
          // return false;
        } else if (match.isDoneSideboarding() && match.getGame() == null) {
          // no sideboarding and not active game -> match seems to hang (maybe the Draw bug)
          logger.debug("- Match with no active game and not in sideboard state:");
          logger.debug("-- matchId:" + match.getId() + " [" + match.getName() + "]");
          // return false;
        }
      }
      // check for active players
      for (Map.Entry<UUID, UUID> userPlayerEntry : userPlayerMap.entrySet()) {
        MatchPlayer matchPlayer = match.getPlayer(userPlayerEntry.getValue());
        if (matchPlayer == null) {
          logger.debug("- Match player not found:");
          logger.debug("-- matchId:" + match.getId());
          logger.debug("-- userId:" + userPlayerEntry.getKey());
          logger.debug("-- playerId:" + userPlayerEntry.getValue());
          continue;
        }
        if (matchPlayer.getPlayer().isHuman()) {
          humanPlayers++;
          if ((table.getState().equals(TableState.WAITING)
                  || table.getState().equals(TableState.STARTING)
                  || table.getState().equals(TableState.READY_TO_START))
              || !match.isDoneSideboarding()
              || (!matchPlayer.hasQuit()
                  && match.getGame() != null
                  && matchPlayer.getPlayer().isInGame())) {
            User user = UserManager.getInstance().getUser(userPlayerEntry.getKey());
            if (user == null) {
              logger.debug("- Active user of match is missing: " + matchPlayer.getName());
              logger.debug("-- matchId:" + match.getId());
              logger.debug("-- userId:" + userPlayerEntry.getKey());
              logger.debug("-- playerId:" + userPlayerEntry.getValue());
              return false;
            }
            // user exits on the server and match player has not quit -> player is valid
            validHumanPlayers++;
          }
        } else {
          aiPlayers++;
        }
      }
      // if at least 2 human players are valid (multiplayer) or all human players are valid the
      // table is valid or it's an AI match
      return validHumanPlayers >= 2 || validHumanPlayers == humanPlayers || aiPlayers > 1;
    }
    return true;
  }

  void cleanUp() {
    if (!table.isTournamentSubTable()) {
      for (Map.Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
        User user = UserManager.getInstance().getUser(entry.getKey());
        if (user != null) {
          user.removeTable(entry.getValue());
        }
      }
    }
    ChatManager.getInstance().destroyChatSession(chatId);
  }

  public synchronized TableState getTableState() {
    return getTable().getState();
  }

  public synchronized boolean changeTableStateToStarting() {
    if (!getTable().getState().equals(TableState.READY_TO_START)) {
      // tournament is not ready, can't start
      return false;
    }
    if (!table.allSeatsAreOccupied()) {
      logger.debug("Not alle Seats are occupied: stop start tableId:" + table.getId());
      return false;
    }
    getTable().setState(TableState.STARTING);
    return true;
  }
}
Exemple #2
0
/**
 * manages users - if a user is disconnected and 10 minutes have passed with no activity the user is
 * removed
 *
 * @author BetaSteward_at_googlemail.com
 */
public class UserManager {

  protected static ScheduledExecutorService expireExecutor =
      Executors.newSingleThreadScheduledExecutor();

  private static final Logger logger = Logger.getLogger(UserManager.class);

  private final ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<>();

  private static final ExecutorService callExecutor =
      ThreadExecutor.getInstance().getCallExecutor();

  private static final UserManager INSTANCE = new UserManager();

  public static UserManager getInstance() {
    return INSTANCE;
  }

  private UserManager() {
    expireExecutor.scheduleAtFixedRate(
        new Runnable() {
          @Override
          public void run() {
            checkExpired();
          }
        },
        60,
        60,
        TimeUnit.SECONDS);
  }

  public User createUser(String userName, String host) {
    if (findUser(userName) != null) {
      return null; // user already exists
    }
    User user = new User(userName, host);
    users.put(user.getId(), user);
    return user;
  }

  public User getUser(UUID userId) {
    if (userId != null) {
      return users.get(userId);
    }
    return null;
  }

  public User findUser(String userName) {
    for (User user : users.values()) {
      if (user.getName().equals(userName)) {
        return user;
      }
    }
    return null;
  }

  public Collection<User> getUsers() {
    return users.values();
  }

  public boolean connectToSession(String sessionId, UUID userId) {
    if (userId != null) {
      User user = users.get(userId);
      if (user != null) {
        user.setSessionId(sessionId);
        return true;
      }
    }
    return false;
  }

  public void disconnect(UUID userId, DisconnectReason reason) {
    if (userId != null) {
      User user = users.get(userId);
      if (user != null) {
        user.setSessionId(""); // Session will be set again with new id if user reconnects
      }
      ChatManager.getInstance().removeUser(userId, reason);
    }
  }

  public boolean isAdmin(UUID userId) {
    if (userId != null) {
      User user = users.get(userId);
      if (user != null) {
        return user.getName().equals("Admin");
      }
    }
    return false;
  }

  public void removeUser(final UUID userId, final DisconnectReason reason) {
    if (userId != null) {
      final User user = users.get(userId);
      if (user != null) {
        callExecutor.execute(
            new Runnable() {
              @Override
              public void run() {
                try {
                  logger.info(
                      "USER REMOVE - "
                          + user.getName()
                          + " ("
                          + reason.toString()
                          + ")  userId: "
                          + userId);
                  user.remove(reason);
                  logger.debug("USER REMOVE END - " + user.getName());
                } catch (Exception ex) {
                  handleException(ex);
                } finally {
                  users.remove(userId);
                }
              }
            });
      } else {
        logger.warn("Trying to remove userId: " + userId + " - but it does not exist.");
      }
    }
  }

  public boolean extendUserSession(UUID userId, String pingInfo) {
    if (userId != null) {
      User user = users.get(userId);
      if (user != null) {
        user.updateLastActivity(pingInfo);
        return true;
      }
    }
    return false;
  }

  /**
   * Is the connection lost for more than 3 minutes, the user will be removed (within 3 minutes the
   * user can reconnect)
   */
  private void checkExpired() {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, -3);
    List<User> usersToCheck = new ArrayList<>();
    usersToCheck.addAll(users.values());
    for (User user : usersToCheck) {
      if (!user.getUserState().equals(UserState.Expired) && user.isExpired(calendar.getTime())) {
        removeUser(user.getId(), DisconnectReason.SessionExpired);
      }
    }
  }

  public void handleException(Exception ex) {
    if (ex != null) {
      logger.fatal(
          "User manager exception " + (ex.getMessage() == null ? "null" : ex.getMessage()));
      if (ex.getCause() != null) {
        logger.debug(
            "- Cause: "
                + (ex.getCause().getMessage() == null ? "null" : ex.getCause().getMessage()));
      }
      ex.printStackTrace();
    } else {
      logger.fatal("User manager exception - null");
    }
  }
}
Exemple #3
0
  private void update() {
    ArrayList<TableView> tableList = new ArrayList<>();
    ArrayList<MatchView> matchList = new ArrayList<>();
    List<Table> allTables = new ArrayList<>(tables.values());
    Collections.sort(allTables, new TableListSorter());
    for (Table table : allTables) {
      if (table.getState() != TableState.FINISHED) {
        tableList.add(new TableView(table));
      } else if (matchList.size() < 50) {
        matchList.add(new MatchView(table));
      } else {
        // more since 50 matches finished since this match so remove it
        if (table.isTournament()) {
          TournamentManager.getInstance().removeTournament(table.getTournament().getId());
        }
        this.removeTable(table.getId());
      }
    }
    tableView = tableList;
    matchView = matchList;
    List<UsersView> users = new ArrayList<>();
    for (User user : UserManager.getInstance().getUsers()) {
      try {
        users.add(
            new UsersView(
                user.getUserData().getFlagName(),
                user.getName(),
                user.getMatchHistory(),
                user.getMatchQuitRatio(),
                user.getTourneyHistory(),
                user.getTourneyQuitRatio(),
                user.getGameInfo(),
                user.getPingInfo()));
      } catch (Exception ex) {
        logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
        users.add(
            new UsersView(
                (user.getUserData() != null && user.getUserData().getFlagName() != null)
                    ? user.getUserData().getFlagName()
                    : "world",
                user.getName() != null ? user.getName() : "<no name>",
                user.getMatchHistory() != null ? user.getMatchHistory() : "<no match history>",
                user.getMatchQuitRatio(),
                user.getTourneyHistory() != null
                    ? user.getTourneyHistory()
                    : "<no tourney history>",
                user.getTourneyQuitRatio(),
                "[exception]",
                user.getPingInfo() != null ? user.getPingInfo() : "<no ping>"));
      }
    }

    Collections.sort(users, new UserNameSorter());
    List<RoomUsersView> roomUserInfo = new ArrayList<>();
    roomUserInfo.add(
        new RoomUsersView(
            users,
            GameManager.getInstance().getNumberActiveGames(),
            ThreadExecutor.getInstance()
                .getActiveThreads(ThreadExecutor.getInstance().getGameExecutor()),
            ConfigSettings.getInstance().getMaxGameThreads()));
    roomUsersView = roomUserInfo;
  }