Introduce CellLocation enum in model and viewmodel layer
This commit is contained in:
parent
7736fb5101
commit
2294c95517
@ -1,16 +1,18 @@
|
|||||||
package de.icaotix.ultimatetictactoe.model;
|
package de.icaotix.ultimatetictactoe.model;
|
||||||
|
|
||||||
|
import de.icaotix.ultimatetictactoe.model.definitions.CellLocation;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface ITicTacToeGame {
|
public interface ITicTacToeGame {
|
||||||
void setCellState(int cell, CellState state);
|
void setCellState(CellLocation cell, CellState state);
|
||||||
|
|
||||||
CellState[] getCells();
|
Map<CellLocation, CellState> getCells();
|
||||||
|
|
||||||
List<Integer> getAvailableFields();
|
List<CellLocation> getAvailableFields();
|
||||||
|
|
||||||
boolean isFinished();
|
boolean isFinished();
|
||||||
|
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
package de.icaotix.ultimatetictactoe.model;
|
package de.icaotix.ultimatetictactoe.model;
|
||||||
|
|
||||||
|
import de.icaotix.ultimatetictactoe.model.definitions.CellLocation;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.Player;
|
import de.icaotix.ultimatetictactoe.model.definitions.Player;
|
||||||
|
|
||||||
public interface IUltimateTicTacToe {
|
public interface IUltimateTicTacToe {
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
ITicTacToeGame getSubGame(int id);
|
ITicTacToeGame getSubGame(CellLocation location);
|
||||||
|
|
||||||
int getActiveField();
|
CellLocation getActiveField();
|
||||||
|
|
||||||
Player getCurrentPlayer();
|
Player getCurrentPlayer();
|
||||||
|
|
||||||
GameState getGlobalGameState();
|
GameState getGlobalGameState();
|
||||||
|
|
||||||
void doPlayerMove(int cell);
|
void doPlayerMove(CellLocation cell);
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,41 @@
|
|||||||
package de.icaotix.ultimatetictactoe.model;
|
package de.icaotix.ultimatetictactoe.model;
|
||||||
|
|
||||||
|
import de.icaotix.ultimatetictactoe.model.definitions.CellLocation;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class TicTacToeGame implements ITicTacToeGame {
|
public class TicTacToeGame implements ITicTacToeGame {
|
||||||
|
|
||||||
public static final Integer[][] winningCombinations = new Integer[][]{
|
public static final CellLocation[][] winningCombinations = new CellLocation[][]{
|
||||||
{0, 1, 2},
|
{CellLocation.TL, CellLocation.TC, CellLocation.TR},
|
||||||
{3, 4, 5},
|
{CellLocation.ML, CellLocation.MC, CellLocation.MR},
|
||||||
{6, 7, 8},
|
{CellLocation.BL, CellLocation.BC, CellLocation.BR},
|
||||||
{0, 3, 6},
|
{CellLocation.TL, CellLocation.ML, CellLocation.BL},
|
||||||
{1, 4, 7},
|
{CellLocation.TC, CellLocation.MC, CellLocation.BC},
|
||||||
{2, 5, 8},
|
{CellLocation.TR, CellLocation.MR, CellLocation.BR},
|
||||||
{0, 4, 8},
|
{CellLocation.TL, CellLocation.MC, CellLocation.BR},
|
||||||
{2, 4, 6}
|
{CellLocation.TR, CellLocation.MC, CellLocation.BL}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final CellState[] cells;
|
private final Map<CellLocation, CellState> cells;
|
||||||
private GameState state;
|
private GameState state;
|
||||||
|
|
||||||
public TicTacToeGame() {
|
public TicTacToeGame() {
|
||||||
this.cells = new CellState[9];
|
this.cells = new HashMap<>();
|
||||||
Arrays.fill(cells, CellState.EMPTY);
|
for (CellLocation value : CellLocation.values) {
|
||||||
|
this.cells.put(value, CellState.EMPTY);
|
||||||
|
}
|
||||||
this.state = GameState.RUNNING;
|
this.state = GameState.RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
public static void printField(ITicTacToeGame game) {
|
public static void printField(ITicTacToeGame game) {
|
||||||
final var cells = game.getCells();
|
|
||||||
System.out.println("--------------CURRENT STATE--------------");
|
System.out.println("--------------CURRENT STATE--------------");
|
||||||
for (int i = 0; i < cells.length; i++) {
|
for (Map.Entry<CellLocation, CellState> entry : game.getCells().entrySet()) {
|
||||||
System.out.print(cells[i] + " ");
|
System.out.print(entry.getValue() + " ");
|
||||||
if ((i + 1) % 3 == 0) {
|
if ((entry.getKey().ordinal() + 1) % 3 == 0) {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,25 +45,25 @@ public class TicTacToeGame implements ITicTacToeGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCellState(int cell, CellState state) {
|
public void setCellState(CellLocation cell, CellState state) {
|
||||||
if (this.state != GameState.RUNNING) return;
|
if (this.state != GameState.RUNNING) return;
|
||||||
if (this.cells[cell] == CellState.EMPTY) {
|
if (this.cells.get(cell) == CellState.EMPTY) {
|
||||||
this.cells[cell] = state;
|
this.cells.put(cell, state);
|
||||||
updateGameState();
|
updateGameState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CellState[] getCells() {
|
public Map<CellLocation, CellState> getCells() {
|
||||||
return cells;
|
return cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Integer> getAvailableFields() {
|
public List<CellLocation> getAvailableFields() {
|
||||||
final LinkedList<Integer> emptyFields = new LinkedList<>();
|
final LinkedList<CellLocation> emptyFields = new LinkedList<>();
|
||||||
for (int i = 0; i < this.cells.length; i++) {
|
for (Map.Entry<CellLocation, CellState> cellStateEntry : this.cells.entrySet()) {
|
||||||
if (this.cells[i] == CellState.EMPTY) {
|
if (cellStateEntry.getValue() == CellState.EMPTY) {
|
||||||
emptyFields.add(i);
|
emptyFields.add(cellStateEntry.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyFields;
|
return emptyFields;
|
||||||
@ -81,13 +80,13 @@ public class TicTacToeGame implements ITicTacToeGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateGameState() {
|
private void updateGameState() {
|
||||||
for (Integer[] winningCombination : winningCombinations) {
|
for (CellLocation[] winningCombination : winningCombinations) {
|
||||||
final var interestingStates = new HashSet<CellState>();
|
final var interestingStates = new HashSet<CellState>();
|
||||||
interestingStates.add(this.cells[winningCombination[0]]);
|
interestingStates.add(this.cells.get(winningCombination[0]));
|
||||||
interestingStates.add(this.cells[winningCombination[1]]);
|
interestingStates.add(this.cells.get(winningCombination[1]));
|
||||||
interestingStates.add(this.cells[winningCombination[2]]);
|
interestingStates.add(this.cells.get(winningCombination[2]));
|
||||||
if (interestingStates.size() == 1 && !interestingStates.contains(CellState.EMPTY)) {
|
if (interestingStates.size() == 1 && !interestingStates.contains(CellState.EMPTY)) {
|
||||||
switch (this.cells[winningCombination[0]]) {
|
switch (this.cells.get(winningCombination[0])) {
|
||||||
case O -> this.state = GameState.O_WON;
|
case O -> this.state = GameState.O_WON;
|
||||||
case X -> this.state = GameState.X_WON;
|
case X -> this.state = GameState.X_WON;
|
||||||
}
|
}
|
||||||
@ -95,7 +94,7 @@ public class TicTacToeGame implements ITicTacToeGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Arrays.stream(this.cells).allMatch(cellState -> cellState != CellState.EMPTY)) {
|
if (!this.cells.containsValue(CellState.EMPTY)) {
|
||||||
this.state = GameState.DRAW;
|
this.state = GameState.DRAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
package de.icaotix.ultimatetictactoe.model;
|
package de.icaotix.ultimatetictactoe.model;
|
||||||
|
|
||||||
|
import de.icaotix.ultimatetictactoe.model.definitions.CellLocation;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.Player;
|
import de.icaotix.ultimatetictactoe.model.definitions.Player;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class UltimateTicTacToe implements IUltimateTicTacToe {
|
public class UltimateTicTacToe implements IUltimateTicTacToe {
|
||||||
|
|
||||||
private ITicTacToeGame[] subGames;
|
private Map<CellLocation, ITicTacToeGame> subGames;
|
||||||
private GameState[] masterGameStates;
|
private Map<CellLocation, GameState> masterGameStates;
|
||||||
private Player currentPlayer;
|
private Player currentPlayer;
|
||||||
private int activeField;
|
private CellLocation activeField;
|
||||||
private GameState globalGameState;
|
private GameState globalGameState;
|
||||||
|
|
||||||
public UltimateTicTacToe() {
|
public UltimateTicTacToe() {
|
||||||
@ -21,26 +23,26 @@ public class UltimateTicTacToe implements IUltimateTicTacToe {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
this.subGames = new ITicTacToeGame[9];
|
this.subGames = new HashMap<>();
|
||||||
for (int i = 0; i < this.subGames.length; i++) {
|
this.masterGameStates = new HashMap<>();
|
||||||
this.subGames[i] = new TicTacToeGame();
|
for (CellLocation value : CellLocation.values) {
|
||||||
|
this.subGames.put(value, new TicTacToeGame());
|
||||||
|
this.masterGameStates.put(value, GameState.RUNNING);
|
||||||
}
|
}
|
||||||
this.masterGameStates = new GameState[9];
|
|
||||||
Arrays.fill(this.masterGameStates, GameState.RUNNING);
|
|
||||||
|
|
||||||
// X always starts in the center field
|
// X always starts in the center field
|
||||||
this.currentPlayer = Player.X;
|
this.currentPlayer = Player.X;
|
||||||
this.activeField = 4;
|
this.activeField = CellLocation.MC;
|
||||||
this.globalGameState = GameState.RUNNING;
|
this.globalGameState = GameState.RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITicTacToeGame getSubGame(int id) {
|
public ITicTacToeGame getSubGame(CellLocation location) {
|
||||||
return this.subGames[id];
|
return this.subGames.get(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getActiveField() {
|
public CellLocation getActiveField() {
|
||||||
return activeField;
|
return activeField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,13 +57,13 @@ public class UltimateTicTacToe implements IUltimateTicTacToe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doPlayerMove(int cell) {
|
public void doPlayerMove(CellLocation cell) {
|
||||||
if (!getSubGame(this.activeField).getAvailableFields().contains(cell)) return;
|
if (!getSubGame(this.activeField).getAvailableFields().contains(cell)) return;
|
||||||
|
|
||||||
var nextCellState = this.currentPlayer == Player.X ? CellState.X : CellState.O;
|
var nextCellState = this.currentPlayer == Player.X ? CellState.X : CellState.O;
|
||||||
getSubGame(this.activeField).setCellState(cell, nextCellState);
|
getSubGame(this.activeField).setCellState(cell, nextCellState);
|
||||||
|
|
||||||
this.masterGameStates[this.activeField] = getSubGame(this.activeField).getState();
|
this.masterGameStates.put(this.activeField, getSubGame(this.activeField).getState());
|
||||||
|
|
||||||
updateGameState();
|
updateGameState();
|
||||||
|
|
||||||
@ -73,13 +75,13 @@ public class UltimateTicTacToe implements IUltimateTicTacToe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateGameState() {
|
private void updateGameState() {
|
||||||
for (Integer[] winningCombination : TicTacToeGame.winningCombinations) {
|
for (CellLocation[] winningCombination : TicTacToeGame.winningCombinations) {
|
||||||
final var interestingStates = new HashSet<GameState>();
|
final var interestingStates = new HashSet<GameState>();
|
||||||
interestingStates.add(this.masterGameStates[winningCombination[0]]);
|
interestingStates.add(this.masterGameStates.get(winningCombination[0]));
|
||||||
interestingStates.add(this.masterGameStates[winningCombination[1]]);
|
interestingStates.add(this.masterGameStates.get(winningCombination[1]));
|
||||||
interestingStates.add(this.masterGameStates[winningCombination[2]]);
|
interestingStates.add(this.masterGameStates.get(winningCombination[2]));
|
||||||
if (interestingStates.size() == 1 && !interestingStates.contains(GameState.RUNNING)) {
|
if (interestingStates.size() == 1 && !interestingStates.contains(GameState.RUNNING)) {
|
||||||
switch (this.masterGameStates[winningCombination[0]]) {
|
switch (this.masterGameStates.get(winningCombination[0])) {
|
||||||
case O_WON -> this.globalGameState = GameState.O_WON;
|
case O_WON -> this.globalGameState = GameState.O_WON;
|
||||||
case X_WON -> this.globalGameState = GameState.X_WON;
|
case X_WON -> this.globalGameState = GameState.X_WON;
|
||||||
}
|
}
|
||||||
@ -87,16 +89,18 @@ public class UltimateTicTacToe implements IUltimateTicTacToe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Arrays.stream(this.masterGameStates).allMatch(gameState -> gameState != GameState.RUNNING)) {
|
if (!this.masterGameStates.containsValue(GameState.RUNNING)) {
|
||||||
this.globalGameState = GameState.DRAW;
|
this.globalGameState = GameState.DRAW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findNextAvailableSubGame(int startingIndex) {
|
private CellLocation findNextAvailableSubGame(CellLocation startingLocation) {
|
||||||
while (this.subGames[startingIndex].isFinished()) {
|
int startingIndex = startingLocation.ordinal();
|
||||||
|
while (this.subGames.get(startingLocation).isFinished()) {
|
||||||
startingIndex = (startingIndex + 1) % 9;
|
startingIndex = (startingIndex + 1) % 9;
|
||||||
|
startingLocation = CellLocation.values[startingIndex];
|
||||||
}
|
}
|
||||||
return startingIndex;
|
return startingLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package de.icaotix.ultimatetictactoe.model.definitions;
|
||||||
|
|
||||||
|
public enum CellLocation {
|
||||||
|
TL("Top Left"),
|
||||||
|
TC("Top Center"),
|
||||||
|
TR("Top Right"),
|
||||||
|
ML("Middle Left"),
|
||||||
|
MC("Middle Center"),
|
||||||
|
MR("Middle Right"),
|
||||||
|
BL("Bottom Left"),
|
||||||
|
BC("Bottom Center"),
|
||||||
|
BR("Bottom Right");
|
||||||
|
|
||||||
|
public static final CellLocation[] values = values();
|
||||||
|
private final String longName;
|
||||||
|
|
||||||
|
CellLocation(String longName) {
|
||||||
|
this.longName = longName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLongName() {
|
||||||
|
return longName;
|
||||||
|
}
|
||||||
|
}
|
@ -2,21 +2,27 @@ package de.icaotix.ultimatetictactoe.viewmodel;
|
|||||||
|
|
||||||
|
|
||||||
import de.icaotix.ultimatetictactoe.model.IUltimateTicTacToe;
|
import de.icaotix.ultimatetictactoe.model.IUltimateTicTacToe;
|
||||||
|
import de.icaotix.ultimatetictactoe.model.definitions.CellLocation;
|
||||||
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class UltimateTicTacToePanelViewModel {
|
public class UltimateTicTacToePanelViewModel {
|
||||||
|
|
||||||
private final TicTacToePanelViewModel[] subGameViewModels;
|
private final Map<CellLocation, TicTacToePanelViewModel> subGameViewModels;
|
||||||
private final IUltimateTicTacToe ultimateTicTacToe;
|
private final IUltimateTicTacToe ultimateTicTacToe;
|
||||||
private Consumer<String> gameResultCallback;
|
private Consumer<String> gameResultCallback;
|
||||||
private Consumer<String> currentPlayerCallback;
|
private Consumer<String> currentPlayerCallback;
|
||||||
|
|
||||||
public UltimateTicTacToePanelViewModel(IUltimateTicTacToe ultimateTicTacToe, ViewModelFactory viewModelFactory) {
|
public UltimateTicTacToePanelViewModel(IUltimateTicTacToe ultimateTicTacToe, ViewModelFactory viewModelFactory) {
|
||||||
this.subGameViewModels = new TicTacToePanelViewModel[9];
|
this.subGameViewModels = new HashMap<>();
|
||||||
for (int i = 0; i < this.subGameViewModels.length; i++) {
|
for (CellLocation value : CellLocation.values) {
|
||||||
this.subGameViewModels[i] = viewModelFactory.getTicTacToePanelViewModel(this);
|
this.subGameViewModels.put(
|
||||||
|
value,
|
||||||
|
viewModelFactory.getTicTacToePanelViewModel(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.ultimateTicTacToe = ultimateTicTacToe;
|
this.ultimateTicTacToe = ultimateTicTacToe;
|
||||||
}
|
}
|
||||||
@ -31,34 +37,41 @@ public class UltimateTicTacToePanelViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Setup next move
|
// Setup next move
|
||||||
final int fieldId = this.ultimateTicTacToe.getActiveField();
|
final var cellLocation = this.ultimateTicTacToe.getActiveField();
|
||||||
final var availableFields = this.ultimateTicTacToe.getSubGame(fieldId).getAvailableFields();
|
final var availableFields = this.ultimateTicTacToe
|
||||||
this.subGameViewModels[fieldId].activate(availableFields);
|
.getSubGame(cellLocation)
|
||||||
|
.getAvailableFields()
|
||||||
|
.stream()
|
||||||
|
.mapToInt(Enum::ordinal)
|
||||||
|
.boxed()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
this.subGameViewModels.get(cellLocation).activate(availableFields);
|
||||||
this.currentPlayerCallback.accept("Current player: "
|
this.currentPlayerCallback.accept("Current player: "
|
||||||
+ this.ultimateTicTacToe.getCurrentPlayer().name());
|
+ this.ultimateTicTacToe.getCurrentPlayer().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCellClicked(int cell) {
|
public void onCellClicked(int cell) {
|
||||||
final int oldActiveGameId = this.ultimateTicTacToe.getActiveField();
|
final var oldActiveGameLocation = this.ultimateTicTacToe.getActiveField();
|
||||||
final var oldActiveGameViewModel = this.subGameViewModels[oldActiveGameId];
|
final var oldActiveGameViewModel = this.subGameViewModels.get(oldActiveGameLocation);
|
||||||
final var oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameId);
|
final var oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameLocation);
|
||||||
oldActiveGameViewModel.deactivate();
|
oldActiveGameViewModel.deactivate();
|
||||||
|
|
||||||
this.ultimateTicTacToe.doPlayerMove(cell);
|
this.ultimateTicTacToe.doPlayerMove(CellLocation.values[cell]);
|
||||||
|
|
||||||
if (oldActiveGame.isFinished()) {
|
if (oldActiveGame.isFinished()) {
|
||||||
final String winnerText = oldActiveGame.getState().displayText;
|
final String winnerText = oldActiveGame.getState().displayText;
|
||||||
oldActiveGameViewModel.setFinishPanel(winnerText);
|
oldActiveGameViewModel.setFinishPanel(winnerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String newText = oldActiveGame.getCells()[cell].displayText;
|
final String newText = oldActiveGame.getCells().get(CellLocation.values[cell]).displayText;
|
||||||
oldActiveGameViewModel.setButtonText(cell, newText);
|
oldActiveGameViewModel.setButtonText(cell, newText);
|
||||||
|
|
||||||
prepareNextMove();
|
prepareNextMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TicTacToePanelViewModel getSubGameViewModel(int subGameId) {
|
public TicTacToePanelViewModel getSubGameViewModel(int subGameId) {
|
||||||
return this.subGameViewModels[subGameId];
|
return this.subGameViewModels.get(CellLocation.values[subGameId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGameResultCallback(Consumer<String> gameResultCallback) {
|
public void setGameResultCallback(Consumer<String> gameResultCallback) {
|
||||||
|
Loading…
Reference in New Issue
Block a user