Merge remote-tracking branch 'origin/289-server' into 289-server

# Conflicts:
#	app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java
#	app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java
#	framework/src/main/java/org/toop/framework/game/games/reversi/BitboardReversi.java
#	framework/src/main/java/org/toop/framework/game/games/tictactoe/BitboardTicTacToe.java
This commit is contained in:
2025-12-12 15:53:45 +01:00
52 changed files with 499 additions and 98 deletions

View File

@@ -1,10 +1,14 @@
package org.toop.app;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.complex.LoadingWidget;
@@ -16,8 +20,12 @@ import org.toop.framework.audio.*;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.eventbus.GlobalEventBus;
import org.toop.framework.networking.NetworkingClientEventListener;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.game.BitboardGame;
import org.toop.framework.game.games.reversi.BitboardReversi;
import org.toop.framework.networking.connection.NetworkingClientEventListener;
import org.toop.framework.networking.connection.NetworkingClientManager;
import org.toop.framework.networking.server.GameDefinition;
import org.toop.framework.networking.server.MasterServer;
import org.toop.framework.resource.ResourceLoader;
import org.toop.framework.resource.ResourceManager;
import org.toop.framework.resource.events.AssetLoaderEvents;
@@ -27,13 +35,8 @@ import org.toop.framework.resource.resources.SoundEffectAsset;
import org.toop.local.AppContext;
import org.toop.local.AppSettings;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -86,7 +89,7 @@ public final class App extends Application {
AppSettings.applySettings();
setKeybinds(root);
setKeybinds(root);
LoadingWidget loading = new LoadingWidget(Primitive.text(
"Loading...", false), 0, 0, Integer.MAX_VALUE, false, false // Just set a high default
@@ -138,7 +141,17 @@ public final class App extends Application {
stage.show();
}
var games = new ConcurrentHashMap<String, GameDefinition<BitboardGame<?>>>();
games.put("tictactoe", new GameDefinition<>("tictactoe", BitboardReversi.class));
games.put("reversi", new GameDefinition<>("reversi", BitboardReversi.class));
var s = new MasterServer(6666, games);
try {
s.start();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void setKeybinds(StackPane root) {
root.addEventHandler(KeyEvent.KEY_PRESSED,event -> {

View File

@@ -14,14 +14,14 @@ import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.gameFramework.controller.GameController;
import org.toop.framework.eventbus.GlobalEventBus;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.framework.networking.clients.TournamentNetworkingClient;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.types.NetworkingConnector;
import org.toop.game.games.reversi.BitboardReversi;
import org.toop.game.games.tictactoe.BitboardTicTacToe;
import org.toop.game.players.ArtificialPlayer;
import org.toop.game.players.OnlinePlayer;
import org.toop.game.players.RandomAI;
import org.toop.framework.networking.connection.clients.TournamentNetworkingClient;
import org.toop.framework.networking.connection.events.NetworkEvents;
import org.toop.framework.networking.connection.types.NetworkingConnector;
import org.toop.framework.game.games.reversi.BitboardReversi;
import org.toop.framework.game.games.tictactoe.BitboardTicTacToe;
import org.toop.framework.game.players.ArtificialPlayer;
import org.toop.framework.game.players.OnlinePlayer;
import org.toop.framework.game.players.RandomAI;
import org.toop.local.AppContext;
import java.util.List;

View File

@@ -15,8 +15,8 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.framework.gameFramework.view.GUIEvents;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.players.LocalPlayer;
import org.toop.framework.networking.connection.events.NetworkEvents;
import org.toop.framework.game.players.LocalPlayer;
public class GenericGameController implements GameController {
protected final EventFlow eventFlow = new EventFlow();

View File

@@ -3,10 +3,10 @@ package org.toop.app.gameControllers;
import org.toop.app.canvas.ReversiBitCanvas;
import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.game.gameThreads.LocalThreadBehaviour;
import org.toop.game.gameThreads.OnlineThreadBehaviour;
import org.toop.game.games.reversi.BitboardReversi;
import org.toop.game.players.OnlinePlayer;
import org.toop.framework.game.gameThreads.LocalThreadBehaviour;
import org.toop.framework.game.gameThreads.OnlineThreadBehaviour;
import org.toop.framework.game.games.reversi.BitboardReversi;
import org.toop.framework.game.players.OnlinePlayer;
public class ReversiBitController extends GenericGameController {
public ReversiBitController(Player[] players) {

View File

@@ -3,11 +3,10 @@ package org.toop.app.gameControllers;
import org.toop.app.canvas.TicTacToeBitCanvas;
import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.game.gameThreads.LocalFixedRateThreadBehaviour;
import org.toop.game.gameThreads.LocalThreadBehaviour;
import org.toop.game.gameThreads.OnlineThreadBehaviour;
import org.toop.game.games.tictactoe.BitboardTicTacToe;
import org.toop.game.players.OnlinePlayer;
import org.toop.framework.game.gameThreads.LocalThreadBehaviour;
import org.toop.framework.game.gameThreads.OnlineThreadBehaviour;
import org.toop.framework.game.games.tictactoe.BitboardTicTacToe;
import org.toop.framework.game.players.OnlinePlayer;
public class TicTacToeBitController extends GenericGameController {
public TicTacToeBitController(Player[] players) {

View File

@@ -2,24 +2,21 @@ package org.toop.app.widget.view;
import javafx.application.Platform;
import org.toop.app.GameInformation;
import org.toop.app.canvas.ReversiBitCanvas;
import org.toop.app.canvas.TicTacToeBitCanvas;
import org.toop.app.gameControllers.GenericGameController;
import org.toop.app.gameControllers.ReversiBitController;
import org.toop.app.gameControllers.TicTacToeBitController;
import org.toop.framework.gameFramework.controller.GameController;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.game.games.reversi.BitboardReversi;
import org.toop.game.games.tictactoe.BitboardTicTacToe;
import org.toop.game.players.ArtificialPlayer;
import org.toop.game.players.LocalPlayer;
import org.toop.framework.game.games.reversi.BitboardReversi;
import org.toop.framework.game.games.tictactoe.BitboardTicTacToe;
import org.toop.framework.game.players.ArtificialPlayer;
import org.toop.framework.game.players.LocalPlayer;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.PlayerInfoWidget;
import org.toop.app.widget.complex.ViewWidget;
import org.toop.app.widget.popup.ErrorPopup;
import org.toop.app.widget.tutorial.*;
import org.toop.game.players.MiniMaxAI;
import org.toop.game.players.RandomAI;
import org.toop.framework.game.players.MiniMaxAI;
import org.toop.framework.game.players.RandomAI;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
@@ -27,9 +24,6 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import org.toop.local.AppSettings;
import java.util.Arrays;
import java.util.Random;
public class LocalMultiplayerView extends ViewWidget {
private final GameInformation information;

View File

@@ -147,7 +147,6 @@
<version>2.42.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View File

@@ -1,10 +1,9 @@
package org.toop.game;
package org.toop.framework.game;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.player.Player;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
// There is AI performance to be gained by getting rid of non-primitives and thus speeding up deepCopy
public abstract class BitboardGame implements TurnBasedGame {

View File

@@ -1,3 +1,3 @@
package org.toop.game;
package org.toop.framework.game;
// TODO: Remove this, only used in ReversiCanvas. Needs to not
public record Move(int position, char value) {}

View File

@@ -1,4 +1,4 @@
package org.toop.game.gameThreads;
package org.toop.framework.game.gameThreads;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.gameFramework.GameState;

View File

@@ -1,4 +1,4 @@
package org.toop.game.gameThreads;
package org.toop.framework.game.gameThreads;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour;

View File

@@ -1,4 +1,4 @@
package org.toop.game.gameThreads;
package org.toop.framework.game.gameThreads;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.gameFramework.model.game.threadBehaviour.AbstractThreadBehaviour;
@@ -6,7 +6,7 @@ import org.toop.framework.gameFramework.view.GUIEvents;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay;
import org.toop.framework.gameFramework.model.player.Player;
import org.toop.game.players.OnlinePlayer;
import org.toop.framework.game.players.OnlinePlayer;
/**
* Handles online multiplayer game logic.

View File

@@ -1,7 +1,6 @@
package org.toop.game.gameThreads;
package org.toop.framework.game.gameThreads;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.networking.events.NetworkEvents;
/**
* Online thread behaviour that adds a fixed delay before processing

View File

@@ -1,4 +1,4 @@
package org.toop.game.players;
package org.toop.framework.game.players;
import org.toop.framework.gameFramework.model.player.*;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;

View File

@@ -1,8 +1,7 @@
package org.toop.game.players;
package org.toop.framework.game.players;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.player.AbstractPlayer;
import org.toop.framework.gameFramework.model.player.Player;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

View File

@@ -1,4 +1,4 @@
package org.toop.game.players;
package org.toop.framework.game.players;
import org.toop.framework.gameFramework.GameState;
import org.toop.framework.gameFramework.model.game.PlayResult;

View File

@@ -1,4 +1,4 @@
package org.toop.game.players;
package org.toop.framework.game.players;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.player.AbstractPlayer;

View File

@@ -1,4 +1,4 @@
package org.toop.game.players;
package org.toop.framework.game.players;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import org.toop.framework.gameFramework.model.player.AbstractAI;

View File

@@ -1,8 +1,7 @@
package org.toop.framework.gameFramework.controller;
import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay;
import org.toop.framework.gameFramework.model.game.threadBehaviour.Controllable;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.connection.events.NetworkEvents;
public interface GameController extends Controllable, UpdatesGameUI {
/** Called when it is this player's turn to make a move. */

View File

@@ -1,7 +1,5 @@
package org.toop.framework.gameFramework.model.game;
import org.toop.framework.networking.events.NetworkEvents;
/**
* Interface for games that support online multiplayer play.
* <p>

View File

@@ -1,13 +1,13 @@
package org.toop.framework.networking;
package org.toop.framework.networking.connection;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.SnowflakeGenerator;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.exceptions.ClientNotFoundException;
import org.toop.framework.networking.interfaces.NetworkingClientManager;
import org.toop.framework.networking.connection.events.NetworkEvents;
import org.toop.framework.networking.connection.exceptions.ClientNotFoundException;
import org.toop.framework.networking.connection.interfaces.NetworkingClientManager;
public class NetworkingClientEventListener {
private static final Logger logger = LogManager.getLogger(NetworkingClientEventListener.class);

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking;
package org.toop.framework.networking.connection;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -10,13 +10,13 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.exceptions.ClientNotFoundException;
import org.toop.framework.networking.exceptions.CouldNotConnectException;
import org.toop.framework.networking.interfaces.NetworkingClient;
import org.toop.framework.networking.types.NetworkingConnector;
import org.toop.framework.networking.connection.events.NetworkEvents;
import org.toop.framework.networking.connection.exceptions.ClientNotFoundException;
import org.toop.framework.networking.connection.exceptions.CouldNotConnectException;
import org.toop.framework.networking.connection.interfaces.NetworkingClient;
import org.toop.framework.networking.connection.types.NetworkingConnector;
public class NetworkingClientManager implements org.toop.framework.networking.interfaces.NetworkingClientManager {
public class NetworkingClientManager implements org.toop.framework.networking.connection.interfaces.NetworkingClientManager {
private static final Logger logger = LogManager.getLogger(NetworkingClientManager.class);
private final EventBus eventBus;

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.clients;
package org.toop.framework.networking.connection.clients;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
@@ -12,9 +12,9 @@ import io.netty.util.CharsetUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.networking.exceptions.CouldNotConnectException;
import org.toop.framework.networking.handlers.NetworkingGameClientHandler;
import org.toop.framework.networking.interfaces.NetworkingClient;
import org.toop.framework.networking.connection.exceptions.CouldNotConnectException;
import org.toop.framework.networking.connection.handlers.NetworkingGameClientHandler;
import org.toop.framework.networking.connection.interfaces.NetworkingClient;
import java.net.InetSocketAddress;

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.events;
package org.toop.framework.networking.connection.events;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -6,8 +6,8 @@ import java.util.concurrent.CompletableFuture;
import org.toop.annotations.AutoResponseResult;
import org.toop.framework.eventbus.GlobalEventBus;
import org.toop.framework.eventbus.events.*;
import org.toop.framework.networking.interfaces.NetworkingClient;
import org.toop.framework.networking.types.NetworkingConnector;
import org.toop.framework.networking.connection.interfaces.NetworkingClient;
import org.toop.framework.networking.connection.types.NetworkingConnector;
/**
* Defines all event types related to the networking subsystem.

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.exceptions;
package org.toop.framework.networking.connection.exceptions;
/**
* Thrown when an operation is attempted on a networking client

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.exceptions;
package org.toop.framework.networking.connection.exceptions;
public class CouldNotConnectException extends RuntimeException {

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.exceptions;
package org.toop.framework.networking.connection.exceptions;
public class NetworkingInitializationException extends RuntimeException {
public NetworkingInitializationException(String message, Throwable cause) {

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.handlers;
package org.toop.framework.networking.connection.handlers;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
@@ -9,7 +9,7 @@ import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.framework.networking.connection.events.NetworkEvents;
public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LogManager.getLogger(NetworkingGameClientHandler.class);

View File

@@ -1,6 +1,6 @@
package org.toop.framework.networking.interfaces;
package org.toop.framework.networking.connection.interfaces;
import org.toop.framework.networking.exceptions.CouldNotConnectException;
import org.toop.framework.networking.connection.exceptions.CouldNotConnectException;
import java.net.InetSocketAddress;

View File

@@ -1,8 +1,8 @@
package org.toop.framework.networking.interfaces;
package org.toop.framework.networking.connection.interfaces;
import org.toop.framework.networking.exceptions.ClientNotFoundException;
import org.toop.framework.networking.exceptions.CouldNotConnectException;
import org.toop.framework.networking.types.NetworkingConnector;
import org.toop.framework.networking.connection.exceptions.ClientNotFoundException;
import org.toop.framework.networking.connection.exceptions.CouldNotConnectException;
import org.toop.framework.networking.connection.types.NetworkingConnector;
public interface NetworkingClientManager {
void startClient(

View File

@@ -1,4 +1,4 @@
package org.toop.framework.networking.types;
package org.toop.framework.networking.connection.types;
import java.util.concurrent.TimeUnit;

View File

@@ -1,3 +1,3 @@
package org.toop.framework.networking.types;
package org.toop.framework.networking.connection.types;
public record ServerCommand(long clientId, String command) {}

View File

@@ -0,0 +1,3 @@
package org.toop.framework.networking.connection.types;
public record ServerMessage(String message) {}

View File

@@ -0,0 +1,30 @@
package org.toop.framework.networking.server;
import org.toop.framework.game.BitboardGame;
public class Game implements OnlineGame {
private long id;
private User[] users;
private GameDefinition<BitboardGame<?>> game;
public Game(GameDefinition game, User... users) {
this.game = game;
this.users = users;
}
@Override
public long id() {
return id;
}
@Override
public GameDefinition game() {
return game;
}
@Override
public User[] users() {
return users;
}
}

View File

@@ -0,0 +1,26 @@
package org.toop.framework.networking.server;
import java.lang.reflect.InvocationTargetException;
public class GameDefinition<T> {
private final String name;
private final Class<T> game;
public GameDefinition(String name, Class<T> game) {
this.name = name;
this.game = game;
}
public String name() {
return name;
}
public T create(String... users) {
try {
return game.getDeclaredConstructor().newInstance(users);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,8 @@
package org.toop.framework.networking.server;
public interface GameServer {
// List<?> gameTypes();
// List<?> ongoingGames();
// void startGame(String gameType, User... users);
// String[] onlineUsers();
}

View File

@@ -0,0 +1,68 @@
package org.toop.framework.networking.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.nio.NioIoHandler;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.toop.framework.SnowflakeGenerator;
import org.toop.framework.game.BitboardGame;
import java.util.Map;
public class MasterServer {
private final int port;
public final Server gs;
public MasterServer(int port, Map<String, GameDefinition<BitboardGame<?>>> gameTypes) {
this.port = port;
this.gs = new Server(gameTypes);
}
public void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.option(ChannelOption.SO_BACKLOG, 128);
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
bootstrap.handler(new LoggingHandler(LogLevel.INFO));
bootstrap.childHandler(
new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(8192));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
long userid = SnowflakeGenerator.nextId();
User user = new User(userid, ""+userid);
pipeline.addLast(new ServerHandler(user, gs));
}
}
);
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("MasterServer listening on port " + port);
future.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

View File

@@ -0,0 +1,7 @@
package org.toop.framework.networking.server;
public interface MessageStore {
void add(String message);
String get();
void reset();
}

View File

@@ -0,0 +1,7 @@
package org.toop.framework.networking.server;
public interface OnlineGame {
long id();
GameDefinition game();
User[] users();
}

View File

@@ -0,0 +1,4 @@
package org.toop.framework.networking.server;
public record ParsedMessage(String command, String... args) {}

View File

@@ -0,0 +1,4 @@
package org.toop.framework.networking.server;
public class Parser {
}

View File

@@ -0,0 +1,4 @@
package org.toop.framework.networking.server;
public interface ServableGame {
}

View File

@@ -0,0 +1,51 @@
package org.toop.framework.networking.server;
import org.toop.framework.game.BitboardGame;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Server implements GameServer {
final private Map<String, GameDefinition<BitboardGame<?>>> gameTypes;
public List<OnlineGame> games = new ArrayList<>();
final private Map<Long, ServerUser> users = new ConcurrentHashMap<>();
public Server(Map<String, GameDefinition<BitboardGame<?>>> gameTypes) {
this.gameTypes = gameTypes;
}
public void addUser(ServerUser user) {
users.putIfAbsent(user.id(), user);
}
public void removeUser(ServerUser user) {
users.remove(user.id());
}
public String[] gameTypes() {
return gameTypes.keySet().toArray(new String[0]);
}
// public List<OnlineGame<BitboardGame<?>>> ongoingGames() {
// return List.of();
// }
public void startGame(String gameType, User... users) {
if (!gameTypes.containsKey(gameType)) return;
try {
var game = new Game(gameTypes.get(gameType).create(), users);
games.addLast(new Game(game, users));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String[] onlineUsers() {
return users.values().stream().map(ServerUser::name).toArray(String[]::new);
}
}

View File

@@ -0,0 +1,117 @@
package org.toop.framework.networking.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class ServerHandler extends SimpleChannelInboundHandler<String> {
private final User user;
private final Server server;
public ServerHandler(User user, Server server) {
this.user = user;
this.server = server;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush("WELCOME " + user.id() + "\n");
user.setCtx(ctx);
server.addUser(user); // TODO set correct name on login
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
ParsedMessage p = parse(msg);
if (p == null) return;
IO.println(p.command() + " " + Arrays.toString(p.args()));
switch (p.command()) {
case "ping" -> ctx.writeAndFlush("PONG\n");
case "login" -> handleLogin(p);
case "get" -> handleGet(p);
case "subscribe" -> handleSubscribe(p);
case "move" -> handleMove(p);
case "challenge" -> handleChallenge(p);
case "message" -> handleMessage(p);
case "help" -> handleHelp(p);
default -> ctx.writeAndFlush("ERROR Unknown command\n");
}
}
private boolean allowedArgs(String... args) {
if (args.length < 1) return false;
return true;
}
private void handleLogin(ParsedMessage p) {
if (!allowedArgs(p.args())) return;
user.setName(p.args()[0]);
}
private void handleGet(ParsedMessage p) {
if (!allowedArgs(p.args())) return;
switch (p.args()[0]) {
case "playerlist" -> user.ctx().writeAndFlush(Arrays.toString(server.onlineUsers()));
case "gamelist" -> user.ctx().writeAndFlush(Arrays.toString(server.gameTypes()));
}
}
private void handleSubscribe(ParsedMessage p) {
// TODO
}
private void handleHelp(ParsedMessage p) {
// TODO
}
private void handleMessage(ParsedMessage p) {
// TODO
}
private void handleChallenge(ParsedMessage p) {
// TODO
}
private void handleMove(ParsedMessage p) {
// TODO
}
private ParsedMessage parse(String msg) {
// TODO, what if empty string.
if (msg.isEmpty()) return null;
msg = msg.trim().toLowerCase();
List<String> parts = new LinkedList<>(List.of(msg.split(" ")));
if (parts.size() > 1) {
String command = parts.removeFirst();
return new ParsedMessage(command, parts.toArray(String[]::new));
}
else {
return new ParsedMessage(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
server.removeUser(user);
}
}

View File

@@ -0,0 +1,27 @@
package org.toop.framework.networking.server;
import java.util.Queue;
public class ServerMessageStore implements MessageStore {
Queue<String> messageQueue;
public ServerMessageStore(Queue<String> messageQueue) {
this.messageQueue = messageQueue;
}
@Override
public void add(String message) {
messageQueue.offer(message);
}
@Override
public String get() {
return messageQueue.poll();
}
@Override
public void reset() {
messageQueue.clear();
}
}

View File

@@ -0,0 +1,9 @@
package org.toop.framework.networking.server;
import java.net.InetSocketAddress;
public interface ServerUser {
long id();
String name();
void setName(String name);
}

View File

@@ -0,0 +1,40 @@
package org.toop.framework.networking.server;
import io.netty.channel.ChannelHandlerContext;
import java.net.InetSocketAddress;
public class User implements ServerUser {
final private long id;
private String name;
private ChannelHandlerContext connectionContext;
public User(long userId, String name) {
this.id = userId;
this.name = name;
}
@Override
public long id() {
return id;
}
@Override
public String name() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
public ChannelHandlerContext ctx() {
return connectionContext;
}
public void setCtx(ChannelHandlerContext ctx) {
this.connectionContext = ctx;
}
}

View File

@@ -1,3 +0,0 @@
package org.toop.framework.networking.types;
public record ServerMessage(String message) {}

View File

@@ -9,7 +9,7 @@
//import org.mockito.*;
//import org.toop.framework.SnowflakeGenerator;
//import org.toop.framework.eventbus.EventFlow;
//import org.toop.framework.networking.events.NetworkEvents;
//import org.toop.framework.networking.connection.events.NetworkEvents;
//
//class NetworkingClientManagerTest {
//

View File

@@ -1,4 +1,4 @@
//package org.toop.framework.networking.events;
//package org.toop.framework.networking.connection.events;
//
//import static org.junit.jupiter.api.Assertions.*;
//