MVP game client

This commit is contained in:
lieght
2025-09-19 00:00:25 +02:00
parent cacf9a29e8
commit c2b6aea71e
16 changed files with 379 additions and 139 deletions

View File

@@ -1,7 +1,6 @@
package org.toop;
import org.apache.logging.log4j.Level;
import org.toop.UI.GameSelectorWindow;
import org.toop.UI.LocalServerSelector;
import org.toop.eventbus.EventRegistry;
import org.toop.eventbus.Events;
import org.toop.eventbus.GlobalEventBus;
@@ -9,9 +8,7 @@ import org.toop.server.backend.ServerManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.toop.server.backend.tictactoe.TicTacToeServer;
import org.toop.server.frontend.ConnectionManager;
import org.toop.server.frontend.TcpClient;
import java.util.concurrent.ExecutionException;
@@ -20,7 +17,8 @@ public class Main {
private static boolean running = false;
public static void main(String[] args) throws ExecutionException, InterruptedException {
Logging.disableAllLogs();
// Logging.disableAllLogs();
Logging.disableLogsForClass(EventRegistry.class);
// Logging.enableLogsForClass(ServerManager.class, Level.ALL);
// Logging.enableLogsForClass(TicTacToeServer.class, Level.ALL);
// Logging.enableLogsForClass(TcpClient.class, Level.ALL);
@@ -28,33 +26,17 @@ public class Main {
initSystems();
registerEvents();
/*
Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720));
Renderer renderer = Renderer.setup(Renderer.API.OPENGL);
// JFrame frame = new JFrame("Server Settings");
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setSize(800, 600);
// frame.setLocationRelativeTo(null);
// frame.setVisible(true);
if (!initEvents()) {
throw new RuntimeException("A event could not be initialized");
}
javax.swing.SwingUtilities.invokeLater(LocalServerSelector::new);
TcpServer server = new TcpServer(5001);
Thread serverThread = new Thread(server);
serverThread.start();
Server.start("127.0.0.1", "5001");
// Testsss.start(""); // Used for testing server.
Window.start("");
*/
// ConsoleGui console = new ConsoleGui();
//
// do {
// console.print();
// } while (console.next());
//
// console.print();
new Thread(() -> {
GameSelectorWindow gameSelectorWindow = new GameSelectorWindow();
}).start();
// new Thread(() -> {
// LocalServerSelector window = new LocalServerSelector();
// }).start();
}

View File

@@ -0,0 +1,21 @@
package org.toop.UI;
import javax.swing.*;
import java.awt.*;
public class BackgroundPanel extends JPanel {
private Image backgroundImage;
public void setBackgroundImage(Image image) {
this.backgroundImage = image;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImage != null) {
g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
}
}
}

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.LocalGameSelector">
<grid id="27dc6" binding="panel1" default-binding="true" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="780" height="600"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="3f7f6" class="javax.swing.JComboBox" binding="gameSelectionComboBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<model/>
</properties>
</component>
<component id="4700f" class="javax.swing.JButton" binding="startGame">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Start game"/>
</properties>
</component>
<component id="41f79" class="javax.swing.JComboBox" binding="playerTypeSelectionBox">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<editable value="false"/>
</properties>
</component>
</children>
</grid>
</form>

View File

@@ -0,0 +1,139 @@
package org.toop.UI;
import jdk.jfr.Event;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.Main;
import org.toop.eventbus.Events;
import org.toop.eventbus.GlobalEventBus;
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
public class LocalGameSelector extends JFrame {
private static final Logger logger = LogManager.getLogger(LocalGameSelector.class);
private JPanel panel1;
private JComboBox gameSelectionComboBox;
private JButton startGame;
private JComboBox playerTypeSelectionBox;
private JPanel cards; // CardLayout panel
private CardLayout cardLayout;
private UIGameBoard tttBoard;
public LocalGameSelector() {
setTitle("Local Game Selector");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1920, 1080);
setLocationRelativeTo(null);
// Setup CardLayout
cardLayout = new CardLayout();
cards = new JPanel(cardLayout);
setContentPane(cards);
// --- Main menu panel ---
panel1 = new JPanel();
panel1.setLayout(new FlowLayout());
gameSelectionComboBox = new JComboBox<>();
gameSelectionComboBox.addItem("Tic Tac Toe");
gameSelectionComboBox.addItem("Reversi");
playerTypeSelectionBox = new JComboBox<>();
playerTypeSelectionBox.addItem("Player vs Player");
playerTypeSelectionBox.addItem("Player vs AI");
playerTypeSelectionBox.addItem("AI vs Player");
panel1.add(gameSelectionComboBox);
panel1.add(playerTypeSelectionBox);
startGame = new JButton("Start Game");
panel1.add(startGame);
cards.add(panel1, "MainMenu");
// Start button action
startGame.addActionListener(e -> startGameClicked());
setVisible(true);
}
private String createServer() {
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest("5001", "tictactoe", serverIdFuture)); // TODO: what if 5001 is in use
try {
return serverIdFuture.get();
} catch (Exception e) {
logger.error("Error getting server ID", e);
}
return null;
}
private String createConnection() {
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest("127.0.0.1", "5001", connectionIdFuture)); // TODO: what if server couldn't be started with port.
try {
return connectionIdFuture.get();
} catch (InterruptedException | ExecutionException e) {
logger.error("Error getting connection ID", e);
}
return null;
}
private void startGameClicked() {
String playerTypes = (String) playerTypeSelectionBox.getSelectedItem();
String selectedGame = (String) gameSelectionComboBox.getSelectedItem();
String serverId = createServer();
String connectionId = createConnection();
final String[] gameId = new String[1];
if ("Player vs AI".equalsIgnoreCase(playerTypes)) {
GlobalEventBus.post(new Events.ServerEvents.SendCommand(connectionId, "create_game", "Player", "AI"));
} else if ("AI vs Player".equalsIgnoreCase(playerTypes)) {
GlobalEventBus.post(new Events.ServerEvents.SendCommand(connectionId, "create_game", "Player", "AI"));
} else { // Player vs Player is default
GlobalEventBus.post(new Events.ServerEvents.SendCommand(connectionId, "create_game", "Player1", "Player2"));
}
CountDownLatch latch = new CountDownLatch(1); // TODO: This is bad, fix later
new Thread(() -> {
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ReceivedMessage.class, event -> {
logger.info(event.message());
if (event.message().toLowerCase().startsWith("game created successfully")) {
String[] parts = event.message().split("\\|");
String gameIdPart = parts[1];
gameId[0] = gameIdPart.split(" ")[1];
latch.countDown();
}
});
}).start();
try {
latch.await(); // TODO: Bad, fix later
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
GlobalEventBus.post(new Events.ServerEvents.SendCommand(connectionId, "START_GAME", gameId[0]));
if ("Tic Tac Toe".equalsIgnoreCase(selectedGame)) {
if (tttBoard == null) {
tttBoard = new UIGameBoard("tic tac toe", connectionId, serverId, gameId[0], this);
cards.add(tttBoard.getTTTPanel(), "TicTacToe");
}
cardLayout.show(cards, "TicTacToe");
}
}
public void showMainMenu() {
cardLayout.show(cards, "MainMenu");
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.LocalServerSelector">
<grid id="27dc6" binding="panel1" default-binding="true" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="7774a" class="javax.swing.JButton" binding="serverButton" default-binding="true">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Server"/>
</properties>
</component>
<component id="ca0c8" class="javax.swing.JButton" binding="localButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Local"/>
</properties>
</component>
<hspacer id="74dd2">
<constraints>
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<hspacer id="50fd">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
</children>
</grid>
</form>

View File

@@ -0,0 +1,34 @@
package org.toop.UI;
import javax.swing.*;
public class LocalServerSelector {
private JPanel panel1;
private JButton serverButton;
private JButton localButton;
private final JFrame frame;
public LocalServerSelector() {
frame = new JFrame("Server Selector");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel1);
frame.setSize(1920, 1080);
frame.setLocationRelativeTo(null); // Sets to center
frame.setVisible(true);
serverButton.addActionListener(e -> onServerClicked());
localButton.addActionListener(e -> onLocalClicked());
}
private void onServerClicked() {
frame.dispose();
new RemoteGameSelector();
}
private void onLocalClicked() {
frame.dispose();
new LocalGameSelector();
}
}

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.GameSelectorWindow">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.RemoteGameSelector">
<grid id="27dc6" binding="mainMenu" layout-manager="GridLayoutManager" row-count="10" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="20" y="20" width="482" height="404"/>
<xy x="20" y="20" width="741" height="625"/>
</constraints>
<properties>
<opaque value="true"/>

View File

@@ -6,14 +6,13 @@ import org.toop.eventbus.GlobalEventBus;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class GameSelectorWindow extends JFrame {
private static final Logger logger = LogManager.getLogger(GameSelectorWindow.class);
public class RemoteGameSelector {
private static final Logger logger = LogManager.getLogger(RemoteGameSelector.class);
private JPanel mainMenu;
private JTextField nameTextField;
@@ -27,7 +26,7 @@ public class GameSelectorWindow extends JFrame {
private JFrame frame;
private JLabel fillAllFields;
public GameSelectorWindow() {
public RemoteGameSelector() {
gameSelectorBox.addItem("Tic Tac Toe");
gameSelectorBox.addItem("Reversi");
//todo get supported games from server and add to gameSelectorBox
@@ -107,8 +106,8 @@ public class GameSelectorWindow extends JFrame {
frame.remove(mainMenu);
UIGameBoard ttt = new UIGameBoard(gameSelectorBox.getSelectedItem().toString(),this);
frame.add(ttt.getTTTPanel());
// UIGameBoard ttt = new UIGameBoard("tic tac toe", "test", "test",this); // TODO: Fix later
// frame.add(ttt.getTTTPanel()); // TODO: Fix later
frame.revalidate();
frame.repaint();
} else {

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.Services">
<grid id="27dc6" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</form>

View File

@@ -0,0 +1,6 @@
package org.toop.UI;
import javax.swing.*;
public class Services {
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.UI.UIGameBoard">
<grid id="27dc6" binding="tttPanel" layout-manager="GridLayoutManager" row-count="2" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="tttPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -10,38 +10,6 @@
</properties>
<border type="none"/>
<children>
<component id="f23e8" class="javax.swing.JLabel" binding="name">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Label"/>
</properties>
</component>
<component id="4db9d" class="javax.swing.JLabel" binding="ip">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Label"/>
</properties>
</component>
<component id="ac2e0" class="javax.swing.JLabel" binding="gameName">
<constraints>
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Label"/>
</properties>
</component>
<grid id="f1c82" binding="cellPanel" layout-manager="FlowLayout" hgap="5" vgap="5" flow-align="1">
<constraints>
<grid row="1" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
<component id="e1a78" class="javax.swing.JButton" binding="backToMainMenuButton" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="1" indent="0" use-parent-layout="false"/>

View File

@@ -1,79 +1,76 @@
package org.toop.UI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.Main;
import org.toop.eventbus.Events;
import org.toop.eventbus.GlobalEventBus;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Objects;
public class UIGameBoard extends JFrame {
private static final Logger logger = LogManager.getLogger(UIGameBoard.class);
public class UIGameBoard {
private static final int TICTACTOE_SIZE = 3;
private final int TICTACTOE_SIZE = 3;
private final int REVERSI_SIZE = 8;
private JLabel name;
private JLabel ip;
private JLabel gameName;
private JPanel tttPanel;
private JPanel cellPanel;
private JPanel tttPanel; // Root panel for this game
private JButton backToMainMenuButton;
private JButton[] buttons = new JButton[9];
private JButton[] cells;
public UIGameBoard(String game,GameSelectorWindow gameSelectorWindow) {
private String currentPlayer = "X";
//cellPanel = new JPanel();
JPanel gamePanel = new JPanel();
if(game.toLowerCase().equals("tic tac toe")) {
gamePanel = createGridPanel(TICTACTOE_SIZE, TICTACTOE_SIZE);
}
if(game.toLowerCase().equals("reversi")) {
gamePanel = createGridPanel(REVERSI_SIZE, REVERSI_SIZE);
private String gameConnectionId;
private String serverId;
private String gameId;
private LocalGameSelector parentSelector;
public UIGameBoard(String gameType, String gameConnectionId, String serverId, String gameId, LocalGameSelector parent) {
this.parentSelector = parent;
this .gameConnectionId = gameConnectionId;
this.serverId = serverId;
this.gameId = gameId;
// Root panel
tttPanel = new JPanel(new BorderLayout());
// Back button
backToMainMenuButton = new JButton("Back to Main Menu");
tttPanel.add(backToMainMenuButton, BorderLayout.SOUTH);
backToMainMenuButton.addActionListener(e ->
// TODO reset game and connections
parent.showMainMenu()
);
// Game grid
JPanel gameGrid = createGridPanel(TICTACTOE_SIZE, TICTACTOE_SIZE);
tttPanel.add(gameGrid, BorderLayout.CENTER);
}
private JPanel createGridPanel(int sizeX, int sizeY) {
JPanel panel = new JPanel(new GridLayout(sizeX, sizeY));
cells = new JButton[sizeX * sizeY];
for (int i = 0; i < sizeX * sizeY; i++) {
cells[i] = new JButton(" ");
cells[i].setFont(new Font("Arial", Font.BOLD, 100 / sizeX));
panel.add(cells[i]);
final int index = i;
cells[i].addActionListener((ActionEvent e) -> {
cells[index].setText(currentPlayer);
if (Objects.equals(currentPlayer, "X")) { currentPlayer = "O"; }
else { currentPlayer = "X"; }
GlobalEventBus.post(new Events.ServerEvents.SendCommand(this.gameConnectionId,
"gameid ", this.gameId,
"player ", this.currentPlayer, // TODO: Actual player names
"MOVE", "" + index));
System.out.println("Cell clicked: " + index);
});
}
cellPanel.removeAll();
cellPanel.add(gamePanel);
cellPanel.revalidate();
cellPanel.repaint();
//tttPanel.add(cellPanel);
backToMainMenuButton.addActionListener((
ActionEvent e) -> {
gameSelectorWindow.returnToMainMenu();
System.out.println("gothere");
});
}
//Set the IP, game name and name
public void setIGN(String ip, String gameName, String name) {
this.ip.setText(ip);
this.gameName.setText(gameName);
this.name.setText(name);
return panel;
}
public JPanel getTTTPanel() {
return tttPanel;
}
//Creates a grid of buttons and adds a global event bus event on click with the index of the button.
private JPanel createGridPanel(int sizeX, int sizeY) {
JPanel cellPanel = new JPanel(new GridLayout(sizeX,sizeY));
cells = new JButton[sizeX*sizeY];
for(int i =0; i < sizeX*sizeY; i++){
cells[i] = new JButton(" ");
cells[i].setPreferredSize(new Dimension(1000/sizeX,1000/sizeY));
cells[i].setFont(new Font("Arial", Font.BOLD, 480/sizeX));
cells[i].setFocusPainted(false);
cellPanel.add(cells[i]);
final int index = i;
cells[i].addActionListener((ActionEvent e) -> {
setCell(index,"X");//■ todo get current player
GlobalEventBus.post(new Events.ServerEvents.CellClicked(index));
logger.info("Grid button {} was clicked.", index);
});
}
return cellPanel;
}
public void setCell(int cell, String newValue){
cells[cell].setText(newValue);
}
}
}

View File

@@ -112,8 +112,9 @@ public class TicTacToeServer extends TcpServer {
String gameId = this.newGame((String) command.arguments.getFirst(), (String) command.arguments.get(1));
this.sendQueue.offer("game created successfully|gameid " + gameId);
} else if (command.command == TicTacToeServerCommand.START_GAME) {
this.runGame((String) command.arguments.getFirst());
this.sendQueue.offer("svr game is running successfully");
boolean success = this.runGame((String) command.arguments.getFirst());
if (success) {this.sendQueue.offer("svr game is running successfully");}
else {this.sendQueue.offer("svr running game failed");}
} else if (command.command == TicTacToeServerCommand.END_GAME) {
this.endGame((String) command.arguments.getFirst());
this.sendQueue.offer("svr game ended successfully");
@@ -150,13 +151,15 @@ public class TicTacToeServer extends TcpServer {
return gameId;
}
public void runGame(String gameId) {
public boolean runGame(String gameId) {
TicTacToe game = this.games.get(gameId);
if (game != null) {
game.run();
logger.info("Running game: {}, players: {}", gameId, game.getPlayers());
return true;
} else {
logger.warn("Tried to run unknown game {}", gameId);
return false;
}
}

View File

@@ -52,10 +52,10 @@ public final class ServerConnection implements Runnable {
// return;
// } // TODO: DO I CARE?
if (!this.running) {
logger.warn("Server has been stopped");
return;
}
// if (!this.running) {
// logger.warn("Server has been stopped");
// return;
// } // TODO: Server not running
String command = String.join(" ", args);