mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
deck improves:
* gui: removed public deck hash info; * gui: improved xmage dck-file - now it correctly load a card's amount (related to files from third party services); * server: fixed wrong cheating warning on deck construction (closes #11877); * refactor: removed outdated hash code and calculations; * other: added docs, added multiple deck hash tests;
This commit is contained in:
parent
889c1125e8
commit
7817a5cac6
32 changed files with 551 additions and 247 deletions
|
|
@ -236,35 +236,51 @@ public class TableController {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and join real player
|
||||
*/
|
||||
public synchronized boolean joinTable(UUID userId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws MageException {
|
||||
// user - must exist
|
||||
Optional<User> _user = managerFactory.userManager().getUser(userId);
|
||||
if (!_user.isPresent()) {
|
||||
logger.error("Join Table: can't find user to join " + name + " Id = " + userId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// table - already joined
|
||||
User user = _user.get();
|
||||
if (userPlayerMap.containsKey(userId) && playerType == PlayerType.HUMAN) {
|
||||
user.showUserMessage("Join Table", new StringBuilder("You can join a table only one time.").toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// table - already started
|
||||
if (table.getState() != TableState.WAITING) {
|
||||
user.showUserMessage("Join Table", "No available seats.");
|
||||
return false;
|
||||
}
|
||||
// check password
|
||||
|
||||
// table - wrong password
|
||||
if (!table.getMatch().getOptions().getPassword().isEmpty() && playerType == PlayerType.HUMAN) {
|
||||
if (!table.getMatch().getOptions().getPassword().equals(password)) {
|
||||
user.showUserMessage("Join Table", "Wrong password.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// table - no more free seats
|
||||
Seat seat = table.getNextAvailableSeat(playerType);
|
||||
if (seat == null) {
|
||||
user.showUserMessage("Join Table", "No available seats.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// deck - try to load real deck (any unknown cards will raise exception error here)
|
||||
// P.S. Quick start button can raise error here on memory problems, but it's ok
|
||||
// (real table creating is unaffected and will not create empty table)
|
||||
Deck deck = Deck.load(deckList, false, false);
|
||||
|
||||
// deck - validate
|
||||
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");
|
||||
List<DeckValidatorError> errorsList = table.getValidator().getErrorsListSorted();
|
||||
|
|
@ -273,13 +289,16 @@ public class TableController {
|
|||
});
|
||||
sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!");
|
||||
user.showUserMessage("Join Table", sb.toString());
|
||||
|
||||
// owner must create table with valid deck only
|
||||
if (isOwner(userId)) {
|
||||
logger.debug("New table removed because owner submitted invalid deck tableId " + table.getId());
|
||||
logger.error("New table removed because owner submitted invalid deck tableId " + table.getId());
|
||||
managerFactory.tableManager().removeTable(table.getId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Check quit ratio.
|
||||
|
||||
// user - restrict by quit ratio
|
||||
int quitRatio = table.getMatch().getOptions().getQuitRatio();
|
||||
if (quitRatio < user.getMatchQuitRatio()) {
|
||||
String message = new StringBuilder("Your quit ratio ").append(user.getMatchQuitRatio())
|
||||
|
|
@ -288,7 +307,7 @@ public class TableController {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check minimum rating.
|
||||
// user - restrict by rating
|
||||
int minimumRating = table.getMatch().getOptions().getMinimumRating();
|
||||
int userRating;
|
||||
if (table.getMatch().getOptions().isLimited()) {
|
||||
|
|
@ -303,7 +322,7 @@ public class TableController {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check power level for table (currently only used for EDH/Commander table)
|
||||
// user - restrict by deck power level and cards colors (see edh power level for details)
|
||||
int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel();
|
||||
if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase(Locale.ENGLISH).equals("commander")) {
|
||||
int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck);
|
||||
|
|
@ -349,6 +368,7 @@ public class TableController {
|
|||
}
|
||||
}
|
||||
|
||||
// player - try to create (human, ai, etc)
|
||||
Optional<Player> playerOpt = createPlayer(name, seat.getPlayerType(), skill);
|
||||
if (!playerOpt.isPresent()) {
|
||||
String message = "Could not create player " + name + " of type " + seat.getPlayerType();
|
||||
|
|
@ -356,14 +376,18 @@ public class TableController {
|
|||
user.showUserMessage("Join Table", message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// player - restrict by player type, e.g. draft bot can join to tourney only
|
||||
Player player = playerOpt.get();
|
||||
if (!player.canJoinTable(table)) {
|
||||
user.showUserMessage("Join Table", "A " + seat.getPlayerType() + " player can't join this table.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// all fine, player can be added
|
||||
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());
|
||||
|
|
@ -373,6 +397,7 @@ public class TableController {
|
|||
user.ccJoinedTable(table.getRoomId(), table.getId(), false);
|
||||
userPlayerMap.put(userId, player.getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -391,8 +416,22 @@ public class TableController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit deck on sideboarding/construction (final deck)
|
||||
*/
|
||||
public synchronized boolean submitDeck(UUID userId, DeckCardLists deckList) throws MageException {
|
||||
UUID playerId = userPlayerMap.get(userId);
|
||||
|
||||
// player - update tourney's player status
|
||||
// TODO: need research, why it here instead real time check?
|
||||
if (table.isTournamentSubTable()) {
|
||||
TournamentPlayer tournamentPlayer = table.getTournament().getPlayer(playerId);
|
||||
if (tournamentPlayer != null) {
|
||||
tournamentPlayer.setStateInfo(""); // reset sideboarding state
|
||||
}
|
||||
}
|
||||
|
||||
// player - already quit
|
||||
if (table.isTournament()) {
|
||||
TournamentPlayer player = tournament.getPlayer(playerId);
|
||||
if (player == null || player.hasQuit()) {
|
||||
|
|
@ -403,23 +442,26 @@ public class TableController {
|
|||
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) {
|
||||
|
||||
// tourney - too late for submit
|
||||
if (table.getState() != TableState.SIDEBOARDING
|
||||
&& table.getState() != TableState.CONSTRUCTING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// deck - try to load real deck (any unknown cards will raise exception error here)
|
||||
Deck deck = Deck.load(deckList, false, false);
|
||||
|
||||
// workaround to keep deck name for Tiny Leaders because it must be hidden for players
|
||||
if (table.getState() == TableState.SIDEBOARDING && table.getMatch() != null) {
|
||||
MatchPlayer mPlayer = table.getMatch().getPlayer(playerId);
|
||||
if (mPlayer != null) {
|
||||
deck.setName(mPlayer.getDeck().getName());
|
||||
}
|
||||
}
|
||||
|
||||
// deck - validate
|
||||
if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
|
||||
Optional<User> _user = managerFactory.userManager().getUser(userId);
|
||||
if (!_user.isPresent()) {
|
||||
|
|
@ -434,21 +476,20 @@ public class TableController {
|
|||
_user.get().showUserMessage("Submit deck", sb.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
submitDeck(userId, playerId, deck);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateDeck(UUID userId, DeckCardLists deckList) throws MageException {
|
||||
boolean validDeck;
|
||||
UUID playerId = userPlayerMap.get(userId);
|
||||
if (table.getState() != TableState.SIDEBOARDING && table.getState() != TableState.CONSTRUCTING) {
|
||||
// TODO: need refactor, many duplicated code like state check
|
||||
if (table.getState() != TableState.SIDEBOARDING
|
||||
&& table.getState() != TableState.CONSTRUCTING) {
|
||||
return;
|
||||
}
|
||||
Deck deck = Deck.load(deckList, false, false);
|
||||
validDeck = updateDeck(userId, playerId, deck);
|
||||
if (!validDeck && getTableState() == TableState.SIDEBOARDING) {
|
||||
logger.warn(" userId: " + userId + " - Modified deck card list!");
|
||||
}
|
||||
updateDeck(userId, playerId, deck);
|
||||
}
|
||||
|
||||
private void submitDeck(UUID userId, UUID playerId, Deck deck) {
|
||||
|
|
@ -461,20 +502,21 @@ public class TableController {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean updateDeck(UUID userId, UUID playerId, Deck deck) {
|
||||
boolean validDeck = true;
|
||||
private void updateDeck(UUID userId, UUID playerId, Deck deck) {
|
||||
if (table.isTournament()) {
|
||||
if (tournament != null) {
|
||||
validDeck = managerFactory.tournamentManager().updateDeck(tournament.getId(), playerId, deck);
|
||||
// TODO: is it possible to update from direct call command in game?!
|
||||
managerFactory.tournamentManager().updateDeck(tournament.getId(), playerId, deck);
|
||||
} else {
|
||||
logger.fatal("Tournament == null table: " + table.getId() + " userId: " + userId);
|
||||
}
|
||||
} else if (TableState.SIDEBOARDING == table.getState()) {
|
||||
validDeck = match.updateDeck(playerId, deck);
|
||||
} else if (table.getState() == TableState.SIDEBOARDING) {
|
||||
match.updateDeck(playerId, deck);
|
||||
} else {
|
||||
// deck was meanwhile submitted so the autoupdate can be ignored
|
||||
// TODO: need research
|
||||
logger.warn("wtf, why it submitting?!");
|
||||
}
|
||||
return validDeck;
|
||||
}
|
||||
|
||||
public boolean watchTable(UUID userId) {
|
||||
|
|
@ -538,6 +580,7 @@ public class TableController {
|
|||
} else {
|
||||
UUID playerId = userPlayerMap.get(userId);
|
||||
if (playerId != null) {
|
||||
// TODO: wtf, need research and document all that checks, can be never used code
|
||||
if (table.getState() == TableState.WAITING || table.getState() == TableState.READY_TO_START) {
|
||||
table.leaveNotStartedTable(playerId);
|
||||
if (table.isTournament()) {
|
||||
|
|
@ -884,7 +927,7 @@ public class TableController {
|
|||
private void autoSideboard() {
|
||||
for (MatchPlayer player : match.getPlayers()) {
|
||||
if (!player.isDoneSideboarding()) {
|
||||
match.submitDeck(player.getPlayer().getId(), player.generateDeck(table.getValidator()));
|
||||
match.submitDeck(player.getPlayer().getId(), player.autoCompleteDeck(table.getValidator()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public interface TournamentManager {
|
|||
|
||||
void submitDeck(UUID tournamentId, UUID playerId, Deck deck);
|
||||
|
||||
boolean updateDeck(UUID tournamentId, UUID playerId, Deck deck);
|
||||
void updateDeck(UUID tournamentId, UUID playerId, Deck deck);
|
||||
|
||||
TournamentView getTournamentView(UUID tournamentId);
|
||||
|
||||
|
|
|
|||
|
|
@ -324,11 +324,13 @@ public class TournamentController {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean updateDeck(UUID playerId, Deck deck) {
|
||||
if (tournamentSessions.containsKey(playerId)) {
|
||||
return tournamentSessions.get(playerId).updateDeck(deck);
|
||||
public void updateDeck(UUID playerId, Deck deck) {
|
||||
TournamentSession session = tournamentSessions.getOrDefault(playerId, null);
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
|
||||
session.updateDeck(deck);
|
||||
}
|
||||
|
||||
public void timeout(UUID userId) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import java.util.concurrent.ConcurrentMap;
|
|||
*/
|
||||
public class TournamentManagerImpl implements TournamentManager {
|
||||
|
||||
// TODO: must check all usage of controllers.get and insert null check and error log
|
||||
private final ManagerFactory managerFactory;
|
||||
private final ConcurrentMap<UUID, TournamentController> controllers = new ConcurrentHashMap<>();
|
||||
|
||||
|
|
@ -61,8 +62,8 @@ public class TournamentManagerImpl implements TournamentManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean updateDeck(UUID tournamentId, UUID playerId, Deck deck) {
|
||||
return controllers.get(tournamentId).updateDeck(playerId, deck);
|
||||
public void updateDeck(UUID tournamentId, UUID playerId, Deck deck) {
|
||||
controllers.get(tournamentId).updateDeck(playerId, deck);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ public class TournamentSession {
|
|||
tournament.submitDeck(playerId, deck);
|
||||
}
|
||||
|
||||
public boolean updateDeck(Deck deck) {
|
||||
return tournament.updateDeck(playerId, deck);
|
||||
public void updateDeck(Deck deck) {
|
||||
tournament.updateDeck(playerId, deck);
|
||||
}
|
||||
|
||||
public void setKilled() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue