mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Local tic tac toe thread
This commit is contained in:
108
src/main/java/org/toop/frontend/ConnectionManager.java
Normal file
108
src/main/java/org/toop/frontend/ConnectionManager.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package org.toop.frontend;
|
||||
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ConnectionManager {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(ConnectionManager.class);
|
||||
|
||||
/**
|
||||
* Map of serverId -> Server instances
|
||||
*/
|
||||
private final Map<String, ServerConnection> serverConnections = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Starts a connection manager, to manage, connections.
|
||||
*/
|
||||
public ConnectionManager() {
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartConnectionRequest.class, this::handleStartConnectionRequest);
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartConnection.class, this::handleStartConnection);
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.SendCommand.class, this::handleCommand);
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.Reconnect.class, this::handleReconnect);
|
||||
// GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ChangeConnection.class, this::handleChangeConnection);
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ForceCloseAllConnections.class, _ -> shutdownAll());
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.RequestsAllConnections.class, this::getAllConnections);
|
||||
}
|
||||
|
||||
private String startConnectionRequest(String ip, String port) {
|
||||
String connectionId = UUID.randomUUID().toString();
|
||||
try {
|
||||
ServerConnection connection = new ServerConnection(connectionId, ip, port);
|
||||
this.serverConnections.put(connectionId, connection);
|
||||
new Thread(connection, "Connection-" + connectionId).start();
|
||||
logger.info("Connected to server {} at {}:{}", connectionId, ip, port);
|
||||
} catch (IOException e) {
|
||||
logger.error("{}", e);
|
||||
}
|
||||
return connectionId;
|
||||
}
|
||||
|
||||
private void handleStartConnectionRequest(Events.ServerEvents.StartConnectionRequest request) {
|
||||
request.future().complete(this.startConnectionRequest(request.ip(), request.port())); // TODO: Maybe post ConnectionEstablished event.
|
||||
}
|
||||
|
||||
private void handleStartConnection(Events.ServerEvents.StartConnection event) {
|
||||
GlobalEventBus.post(new Events.ServerEvents.ConnectionEstablished(
|
||||
this.startConnectionRequest(event.ip(), event.port()),
|
||||
event.ip(),
|
||||
event.port()
|
||||
));
|
||||
}
|
||||
|
||||
private void handleCommand(Events.ServerEvents.SendCommand event) { // TODO: Move this to ServerConnection class, keep it internal.
|
||||
ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
||||
if (serverConnection != null) {
|
||||
serverConnection.sendCommandByString(event.args());
|
||||
} else {
|
||||
logger.warn("Server {} not found for command '{}'", event.connectionId(), event.args());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReconnect(Events.ServerEvents.Reconnect event) {
|
||||
ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
||||
if (serverConnection != null) {
|
||||
try {
|
||||
serverConnection.reconnect();
|
||||
logger.info("Server {} reconnected", event.connectionId());
|
||||
} catch (Exception e) {
|
||||
logger.error("Server {} failed to reconnect", event.connectionId(), e);
|
||||
GlobalEventBus.post(new Events.ServerEvents.CouldNotConnect(event.connectionId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void handleChangeConnection(Events.ServerEvents.ChangeConnection event) {
|
||||
// ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
||||
// if (serverConnection != null) {
|
||||
// try {
|
||||
// serverConnection.connect(event.ip(), event.port());
|
||||
// logger.info("Server {} changed connection to {}:{}", event.connectionId(), event.ip(), event.port());
|
||||
// } catch (Exception e) {
|
||||
// logger.error("Server {} failed to change connection", event.connectionId(), e);
|
||||
// GlobalEventBus.post(new Events.ServerEvents.CouldNotConnect(event.connectionId()));
|
||||
// }
|
||||
// }
|
||||
// } TODO
|
||||
|
||||
private void getAllConnections(Events.ServerEvents.RequestsAllConnections request) {
|
||||
List<ServerConnection> a = new ArrayList<>(this.serverConnections.values());
|
||||
request.future().complete(a.toString());
|
||||
}
|
||||
|
||||
public void shutdownAll() {
|
||||
this.serverConnections.values().forEach(ServerConnection::closeConnection);
|
||||
this.serverConnections.clear();
|
||||
logger.info("All servers shut down");
|
||||
}
|
||||
}
|
||||
172
src/main/java/org/toop/frontend/ServerConnection.java
Normal file
172
src/main/java/org/toop/frontend/ServerConnection.java
Normal file
@@ -0,0 +1,172 @@
|
||||
package org.toop.frontend;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public final class ServerConnection extends TcpClient implements Runnable {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(ServerConnection.class);
|
||||
|
||||
private final BlockingQueue<String> receivedQueue = new LinkedBlockingQueue<>();
|
||||
private final BlockingQueue<String> sendQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||
|
||||
String uuid;
|
||||
volatile boolean running = false;
|
||||
|
||||
public ServerConnection(String uuid, String ip, String port) throws IOException {
|
||||
super(ip, Integer.parseInt(port)); // TODO: Verify if port is integer first, to avoid crash.
|
||||
this.uuid = uuid;
|
||||
this.initEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sends a command to the server.
|
||||
*
|
||||
* @param args The arguments for the command.
|
||||
*/
|
||||
public void sendCommandByString(String... args) {
|
||||
// if (!TicTacToeServerCommand.isValid(command)) {
|
||||
// logger.error("Invalid command: {}", command);
|
||||
// return;
|
||||
// } // TODO: DO I CARE?
|
||||
|
||||
// if (!this.running) {
|
||||
// logger.warn("Server has been stopped");
|
||||
// return;
|
||||
// } // TODO: Server not running
|
||||
|
||||
String command = String.join(" ", args);
|
||||
|
||||
this.sendQueue.add(command);
|
||||
logger.info("Command '{}' added to the queue", command); // TODO: Better log, which uuid?
|
||||
}
|
||||
|
||||
private void addReceivedMessageToQueue(String message) {
|
||||
try {
|
||||
receivedQueue.put(message);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("{}", e); // TODO: Make more informative
|
||||
}
|
||||
}
|
||||
|
||||
private void initEvents() {}
|
||||
|
||||
private void startWorkers() {
|
||||
running = true;
|
||||
this.executor.submit(this::inputLoop);
|
||||
this.executor.submit(this::outputLoop);
|
||||
}
|
||||
|
||||
private void stopWorkers() {
|
||||
this.running = false;
|
||||
this.sendQueue.clear();
|
||||
try {
|
||||
this.closeSocket();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error closing client socket", e); // TODO: Better log
|
||||
}
|
||||
|
||||
this.executor.shutdownNow();
|
||||
}
|
||||
|
||||
private void inputLoop() {
|
||||
logger.info("Starting {}:{} connection read", this.serverAddress, this.serverPort);
|
||||
try {
|
||||
while (running) {
|
||||
String received = this.readLine(); // blocks
|
||||
if (received != null) {
|
||||
logger.info("Connection: {} received: '{}'", this.uuid, received);
|
||||
// this.addReceivedMessageToQueue(received); // TODO: Will never go empty
|
||||
GlobalEventBus.post(new Events.ServerEvents.ReceivedMessage(this.uuid, received)); // TODO: mb change
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Error reading from server", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void outputLoop() {
|
||||
logger.info("Starting {}:{} connection write", this.serverAddress, this.serverPort);
|
||||
try {
|
||||
while (this.running) {
|
||||
String command = this.sendQueue.poll(500, TimeUnit.MILLISECONDS);
|
||||
if (command != null) {
|
||||
this.sendMessage(command);
|
||||
logger.info("Sent command: '{}'", command);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error sending command", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Connect to a new server.
|
||||
*
|
||||
* @param ip The ip to connect to.
|
||||
* @param port The port to connect to.
|
||||
*/
|
||||
public void connect(InetAddress ip, int port) {
|
||||
if (this.running) {
|
||||
this.closeConnection(); // Also stops workers.
|
||||
}
|
||||
|
||||
this.serverAddress = ip;
|
||||
this.serverPort = port;
|
||||
|
||||
this.startWorkers();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Reconnects to previous address.
|
||||
*
|
||||
* @throws IOException wip
|
||||
*/
|
||||
public void reconnect() throws IOException {
|
||||
this.connect(this.serverAddress, this.serverPort);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Close connection to server.
|
||||
*
|
||||
*/
|
||||
public void closeConnection() {
|
||||
this.stopWorkers();
|
||||
logger.info("Closed connection: {}, to server {}:{}", this.uuid, this.serverAddress, this.serverPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
reconnect();
|
||||
} catch (IOException e) {
|
||||
logger.error("Initial connection failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"Server {ip: \"%s\", port: \"%s\", running: %s}",
|
||||
this.serverAddress, this.serverPort, this.running
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
65
src/main/java/org/toop/frontend/TcpClient.java
Normal file
65
src/main/java/org/toop/frontend/TcpClient.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.toop.frontend;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
/**
|
||||
* A simple wrapper for creating TCP clients.
|
||||
*/
|
||||
public abstract class TcpClient {
|
||||
|
||||
InetAddress serverAddress;
|
||||
int serverPort;
|
||||
Socket socket;
|
||||
BufferedReader in;
|
||||
PrintWriter out;
|
||||
|
||||
public TcpClient(byte[] serverIp, int serverPort) throws IOException {
|
||||
this.serverAddress = InetAddress.getByAddress(serverIp);
|
||||
this.serverPort = serverPort;
|
||||
this.socket = createSocket();
|
||||
this.in = createIn();
|
||||
this.out = createOut();
|
||||
}
|
||||
|
||||
public TcpClient(String serverIp, int serverPort) throws IOException {
|
||||
this.serverAddress = InetAddress.getByName(serverIp);
|
||||
this.serverPort = serverPort;
|
||||
this.socket = createSocket();
|
||||
this.in = createIn();
|
||||
this.out = createOut();
|
||||
}
|
||||
|
||||
public Socket createSocket() throws IOException {
|
||||
return new Socket(serverAddress, serverPort);
|
||||
}
|
||||
|
||||
public void closeSocket() throws IOException {
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
private BufferedReader createIn() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
|
||||
}
|
||||
|
||||
private PrintWriter createOut() throws IOException {
|
||||
return new PrintWriter(this.socket.getOutputStream(), true);
|
||||
}
|
||||
|
||||
public void sendMessage(String message) throws IOException {
|
||||
this.out.println(message);
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
return this.in.readLine();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
}
|
||||
21
src/main/java/org/toop/frontend/UI/BackgroundPanel.java
Normal file
21
src/main/java/org/toop/frontend/UI/BackgroundPanel.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.toop.frontend.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/main/java/org/toop/frontend/UI/LocalGameSelector.form
Normal file
37
src/main/java/org/toop/frontend/UI/LocalGameSelector.form
Normal 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.frontend.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>
|
||||
83
src/main/java/org/toop/frontend/UI/LocalGameSelector.java
Normal file
83
src/main/java/org/toop/frontend/UI/LocalGameSelector.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package org.toop.frontend.UI;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
import org.toop.frontend.games.LocalTicTacToe;
|
||||
|
||||
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 void startGameClicked() {
|
||||
String playerTypes = (String) playerTypeSelectionBox.getSelectedItem();
|
||||
String selectedGame = (String) gameSelectionComboBox.getSelectedItem();
|
||||
|
||||
LocalTicTacToe lttt = new LocalTicTacToe(true, "127.0.0.1", "5001");
|
||||
|
||||
if ("Tic Tac Toe".equalsIgnoreCase(selectedGame)) {
|
||||
if (tttBoard == null) {
|
||||
tttBoard = new UIGameBoard(lttt, this);
|
||||
cards.add(tttBoard.getTTTPanel(), "TicTacToe");
|
||||
}
|
||||
cardLayout.show(cards, "TicTacToe");
|
||||
}
|
||||
}
|
||||
|
||||
public void showMainMenu() {
|
||||
cardLayout.show(cards, "MainMenu");
|
||||
}
|
||||
}
|
||||
39
src/main/java/org/toop/frontend/UI/LocalServerSelector.form
Normal file
39
src/main/java/org/toop/frontend/UI/LocalServerSelector.form
Normal 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.frontend.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>
|
||||
34
src/main/java/org/toop/frontend/UI/LocalServerSelector.java
Normal file
34
src/main/java/org/toop/frontend/UI/LocalServerSelector.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.toop.frontend.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();
|
||||
}
|
||||
|
||||
}
|
||||
147
src/main/java/org/toop/frontend/UI/RemoteGameSelector.form
Normal file
147
src/main/java/org/toop/frontend/UI/RemoteGameSelector.form
Normal file
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.frontend.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="741" height="625"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<opaque value="true"/>
|
||||
<preferredSize width="800" height="600"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="ed019" class="javax.swing.JTextField" binding="nameTextField">
|
||||
<constraints>
|
||||
<grid row="2" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value=""/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="4e05c">
|
||||
<constraints>
|
||||
<grid row="9" column="2" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="1d2f1" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="2" 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="Name:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="70d8c" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="1" 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="Welcome to ISY games selector!"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="5cc82" class="javax.swing.JTextField" binding="name2TextField">
|
||||
<constraints>
|
||||
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value=""/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="27f2a">
|
||||
<constraints>
|
||||
<grid row="3" 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>
|
||||
<component id="3874e" class="javax.swing.JLabel" binding="fillAllFields">
|
||||
<constraints>
|
||||
<grid row="8" 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>
|
||||
<enabled value="true"/>
|
||||
<foreground color="-65536"/>
|
||||
<text value="Please fill all fields"/>
|
||||
<visible value="false"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a2f0" class="javax.swing.JButton" binding="connectButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="7" 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="Connect"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="6a4c6" class="javax.swing.JTextField" binding="ipTextField">
|
||||
<constraints>
|
||||
<grid row="4" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="4214e" class="javax.swing.JComboBox" binding="gameSelectorBox">
|
||||
<constraints>
|
||||
<grid row="6" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<model/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="4225c" class="javax.swing.JTextField" binding="portTextField">
|
||||
<constraints>
|
||||
<grid row="5" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="43a89" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="6" 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="Select game:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="3abc3" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="5" 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="Port:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="514db" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="4" 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="IP:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="5723c" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="3" 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="Name 2:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="2e34c">
|
||||
<constraints>
|
||||
<grid row="3" 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>
|
||||
<vspacer id="93fc6">
|
||||
<constraints>
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
124
src/main/java/org/toop/frontend/UI/RemoteGameSelector.java
Normal file
124
src/main/java/org/toop/frontend/UI/RemoteGameSelector.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package org.toop.frontend.UI;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
|
||||
public class RemoteGameSelector {
|
||||
private static final Logger logger = LogManager.getLogger(RemoteGameSelector.class);
|
||||
|
||||
private JPanel mainMenu;
|
||||
private JTextField nameTextField;
|
||||
private JTextField name2TextField;
|
||||
private JTextField ipTextField;
|
||||
private JTextField portTextField;
|
||||
private JButton connectButton;
|
||||
private JComboBox gameSelectorBox;
|
||||
private JPanel cards;
|
||||
private JPanel gameSelector;
|
||||
private JFrame frame;
|
||||
private JLabel fillAllFields;
|
||||
|
||||
public RemoteGameSelector() {
|
||||
gameSelectorBox.addItem("Tic Tac Toe");
|
||||
gameSelectorBox.addItem("Reversi");
|
||||
//todo get supported games from server and add to gameSelectorBox
|
||||
frame = new JFrame("Game Selector");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(1920, 1080);
|
||||
frame.setResizable(true);
|
||||
|
||||
init();
|
||||
frame.add(mainMenu);
|
||||
frame.setVisible(true);
|
||||
//GlobalEventBus.subscribeAndRegister() Todo add game panel to frame when connection succeeds
|
||||
|
||||
}
|
||||
private void init() {
|
||||
connectButton.addActionListener((ActionEvent e) -> {
|
||||
if( !nameTextField.getText().isEmpty() &&
|
||||
!name2TextField.getText().isEmpty() &&
|
||||
!ipTextField.getText().isEmpty() &&
|
||||
!portTextField.getText().isEmpty()) {
|
||||
|
||||
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
||||
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest(
|
||||
portTextField.getText(),
|
||||
Objects.requireNonNull(gameSelectorBox.getSelectedItem()).toString().toLowerCase().replace(" ", ""),
|
||||
serverIdFuture
|
||||
));
|
||||
String serverId;
|
||||
try {
|
||||
serverId = serverIdFuture.get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} // TODO: Better error handling to not crash the system.
|
||||
|
||||
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
||||
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest(
|
||||
ipTextField.getText(),
|
||||
portTextField.getText(),
|
||||
connectionIdFuture
|
||||
));
|
||||
String connectionId;
|
||||
try {
|
||||
connectionId = connectionIdFuture.get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} // TODO: Better error handling to not crash the system.
|
||||
|
||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ReceivedMessage.class,
|
||||
event -> {
|
||||
if (event.message().equalsIgnoreCase("ok")) {
|
||||
logger.info("received ok from server.");
|
||||
} else if (event.message().toLowerCase().startsWith("gameid")) {
|
||||
String gameId = event.message().toLowerCase().replace("gameid ", "");
|
||||
GlobalEventBus.post(new Events.ServerEvents.SendCommand("start_game " + gameId));
|
||||
}
|
||||
else {
|
||||
logger.info("{}", event.message());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
GlobalEventBus.post(new Events.ServerEvents.SendCommand(connectionId, "create_game", nameTextField.getText(), name2TextField.getText()));
|
||||
|
||||
// CompletableFuture<String> ticTacToeGame = new CompletableFuture<>();
|
||||
// GlobalEventBus.post(new Events.ServerEvents.CreateTicTacToeGameRequest( // TODO: Make this happen through commands send through the connection, instead of an event.
|
||||
// serverId,
|
||||
// nameTextField.getText(),
|
||||
// name2TextField.getText(),
|
||||
// ticTacToeGame
|
||||
// ));
|
||||
// String ticTacToeGameId;
|
||||
// try {
|
||||
// ticTacToeGameId = ticTacToeGame.get();
|
||||
// } catch (InterruptedException | ExecutionException ex) {
|
||||
// throw new RuntimeException(ex);
|
||||
// } // TODO: Better error handling to not crash the system.
|
||||
|
||||
|
||||
frame.remove(mainMenu);
|
||||
// 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 {
|
||||
fillAllFields.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
public void returnToMainMenu() {
|
||||
frame.removeAll();
|
||||
frame.add(mainMenu);
|
||||
frame.revalidate();
|
||||
frame.repaint();
|
||||
}
|
||||
}
|
||||
12
src/main/java/org/toop/frontend/UI/Services.form
Normal file
12
src/main/java/org/toop/frontend/UI/Services.form
Normal 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.frontend.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>
|
||||
6
src/main/java/org/toop/frontend/UI/Services.java
Normal file
6
src/main/java/org/toop/frontend/UI/Services.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package org.toop.frontend.UI;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class Services {
|
||||
}
|
||||
23
src/main/java/org/toop/frontend/UI/UIGameBoard.form
Normal file
23
src/main/java/org/toop/frontend/UI/UIGameBoard.form
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.toop.frontend.UI.UIGameBoard">
|
||||
<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"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<preferredSize width="800" height="600"/>
|
||||
</properties>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<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"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Back to main menu"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
68
src/main/java/org/toop/frontend/UI/UIGameBoard.java
Normal file
68
src/main/java/org/toop/frontend/UI/UIGameBoard.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package org.toop.frontend.UI;
|
||||
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
import org.toop.frontend.games.LocalTicTacToe;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Objects;
|
||||
|
||||
public class UIGameBoard {
|
||||
private static final int TICTACTOE_SIZE = 3;
|
||||
|
||||
private JPanel tttPanel; // Root panel for this game
|
||||
private JButton backToMainMenuButton;
|
||||
private JButton[] cells;
|
||||
private String currentPlayer = "X";
|
||||
|
||||
private LocalGameSelector parentSelector;
|
||||
private LocalTicTacToe localTicTacToe;
|
||||
|
||||
public UIGameBoard(LocalTicTacToe lttt, LocalGameSelector parent) {
|
||||
this.parentSelector = parent;
|
||||
this.localTicTacToe = lttt;
|
||||
|
||||
// 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"; }
|
||||
this.localTicTacToe.move(index);
|
||||
System.out.println("Cell clicked: " + index);
|
||||
});
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
public JPanel getTTTPanel() {
|
||||
return tttPanel;
|
||||
}
|
||||
}
|
||||
170
src/main/java/org/toop/frontend/games/LocalTicTacToe.java
Normal file
170
src/main/java/org/toop/frontend/games/LocalTicTacToe.java
Normal file
@@ -0,0 +1,170 @@
|
||||
package org.toop.frontend.games;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.eventbus.Events;
|
||||
import org.toop.eventbus.GlobalEventBus;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class LocalTicTacToe { // TODO: Implement runnable
|
||||
private static final Logger logger = LogManager.getLogger(LocalTicTacToe.class);
|
||||
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final BlockingQueue<String> receivedQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
Object receivedMessageListener;
|
||||
|
||||
volatile String gameId;
|
||||
volatile String connectionId;
|
||||
volatile String serverId;
|
||||
|
||||
/**
|
||||
* Is either 1 or 2.
|
||||
*/
|
||||
private int playersTurn = 1;
|
||||
|
||||
// LocalTicTacToe(String gameId, String connectionId, String serverId) {
|
||||
// this.gameId = gameId;
|
||||
// this.connectionId = connectionId;
|
||||
// this.serverId = serverId;
|
||||
// this.receivedMessageListener = GlobalEventBus.subscribe(Events.ServerEvents.ReceivedMessage.class, this::receiveMessageAction);
|
||||
// GlobalEventBus.register(this.receivedMessageListener);
|
||||
//
|
||||
//
|
||||
// this.executor.submit(this::gameThread);
|
||||
// } TODO: If remote server
|
||||
|
||||
public LocalTicTacToe(boolean isLocalServer, String ip, String port) {
|
||||
this.receivedMessageListener = GlobalEventBus.subscribe(Events.ServerEvents.ReceivedMessage.class, this::receiveMessageAction);
|
||||
GlobalEventBus.register(this.receivedMessageListener);
|
||||
|
||||
// TODO: Is blocking
|
||||
if (isLocalServer) { this.serverId = this.createServer(port); }
|
||||
else { this.serverId = null; } // TODO: What if null?
|
||||
this.connectionId = this.createConnection(ip, port);
|
||||
this.createGame(ip, port);
|
||||
|
||||
this.executor.submit(this::gameThread);
|
||||
}
|
||||
|
||||
private String createServer(String port) {
|
||||
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
||||
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest(port, "tictactoe", serverIdFuture));
|
||||
try {
|
||||
return serverIdFuture.get();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error getting server ID", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String createConnection(String ip, String port) {
|
||||
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
||||
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest(ip, port, 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 createGame(String nameA, String nameB) {
|
||||
nameA = nameA.trim().replace(" ", "-");
|
||||
nameB = nameB.trim().replace(" ", "-");
|
||||
this.sendCommand("create_game", nameA, nameB);
|
||||
}
|
||||
|
||||
private void startGame() {
|
||||
if (this.gameId == null) { return; }
|
||||
this.sendCommand("start_game", this.gameId);
|
||||
}
|
||||
|
||||
void gameThread() {
|
||||
logger.info("Starting local game thread, connection: {}, server: {}", this.connectionId, this.serverId);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1); // TODO: This is bad, fix later
|
||||
|
||||
new Thread(() -> {
|
||||
while(true) {
|
||||
String msg = this.receivedQueue.poll();
|
||||
if (msg == null) {continue;}
|
||||
if (msg.toLowerCase().startsWith("game created successfully")) {
|
||||
String[] parts = msg.split("\\|");
|
||||
String gameIdPart = parts[1];
|
||||
this.gameId = gameIdPart.split(" ")[1];
|
||||
latch.countDown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
try {
|
||||
latch.await(); // TODO: Bad, fix later
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
startGame(); // TODO: Actually need to wait, but is fine for now.
|
||||
boolean running = true;
|
||||
while (running) {
|
||||
try {
|
||||
String rec = this.receivedQueue.take();
|
||||
if (rec.equalsIgnoreCase("ok")) {continue;}
|
||||
else if (rec.equalsIgnoreCase("svr game yourturn")) {
|
||||
if (this.playersTurn == 1) {
|
||||
this.playersTurn += 1;
|
||||
} else {
|
||||
this.playersTurn -= 1;
|
||||
}
|
||||
logger.info("Player turn: {}", this.playersTurn);
|
||||
}
|
||||
else if (rec.equalsIgnoreCase("svr game win")) {
|
||||
endListeners();
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e); // TODO: Error handling
|
||||
}
|
||||
}
|
||||
|
||||
this.endListeners();
|
||||
|
||||
}
|
||||
|
||||
public void endGame() {
|
||||
sendCommand("gameid", "end_game"); // TODO: Command is a bit wrong.
|
||||
}
|
||||
|
||||
public void move(int index) {
|
||||
sendCommand("gameid", this.gameId, "player", "test", "move", String.valueOf(index));
|
||||
}
|
||||
|
||||
private void endTheGame() {
|
||||
this.sendCommand("end_game", this.gameId);
|
||||
this.endListeners();
|
||||
}
|
||||
|
||||
void receiveMessageAction(Events.ServerEvents.ReceivedMessage receivedMessage) {
|
||||
if (!receivedMessage.ConnectionUuid().equals(this.connectionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info("Received message from " + this.connectionId + ": " + receivedMessage.message());
|
||||
this.receivedQueue.put(receivedMessage.message());
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Error waiting for received Message", e);
|
||||
}
|
||||
}
|
||||
|
||||
void sendCommand(String... args) {
|
||||
GlobalEventBus.post(new Events.ServerEvents.SendCommand(this.connectionId, args));
|
||||
}
|
||||
|
||||
void endListeners() {
|
||||
GlobalEventBus.unregister(this.receivedMessageListener);
|
||||
}
|
||||
|
||||
}
|
||||
50
src/main/java/org/toop/frontend/graphics/Renderer.java
Normal file
50
src/main/java/org/toop/frontend/graphics/Renderer.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package org.toop.frontend.graphics;
|
||||
|
||||
import org.toop.frontend.platform.graphics.opengl.OpenglRenderer;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
public abstract class Renderer {
|
||||
public enum API {
|
||||
NONE,
|
||||
OPENGL,
|
||||
};
|
||||
|
||||
protected static final Logger logger = LogManager.getLogger(Renderer.class);
|
||||
|
||||
private static API api = API.NONE;
|
||||
private static Renderer instance = null;
|
||||
|
||||
public static Renderer setup(API api) {
|
||||
if (instance != null) {
|
||||
logger.warn("Renderer is already setup.");
|
||||
return instance;
|
||||
}
|
||||
|
||||
switch (api) {
|
||||
case OPENGL:
|
||||
instance = new OpenglRenderer();
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.fatal("No valid renderer api chosen");
|
||||
return null;
|
||||
}
|
||||
|
||||
Renderer.api = api;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static API getApi() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
instance = null;
|
||||
logger.info("Renderer cleanup.");
|
||||
}
|
||||
|
||||
public abstract void clear();
|
||||
public abstract void render();
|
||||
}
|
||||
26
src/main/java/org/toop/frontend/graphics/Shader.java
Normal file
26
src/main/java/org/toop/frontend/graphics/Shader.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.toop.frontend.graphics;
|
||||
|
||||
import org.toop.frontend.platform.graphics.opengl.OpenglShader;
|
||||
|
||||
public abstract class Shader {
|
||||
public static Shader create(String vertexPath, String fragmentPath) {
|
||||
Shader shader = null;
|
||||
|
||||
switch (Renderer.getApi()) {
|
||||
case OPENGL:
|
||||
shader = new OpenglShader(vertexPath, fragmentPath);
|
||||
break;
|
||||
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
public abstract void cleanup();
|
||||
|
||||
public abstract void start();
|
||||
public abstract void stop();
|
||||
}
|
||||
26
src/main/java/org/toop/frontend/graphics/node/Button.java
Normal file
26
src/main/java/org/toop/frontend/graphics/node/Button.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.toop.frontend.graphics.node;
|
||||
|
||||
import org.toop.core.*;
|
||||
import org.toop.frontend.math.Color;
|
||||
|
||||
public class Button extends Node {
|
||||
ICallable<Boolean> onHover;
|
||||
ICallable<Boolean> onClick;
|
||||
|
||||
public Button(int x, int y, int width, int height, Color color, ICallable<Boolean> onHover, ICallable<Boolean> onClick) {
|
||||
super(x, y, width, height, color);
|
||||
|
||||
this.onHover = onHover;
|
||||
this.onClick = onClick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover() {
|
||||
onHover.call();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click() {
|
||||
onClick.call();
|
||||
}
|
||||
}
|
||||
21
src/main/java/org/toop/frontend/graphics/node/Node.java
Normal file
21
src/main/java/org/toop/frontend/graphics/node/Node.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.toop.frontend.graphics.node;
|
||||
|
||||
import org.toop.frontend.math.Bounds;
|
||||
import org.toop.frontend.math.Color;
|
||||
|
||||
public abstract class Node {
|
||||
protected Bounds bounds;
|
||||
protected Color color;
|
||||
|
||||
public Node(int x, int y, int width, int height, Color color) {
|
||||
bounds = new Bounds(x, y, width, height);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public boolean check(int x, int y) {
|
||||
return bounds.check(x, y);
|
||||
}
|
||||
|
||||
public void hover() {}
|
||||
public void click() {}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.toop.frontend.graphics.node;
|
||||
|
||||
import org.toop.eventbus.*;
|
||||
import org.toop.frontend.graphics.Shader;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
public class NodeManager {
|
||||
private static final Logger logger = LogManager.getLogger(NodeManager.class);
|
||||
|
||||
private static NodeManager instance = null;
|
||||
|
||||
public static NodeManager setup() {
|
||||
if (instance != null) {
|
||||
logger.warn("NodeManager is already setup.");
|
||||
return instance;
|
||||
}
|
||||
|
||||
instance = new NodeManager();
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Shader shader;
|
||||
private ArrayList<Node> nodes;
|
||||
private Node active;
|
||||
|
||||
private NodeManager() {
|
||||
shader = Shader.create(
|
||||
"src/main/resources/shaders/gui_vertex.glsl",
|
||||
"src/main/resources/shaders/gui_fragment.glsl");
|
||||
|
||||
nodes = new ArrayList<Node>();
|
||||
|
||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node node = nodes.get(i);
|
||||
|
||||
if (node.check(event.x(), event.y())) {
|
||||
active = node;
|
||||
node.hover();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseClick.class, event -> {
|
||||
if (active != null) {
|
||||
active.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
}
|
||||
|
||||
public void add(Node node) {
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
public void render() {
|
||||
}
|
||||
}
|
||||
49
src/main/java/org/toop/frontend/graphics/node/Widget.java
Normal file
49
src/main/java/org/toop/frontend/graphics/node/Widget.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package org.toop.frontend.graphics.node;
|
||||
|
||||
import org.toop.frontend.math.Bounds;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Widget {
|
||||
Bounds bounds;
|
||||
private ArrayList<Node> nodes;
|
||||
|
||||
public Widget(Bounds bounds) {
|
||||
this.bounds = bounds;
|
||||
nodes = new ArrayList<Node>();
|
||||
}
|
||||
|
||||
public boolean check(int x, int y) {
|
||||
return bounds.check(x, y);
|
||||
}
|
||||
|
||||
public void add(Node node) {
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
public boolean hover(int x, int y) {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node node = nodes.get(i);
|
||||
|
||||
if (node.check(x, y)) {
|
||||
node.hover();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean click(int x, int y) {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node node = nodes.get(i);
|
||||
|
||||
if (node.check(x, y)) {
|
||||
node.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
30
src/main/java/org/toop/frontend/math/Bounds.java
Normal file
30
src/main/java/org/toop/frontend/math/Bounds.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package org.toop.frontend.math;
|
||||
|
||||
public class Bounds {
|
||||
private int x;
|
||||
private int y;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public Bounds(int x, int y, int width, int height) {
|
||||
set(x, y, width, height);
|
||||
}
|
||||
|
||||
public void set(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getX() { return x; }
|
||||
public int getY() { return y; }
|
||||
public int getWidth() { return width; }
|
||||
public int getHeight() { return height; }
|
||||
|
||||
public boolean check(int x, int y) {
|
||||
return
|
||||
x >= this.x && x <= this.x + this.width &&
|
||||
y >= this.y && y <= this.y + this.height;
|
||||
}
|
||||
}
|
||||
17
src/main/java/org/toop/frontend/math/Color.java
Normal file
17
src/main/java/org/toop/frontend/math/Color.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package org.toop.frontend.math;
|
||||
|
||||
public class Color {
|
||||
private float r;
|
||||
private float g;
|
||||
private float b;
|
||||
|
||||
public Color(float r, float g, float b) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public float r() { return r; }
|
||||
public float g() { return g; }
|
||||
public float b() { return b; }
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.toop.frontend.platform.core.glfw;
|
||||
|
||||
import org.toop.core.*;
|
||||
import org.toop.eventbus.*;
|
||||
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.system.*;
|
||||
|
||||
public class GlfwWindow extends Window {
|
||||
private long window;
|
||||
|
||||
public GlfwWindow(String title, Size size) {
|
||||
if (!GLFW.glfwInit()) {
|
||||
logger.fatal("Failed to initialize glfw");
|
||||
return;
|
||||
}
|
||||
|
||||
GLFW.glfwDefaultWindowHints();
|
||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
|
||||
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
|
||||
|
||||
GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
|
||||
|
||||
int width = size.width();
|
||||
int height = size.height();
|
||||
|
||||
if (width <= 0 || height <= 0 || width > videoMode.width() || height > videoMode.height()) {
|
||||
width = videoMode.width();
|
||||
height = videoMode.height();
|
||||
|
||||
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE);
|
||||
}
|
||||
|
||||
long window = GLFW.glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL);
|
||||
|
||||
if (window == MemoryUtil.NULL) {
|
||||
GLFW.glfwTerminate();
|
||||
|
||||
logger.fatal("Failed to create glfw window");
|
||||
return;
|
||||
}
|
||||
|
||||
int[] widthBuffer = new int[1];
|
||||
int[] heightBuffer = new int[1];
|
||||
GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
|
||||
|
||||
GLFW.glfwMakeContextCurrent(window);
|
||||
GLFW.glfwSwapInterval(1);
|
||||
|
||||
GLFW.glfwSetWindowCloseCallback(window, (lwindow) -> {
|
||||
GlobalEventBus.post(new Events.WindowEvents.OnQuitRequested());
|
||||
});
|
||||
|
||||
GLFW.glfwSetFramebufferSizeCallback(window, (lwindow, lwidth, lheight) -> {
|
||||
GlobalEventBus.post(new Events.WindowEvents.OnResize(new Size(lwidth, lheight)));
|
||||
});
|
||||
|
||||
GLFW.glfwSetCursorPosCallback(window, (lwindow, lx, ly) -> {
|
||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseMove((int)lx, (int)ly));
|
||||
});
|
||||
|
||||
GLFW.glfwSetMouseButtonCallback(window, (lwindow, lbutton, laction, lmods) -> {
|
||||
switch (laction) {
|
||||
case GLFW.GLFW_PRESS:
|
||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseClick(lbutton));
|
||||
break;
|
||||
|
||||
case GLFW.GLFW_RELEASE:
|
||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseRelease(lbutton));
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
});
|
||||
|
||||
this.window = window;
|
||||
GLFW.glfwShowWindow(window);
|
||||
|
||||
logger.info("Glfw window setup. Title: {}. Width: {}. Height: {}.", title, size.width(), size.height());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
GLFW.glfwDestroyWindow(window);
|
||||
GLFW.glfwTerminate();
|
||||
|
||||
super.cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
GLFW.glfwSwapBuffers(window);
|
||||
GLFW.glfwPollEvents();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.toop.frontend.platform.graphics.opengl;
|
||||
|
||||
import org.toop.eventbus.*;
|
||||
import org.toop.frontend.graphics.Renderer;
|
||||
import org.toop.frontend.graphics.Shader;
|
||||
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.system.*;
|
||||
|
||||
public class OpenglRenderer extends Renderer {
|
||||
private Shader shader;
|
||||
private int vao;
|
||||
|
||||
public OpenglRenderer() {
|
||||
GL.createCapabilities();
|
||||
GL45.glClearColor(0.65f, 0.9f, 0.65f, 1f);
|
||||
|
||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnResize.class, event -> {
|
||||
GL45.glViewport(0, 0, event.size().width(), event.size().height());
|
||||
});
|
||||
|
||||
logger.info("Opengl renderer setup.");
|
||||
|
||||
// Form here on, everything is temporary
|
||||
float vertices[] = {
|
||||
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
|
||||
};
|
||||
|
||||
int indicies[] = {
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
};
|
||||
|
||||
vao = GL45.glCreateVertexArrays();
|
||||
GL45.glBindVertexArray(vao);
|
||||
|
||||
int vbo = GL45.glCreateBuffers();
|
||||
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo);
|
||||
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, vertices, GL45.GL_STATIC_DRAW);
|
||||
|
||||
GL45.glVertexAttribPointer(0, 2, GL45.GL_FLOAT, false, 5 * 4, 0);
|
||||
GL45.glVertexAttribPointer(1, 3, GL45.GL_FLOAT, false, 5 * 4, 2 * 4);
|
||||
|
||||
GL45.glEnableVertexAttribArray(0);
|
||||
GL45.glEnableVertexAttribArray(1);
|
||||
|
||||
int ib = GL45.glCreateBuffers();
|
||||
GL45.glBindBuffer(GL45.GL_ELEMENT_ARRAY_BUFFER, ib);
|
||||
GL45.glBufferData(GL45.GL_ELEMENT_ARRAY_BUFFER, indicies, GL45.GL_STATIC_DRAW);
|
||||
|
||||
shader = Shader.create(
|
||||
"src/main/resources/shaders/gui_vertex.glsl",
|
||||
"src/main/resources/shaders/gui_fragment.glsl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
super.cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
GL45.glClear(GL45.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
// temporary
|
||||
// shader.start();
|
||||
GL45.glBindVertexArray(vao);
|
||||
GL45.glDrawElements(GL45.GL_TRIANGLES, 6, GL45.GL_UNSIGNED_INT, MemoryUtil.NULL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.toop.frontend.platform.graphics.opengl;
|
||||
|
||||
import org.toop.core.*;
|
||||
import org.toop.frontend.graphics.Shader;
|
||||
|
||||
import org.lwjgl.opengl.*;
|
||||
|
||||
public class OpenglShader extends Shader {
|
||||
private int programID;
|
||||
|
||||
public OpenglShader(String vertexPath, String fragmentPath) {
|
||||
FileSystem.File vertexSource = FileSystem.read(vertexPath);
|
||||
FileSystem.File fragmentSource = FileSystem.read(fragmentPath);
|
||||
|
||||
if (vertexSource == null || fragmentPath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
programID = GL45.glCreateProgram();
|
||||
|
||||
int vertexShader = GL45.glCreateShader(GL45.GL_VERTEX_SHADER);
|
||||
int fragmentShader = GL45.glCreateShader(GL45.GL_FRAGMENT_SHADER);
|
||||
|
||||
GL45.glShaderSource(vertexShader, vertexSource.buffer());
|
||||
GL45.glShaderSource(fragmentShader, fragmentSource.buffer());
|
||||
|
||||
GL45.glCompileShader(vertexShader);
|
||||
GL45.glCompileShader(fragmentShader);
|
||||
|
||||
GL45.glAttachShader(programID, vertexShader);
|
||||
GL45.glAttachShader(programID, fragmentShader);
|
||||
|
||||
GL45.glLinkProgram(programID);
|
||||
GL45.glValidateProgram(programID);
|
||||
|
||||
GL45.glDetachShader(programID, vertexShader);
|
||||
GL45.glDetachShader(programID, fragmentShader);
|
||||
|
||||
GL45.glDeleteShader(vertexShader);
|
||||
GL45.glDeleteShader(fragmentShader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
stop();
|
||||
GL45.glDeleteProgram(programID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
GL45.glUseProgram(programID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
GL45.glUseProgram(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user