Refactoring, removed unnecessary files and classes.

This commit is contained in:
lieght
2025-09-24 14:23:36 +02:00
parent f712fca14e
commit 35620e983a
28 changed files with 0 additions and 2144 deletions

36
pom.xml
View File

@@ -15,28 +15,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main-class>org.toop.Main</main-class> <main-class>org.toop.Main</main-class>
<lwjgl.version>3.3.6</lwjgl.version>
</properties> </properties>
<profiles>
<profile><id>lwjgl-natives-windows-amd64</id><activation><os><family>windows</family><arch>amd64</arch></os></activation><properties><lwjgl.natives>natives-windows</lwjgl.natives></properties></profile>
<profile><id>lwjgl-natives-linux-amd64</id><activation><os><family>unix</family><name>linux</name><arch>amd64</arch></os></activation><properties><lwjgl.natives>natives-linux</lwjgl.natives></properties></profile>
<profile><id>lwjgl-natives-macos-aarch64</id><activation><os><family>mac</family><arch>aarch64</arch></os></activation><properties><lwjgl.natives>natives-macos-arm64</lwjgl.natives></properties></profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-bom</artifactId>
<version>${lwjgl.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.junit</groupId> <groupId>org.junit</groupId>
@@ -117,15 +97,6 @@
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.2.6.Final</version> <version>4.2.6.Final</version>
</dependency> </dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl</artifactId></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-glfw</artifactId></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-opengl</artifactId></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-stb</artifactId></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-glfw</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-opengl</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-stb</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
@@ -198,13 +169,6 @@
<reflowLongStrings>true</reflowLongStrings> <reflowLongStrings>true</reflowLongStrings>
<formatJavadoc>true</formatJavadoc> <formatJavadoc>true</formatJavadoc>
</googleJavaFormat> </googleJavaFormat>
<!-- make sure every file has the following copyright header.
optionally, Spotless can set copyright years by digging
through git history (see "license" section below) -->
<!-- <licenseHeader>-->
<!-- <content>/* (C)$YEAR */</content> &lt;!&ndash; or <file>${project.basedir}/license-header</file> &ndash;&gt;-->
<!-- </licenseHeader>-->
</java> </java>
</configuration> </configuration>
</plugin> </plugin>

View File

@@ -1,214 +0,0 @@
//package org.toop;
//
//import java.util.*;
//import java.util.concurrent.CompletableFuture;
//import java.util.concurrent.ExecutionException;
//import org.apache.logging.log4j.LogManager;
//import org.apache.logging.log4j.Logger;
//import org.toop.eventbus.events.Events;
//import org.toop.eventbus.GlobalEventBus;
//import org.toop.game.tictactoe.*;
//import org.toop.game.tictactoe.ai.MinMaxTicTacToe;
//
//public class ConsoleGui {
//
// private static final Logger logger = LogManager.getLogger(ConsoleGui.class);
//
// private Scanner scanner;
//
// private TicTacToe game;
// private MinMaxTicTacToe ai;
//
// private String ai1 = null;
// private String ai2 = null;
//
// private String serverId = null;
// private String connectionId = null;
// private String ticTacToeGameId = null;
//
// public ConsoleGui() throws ExecutionException, InterruptedException {
// scanner = new Scanner(System.in);
// Random random = new Random(3453498);
//
// int mode = -1;
//
// System.out.print(
// """
// 1. player vs player
// 2. player vs ai
// 3. ai vs player
// 4. ai v ai
// Choose mode (default is 1):\s\
// """);
// String modeString = scanner.nextLine();
//
// try {
// mode = Integer.parseInt(modeString);
// } catch (Exception e) {
// logger.error(e.getMessage());
// }
//
// String player1 = null;
// String player2 = null;
//
// switch (mode) {
// // player vs ai
// case 2:
// {
// System.out.print("Please enter your name: ");
// String name = scanner.nextLine();
//
// player1 = name;
// ai2 = player2 = "AI#" + random.nextInt();
//
// break;
// }
//
// // ai vs player
// case 3:
// {
// System.out.print("Enter your name: ");
// String name = scanner.nextLine();
//
// ai1 = player1 = "AI#" + random.nextInt();
// player2 = name;
//
// break;
// }
//
// // ai vs ai
// case 4:
// {
// ai1 = player1 = "AI#" + random.nextInt();
// ai2 = player2 = "AI2" + random.nextInt();
//
// break;
// }
//
// // player vs player
// case 1:
// default:
// {
// System.out.print("Player 1. Please enter your name: ");
// String name1 = scanner.nextLine();
//
// System.out.print("Player 2. Please enter your name: ");
// String name2 = scanner.nextLine();
//
// player1 = name1;
// player2 = name2;
// }
// }
//
// game = new TicTacToe(player1, player2);
// ai = new MinMaxTicTacToe();
//
// CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
// GlobalEventBus.post(
// new Events.ServerEvents.StartServerRequest("5001", "tictactoe", serverIdFuture));
// serverId = serverIdFuture.get();
//
// CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
// GlobalEventBus.post(
// new Events.ServerEvents.StartConnectionRequest(
// "127.0.0.1", "5001", connectionIdFuture));
// connectionId = connectionIdFuture.get();
//
// CompletableFuture<String> ticTacToeGame = new CompletableFuture<>();
// GlobalEventBus.post(
// new Events.ServerEvents.CreateTicTacToeGameRequest(
// serverId, player1, player2, ticTacToeGame));
// ticTacToeGameId = ticTacToeGame.get();
// GlobalEventBus.post(new Events.ServerEvents.RunTicTacToeGame(serverId, ticTacToeGameId));
// }
//
// public void print() {
// char[] seperator = new char[game.getSize() * 4 - 1];
// Arrays.fill(seperator, '-');
//
// for (int i = 0; i < game.getSize(); i++) {
// String buffer = " ";
//
// for (int j = 0; j < game.getSize() - 1; j++) {
// buffer += game.getGrid()[i * game.getSize() + j] + " | ";
// }
//
// buffer += game.getGrid()[i * game.getSize() + game.getSize() - 1];
// System.out.println(buffer);
//
// if (i < game.getSize() - 1) {
// System.out.println(seperator);
// }
// }
// }
//
// public boolean next() {
// Player current = game.getCurrentPlayer();
// int move = -1;
//
// if (ai1 != null && current.getName() == ai1 || ai2 != null && current.getName() == ai2) {
// move = ai.findBestMove(game);
// } else {
// System.out.printf(
// "%s's (%c) turn. Please choose an empty cell between 0-8: ",
// current.getName(), current.getSymbol());
// String input = scanner.nextLine();
//
// try {
// move = Integer.parseInt(input);
// } catch (NumberFormatException e) {
// }
// }
//
// GameBase.State state = game.play(move);
// boolean keepRunning = true;
//
// switch (state) {
// case INVALID:
// {
// System.out.println("Please select an empty cell. Between 0-8");
// return true;
// }
//
// case DRAW:
// {
// System.out.println("Game ended in a draw.");
// keepRunning = false;
// break;
// }
//
// case WIN:
// {
// System.out.printf("%s has won the game.\n", current.getName());
// keepRunning = false;
// break;
// }
//
// case NORMAL:
// default:
// {
// keepRunning = true;
// break;
// }
// }
//
// GlobalEventBus.post(
// new Events.ServerEvents.SendCommand(
// connectionId,
// "gameid " + ticTacToeGameId,
// "player " + current.getName(),
// "MOVE",
// String.valueOf(move)));
//
// if (!keepRunning) {
// GlobalEventBus.post(
// new Events.ServerEvents.EndTicTacToeGame(serverId, ticTacToeGameId));
// }
//
// return keepRunning;
// }
//
// public GameBase getGame() {
// return game;
// }
//}

View File

@@ -7,7 +7,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.toop.backend.tictactoe.ServerManager; import org.toop.backend.tictactoe.ServerManager;
import org.toop.eventbus.EventPublisher; import org.toop.eventbus.EventPublisher;
import org.toop.eventbus.EventRegistry;
import org.toop.eventbus.events.Events; import org.toop.eventbus.events.Events;
import org.toop.eventbus.GlobalEventBus; import org.toop.eventbus.GlobalEventBus;
import org.toop.frontend.UI.LocalServerSelector; import org.toop.frontend.UI.LocalServerSelector;

View File

@@ -1,25 +0,0 @@
package org.toop;
import org.toop.eventbus.EventFlow;
import org.toop.eventbus.GlobalEventBus;
import org.toop.eventbus.events.NetworkEvents;
import org.toop.frontend.networking.NetworkingGameClientHandler;
import java.util.function.Supplier;
public class MainTest {
MainTest() {
var a = new EventFlow()
.addPostEvent(NetworkEvents.StartClient.class,
(Supplier<NetworkingGameClientHandler>) NetworkingGameClientHandler::new,
"127.0.0.1",
5001)
.onResponse(NetworkEvents.StartClientSuccess.class)
.perform(this::handleStartClientSuccess)
.unsubscribeAfterSuccess().asyncPostEvent();
}
private void handleStartClientSuccess(NetworkEvents.StartClientSuccess event) {
GlobalEventBus.post(new NetworkEvents.CloseClient(event.clientId()));
}
}

View File

@@ -1,216 +0,0 @@
package org.toop.backend.tictactoe;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ParsedCommand {
private static final Logger logger = LogManager.getLogger(ParsedCommand.class);
public TicTacToeServerCommand command;
public ArrayList<Object> arguments;
public boolean isValidCommand;
public boolean isServerCommand;
public TicTacToeServerMessage returnMessage;
public String errorMessage;
public String originalCommand;
public String gameId;
public String player;
public ParsedCommand(String receivedCommand) {
if (receivedCommand.isEmpty()) {
logger.info("Received empty command");
this.gameId = null;
this.player = null;
this.command = null;
this.arguments = null;
this.isValidCommand = false;
this.isServerCommand = true;
this.returnMessage = TicTacToeServerMessage.ERR;
this.errorMessage = "The received command is empty";
this.originalCommand = receivedCommand;
return;
}
// Case-insensitive regex to match: game_id {id} player {name}
Pattern pattern =
Pattern.compile(
"(?i)\\bgame[_]?id\\s+(\\S+)\\s+player\\s+(\\S+)",
Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(receivedCommand);
String tempGameId = null;
String tempPlayer = null;
String tempPayload = receivedCommand;
if (matcher.find()) {
tempGameId = matcher.group(1); // first capture group → game_id
tempPlayer = matcher.group(2); // second capture group → player
// Remove the matched part from the original command
tempPayload = matcher.replaceFirst("").trim();
}
this.gameId = tempGameId;
this.player = tempPlayer;
receivedCommand = tempPayload;
logger.info("Received gameId: {}", gameId);
logger.info("Received player: {}", player);
logger.info("Received command: {}", receivedCommand);
String[] segments = receivedCommand.split(" ");
if (segments[0].isEmpty()) {
this.command = null;
this.arguments = null;
this.isValidCommand = false;
this.isServerCommand = true;
this.returnMessage = TicTacToeServerMessage.ERR;
this.errorMessage = "The received command is empty or couldn't be split";
this.originalCommand = receivedCommand;
return;
}
TicTacToeServerCommand commandEnum = TicTacToeServerCommand.getCommand(segments[0]);
switch (commandEnum) {
case CREATE_GAME -> {
if (segments.length == 3 && !segments[1].isEmpty() && !segments[2].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(2);
this.arguments.add(segments[1]);
this.arguments.add(segments[2]);
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
}
}
case END_GAME, START_GAME -> {
if (segments.length == 2 && !segments[1].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(1);
this.arguments.add(segments[1]);
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
}
}
case MOVE -> {
if (segments.length == 2 && !segments[1].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(1);
this.arguments.add(segments[1]);
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = false;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
}
}
case MESSAGE -> {
if (segments.length == 3 && !segments[2].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(2);
this.arguments.add(segments[2]);
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
}
}
case CHALLENGE -> {
if (!segments[1].isEmpty()
&& segments[1].equals("accept")
&& !segments[2].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(2);
this.arguments.add(segments[1]);
this.arguments.add(segments[2]); // TODO: Needs to be a number.
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
} else {
this.command = commandEnum;
this.arguments = null;
this.returnMessage = TicTacToeServerMessage.ERR;
this.isValidCommand = false;
this.isServerCommand = true;
this.errorMessage = "The challenge was not parsable";
this.originalCommand = receivedCommand;
return;
}
}
case LOGIN -> { // TODO: Challenge needs to accept different game types later.
if (!segments[1].isEmpty()) {
this.command = commandEnum;
this.arguments = new ArrayList<>(1);
this.arguments.add(segments[1]);
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
} else {
this.command = commandEnum;
this.arguments = null;
this.returnMessage = TicTacToeServerMessage.ERR;
this.isValidCommand = false;
this.isServerCommand = true;
this.errorMessage = "The received name is empty or couldn't be understood";
this.originalCommand = receivedCommand;
return;
}
}
// case GET -> { // TODO: Get needs to accept different game types later.
// And get the players
//
// }
case BYE, DISCONNECT, LOGOUT, QUIT, EXIT, FORFEIT, SUBSCRIBE -> {
this.command = commandEnum;
this.arguments = null;
this.returnMessage = TicTacToeServerMessage.OK;
this.isValidCommand = true;
this.isServerCommand = true;
this.errorMessage = null;
this.originalCommand = receivedCommand;
return;
}
case null, default -> {
this.command = null;
this.arguments = null;
this.returnMessage = TicTacToeServerMessage.ERR;
this.isValidCommand = false;
this.isServerCommand = true;
this.errorMessage = segments[0] + " is not a supported command";
this.originalCommand = receivedCommand;
return;
}
}
}
@Override
public String toString() {
return this.originalCommand; // TODO: Maybe return more info.
}
//
// public ParsedCommand parseCommand(String command) {
// return null;
// }
}

View File

@@ -1,102 +0,0 @@
package org.toop.backend.tictactoe;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.eventbus.EventPublisher;
import org.toop.eventbus.events.Events;
// TODO more methods.
public class ServerManager {
private static final Logger logger = LogManager.getLogger(ServerManager.class);
/** Map of serverId -> Server instances */
private final Map<String, TcpServer> servers = new ConcurrentHashMap<>();
/** Starts a server manager, to manage, servers. */
public ServerManager() {
new EventPublisher<>(Events.ServerEvents.StartServerRequest.class, this::handleStartServerRequest);
new EventPublisher<>(Events.ServerEvents.StartServer.class, this::handleStartServer);
new EventPublisher<>(Events.ServerEvents.ForceCloseAllServers.class, _ -> shutdownAll());
new EventPublisher<>(Events.ServerEvents.CreateTicTacToeGameRequest.class, this::handleStartTicTacToeGameOnAServer);
new EventPublisher<>(Events.ServerEvents.RunTicTacToeGame.class, this::handleRunTicTacToeGameOnAServer);
new EventPublisher<>(Events.ServerEvents.EndTicTacToeGame.class, this::handleEndTicTacToeGameOnAServer);
}
private String startServer(int port, String gameType) {
String serverId = UUID.randomUUID().toString();
gameType = gameType.toLowerCase();
try {
TcpServer server = null;
if (Objects.equals(gameType, "tictactoe")) {
server = new TicTacToeServer(port);
} else {
logger.error("Manager could not create a server for game type: {}", gameType);
return null;
}
this.servers.put(serverId, server);
new Thread(server, "Server-" + serverId).start();
logger.info("Created server with id: {}, port: {}", serverId, port);
return serverId;
} catch (Exception e) {
logger.error("Failed to start server", e);
return null;
}
}
private void handleStartServerRequest(Events.ServerEvents.StartServerRequest request) {
request.future()
.complete(
this.startServer(
request.port(),
request.gameType())); // TODO: Maybe post StartServer event.
}
private void handleStartServer(Events.ServerEvents.StartServer event) {
new EventPublisher<>(Events.ServerEvents.ServerStarted.class, this.startServer(event.port(), event.gameType()), event.port());
}
private void handleStartTicTacToeGameOnAServer(
Events.ServerEvents.CreateTicTacToeGameRequest event) {
TicTacToeServer serverThing = (TicTacToeServer) this.servers.get(event.serverUuid());
String gameId = null;
if (serverThing != null) {
try {
gameId = serverThing.newGame(event.playerA(), event.playerB());
logger.info("Created game on server: {}", event.serverUuid());
} catch (Exception e) { // TODO: Error handling
logger.error("Could not create game on server: {}", event.serverUuid());
}
} else {
logger.warn("Could not find server: {}", event.serverUuid());
}
event.future().complete(gameId);
}
private void handleRunTicTacToeGameOnAServer(Events.ServerEvents.RunTicTacToeGame event) {
TicTacToeServer gameServer = (TicTacToeServer) this.servers.get(event.serverUuid());
gameServer.runGame(event.gameUuid());
}
private void handleEndTicTacToeGameOnAServer(Events.ServerEvents.EndTicTacToeGame event) {
TicTacToeServer gameServer = (TicTacToeServer) this.servers.get(event.serverUuid());
gameServer.endGame(event.gameUuid());
}
private void getAllServers(Events.ServerEvents.RequestsAllServers request) {
ArrayList<TcpServer> a = new ArrayList<>(this.servers.values());
request.future().complete(a.toString());
}
public void shutdownAll() {
this.servers.values().forEach(TcpServer::stop);
this.servers.clear();
logger.info("All servers shut down");
}
}

View File

@@ -1,230 +0,0 @@
package org.toop.backend.tictactoe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Lightweight, thread-pool based TCP server base class.
*
* <p>Responsibilities: - accept sockets - hand off socket I/O to connectionExecutor (pooled
* threads) - provide thread-safe queues (receivedQueue / sendQueue) to subclasses
*
* <p>Notes: - Subclasses should consume receivedQueue (or call getNewestCommand()) and use
* sendQueue to send messages to all clients (or per-client, if implemented).
*/
public abstract class TcpServer implements Runnable {
protected static final Logger logger = LogManager.getLogger(TcpServer.class);
// Executor used for per-connection I/O tasks (reading/writing)
protected final ExecutorService connectionExecutor = Executors.newCachedThreadPool();
// Shared queues for subclasses / consumers
public final BlockingQueue<String> receivedQueue = new LinkedBlockingQueue<>();
public final BlockingQueue<String> sendQueue = new LinkedBlockingQueue<>();
// Association for sockets -> player ids
public final Map<Socket, String> knownPlayers = new ConcurrentHashMap<>();
public final Map<String, String> playersGames = new ConcurrentHashMap<>();
// tunables
public final int WAIT_TIME = 500; // ms used by poll-based methods
public final int RETRY_ATTEMPTS = 3;
protected final int port;
protected final ServerSocket serverSocket;
private volatile boolean running = true;
public TcpServer(int port) throws IOException {
this.port = port;
this.serverSocket = new ServerSocket(port);
}
public boolean isRunning() {
return running;
}
/**
* Default run: accept connections and hand off to connectionExecutor. Subclasses overriding
* run() should still call startWorkers(Socket) for each accepted socket.
*/
@Override
public void run() {
logger.info("Server listening on port {}", port);
try {
while (running) {
Socket clientSocket = serverSocket.accept();
logger.info("Accepted connection from {}", clientSocket.getRemoteSocketAddress());
// hand off to pool to manage I/O for this socket
connectionExecutor.submit(() -> startWorkers(clientSocket));
}
} catch (IOException e) {
if (running) {
logger.error("Accept failed", e);
} else {
logger.info("Server socket closed, stopping acceptor");
}
}
}
/**
* Listen/Write workers for an accepted client socket. This method submits two tasks to the
* connectionExecutor: - inputLoop: reads lines and enqueues them to receivedQueue - outputLoop:
* polls sendQueue and writes messages to the client
*
* <p>Note: This is a simple model where sendQueue is global; if you need per-client
* send-queues, adapt this method to use one per socket.
*/
protected void startWorkers(Socket clientSocket) {
try {
BufferedReader in =
new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
// Input task: read lines and put them on receivedQueue
Runnable inputTask =
() -> {
logger.info(
"Starting read loop for {}", clientSocket.getRemoteSocketAddress());
try {
String line;
while (running && (line = in.readLine()) != null) {
if (line.isEmpty()) continue;
logger.debug(
"Received from {}: {}",
clientSocket.getRemoteSocketAddress(),
line);
boolean offered = false;
for (int i = 0; i < RETRY_ATTEMPTS && !offered; i++) {
try {
// Use offer to avoid blocking indefinitely; adapt
// timeout/policy as needed
offered =
this.receivedQueue.offer(
line, 200, TimeUnit.MILLISECONDS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
if (!offered) {
logger.warn(
"Backpressure: dropping line from {}: {}",
clientSocket.getRemoteSocketAddress(),
line);
// Policy choice: drop, notify, or close connection. We drop
// here.
}
}
} catch (IOException e) {
logger.info(
"Connection closed by remote: {}",
clientSocket.getRemoteSocketAddress());
} finally {
try {
clientSocket.close();
} catch (IOException ignored) {
}
logger.info(
"Stopped read loop for {}",
clientSocket.getRemoteSocketAddress());
}
};
// Output task: poll global sendQueue and write to this specific client.
// NOTE: With a single global sendQueue, every message is sent to every connected
// client.
// If you want per-client sends, change this to use per-client queue map.
Runnable outputTask =
() -> {
logger.info(
"Starting write loop for {}",
clientSocket.getRemoteSocketAddress());
try {
while (running && !clientSocket.isClosed()) {
String msg = sendQueue.poll(WAIT_TIME, TimeUnit.MILLISECONDS);
if (msg != null) {
out.println(msg);
out.flush();
logger.debug(
"Sent to {}: {}",
clientSocket.getRemoteSocketAddress(),
msg);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.info(
"Writer interrupted for {}",
clientSocket.getRemoteSocketAddress());
} catch (Exception e) {
logger.error(
"Writer error for {}: {}",
clientSocket.getRemoteSocketAddress(),
e.toString());
} finally {
try {
clientSocket.close();
} catch (IOException ignored) {
}
logger.info(
"Stopped write loop for {}",
clientSocket.getRemoteSocketAddress());
}
};
// Input and Output mappings
connectionExecutor.submit(inputTask);
connectionExecutor.submit(outputTask);
} catch (IOException e) {
logger.error("Could not start workers for client: {}", e.toString());
try {
clientSocket.close();
} catch (IOException ignored) {
}
}
}
/**
* Convenience: wrapper to obtain the latest command (non-blocking poll). Subclasses can use
* this, but for blocking behavior consider using receivedQueue.take()
*/
protected ParsedCommand getNewestCommand() {
try {
String rec = receivedQueue.poll(WAIT_TIME, TimeUnit.MILLISECONDS);
if (rec != null) return new ParsedCommand(rec);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Interrupted while polling receivedQueue", e);
}
return null;
}
/** Stop server and cleanup executors/sockets. */
public void stop() {
running = false;
try {
serverSocket.close();
} catch (IOException ignored) {
}
connectionExecutor.shutdownNow();
logger.info(
"TcpServer stopped. receivedQueue size={}, sendQueue size={}",
receivedQueue.size(),
sendQueue.size());
}
}

View File

@@ -1,186 +0,0 @@
package org.toop.backend.tictactoe;
import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.game.tictactoe.TicTacToe;
public class TicTacToeServer extends TcpServer {
protected static final Logger logger = LogManager.getLogger(TicTacToeServer.class);
private final ExecutorService connectionExecutor =
Executors.newCachedThreadPool(); // socket I/O
private final ExecutorService dispatcherExecutor;
private final ExecutorService forwarderExecutor = Executors.newSingleThreadExecutor();
private final BlockingQueue<ParsedCommand> incomingCommands;
private final Map<String, TicTacToe> games = new ConcurrentHashMap<>();
public TicTacToeServer(int port) throws IOException {
super(port);
int dispatchers = Math.max(2, Runtime.getRuntime().availableProcessors());
this.dispatcherExecutor =
Executors.newFixedThreadPool(
dispatchers + 1); // TODO: Magic number for forwardMessages
this.incomingCommands = new LinkedBlockingQueue<>(5_000);
forwarderExecutor.submit(this::forwardLoop);
for (int i = 0; i < dispatchers; i++) {
dispatcherExecutor.submit(this::dispatchLoop);
}
}
@Override
public void run() {
try {
logger.info("TicTacToe server listening on port {}", this.port);
while (isRunning()) {
Socket clientSocket = this.serverSocket.accept();
logger.info("Connected to client: {}", clientSocket.getInetAddress());
connectionExecutor.submit(() -> this.startWorkers(clientSocket));
}
} catch (IOException e) {
logger.error("I/O error in server run loop", e);
}
}
/** Forwards raw messages from TcpServer.receivedQueue into ParsedCommand objects. */
private void forwardLoop() {
logger.info("Forwarder loop started");
try {
while (isRunning()) {
String raw = this.receivedQueue.take(); // blocks
logger.info("Received command: {}", raw);
try {
ParsedCommand pc = new ParsedCommand(raw);
this.incomingCommands.put(pc); // blocks if full
} catch (Exception e) {
logger.warn("Invalid message ignored: {}", raw, e);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.info("Forwarder loop interrupted");
}
}
/** Dispatches parsed commands into the game logic. */
private void dispatchLoop() {
logger.info("Dispatcher thread started");
try {
while (isRunning()) {
ParsedCommand command = this.incomingCommands.take(); // blocks
if (command.isServerCommand) {
handleServerCommand(command);
continue;
}
// Find game by ID
TicTacToe game = this.games.get(command.gameId);
if (game != null) {
game.addCommandToQueue(command);
logger.info(
"Dispatched command {} to game {}", command.toString(), command.gameId);
} else {
logger.warn(
"No active game with ID {} for command {}",
command.gameId,
command.toString());
// TODO: reply back
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.info("Dispatcher interrupted");
}
}
private void handleServerCommand(ParsedCommand command) {
if (command.isValidCommand) {
this.sendQueue.offer("ok");
}
if (command.command == TicTacToeServerCommand.CREATE_GAME) {
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) {
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");
} else if (command.command == TicTacToeServerCommand.LOGIN) {
this.endGame((String) command.arguments.getFirst());
this.sendQueue.offer("svr login successful");
} else if (command.command == TicTacToeServerCommand.SUBSCRIBE) {
this.endGame((String) command.arguments.getFirst());
this.sendQueue.offer("svr added {} to the queue");
}
}
public void forwardGameMessages(TicTacToe game) {
dispatcherExecutor.submit(
() -> {
try {
while (isRunning()) {
String msg =
game.sendQueue
.take(); // blocks until a message is added to the queue
logger.info(
"Games: {}, Adding: {} to the send queue", game.gameId, msg);
this.sendQueue.put(msg); // push to network layer
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
public String newGame(String playerA, String playerB) {
logger.info("Creating a new game: {} vs {}", playerA, playerB);
String gameId = UUID.randomUUID().toString();
TicTacToe game = new TicTacToe(playerA, playerB, gameId);
this.games.put(gameId, game);
forwardGameMessages(game);
logger.info("Created new game: {}. {} vs {}", gameId, playerA, playerB);
return 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;
}
}
public void endGame(String gameId) {
TicTacToe game = this.games.remove(gameId);
if (game != null) {
logger.info("Ended game: {}", gameId);
// TODO: gracefully stop game thread
} else {
logger.warn("Tried to end unknown game {}", gameId);
}
}
}

View File

@@ -1,71 +0,0 @@
package org.toop.backend.tictactoe;
import java.util.EnumSet;
public enum TicTacToeServerCommand {
CREATE_GAME,
START_GAME,
END_GAME,
/** Login, "username" */
LOGIN,
/** Logout, "username" */
LOGOUT,
EXIT,
QUIT,
DISCONNECT,
BYE,
GET,
SUBSCRIBE,
MOVE,
CHALLENGE,
FORFEIT,
MESSAGE,
HELP;
private static final EnumSet<TicTacToeServerCommand> VALID_COMMANDS =
EnumSet.of(
TicTacToeServerCommand.LOGIN,
TicTacToeServerCommand.LOGOUT,
TicTacToeServerCommand.EXIT,
TicTacToeServerCommand.QUIT,
TicTacToeServerCommand.DISCONNECT,
TicTacToeServerCommand.BYE,
TicTacToeServerCommand.GET,
TicTacToeServerCommand.SUBSCRIBE,
TicTacToeServerCommand.MOVE,
TicTacToeServerCommand.CHALLENGE,
TicTacToeServerCommand.FORFEIT,
TicTacToeServerCommand.MESSAGE,
TicTacToeServerCommand.HELP);
public static EnumSet<TicTacToeServerCommand> getValidCommands() {
return VALID_COMMANDS;
}
// TODO: Garbage code.
/**
* @param command Checks if string is a valid command.
* @return returns a boolean if string is a valid command.
*/
public static boolean isValid(String command) {
try {
TicTacToeServerCommand.valueOf(command.toUpperCase());
return true;
} catch (IllegalArgumentException err) {
return false;
}
}
// TODO: Return something better
/**
* @param command Converts a string into a TicTacToeServerCommand.
* @return returns a TicTacToeServerCommand enum.
*/
public static TicTacToeServerCommand getCommand(String command) {
if (isValid(command)) {
return TicTacToeServerCommand.valueOf(command.toUpperCase());
}
return null;
}
}

View File

@@ -1,45 +0,0 @@
package org.toop.backend.tictactoe;
import java.util.EnumSet;
public enum TicTacToeServerMessage {
OK,
ERR,
SVR;
private static final EnumSet<TicTacToeServerMessage> VALID_COMMANDS =
EnumSet.of(
TicTacToeServerMessage.OK,
TicTacToeServerMessage.ERR,
TicTacToeServerMessage.SVR);
public static EnumSet<TicTacToeServerMessage> getValidCommands() {
return VALID_COMMANDS;
}
// TODO: Garbage code.
/**
* @param command Checks if string is a valid command.
* @return returns a boolean if string is a valid command.
*/
public static boolean isValid(String command) {
try {
TicTacToeServerMessage.valueOf(command.toUpperCase());
return true;
} catch (IllegalArgumentException err) {
return false;
}
}
// TODO: Return something better
/**
* @param command Converts a string into a ServerCommand.
* @return returns a ServerCommand enum.
*/
public static TicTacToeServerMessage getCommand(String command) {
if (isValid(command)) {
return TicTacToeServerMessage.valueOf(command.toUpperCase());
}
return null;
}
}

View File

@@ -1,34 +0,0 @@
package org.toop.core;
import java.io.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class FileSystem {
public record File(String path, CharSequence buffer) {}
;
private static final Logger logger = LogManager.getLogger(FileSystem.class);
public static File read(String path) {
File file;
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
StringBuilder buffer = new StringBuilder();
String line = reader.readLine();
while (line != null) {
buffer.append(line);
buffer.append(System.lineSeparator());
line = reader.readLine();
}
file = new File(path, buffer);
} catch (IOException e) {
logger.error("{}", e.getMessage());
return null;
}
return file;
}
}

View File

@@ -1,5 +0,0 @@
package org.toop.core;
public interface ICallable<T> {
public T call();
}

View File

@@ -1,50 +0,0 @@
//package org.toop.core;
//
//import org.apache.logging.log4j.LogManager;
//import org.apache.logging.log4j.Logger;
//import org.toop.frontend.platform.core.glfw.GlfwWindow;
//
//public abstract class Window {
// public enum API {
// NONE,
// GLFW,
// }
//
// public record Size(int width, int height) {}
//
// protected static final Logger logger = LogManager.getLogger(Window.class);
//
// private static API api = API.NONE;
// private static Window instance = null;
//
// public static Window setup(API api, String title, Size size) {
// if (instance != null) {
// logger.warn("Window is already setup.");
// return instance;
// }
//
// switch (api) {
// case GLFW:
// instance = new GlfwWindow(title, size);
// break;
//
// default:
// logger.fatal("No valid window api chosen");
// return null;
// }
//
// Window.api = api;
// return instance;
// }
//
// public static API getApi() {
// return api;
// }
//
// public void cleanup() {
// instance = null;
// logger.info("Window cleanup.");
// }
//
// public abstract void update();
//}

View File

@@ -1,42 +0,0 @@
package org.toop.eventbus;
/** Wraps an event with its type and a ready flag. */
public class EventMeta<T> {
private final Class<T> type;
private final Object event;
private boolean ready;
public EventMeta(Class<T> type, Object event) {
this.type = type;
this.event = event;
this.ready = false; // default not ready
}
public Class<T> getType() {
return type;
}
public Object getEvent() {
return event;
}
public boolean isReady() {
return ready;
}
public void setReady(boolean ready) {
this.ready = ready;
}
@Override
public String toString() {
return "ReadyEvent{"
+ "type="
+ type.getSimpleName()
+ ", event="
+ event
+ ", ready="
+ ready
+ '}';
}
}

View File

@@ -1,101 +0,0 @@
package org.toop.eventbus;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** Thread-safe registry for storing events and tracking readiness of event types. */
public class EventRegistry {
private static final Logger logger = LogManager.getLogger(EventRegistry.class);
private static final Map<Class<?>, CopyOnWriteArrayList<EventEntry<?>>> eventHistory =
new ConcurrentHashMap<>();
private static final Map<Class<?>, Boolean> readyStates = new ConcurrentHashMap<>();
/** Stores an event in the registry. Safe for concurrent use. */
public static <T> void storeEvent(EventMeta<T> eventMeta) {
logger.info("Storing event: {}", eventMeta.toString());
eventHistory
.computeIfAbsent(eventMeta.getType(), k -> new CopyOnWriteArrayList<>())
.add(new EventEntry<>(eventMeta));
}
/** Marks a specific event type as ready (safe to post). */
public static <T> void markReady(Class<T> type) {
logger.info("Marking event as ready: {}", type.toString());
readyStates.put(type, true);
}
/** Marks a specific event type as not ready (posting will fail). */
public static <T> void markNotReady(Class<T> type) {
logger.info("Marking event as not ready: {}", type.toString());
readyStates.put(type, false);
}
/** Returns true if this event type is marked ready. */
public static <T> boolean isReady(Class<T> type) {
return readyStates.getOrDefault(type, false);
}
/** Gets all stored events of a given type. */
@SuppressWarnings("unchecked")
public static <T> List<EventEntry<T>> getEvents(Class<T> type) {
return (List<EventEntry<T>>)
(List<?>) eventHistory.getOrDefault(type, new CopyOnWriteArrayList<>());
}
/** Gets the most recent event of a given type, or null if none exist. */
@SuppressWarnings("unchecked")
public static <T> EventEntry<T> getLastEvent(Class<T> type) {
List<EventEntry<?>> entries = eventHistory.get(type);
if (entries == null || entries.isEmpty()) {
return null;
}
return (EventEntry<T>) entries.getLast();
}
/** Clears the stored events for a given type. */
public static <T> void clearEvents(Class<T> type) {
logger.info("Clearing events: {}", type.toString());
eventHistory.remove(type);
}
/** Clears all events and resets readiness. */
public static void reset() {
logger.info("Resetting event registry events");
eventHistory.clear();
readyStates.clear();
}
/** Wrapper for stored events, with a ready flag for per-event state. */
public static class EventEntry<T> {
private final T event;
private volatile boolean ready = false;
public EventEntry(T event) {
this.event = event;
}
public T getEvent() {
return event;
}
public boolean isReady() {
return ready;
}
public void setReady(boolean ready) {
this.ready = ready;
}
@Override
public String toString() {
return "EventEntry{" + "event=" + event + ", ready=" + ready + '}';
}
}
}

View File

@@ -1,50 +0,0 @@
//package org.toop.frontend.graphics;
//
//import org.apache.logging.log4j.LogManager;
//import org.apache.logging.log4j.Logger;
//import org.toop.frontend.platform.graphics.opengl.OpenglRenderer;
//
//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();
//}

View File

@@ -1,27 +0,0 @@
//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();
//}

View File

@@ -1,33 +0,0 @@
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();
}
}

View File

@@ -1,22 +0,0 @@
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() {}
}

View File

@@ -1,68 +0,0 @@
//package org.toop.frontend.graphics.node;
//
//import java.util.*;
//import org.apache.logging.log4j.LogManager;
//import org.apache.logging.log4j.Logger;
//import org.toop.eventbus.*;
//import org.toop.eventbus.events.Events;
//import org.toop.frontend.graphics.Shader;
//
//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() {}
//}

View File

@@ -1,48 +0,0 @@
package org.toop.frontend.graphics.node;
import java.util.*;
import org.toop.frontend.math.Bounds;
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;
}
}

View File

@@ -1,39 +0,0 @@
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;
}
}

View File

@@ -1,25 +0,0 @@
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;
}
}

View File

@@ -1,167 +0,0 @@
package org.toop.frontend.networking;
import java.io.IOException;
import java.net.InetAddress;
import java.util.concurrent.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.eventbus.EventPublisher;
import org.toop.eventbus.events.Events;
import org.toop.eventbus.GlobalEventBus;
import org.toop.eventbus.events.NetworkEvents;
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));
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();
}
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
new EventPublisher<>(NetworkEvents.ReceivedMessage.class, this.uuid, received).postEvent();
} else {
break;
}
}
} catch (IOException e) {
logger.error("Error reading from server", e);
}
}
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);
}
}

View File

@@ -1,62 +0,0 @@
package org.toop.frontend.networking;
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();
}
BufferedReader createIn() throws IOException {
return new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
}
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();
}
}

View File

@@ -1,109 +0,0 @@
//package org.toop.frontend.platform.core.glfw;
//
//import org.lwjgl.glfw.*;
//import org.lwjgl.system.*;
//import org.toop.core.*;
//import org.toop.eventbus.*;
//import org.toop.eventbus.events.Events;
//
//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();
// }
//}

View File

@@ -1,79 +0,0 @@
//package org.toop.frontend.platform.graphics.opengl;
//
//import org.lwjgl.opengl.*;
//import org.lwjgl.system.*;
//import org.toop.eventbus.*;
//import org.toop.eventbus.events.Events;
//import org.toop.frontend.graphics.Renderer;
//import org.toop.frontend.graphics.Shader;
//
//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);
// }
//}

View File

@@ -1,57 +0,0 @@
//package org.toop.frontend.platform.graphics.opengl;
//
//import org.lwjgl.opengl.*;
//import org.toop.core.*;
//import org.toop.frontend.graphics.Shader;
//
//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);
// }
//}