mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
Record game histories. Compute user stats and show them in the user panel.
This commit is contained in:
parent
9f3e2aa4c4
commit
550648ccbe
22 changed files with 866 additions and 60 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -94,3 +94,5 @@ Mage.Client/serverlist.txt
|
|||
/target/
|
||||
|
||||
client_secrets.json
|
||||
|
||||
dependency-reduced-pom.xml
|
||||
|
|
|
@ -61,7 +61,7 @@ public class PlayersChatPanel extends javax.swing.JPanel {
|
|||
|
||||
private final List<String> players = new ArrayList<>();
|
||||
private final UserTableModel userTableModel;
|
||||
private static final int[] defaultColumnsWidth = {20, 100, 100, 80, 80};
|
||||
private static final int[] defaultColumnsWidth = {20, 100, 100, 100, 80, 80};
|
||||
|
||||
|
||||
/*
|
||||
|
@ -118,7 +118,7 @@ public class PlayersChatPanel extends javax.swing.JPanel {
|
|||
|
||||
class UserTableModel extends AbstractTableModel {
|
||||
|
||||
private final String[] columnNames = new String[]{"Loc", "Players", "Info", "Games", "Connection"};
|
||||
private final String[] columnNames = new String[]{"Loc", "Players", "History", "Info", "Games", "Connection"};
|
||||
private UsersView[] players = new UsersView[0];
|
||||
|
||||
public void loadData(Collection<RoomUsersView> roomUserInfoList) throws MageRemoteException {
|
||||
|
@ -154,10 +154,12 @@ public class PlayersChatPanel extends javax.swing.JPanel {
|
|||
case 1:
|
||||
return players[arg0].getUserName();
|
||||
case 2:
|
||||
return players[arg0].getInfoState();
|
||||
return players[arg0].getHistory();
|
||||
case 3:
|
||||
return players[arg0].getInfoGames();
|
||||
return players[arg0].getInfoState();
|
||||
case 4:
|
||||
return players[arg0].getInfoGames();
|
||||
case 5:
|
||||
return players[arg0].getInfoPing();
|
||||
}
|
||||
return "";
|
||||
|
|
|
@ -39,12 +39,14 @@ public class UsersView implements Serializable {
|
|||
|
||||
private final String flagName;
|
||||
private final String userName;
|
||||
private final String history;
|
||||
private final String infoState;
|
||||
private final String infoGames;
|
||||
private final String infoPing;
|
||||
|
||||
public UsersView(String flagName, String userName, String infoState, String infoGames, String infoPing) {
|
||||
public UsersView(String flagName, String userName, String history, String infoState, String infoGames, String infoPing) {
|
||||
this.flagName = flagName;
|
||||
this.history = history;
|
||||
this.userName = userName;
|
||||
this.infoState = infoState;
|
||||
this.infoGames = infoGames;
|
||||
|
@ -59,6 +61,10 @@ public class UsersView implements Serializable {
|
|||
return userName;
|
||||
}
|
||||
|
||||
public String getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
public String getInfoState() {
|
||||
return infoState;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,11 @@
|
|||
<artifactId>jersey-multipart</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.7.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -438,7 +438,7 @@ public class MageServerImpl implements MageServer {
|
|||
// }
|
||||
@Override
|
||||
public boolean startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
|
||||
if (!TableManager.getInstance().getController(tableId).changeTableState(TableState.STARTING)) {
|
||||
if (!TableManager.getInstance().getController(tableId).changeTableStateToStarting()) {
|
||||
return false;
|
||||
}
|
||||
execute("startMatch", sessionId, new Action() {
|
||||
|
@ -463,7 +463,7 @@ public class MageServerImpl implements MageServer {
|
|||
// }
|
||||
@Override
|
||||
public boolean startTournament(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
|
||||
if (!TableManager.getInstance().getController(tableId).changeTableState(TableState.STARTING)) {
|
||||
if (!TableManager.getInstance().getController(tableId).changeTableStateToStarting()) {
|
||||
return false;
|
||||
}
|
||||
execute("startTournament", sessionId, new Action() {
|
||||
|
|
|
@ -32,10 +32,20 @@ import java.io.FilenameFilter;
|
|||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.management.MBeanServer;
|
||||
import mage.cards.repository.CardScanner;
|
||||
import mage.game.match.MatchType;
|
||||
import mage.game.result.ResultProtos.MatchPlayerProto;
|
||||
import mage.game.result.ResultProtos.MatchProto;
|
||||
import mage.game.result.ResultProtos.TableProto;
|
||||
import mage.game.result.ResultProtos.TourneyPlayerProto;
|
||||
import mage.game.result.ResultProtos.TourneyProto;
|
||||
import mage.game.result.ResultProtos.UserStatsProto;
|
||||
import mage.game.tournament.TournamentType;
|
||||
import mage.interfaces.MageServer;
|
||||
import mage.remote.Connection;
|
||||
|
@ -43,6 +53,10 @@ import mage.server.draft.CubeFactory;
|
|||
import mage.server.game.DeckValidatorFactory;
|
||||
import mage.server.game.GameFactory;
|
||||
import mage.server.game.PlayerFactory;
|
||||
import mage.server.record.TableRecord;
|
||||
import mage.server.record.TableRecordRepository;
|
||||
import mage.server.record.UserStats;
|
||||
import mage.server.record.UserStatsRepository;
|
||||
import mage.server.tournament.TournamentFactory;
|
||||
import mage.server.util.ConfigSettings;
|
||||
import mage.server.util.PluginClassLoader;
|
||||
|
@ -88,6 +102,8 @@ public class Main {
|
|||
protected static boolean testMode;
|
||||
protected static boolean fastDbMode;
|
||||
|
||||
private static final ScheduledExecutorService updateUserStatsTaskExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
|
@ -174,6 +190,13 @@ public class Main {
|
|||
} catch (Exception ex) {
|
||||
logger.fatal("Failed to start server - " + connection.toString(), ex);
|
||||
}
|
||||
|
||||
updateUserStatsTaskExecutor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUserStats();
|
||||
}
|
||||
}, 60, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
static void initStatistics() {
|
||||
|
@ -368,4 +391,68 @@ public class Main {
|
|||
public static boolean isTestMode() {
|
||||
return testMode;
|
||||
}
|
||||
|
||||
private static void updateUserStats() {
|
||||
long latestEndTimeMs = UserStatsRepository.instance.getLatestEndTimeMs();
|
||||
List<TableRecord> records = TableRecordRepository.instance.getAfter(latestEndTimeMs);
|
||||
for (TableRecord record : records) {
|
||||
TableProto table = record.getProto();
|
||||
if (table.getControllerName().equals("System")) {
|
||||
// This is a sub table within a tournament, so it's already handled by the main
|
||||
// tournament table.
|
||||
continue;
|
||||
}
|
||||
if (table.hasMatch()) {
|
||||
MatchProto match = table.getMatch();
|
||||
for (MatchPlayerProto player : match.getPlayersList()) {
|
||||
UserStats userStats = UserStatsRepository.instance.getUser(player.getName());
|
||||
UserStatsProto proto = userStats != null ? userStats.getProto() :
|
||||
UserStatsProto.newBuilder().setName(player.getName()).build();
|
||||
UserStatsProto.Builder builder = UserStatsProto.newBuilder(proto)
|
||||
.setMatches(proto.getMatches() + 1);
|
||||
switch (player.getQuit()) {
|
||||
case IDLE_TIMEOUT:
|
||||
builder.setMatchesIdleTimeout(proto.getMatchesIdleTimeout() + 1);
|
||||
break;
|
||||
case TIMER_TIMEOUT:
|
||||
builder.setMatchesTimerTimeout(proto.getMatchesTimerTimeout() + 1);
|
||||
break;
|
||||
case QUIT:
|
||||
builder.setMatchesQuit(proto.getMatchesQuit() + 1);
|
||||
break;
|
||||
}
|
||||
if (userStats == null) {
|
||||
UserStatsRepository.instance.add(new UserStats(builder.build(), table.getEndTimeMs()));
|
||||
} else {
|
||||
UserStatsRepository.instance.update(new UserStats(builder.build(), table.getEndTimeMs()));
|
||||
}
|
||||
}
|
||||
} else if (table.hasTourney()) {
|
||||
TourneyProto tourney = table.getTourney();
|
||||
for (TourneyPlayerProto player : tourney.getPlayersList()) {
|
||||
UserStats userStats = UserStatsRepository.instance.getUser(player.getName());
|
||||
UserStatsProto proto = userStats != null ? userStats.getProto() :
|
||||
UserStatsProto.newBuilder().setName(player.getName()).build();
|
||||
UserStatsProto.Builder builder = UserStatsProto.newBuilder(proto)
|
||||
.setTourneys(proto.getTourneys() + 1);
|
||||
switch (player.getQuit()) {
|
||||
case DURING_ROUND:
|
||||
builder.setTourneysQuitDuringRound(proto.getTourneysQuitDuringRound() + 1);
|
||||
break;
|
||||
case DURING_DRAFTING:
|
||||
builder.setTourneysQuitDuringDrafting(proto.getTourneysQuitDuringDrafting() + 1);
|
||||
break;
|
||||
case DURING_CONSTRUCTION:
|
||||
builder.setTourneysQuitDuringConstruction(proto.getTourneysQuitDuringConstruction() + 1);
|
||||
break;
|
||||
}
|
||||
if (userStats == null) {
|
||||
UserStatsRepository.instance.add(new UserStats(builder.build(), table.getEndTimeMs()));
|
||||
} else {
|
||||
UserStatsRepository.instance.update(new UserStats(builder.build(), table.getEndTimeMs()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ import mage.server.game.DeckValidatorFactory;
|
|||
import mage.server.game.GameFactory;
|
||||
import mage.server.game.GameManager;
|
||||
import mage.server.game.PlayerFactory;
|
||||
import mage.server.record.TableRecorderImpl;
|
||||
import mage.server.services.LogKeys;
|
||||
import mage.server.services.impl.LogServiceImpl;
|
||||
import mage.server.tournament.TournamentController;
|
||||
|
@ -105,7 +106,7 @@ public class TableController {
|
|||
} else {
|
||||
controllerName = "System";
|
||||
}
|
||||
table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes(), match);
|
||||
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();
|
||||
}
|
||||
|
@ -124,7 +125,7 @@ public class TableController {
|
|||
} else {
|
||||
controllerName = "System";
|
||||
}
|
||||
table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getMatchOptions().getDeckType()), options.getPlayerTypes(), tournament);
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -237,6 +238,7 @@ public class TableController {
|
|||
|
||||
TournamentPlayer newTournamentPlayer = tournament.getPlayer(newPlayer.getId());
|
||||
newTournamentPlayer.setState(oldTournamentPlayer.getState());
|
||||
newTournamentPlayer.setReplacedTournamentPlayer(oldTournamentPlayer);
|
||||
|
||||
DraftManager.getInstance().getController(table.getId()).replacePlayer(oldPlayer, newPlayer);
|
||||
return true;
|
||||
|
@ -957,26 +959,16 @@ public class TableController {
|
|||
return getTable().getState();
|
||||
}
|
||||
|
||||
public synchronized boolean changeTableState(TableState newTableState) {
|
||||
switch (newTableState) {
|
||||
case WAITING:
|
||||
if (getTable().getState().equals(TableState.STARTING)) {
|
||||
// tournament already started
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case STARTING:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
public synchronized boolean changeTableStateToStarting() {
|
||||
if (!getTable().getState().equals(TableState.READY_TO_START)) {
|
||||
// tournament is not ready, can't start
|
||||
return false;
|
||||
}
|
||||
getTable().setState(newTableState);
|
||||
if (!table.allSeatsAreOccupied()) {
|
||||
logger.debug("Not alle Seats are occupied: stop start tableId:" + table.getId());
|
||||
return false;
|
||||
}
|
||||
getTable().setState(TableState.STARTING);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,11 +43,14 @@ import mage.constants.TableState;
|
|||
import mage.game.GameException;
|
||||
import mage.game.Table;
|
||||
import mage.game.match.MatchOptions;
|
||||
import mage.game.result.ResultProtos.UserStatsProto;
|
||||
import mage.game.tournament.TournamentOptions;
|
||||
import mage.server.RoomImpl;
|
||||
import mage.server.TableManager;
|
||||
import mage.server.User;
|
||||
import mage.server.UserManager;
|
||||
import mage.server.record.UserStats;
|
||||
import mage.server.record.UserStatsRepository;
|
||||
import mage.server.tournament.TournamentManager;
|
||||
import mage.server.util.ConfigSettings;
|
||||
import mage.server.util.ThreadExecutor;
|
||||
|
@ -91,6 +94,74 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
|||
return tableView;
|
||||
}
|
||||
|
||||
private static void joinStrings(StringBuilder joined, List<String> strings, String separator) {
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
if (i > 0) {
|
||||
joined.append(separator);
|
||||
}
|
||||
joined.append(strings.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static String joinBuilders(List<StringBuilder> builders) {
|
||||
if (builders.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder = builders.get(0);
|
||||
for (int i = 1; i < builders.size(); ++i) {
|
||||
builder.append(" ");
|
||||
builder.append(builders.get(i));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String userStatsToString(UserStatsProto proto) {
|
||||
List<StringBuilder> builders = new ArrayList();
|
||||
if (proto.getMatches() > 0) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Matches:");
|
||||
builder.append(proto.getMatches());
|
||||
List<String> quit = new ArrayList();
|
||||
if (proto.getMatchesIdleTimeout() > 0) {
|
||||
quit.add("I:" + Integer.toString(proto.getMatchesIdleTimeout()));
|
||||
}
|
||||
if (proto.getMatchesTimerTimeout() > 0) {
|
||||
quit.add("T:" + Integer.toString(proto.getMatchesTimerTimeout()));
|
||||
}
|
||||
if (proto.getMatchesQuit() > 0) {
|
||||
quit.add("Q:" + Integer.toString(proto.getMatchesQuit()));
|
||||
}
|
||||
if (quit.size() > 0) {
|
||||
builder.append(" (");
|
||||
joinStrings(builder, quit, " ");
|
||||
builder.append(")");
|
||||
}
|
||||
builders.add(builder);
|
||||
}
|
||||
if (proto.getTourneys() > 0) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Tourneys:");
|
||||
builder.append(proto.getTourneys());
|
||||
List<String> quit = new ArrayList();
|
||||
if (proto.getTourneysQuitDuringDrafting() > 0) {
|
||||
quit.add("D:" + Integer.toString(proto.getTourneysQuitDuringDrafting()));
|
||||
}
|
||||
if (proto.getTourneysQuitDuringConstruction() > 0) {
|
||||
quit.add("C:" + Integer.toString(proto.getTourneysQuitDuringConstruction()));
|
||||
}
|
||||
if (proto.getTourneysQuitDuringRound() > 0) {
|
||||
quit.add("R:" + Integer.toString(proto.getTourneysQuitDuringRound()));
|
||||
}
|
||||
if (quit.size() > 0) {
|
||||
builder.append(" (");
|
||||
joinStrings(builder, quit, " ");
|
||||
builder.append(")");
|
||||
}
|
||||
builders.add(builder);
|
||||
}
|
||||
return joinBuilders(builders);
|
||||
}
|
||||
|
||||
private void update() {
|
||||
ArrayList<TableView> tableList = new ArrayList<>();
|
||||
ArrayList<MatchView> matchList = new ArrayList<>();
|
||||
|
@ -100,11 +171,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
|||
if (table.getState() != TableState.FINISHED) {
|
||||
tableList.add(new TableView(table));
|
||||
} else if (matchList.size() < 50) {
|
||||
if (table.isTournament()) {
|
||||
matchList.add(new MatchView(table));
|
||||
} else {
|
||||
matchList.add(new MatchView(table));
|
||||
}
|
||||
matchList.add(new MatchView(table));
|
||||
} else {
|
||||
// more since 50 matches finished since this match so remove it
|
||||
if (table.isTournament()) {
|
||||
|
@ -117,13 +184,19 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
|||
matchView = matchList;
|
||||
List<UsersView> users = new ArrayList<>();
|
||||
for (User user : UserManager.getInstance().getUsers()) {
|
||||
String history = null;
|
||||
UserStats stats = UserStatsRepository.instance.getUser(user.getName());
|
||||
if (stats != null) {
|
||||
history = userStatsToString(stats.getProto());
|
||||
}
|
||||
try {
|
||||
users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo()));
|
||||
users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), history, user.getInfo(), 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>",
|
||||
history != null ? history : "<no history>",
|
||||
user.getInfo() != null ? user.getInfo() : "<no info>",
|
||||
"[exception]",
|
||||
user.getPingInfo() != null ? user.getPingInfo() : "<no ping>"));
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package mage.server.record;
|
||||
|
||||
import com.j256.ormlite.field.DataType;
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
import mage.game.result.ResultProtos.TableProto;
|
||||
import org.mage.mage.shaded.protobuf.InvalidProtocolBufferException;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@DatabaseTable(tableName = "table_history")
|
||||
public class TableRecord {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TableRecord.class);
|
||||
|
||||
@DatabaseField(dataType = DataType.BYTE_ARRAY, indexName = "proto_index", unique = true)
|
||||
protected byte[] proto;
|
||||
|
||||
@DatabaseField(indexName = "end_time_ms")
|
||||
protected long endTimeMs;
|
||||
|
||||
public TableRecord() {
|
||||
}
|
||||
|
||||
public TableRecord(TableProto proto, long endTimeMs) {
|
||||
this.proto = proto.toByteArray();
|
||||
this.endTimeMs = endTimeMs;
|
||||
}
|
||||
|
||||
public TableProto getProto() {
|
||||
try {
|
||||
return TableProto.parseFrom(this.proto);
|
||||
} catch (InvalidProtocolBufferException ex) {
|
||||
logger.error("Failed to parse serialized proto", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package mage.server.record;
|
||||
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public enum TableRecordRepository {
|
||||
|
||||
instance;
|
||||
|
||||
private static final String JDBC_URL = "jdbc:sqlite:./db/table_record.db";
|
||||
private static final String VERSION_ENTITY_NAME = "table_record";
|
||||
// raise this if db structure was changed
|
||||
private static final long DB_VERSION = 0;
|
||||
|
||||
private Dao<TableRecord, Object> dao;
|
||||
|
||||
private TableRecordRepository() {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
|
||||
|
||||
if (obsolete) {
|
||||
TableUtils.dropTable(connectionSource, TableRecord.class, true);
|
||||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, TableRecord.class);
|
||||
dao = DaoManager.createDao(connectionSource, TableRecord.class);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error creating table_record repository - ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(TableRecord tableHistory) {
|
||||
try {
|
||||
dao.create(tableHistory);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error adding a table_record to DB - ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public List<TableRecord> getAfter(long endTimeMs) {
|
||||
try {
|
||||
QueryBuilder<TableRecord, Object> qb = dao.queryBuilder();
|
||||
qb.where().gt("endTimeMs", endTimeMs);
|
||||
qb.orderBy("endTimeMs", true);
|
||||
return dao.query(qb.prepare());
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error getting table_records from DB - ", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void closeDB() {
|
||||
try {
|
||||
if (dao != null && dao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection();
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error closing table_record repository - ", ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package mage.server.record;
|
||||
|
||||
import mage.game.Table;
|
||||
import mage.game.Table.TableRecorder;
|
||||
import mage.game.result.ResultProtos.TableProto;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class TableRecorderImpl implements TableRecorder {
|
||||
|
||||
private static TableRecorderImpl INSTANCE = new TableRecorderImpl();
|
||||
private static final Logger logger = Logger.getLogger(TableRecorderImpl.class);
|
||||
|
||||
public static TableRecorderImpl getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void record(Table table) {
|
||||
TableProto proto = table.toProto();
|
||||
logger.info("Adding record:\n" + proto.toString());
|
||||
TableRecordRepository.instance.add(new TableRecord(proto, proto.getEndTimeMs()));
|
||||
}
|
||||
}
|
45
Mage.Server/src/main/java/mage/server/record/UserStats.java
Normal file
45
Mage.Server/src/main/java/mage/server/record/UserStats.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package mage.server.record;
|
||||
|
||||
import com.j256.ormlite.field.DataType;
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
import mage.game.result.ResultProtos.UserStatsProto;
|
||||
import org.mage.mage.shaded.protobuf.InvalidProtocolBufferException;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@DatabaseTable(tableName = "user_stats")
|
||||
public class UserStats {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TableRecord.class);
|
||||
|
||||
@DatabaseField(indexName = "user_name_index", unique = true, id = true)
|
||||
protected String userName;
|
||||
|
||||
@DatabaseField(dataType = DataType.BYTE_ARRAY)
|
||||
protected byte[] proto;
|
||||
|
||||
@DatabaseField(indexName = "end_time_ms_index")
|
||||
protected long endTimeMs;
|
||||
|
||||
public UserStats() {
|
||||
}
|
||||
|
||||
public UserStats(UserStatsProto proto, long endTimeMs) {
|
||||
this.userName = proto.getName();
|
||||
this.proto = proto.toByteArray();
|
||||
this.endTimeMs = endTimeMs;
|
||||
}
|
||||
|
||||
public UserStatsProto getProto() {
|
||||
try {
|
||||
return UserStatsProto.parseFrom(this.proto);
|
||||
} catch (InvalidProtocolBufferException ex) {
|
||||
logger.error("Failed to parse serialized proto", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getEndTimeMs() {
|
||||
return this.endTimeMs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package mage.server.record;
|
||||
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public enum UserStatsRepository {
|
||||
|
||||
instance;
|
||||
|
||||
private static final String JDBC_URL = "jdbc:sqlite:./db/user_stats.db";
|
||||
private static final String VERSION_ENTITY_NAME = "user_stats";
|
||||
// raise this if db structure was changed
|
||||
private static final long DB_VERSION = 0;
|
||||
|
||||
private Dao<UserStats, Object> dao;
|
||||
|
||||
private UserStatsRepository() {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
|
||||
|
||||
if (obsolete) {
|
||||
TableUtils.dropTable(connectionSource, UserStats.class, true);
|
||||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, UserStats.class);
|
||||
dao = DaoManager.createDao(connectionSource, UserStats.class);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error creating user_stats repository - ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(UserStats userStats) {
|
||||
try {
|
||||
dao.create(userStats);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error adding a user_stats to DB - ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(UserStats userStats) {
|
||||
try {
|
||||
dao.update(userStats);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error updating a user_stats in DB - ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public UserStats getUser(String userName) {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
qb.where().eq("userName", userName);
|
||||
List<UserStats> users = dao.query(qb.prepare());
|
||||
if (users.size() == 1) {
|
||||
return users.get(0);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error getting a user from DB - ", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UserStats> getAllUsers() {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
return dao.query(qb.prepare());
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error getting all users from DB - ", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getLatestEndTimeMs() {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
qb.orderBy("endTimeMs", false).limit(1);
|
||||
List<UserStats> users = dao.query(qb.prepare());
|
||||
if (users.size() == 1) {
|
||||
return users.get(0).getEndTimeMs();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error getting the latest end time from DB - ", ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void closeDB() {
|
||||
try {
|
||||
if (dao != null && dao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection();
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error closing user_stats repository - ", ex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ import mage.game.events.TableEvent;
|
|||
import static mage.game.events.TableEvent.EventType.CONSTRUCT;
|
||||
import mage.game.match.Match;
|
||||
import mage.game.match.MatchOptions;
|
||||
import mage.game.result.ResultProtos.TourneyQuitStatus;
|
||||
import mage.game.tournament.Tournament;
|
||||
import mage.game.tournament.TournamentPairing;
|
||||
import mage.game.tournament.TournamentPlayer;
|
||||
|
@ -351,31 +352,34 @@ public class TournamentController {
|
|||
tournamentSession.setKilled();
|
||||
if (tournamentPlayer.isInTournament()) {
|
||||
String info;
|
||||
TourneyQuitStatus status;
|
||||
if (tournament.isDoneConstructing()) {
|
||||
info = new StringBuilder("during round ").append(tournament.getRounds().size()).toString();
|
||||
// quit active matches of that tournament
|
||||
TableManager.getInstance().userQuitTournamentSubTables(tournament.getId(), userId);
|
||||
} else {
|
||||
if (tournamentPlayer.getState().equals(TournamentPlayerState.DRAFTING)) {
|
||||
info = "during Draft phase";
|
||||
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
|
||||
this.abortDraftTournament();
|
||||
} else {
|
||||
DraftController draftController = DraftManager.getInstance().getController(tableId);
|
||||
if (draftController != null) {
|
||||
DraftSession draftSession = draftController.getDraftSession(playerId);
|
||||
if (draftSession != null) {
|
||||
DraftManager.getInstance().kill(draftSession.getDraftId(), userId);
|
||||
}
|
||||
status = TourneyQuitStatus.DURING_ROUND;
|
||||
} else if (tournamentPlayer.getState().equals(TournamentPlayerState.DRAFTING)) {
|
||||
info = "during Draft phase";
|
||||
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
|
||||
this.abortDraftTournament();
|
||||
} else {
|
||||
DraftController draftController = DraftManager.getInstance().getController(tableId);
|
||||
if (draftController != null) {
|
||||
DraftSession draftSession = draftController.getDraftSession(playerId);
|
||||
if (draftSession != null) {
|
||||
DraftManager.getInstance().kill(draftSession.getDraftId(), userId);
|
||||
}
|
||||
}
|
||||
} else if (tournamentPlayer.getState().equals(TournamentPlayerState.CONSTRUCTING)) {
|
||||
info = "during Construction phase";
|
||||
} else {
|
||||
info = "";
|
||||
}
|
||||
status = TourneyQuitStatus.DURING_DRAFTING;
|
||||
} else if (tournamentPlayer.getState().equals(TournamentPlayerState.CONSTRUCTING)) {
|
||||
info = "during Construction phase";
|
||||
status = TourneyQuitStatus.DURING_CONSTRUCTION;
|
||||
} else {
|
||||
info = "";
|
||||
status = TourneyQuitStatus.NO_TOURNEY_QUIT;
|
||||
}
|
||||
tournamentPlayer.setQuit(info);
|
||||
tournamentPlayer.setQuit(info, status);
|
||||
tournament.quit(playerId);
|
||||
tournamentSession.quit();
|
||||
ChatManager.getInstance().broadcast(chatId, "", tournamentPlayer.getPlayer().getLogName() + " has quit the tournament", MessageColor.BLACK, true, MessageType.STATUS, SoundToPlay.PlayerQuitTournament);
|
||||
|
|
137
Mage/pom.xml
137
Mage/pom.xml
|
@ -36,6 +36,11 @@
|
|||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -51,11 +56,141 @@
|
|||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- http://vlkan.com/blog/post/2015/11/27/maven-protobuf/ -->
|
||||
<!-- copy protoc binary into build directory -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>${maven-dependency-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-protoc</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protoc</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<type>exe</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- compile proto buffer files using copied protoc binary -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>${maven-antrun-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>exec-protoc</id>
|
||||
<phase>generate-sources</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<property name="protoc.filename" value="protoc-${protobuf.version}-${os.detected.classifier}.exe"/>
|
||||
<property name="protoc.filepath" value="${project.build.directory}/${protoc.filename}"/>
|
||||
<chmod file="${protoc.filepath}" perm="ugo+rx"/>
|
||||
<mkdir dir="${protobuf.output.directory}" />
|
||||
<path id="protobuf.input.filepaths.path">
|
||||
<fileset dir="${protobuf.input.directory}">
|
||||
<include name="**/*.proto"/>
|
||||
</fileset>
|
||||
</path>
|
||||
<pathconvert pathsep=" " property="protobuf.input.filepaths" refid="protobuf.input.filepaths.path"/>
|
||||
<exec executable="${protoc.filepath}" failonerror="true">
|
||||
<arg value="-I"/>
|
||||
<arg value="${protobuf.input.directory}"/>
|
||||
<arg value="--java_out"/>
|
||||
<arg value="${protobuf.output.directory}"/>
|
||||
<arg line="${protobuf.input.filepaths}"/>
|
||||
</exec>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- add generated proto buffer classes into the package -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>${build-helper-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-classes</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${protobuf.output.directory}</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- shade protobuf to avoid version conflicts -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.google.protobuf</pattern>
|
||||
<shadedPattern>${project.groupId}.${project.artifactId}.shaded.protobuf</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<finalName>mage</finalName>
|
||||
|
||||
<!-- http://vlkan.com/blog/post/2015/11/27/maven-protobuf/ -->
|
||||
<extensions>
|
||||
<!-- provides os.detected.classifier (i.e. linux-x86_64, osx-x86_64) property -->
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>${os-maven-plugin.version}</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
</build>
|
||||
|
||||
<properties/>
|
||||
<!-- http://vlkan.com/blog/post/2015/11/27/maven-protobuf/ -->
|
||||
<properties>
|
||||
<!-- protobuf paths -->
|
||||
<protobuf.input.directory>${project.basedir}/src/main/proto</protobuf.input.directory>
|
||||
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
||||
|
||||
<!-- library versions -->
|
||||
<build-helper-maven-plugin.version>1.9.1</build-helper-maven-plugin.version>
|
||||
<maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
|
||||
<maven-dependency-plugin.version>2.10</maven-dependency-plugin.version>
|
||||
<maven-shade-plugin.version>2.4.2</maven-shade-plugin.version>
|
||||
<os-maven-plugin.version>1.4.1.Final</os-maven-plugin.version>
|
||||
<protobuf.version>3.0.0-beta-1</protobuf.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -38,6 +38,7 @@ import mage.game.events.Listener;
|
|||
import mage.game.events.TableEvent;
|
||||
import mage.game.events.TableEventSource;
|
||||
import mage.game.match.Match;
|
||||
import mage.game.result.ResultProtos.TableProto;
|
||||
import mage.game.tournament.Tournament;
|
||||
import mage.players.Player;
|
||||
|
||||
|
@ -61,24 +62,29 @@ public class Table implements Serializable {
|
|||
private TableState state;
|
||||
private Match match;
|
||||
private Tournament tournament;
|
||||
private TableRecorder recorder;
|
||||
|
||||
public interface TableRecorder {
|
||||
void record(Table table);
|
||||
};
|
||||
|
||||
protected TableEventSource tableEventSource = new TableEventSource();
|
||||
|
||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, Tournament tournament) {
|
||||
this(roomId, gameType, name, controllerName, validator, playerTypes);
|
||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, TableRecorder recorder, Tournament tournament) {
|
||||
this(roomId, gameType, name, controllerName, validator, playerTypes, recorder);
|
||||
this.tournament = tournament;
|
||||
this.isTournament = true;
|
||||
setState(TableState.WAITING);
|
||||
}
|
||||
|
||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, Match match) {
|
||||
this(roomId, gameType, name, controllerName, validator, playerTypes);
|
||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, TableRecorder recorder, Match match) {
|
||||
this(roomId, gameType, name, controllerName, validator, playerTypes, recorder);
|
||||
this.match = match;
|
||||
this.isTournament = false;
|
||||
setState(TableState.WAITING);
|
||||
}
|
||||
|
||||
protected Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes) {
|
||||
protected Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, TableRecorder recorder) {
|
||||
tableId = UUID.randomUUID();
|
||||
this.roomId = roomId;
|
||||
this.numSeats = playerTypes.size();
|
||||
|
@ -88,6 +94,7 @@ public class Table implements Serializable {
|
|||
this.createTime = new Date();
|
||||
createSeats(playerTypes);
|
||||
this.validator = validator;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
private void createSeats(List<String> playerTypes) {
|
||||
|
@ -235,6 +242,9 @@ public class Table implements Serializable {
|
|||
if (isTournament()) {
|
||||
getTournament().setTournamentState(state.toString());
|
||||
}
|
||||
if (state == TableState.FINISHED) {
|
||||
this.recorder.record(this);
|
||||
}
|
||||
}
|
||||
|
||||
public TableState getState() {
|
||||
|
@ -296,5 +306,21 @@ public class Table implements Serializable {
|
|||
return match.getEndTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public TableProto toProto() {
|
||||
TableProto.Builder builder = TableProto.newBuilder();
|
||||
if (this.isTournament()) {
|
||||
builder.getTourneyBuilder().mergeFrom(this.getTournament().toProto());
|
||||
} else {
|
||||
builder.getMatchBuilder().mergeFrom(this.getMatch().toProto());
|
||||
}
|
||||
return builder.setGameType(this.getGameType())
|
||||
.setName(this.getName())
|
||||
.setGameType(this.getGameType())
|
||||
.setDeckType(this.getDeckType())
|
||||
.setControllerName(this.getControllerName())
|
||||
.setStartTimeMs(this.getStartTime().getTime())
|
||||
.setEndTimeMs(this.getEndTime().getTime())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.game.GameInfo;
|
||||
import mage.game.result.ResultProtos.MatchProto;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -111,4 +112,6 @@ public interface Match {
|
|||
|
||||
void setTableId(UUID tableId);
|
||||
void setTournamentRound(int round);
|
||||
|
||||
MatchProto toProto();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import mage.game.events.Listener;
|
|||
import mage.game.events.TableEvent;
|
||||
import mage.game.events.TableEvent.EventType;
|
||||
import mage.game.events.TableEventSource;
|
||||
import mage.game.result.ResultProtos.MatchProto;
|
||||
import mage.game.result.ResultProtos.MatchQuitStatus;
|
||||
import mage.players.Player;
|
||||
import mage.util.DateFormat;
|
||||
import org.apache.log4j.Logger;
|
||||
|
@ -488,4 +490,25 @@ public abstract class MatchImpl implements Match {
|
|||
this.getGames().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchProto toProto() {
|
||||
MatchProto.Builder builder = MatchProto.newBuilder()
|
||||
.setName(this.getName())
|
||||
.setGameType(this.getOptions().getGameType())
|
||||
.setDeckType(this.getOptions().getDeckType())
|
||||
.setGames(this.getNumGames())
|
||||
.setDraws(this.getDraws());
|
||||
for (MatchPlayer matchPlayer : this.getPlayers()) {
|
||||
MatchQuitStatus status = !matchPlayer.hasQuit() ? MatchQuitStatus.NO_MATCH_QUIT :
|
||||
matchPlayer.getPlayer().hasTimerTimeout() ? MatchQuitStatus.TIMER_TIMEOUT :
|
||||
matchPlayer.getPlayer().hasIdleTimeout() ? MatchQuitStatus.IDLE_TIMEOUT :
|
||||
MatchQuitStatus.QUIT;
|
||||
builder.addPlayersBuilder()
|
||||
.setName(matchPlayer.getName())
|
||||
.setQuit(status)
|
||||
.setWins(matchPlayer.getWins());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import mage.game.draft.Draft;
|
|||
import mage.game.events.Listener;
|
||||
import mage.game.events.PlayerQueryEvent;
|
||||
import mage.game.events.TableEvent;
|
||||
import mage.game.result.ResultProtos.TourneyProto;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
|
@ -98,4 +99,6 @@ public interface Tournament {
|
|||
|
||||
void clearDraft();
|
||||
Draft getDraft();
|
||||
|
||||
TourneyProto toProto();
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ import mage.game.events.TableEvent.EventType;
|
|||
import mage.game.events.TableEventSource;
|
||||
import mage.game.match.Match;
|
||||
import mage.game.match.MatchPlayer;
|
||||
import mage.game.result.ResultProtos.MatchPlayerProto;
|
||||
import mage.game.result.ResultProtos.MatchProto;
|
||||
import mage.game.result.ResultProtos.MatchQuitStatus;
|
||||
import mage.game.result.ResultProtos.TourneyProto;
|
||||
import mage.game.result.ResultProtos.TourneyRoundProto;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
@ -555,4 +560,51 @@ public abstract class TournamentImpl implements Tournament {
|
|||
return draft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TourneyProto toProto() {
|
||||
TourneyProto.Builder tourneyBuilder = TourneyProto.newBuilder()
|
||||
.setBoosterInfo(this.getBoosterInfo());
|
||||
for (TournamentPlayer player : players.values()) {
|
||||
TournamentPlayer replacedPlayer = player.getReplacedTournamentPlayer();
|
||||
if (replacedPlayer != null) {
|
||||
player = replacedPlayer;
|
||||
}
|
||||
tourneyBuilder.addPlayersBuilder().mergeFrom(player.toProto());
|
||||
}
|
||||
for (Round round : rounds) {
|
||||
TourneyRoundProto.Builder roundBuilder = tourneyBuilder.addRoundsBuilder()
|
||||
.setRound(round.getRoundNumber());
|
||||
for (TournamentPairing pair : round.getPairs()) {
|
||||
Match match = pair.getMatch();
|
||||
if (match != null && match.hasEnded()) {
|
||||
MatchProto.Builder matchBuilder = roundBuilder.addMatchesBuilder()
|
||||
.setName(match.getName())
|
||||
.setGameType(match.getOptions().getGameType())
|
||||
.setDeckType(match.getOptions().getDeckType())
|
||||
.setGames(match.getNumGames())
|
||||
.setDraws(match.getDraws())
|
||||
.addPlayers(matchToProto(match, pair.getPlayer1()))
|
||||
.addPlayers(matchToProto(match, pair.getPlayer2()));
|
||||
}
|
||||
}
|
||||
for (TournamentPlayer tp : round.getPlayerByes()) {
|
||||
roundBuilder.addByes(tp.getPlayer().getName());
|
||||
}
|
||||
}
|
||||
return tourneyBuilder.build();
|
||||
}
|
||||
|
||||
private MatchPlayerProto matchToProto(Match match, TournamentPlayer player) {
|
||||
MatchPlayer matchPlayer = match.getPlayer(player.getPlayer().getId());
|
||||
MatchQuitStatus quit = !matchPlayer.hasQuit() ? MatchQuitStatus.NO_MATCH_QUIT :
|
||||
matchPlayer.getPlayer().hasIdleTimeout() ? MatchQuitStatus.IDLE_TIMEOUT :
|
||||
matchPlayer.getPlayer().hasTimerTimeout() ? MatchQuitStatus.TIMER_TIMEOUT :
|
||||
MatchQuitStatus.QUIT;
|
||||
return MatchPlayerProto.newBuilder()
|
||||
.setName(player.getPlayer().getName())
|
||||
.setWins(matchPlayer.getWins())
|
||||
.setQuit(quit)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ package mage.game.tournament;
|
|||
import java.util.Set;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.constants.TournamentPlayerState;
|
||||
import mage.game.result.ResultProtos.TourneyPlayerProto;
|
||||
import mage.game.result.ResultProtos.TourneyQuitStatus;
|
||||
import mage.players.Player;
|
||||
import mage.util.TournamentUtil;
|
||||
|
||||
|
@ -52,6 +54,8 @@ public class TournamentPlayer {
|
|||
protected boolean quit = false;
|
||||
protected boolean doneConstructing;
|
||||
protected boolean joined = false;
|
||||
protected TourneyQuitStatus quitStatus = TourneyQuitStatus.NO_TOURNEY_QUIT;
|
||||
protected TournamentPlayer replacedTournamentPlayer;
|
||||
|
||||
public TournamentPlayer(Player player, String playerType) {
|
||||
this.player = player;
|
||||
|
@ -60,7 +64,6 @@ public class TournamentPlayer {
|
|||
this.stateInfo = "";
|
||||
this.disconnectInfo = "";
|
||||
this.results = "";
|
||||
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
|
@ -185,12 +188,13 @@ public class TournamentPlayer {
|
|||
return quit;
|
||||
}
|
||||
|
||||
public void setQuit(String info) {
|
||||
public void setQuit(String info, TourneyQuitStatus status) {
|
||||
setEliminated();
|
||||
this.setState(TournamentPlayerState.CANCELED);
|
||||
this.setStateInfo(info);
|
||||
this.quit = true;
|
||||
this.doneConstructing = true;
|
||||
this.quitStatus = status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -216,5 +220,23 @@ public class TournamentPlayer {
|
|||
&& !this.getState().equals(TournamentPlayerState.ELIMINATED)
|
||||
&& !this.getState().equals(TournamentPlayerState.FINISHED);
|
||||
}
|
||||
|
||||
public TournamentPlayer getReplacedTournamentPlayer() {
|
||||
return this.replacedTournamentPlayer;
|
||||
}
|
||||
|
||||
public void setReplacedTournamentPlayer(TournamentPlayer player) {
|
||||
this.replacedTournamentPlayer = player;
|
||||
}
|
||||
|
||||
public TourneyPlayerProto toProto() {
|
||||
return TourneyPlayerProto.newBuilder()
|
||||
.setName(this.player.getName())
|
||||
.setPlayerType(this.playerType)
|
||||
.setStateInfo(this.stateInfo)
|
||||
.setDisconnectInfo(this.disconnectInfo)
|
||||
.setQuit(this.quitStatus)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
79
Mage/src/main/proto/result.proto
Normal file
79
Mage/src/main/proto/result.proto
Normal file
|
@ -0,0 +1,79 @@
|
|||
package mage.game.result;
|
||||
|
||||
option java_outer_classname = "ResultProtos";
|
||||
|
||||
message TableProto {
|
||||
optional MatchProto match = 1;
|
||||
optional TourneyProto tourney = 2;
|
||||
optional string game_type = 3;
|
||||
optional string deck_type = 4;
|
||||
optional string name = 5;
|
||||
optional string controller_name = 6;
|
||||
optional int64 start_time_ms = 7;
|
||||
optional int64 end_time_ms = 8;
|
||||
}
|
||||
|
||||
message MatchProto {
|
||||
optional string name = 1;
|
||||
optional string game_type = 2;
|
||||
optional string deck_type = 3;
|
||||
optional int32 games = 4;
|
||||
optional int32 draws = 5;
|
||||
repeated MatchPlayerProto players = 6;
|
||||
}
|
||||
|
||||
message MatchPlayerProto {
|
||||
optional string name = 1;
|
||||
optional int32 wins = 2;
|
||||
optional MatchQuitStatus quit = 3;
|
||||
optional bool bye = 4;
|
||||
}
|
||||
|
||||
enum MatchQuitStatus {
|
||||
NO_MATCH_QUIT = 0;
|
||||
IDLE_TIMEOUT = 1; // I
|
||||
TIMER_TIMEOUT = 2; // T
|
||||
QUIT = 3; // Q
|
||||
}
|
||||
|
||||
message TourneyProto {
|
||||
optional string booster_info = 1;
|
||||
repeated TourneyPlayerProto players = 2;
|
||||
repeated TourneyRoundProto rounds = 3;
|
||||
}
|
||||
|
||||
message TourneyPlayerProto {
|
||||
optional string name = 1;
|
||||
optional string player_type = 2;
|
||||
optional string replaced_player_name = 3;
|
||||
optional string state_info = 4;
|
||||
optional string disconnect_info = 5;
|
||||
optional TourneyQuitStatus quit = 6;
|
||||
}
|
||||
|
||||
enum TourneyQuitStatus {
|
||||
NO_TOURNEY_QUIT = 0;
|
||||
DURING_ROUND = 1;
|
||||
DURING_DRAFTING = 2;
|
||||
DURING_CONSTRUCTION = 3;
|
||||
}
|
||||
|
||||
message TourneyRoundProto {
|
||||
optional int32 round = 1;
|
||||
repeated MatchProto matches = 2;
|
||||
repeated string byes = 3;
|
||||
}
|
||||
|
||||
message UserStatsProto {
|
||||
optional string name = 1;
|
||||
|
||||
optional int32 tourneys = 2;
|
||||
optional int32 tourneys_quit_during_round = 3;
|
||||
optional int32 tourneys_quit_during_drafting = 4;
|
||||
optional int32 tourneys_quit_during_construction = 5;
|
||||
|
||||
optional int32 matches = 6;
|
||||
optional int32 matches_idle_timeout = 7;
|
||||
optional int32 matches_timer_timeout = 8;
|
||||
optional int32 matches_quit = 9;
|
||||
}
|
Loading…
Reference in a new issue