forked from External/mage
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/
|
/target/
|
||||||
|
|
||||||
client_secrets.json
|
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 List<String> players = new ArrayList<>();
|
||||||
private final UserTableModel userTableModel;
|
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 {
|
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];
|
private UsersView[] players = new UsersView[0];
|
||||||
|
|
||||||
public void loadData(Collection<RoomUsersView> roomUserInfoList) throws MageRemoteException {
|
public void loadData(Collection<RoomUsersView> roomUserInfoList) throws MageRemoteException {
|
||||||
|
|
@ -154,10 +154,12 @@ public class PlayersChatPanel extends javax.swing.JPanel {
|
||||||
case 1:
|
case 1:
|
||||||
return players[arg0].getUserName();
|
return players[arg0].getUserName();
|
||||||
case 2:
|
case 2:
|
||||||
return players[arg0].getInfoState();
|
return players[arg0].getHistory();
|
||||||
case 3:
|
case 3:
|
||||||
return players[arg0].getInfoGames();
|
return players[arg0].getInfoState();
|
||||||
case 4:
|
case 4:
|
||||||
|
return players[arg0].getInfoGames();
|
||||||
|
case 5:
|
||||||
return players[arg0].getInfoPing();
|
return players[arg0].getInfoPing();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,14 @@ public class UsersView implements Serializable {
|
||||||
|
|
||||||
private final String flagName;
|
private final String flagName;
|
||||||
private final String userName;
|
private final String userName;
|
||||||
|
private final String history;
|
||||||
private final String infoState;
|
private final String infoState;
|
||||||
private final String infoGames;
|
private final String infoGames;
|
||||||
private final String infoPing;
|
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.flagName = flagName;
|
||||||
|
this.history = history;
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.infoState = infoState;
|
this.infoState = infoState;
|
||||||
this.infoGames = infoGames;
|
this.infoGames = infoGames;
|
||||||
|
|
@ -59,6 +61,10 @@ public class UsersView implements Serializable {
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHistory() {
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
public String getInfoState() {
|
public String getInfoState() {
|
||||||
return infoState;
|
return infoState;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,11 @@
|
||||||
<artifactId>jersey-multipart</artifactId>
|
<artifactId>jersey-multipart</artifactId>
|
||||||
<version>1.19</version>
|
<version>1.19</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xerial</groupId>
|
||||||
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
|
<version>3.7.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -438,7 +438,7 @@ public class MageServerImpl implements MageServer {
|
||||||
// }
|
// }
|
||||||
@Override
|
@Override
|
||||||
public boolean startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
execute("startMatch", sessionId, new Action() {
|
execute("startMatch", sessionId, new Action() {
|
||||||
|
|
@ -463,7 +463,7 @@ public class MageServerImpl implements MageServer {
|
||||||
// }
|
// }
|
||||||
@Override
|
@Override
|
||||||
public boolean startTournament(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
execute("startTournament", sessionId, new Action() {
|
execute("startTournament", sessionId, new Action() {
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,20 @@ import java.io.FilenameFilter;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.management.MBeanServer;
|
import javax.management.MBeanServer;
|
||||||
import mage.cards.repository.CardScanner;
|
import mage.cards.repository.CardScanner;
|
||||||
import mage.game.match.MatchType;
|
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.game.tournament.TournamentType;
|
||||||
import mage.interfaces.MageServer;
|
import mage.interfaces.MageServer;
|
||||||
import mage.remote.Connection;
|
import mage.remote.Connection;
|
||||||
|
|
@ -43,6 +53,10 @@ import mage.server.draft.CubeFactory;
|
||||||
import mage.server.game.DeckValidatorFactory;
|
import mage.server.game.DeckValidatorFactory;
|
||||||
import mage.server.game.GameFactory;
|
import mage.server.game.GameFactory;
|
||||||
import mage.server.game.PlayerFactory;
|
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.tournament.TournamentFactory;
|
||||||
import mage.server.util.ConfigSettings;
|
import mage.server.util.ConfigSettings;
|
||||||
import mage.server.util.PluginClassLoader;
|
import mage.server.util.PluginClassLoader;
|
||||||
|
|
@ -88,6 +102,8 @@ public class Main {
|
||||||
protected static boolean testMode;
|
protected static boolean testMode;
|
||||||
protected static boolean fastDbMode;
|
protected static boolean fastDbMode;
|
||||||
|
|
||||||
|
private static final ScheduledExecutorService updateUserStatsTaskExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param args the command line arguments
|
* @param args the command line arguments
|
||||||
*/
|
*/
|
||||||
|
|
@ -174,6 +190,13 @@ public class Main {
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.fatal("Failed to start server - " + connection.toString(), 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() {
|
static void initStatistics() {
|
||||||
|
|
@ -368,4 +391,68 @@ public class Main {
|
||||||
public static boolean isTestMode() {
|
public static boolean isTestMode() {
|
||||||
return testMode;
|
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.GameFactory;
|
||||||
import mage.server.game.GameManager;
|
import mage.server.game.GameManager;
|
||||||
import mage.server.game.PlayerFactory;
|
import mage.server.game.PlayerFactory;
|
||||||
|
import mage.server.record.TableRecorderImpl;
|
||||||
import mage.server.services.LogKeys;
|
import mage.server.services.LogKeys;
|
||||||
import mage.server.services.impl.LogServiceImpl;
|
import mage.server.services.impl.LogServiceImpl;
|
||||||
import mage.server.tournament.TournamentController;
|
import mage.server.tournament.TournamentController;
|
||||||
|
|
@ -105,7 +106,7 @@ public class TableController {
|
||||||
} else {
|
} else {
|
||||||
controllerName = "System";
|
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());
|
chatId = ChatManager.getInstance().createChatSession("Match Table " + table.getId());
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +125,7 @@ public class TableController {
|
||||||
} else {
|
} else {
|
||||||
controllerName = "System";
|
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());
|
chatId = ChatManager.getInstance().createChatSession("Tourn. table " + table.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,6 +238,7 @@ public class TableController {
|
||||||
|
|
||||||
TournamentPlayer newTournamentPlayer = tournament.getPlayer(newPlayer.getId());
|
TournamentPlayer newTournamentPlayer = tournament.getPlayer(newPlayer.getId());
|
||||||
newTournamentPlayer.setState(oldTournamentPlayer.getState());
|
newTournamentPlayer.setState(oldTournamentPlayer.getState());
|
||||||
|
newTournamentPlayer.setReplacedTournamentPlayer(oldTournamentPlayer);
|
||||||
|
|
||||||
DraftManager.getInstance().getController(table.getId()).replacePlayer(oldPlayer, newPlayer);
|
DraftManager.getInstance().getController(table.getId()).replacePlayer(oldPlayer, newPlayer);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -957,26 +959,16 @@ public class TableController {
|
||||||
return getTable().getState();
|
return getTable().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean changeTableState(TableState newTableState) {
|
public synchronized boolean changeTableStateToStarting() {
|
||||||
switch (newTableState) {
|
if (!getTable().getState().equals(TableState.READY_TO_START)) {
|
||||||
case WAITING:
|
// tournament is not ready, can't start
|
||||||
if (getTable().getState().equals(TableState.STARTING)) {
|
return false;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,14 @@ import mage.constants.TableState;
|
||||||
import mage.game.GameException;
|
import mage.game.GameException;
|
||||||
import mage.game.Table;
|
import mage.game.Table;
|
||||||
import mage.game.match.MatchOptions;
|
import mage.game.match.MatchOptions;
|
||||||
|
import mage.game.result.ResultProtos.UserStatsProto;
|
||||||
import mage.game.tournament.TournamentOptions;
|
import mage.game.tournament.TournamentOptions;
|
||||||
import mage.server.RoomImpl;
|
import mage.server.RoomImpl;
|
||||||
import mage.server.TableManager;
|
import mage.server.TableManager;
|
||||||
import mage.server.User;
|
import mage.server.User;
|
||||||
import mage.server.UserManager;
|
import mage.server.UserManager;
|
||||||
|
import mage.server.record.UserStats;
|
||||||
|
import mage.server.record.UserStatsRepository;
|
||||||
import mage.server.tournament.TournamentManager;
|
import mage.server.tournament.TournamentManager;
|
||||||
import mage.server.util.ConfigSettings;
|
import mage.server.util.ConfigSettings;
|
||||||
import mage.server.util.ThreadExecutor;
|
import mage.server.util.ThreadExecutor;
|
||||||
|
|
@ -91,6 +94,74 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
||||||
return tableView;
|
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() {
|
private void update() {
|
||||||
ArrayList<TableView> tableList = new ArrayList<>();
|
ArrayList<TableView> tableList = new ArrayList<>();
|
||||||
ArrayList<MatchView> matchList = new ArrayList<>();
|
ArrayList<MatchView> matchList = new ArrayList<>();
|
||||||
|
|
@ -100,11 +171,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
||||||
if (table.getState() != TableState.FINISHED) {
|
if (table.getState() != TableState.FINISHED) {
|
||||||
tableList.add(new TableView(table));
|
tableList.add(new TableView(table));
|
||||||
} else if (matchList.size() < 50) {
|
} else if (matchList.size() < 50) {
|
||||||
if (table.isTournament()) {
|
matchList.add(new MatchView(table));
|
||||||
matchList.add(new MatchView(table));
|
|
||||||
} else {
|
|
||||||
matchList.add(new MatchView(table));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// more since 50 matches finished since this match so remove it
|
// more since 50 matches finished since this match so remove it
|
||||||
if (table.isTournament()) {
|
if (table.isTournament()) {
|
||||||
|
|
@ -117,13 +184,19 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
||||||
matchView = matchList;
|
matchView = matchList;
|
||||||
List<UsersView> users = new ArrayList<>();
|
List<UsersView> users = new ArrayList<>();
|
||||||
for (User user : UserManager.getInstance().getUsers()) {
|
for (User user : UserManager.getInstance().getUsers()) {
|
||||||
|
String history = null;
|
||||||
|
UserStats stats = UserStatsRepository.instance.getUser(user.getName());
|
||||||
|
if (stats != null) {
|
||||||
|
history = userStatsToString(stats.getProto());
|
||||||
|
}
|
||||||
try {
|
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) {
|
} catch (Exception ex) {
|
||||||
logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
|
logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
|
||||||
users.add(new UsersView(
|
users.add(new UsersView(
|
||||||
(user.getUserData() != null && user.getUserData().getFlagName() != null) ? user.getUserData().getFlagName() : "world",
|
(user.getUserData() != null && user.getUserData().getFlagName() != null) ? user.getUserData().getFlagName() : "world",
|
||||||
user.getName() != null ? user.getName() : "<no name>",
|
user.getName() != null ? user.getName() : "<no name>",
|
||||||
|
history != null ? history : "<no history>",
|
||||||
user.getInfo() != null ? user.getInfo() : "<no info>",
|
user.getInfo() != null ? user.getInfo() : "<no info>",
|
||||||
"[exception]",
|
"[exception]",
|
||||||
user.getPingInfo() != null ? user.getPingInfo() : "<no ping>"));
|
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 static mage.game.events.TableEvent.EventType.CONSTRUCT;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
import mage.game.match.MatchOptions;
|
import mage.game.match.MatchOptions;
|
||||||
|
import mage.game.result.ResultProtos.TourneyQuitStatus;
|
||||||
import mage.game.tournament.Tournament;
|
import mage.game.tournament.Tournament;
|
||||||
import mage.game.tournament.TournamentPairing;
|
import mage.game.tournament.TournamentPairing;
|
||||||
import mage.game.tournament.TournamentPlayer;
|
import mage.game.tournament.TournamentPlayer;
|
||||||
|
|
@ -351,31 +352,34 @@ public class TournamentController {
|
||||||
tournamentSession.setKilled();
|
tournamentSession.setKilled();
|
||||||
if (tournamentPlayer.isInTournament()) {
|
if (tournamentPlayer.isInTournament()) {
|
||||||
String info;
|
String info;
|
||||||
|
TourneyQuitStatus status;
|
||||||
if (tournament.isDoneConstructing()) {
|
if (tournament.isDoneConstructing()) {
|
||||||
info = new StringBuilder("during round ").append(tournament.getRounds().size()).toString();
|
info = new StringBuilder("during round ").append(tournament.getRounds().size()).toString();
|
||||||
// quit active matches of that tournament
|
// quit active matches of that tournament
|
||||||
TableManager.getInstance().userQuitTournamentSubTables(tournament.getId(), userId);
|
TableManager.getInstance().userQuitTournamentSubTables(tournament.getId(), userId);
|
||||||
} else {
|
status = TourneyQuitStatus.DURING_ROUND;
|
||||||
if (tournamentPlayer.getState().equals(TournamentPlayerState.DRAFTING)) {
|
} else if (tournamentPlayer.getState().equals(TournamentPlayerState.DRAFTING)) {
|
||||||
info = "during Draft phase";
|
info = "during Draft phase";
|
||||||
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
|
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
|
||||||
this.abortDraftTournament();
|
this.abortDraftTournament();
|
||||||
} else {
|
} else {
|
||||||
DraftController draftController = DraftManager.getInstance().getController(tableId);
|
DraftController draftController = DraftManager.getInstance().getController(tableId);
|
||||||
if (draftController != null) {
|
if (draftController != null) {
|
||||||
DraftSession draftSession = draftController.getDraftSession(playerId);
|
DraftSession draftSession = draftController.getDraftSession(playerId);
|
||||||
if (draftSession != null) {
|
if (draftSession != null) {
|
||||||
DraftManager.getInstance().kill(draftSession.getDraftId(), userId);
|
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);
|
tournament.quit(playerId);
|
||||||
tournamentSession.quit();
|
tournamentSession.quit();
|
||||||
ChatManager.getInstance().broadcast(chatId, "", tournamentPlayer.getPlayer().getLogName() + " has quit the tournament", MessageColor.BLACK, true, MessageType.STATUS, SoundToPlay.PlayerQuitTournament);
|
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>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.12</version>
|
<version>4.12</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.protobuf</groupId>
|
||||||
|
<artifactId>protobuf-java</artifactId>
|
||||||
|
<version>${protobuf.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
@ -51,11 +56,141 @@
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</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>
|
</plugins>
|
||||||
|
|
||||||
<finalName>mage</finalName>
|
<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>
|
</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>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import mage.game.events.Listener;
|
||||||
import mage.game.events.TableEvent;
|
import mage.game.events.TableEvent;
|
||||||
import mage.game.events.TableEventSource;
|
import mage.game.events.TableEventSource;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
|
import mage.game.result.ResultProtos.TableProto;
|
||||||
import mage.game.tournament.Tournament;
|
import mage.game.tournament.Tournament;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
|
@ -61,24 +62,29 @@ public class Table implements Serializable {
|
||||||
private TableState state;
|
private TableState state;
|
||||||
private Match match;
|
private Match match;
|
||||||
private Tournament tournament;
|
private Tournament tournament;
|
||||||
|
private TableRecorder recorder;
|
||||||
|
|
||||||
|
public interface TableRecorder {
|
||||||
|
void record(Table table);
|
||||||
|
};
|
||||||
|
|
||||||
protected TableEventSource tableEventSource = new TableEventSource();
|
protected TableEventSource tableEventSource = new TableEventSource();
|
||||||
|
|
||||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, Tournament tournament) {
|
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);
|
this(roomId, gameType, name, controllerName, validator, playerTypes, recorder);
|
||||||
this.tournament = tournament;
|
this.tournament = tournament;
|
||||||
this.isTournament = true;
|
this.isTournament = true;
|
||||||
setState(TableState.WAITING);
|
setState(TableState.WAITING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Table(UUID roomId, String gameType, String name, String controllerName, DeckValidator validator, List<String> playerTypes, Match match) {
|
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);
|
this(roomId, gameType, name, controllerName, validator, playerTypes, recorder);
|
||||||
this.match = match;
|
this.match = match;
|
||||||
this.isTournament = false;
|
this.isTournament = false;
|
||||||
setState(TableState.WAITING);
|
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();
|
tableId = UUID.randomUUID();
|
||||||
this.roomId = roomId;
|
this.roomId = roomId;
|
||||||
this.numSeats = playerTypes.size();
|
this.numSeats = playerTypes.size();
|
||||||
|
|
@ -88,6 +94,7 @@ public class Table implements Serializable {
|
||||||
this.createTime = new Date();
|
this.createTime = new Date();
|
||||||
createSeats(playerTypes);
|
createSeats(playerTypes);
|
||||||
this.validator = validator;
|
this.validator = validator;
|
||||||
|
this.recorder = recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSeats(List<String> playerTypes) {
|
private void createSeats(List<String> playerTypes) {
|
||||||
|
|
@ -235,6 +242,9 @@ public class Table implements Serializable {
|
||||||
if (isTournament()) {
|
if (isTournament()) {
|
||||||
getTournament().setTournamentState(state.toString());
|
getTournament().setTournamentState(state.toString());
|
||||||
}
|
}
|
||||||
|
if (state == TableState.FINISHED) {
|
||||||
|
this.recorder.record(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableState getState() {
|
public TableState getState() {
|
||||||
|
|
@ -296,5 +306,21 @@ public class Table implements Serializable {
|
||||||
return match.getEndTime();
|
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.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.game.GameInfo;
|
import mage.game.GameInfo;
|
||||||
|
import mage.game.result.ResultProtos.MatchProto;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -111,4 +112,6 @@ public interface Match {
|
||||||
|
|
||||||
void setTableId(UUID tableId);
|
void setTableId(UUID tableId);
|
||||||
void setTournamentRound(int round);
|
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;
|
||||||
import mage.game.events.TableEvent.EventType;
|
import mage.game.events.TableEvent.EventType;
|
||||||
import mage.game.events.TableEventSource;
|
import mage.game.events.TableEventSource;
|
||||||
|
import mage.game.result.ResultProtos.MatchProto;
|
||||||
|
import mage.game.result.ResultProtos.MatchQuitStatus;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.DateFormat;
|
import mage.util.DateFormat;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
@ -488,4 +490,25 @@ public abstract class MatchImpl implements Match {
|
||||||
this.getGames().clear();
|
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.Listener;
|
||||||
import mage.game.events.PlayerQueryEvent;
|
import mage.game.events.PlayerQueryEvent;
|
||||||
import mage.game.events.TableEvent;
|
import mage.game.events.TableEvent;
|
||||||
|
import mage.game.result.ResultProtos.TourneyProto;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -98,4 +99,6 @@ public interface Tournament {
|
||||||
|
|
||||||
void clearDraft();
|
void clearDraft();
|
||||||
Draft getDraft();
|
Draft getDraft();
|
||||||
|
|
||||||
|
TourneyProto toProto();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,11 @@ import mage.game.events.TableEvent.EventType;
|
||||||
import mage.game.events.TableEventSource;
|
import mage.game.events.TableEventSource;
|
||||||
import mage.game.match.Match;
|
import mage.game.match.Match;
|
||||||
import mage.game.match.MatchPlayer;
|
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 mage.players.Player;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
|
@ -555,4 +560,51 @@ public abstract class TournamentImpl implements Tournament {
|
||||||
return draft;
|
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 java.util.Set;
|
||||||
import mage.cards.decks.Deck;
|
import mage.cards.decks.Deck;
|
||||||
import mage.constants.TournamentPlayerState;
|
import mage.constants.TournamentPlayerState;
|
||||||
|
import mage.game.result.ResultProtos.TourneyPlayerProto;
|
||||||
|
import mage.game.result.ResultProtos.TourneyQuitStatus;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.TournamentUtil;
|
import mage.util.TournamentUtil;
|
||||||
|
|
||||||
|
|
@ -52,6 +54,8 @@ public class TournamentPlayer {
|
||||||
protected boolean quit = false;
|
protected boolean quit = false;
|
||||||
protected boolean doneConstructing;
|
protected boolean doneConstructing;
|
||||||
protected boolean joined = false;
|
protected boolean joined = false;
|
||||||
|
protected TourneyQuitStatus quitStatus = TourneyQuitStatus.NO_TOURNEY_QUIT;
|
||||||
|
protected TournamentPlayer replacedTournamentPlayer;
|
||||||
|
|
||||||
public TournamentPlayer(Player player, String playerType) {
|
public TournamentPlayer(Player player, String playerType) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
|
@ -60,7 +64,6 @@ public class TournamentPlayer {
|
||||||
this.stateInfo = "";
|
this.stateInfo = "";
|
||||||
this.disconnectInfo = "";
|
this.disconnectInfo = "";
|
||||||
this.results = "";
|
this.results = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
|
|
@ -185,12 +188,13 @@ public class TournamentPlayer {
|
||||||
return quit;
|
return quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setQuit(String info) {
|
public void setQuit(String info, TourneyQuitStatus status) {
|
||||||
setEliminated();
|
setEliminated();
|
||||||
this.setState(TournamentPlayerState.CANCELED);
|
this.setState(TournamentPlayerState.CANCELED);
|
||||||
this.setStateInfo(info);
|
this.setStateInfo(info);
|
||||||
this.quit = true;
|
this.quit = true;
|
||||||
this.doneConstructing = true;
|
this.doneConstructing = true;
|
||||||
|
this.quitStatus = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -216,5 +220,23 @@ public class TournamentPlayer {
|
||||||
&& !this.getState().equals(TournamentPlayerState.ELIMINATED)
|
&& !this.getState().equals(TournamentPlayerState.ELIMINATED)
|
||||||
&& !this.getState().equals(TournamentPlayerState.FINISHED);
|
&& !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…
Add table
Add a link
Reference in a new issue