Merge branch 'main' into 'feat/game-ui'
This commit is contained in:
@@ -3,8 +3,25 @@ package ch.unibas.dmi.dbis.cs108.casono;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.client.ClientApp;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.ServerApp;
|
||||
|
||||
/**
|
||||
* Main entry point for Casono application.
|
||||
* Handles client and server startup.
|
||||
* <p>
|
||||
* Standardkonstruktor für die Anwendung.
|
||||
*/
|
||||
public final class Main {
|
||||
|
||||
/**
|
||||
* Standardkonstruktor.
|
||||
*/
|
||||
public Main() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for Casono.
|
||||
* @param args Command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (!isValid(args)) {
|
||||
printUsage();
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client;
|
||||
|
||||
/**
|
||||
* Entry point for the Casono client application.
|
||||
* Handles client startup and connection parameters.
|
||||
*/
|
||||
import ch.unibas.dmi.dbis.cs108.casono.client.ui.Launcher;
|
||||
/**
|
||||
* Entry point for the Casono client application.
|
||||
* Handles client startup and connection parameters.
|
||||
* <p>
|
||||
* Standardkonstruktor für die Anwendung.
|
||||
*/
|
||||
public class ClientApp {
|
||||
|
||||
/**
|
||||
* Standardkonstruktor.
|
||||
*/
|
||||
public ClientApp() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
/**
|
||||
* Starts the client application with the given address.
|
||||
* @param arg Address in the format "ip:port".
|
||||
* @throws IllegalArgumentException if the address format is invalid.
|
||||
*/
|
||||
public static void start(String arg) {
|
||||
String[] parts = arg.split(":", 2);
|
||||
if (parts.length != 2) {
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI;
|
||||
import javafx.application.Application;
|
||||
|
||||
/**
|
||||
* Launcher-Klasse für die Casino-Anwendung.
|
||||
*
|
||||
* Startet die JavaFX-Anwendung. Während der Entwicklung kann hier direkt
|
||||
* `Casinogameui` für Tests aufgerufen werden. Im fertigen Spiel erfolgt
|
||||
* der Aufruf von `Casinogameui` über die `Casinomainui`.
|
||||
*
|
||||
* Starte den Client über das Terminal mit:
|
||||
* {@code ./gradlew run --args="client 0.0.0.0:1234"}
|
||||
* Launcher for the Casono main UI.
|
||||
* <p>
|
||||
* Standardkonstruktor für die Anwendung.
|
||||
*/
|
||||
public class Launcher {
|
||||
|
||||
/**
|
||||
* Standardkonstruktor.
|
||||
*/
|
||||
public Launcher() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
/**
|
||||
* Main entry point for launching the UI.
|
||||
* @param args Command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Application.launch(ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui.class, args);
|
||||
Application.launch(CasinoGameUI.class, args);
|
||||
Application.launch(ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||
|
||||
/**
|
||||
* Main UI application for Casono.
|
||||
* Loads the main FXML layout and sets up the stage.
|
||||
*/
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
@@ -7,8 +11,25 @@ import javafx.stage.Stage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JavaFX Application class for the Casono main UI.
|
||||
* <p>
|
||||
* Standardkonstruktor für die Anwendung.
|
||||
*/
|
||||
public class Casinomainui extends Application {
|
||||
|
||||
/**
|
||||
* Standardkonstruktor.
|
||||
*/
|
||||
public Casinomainui() {
|
||||
// Standardkonstruktor
|
||||
}
|
||||
@Override
|
||||
/**
|
||||
* Starts the JavaFX application and loads the main UI.
|
||||
* @param stage The primary stage for this application.
|
||||
* @throws IOException If loading the FXML fails.
|
||||
*/
|
||||
public void start(Stage stage) throws IOException {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ui-structure/Casinomainui.fxml"));
|
||||
Scene scene = new Scene(fxmlLoader.load(), 1200, 800);
|
||||
@@ -20,6 +41,10 @@ public class Casinomainui extends Application {
|
||||
stage.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for launching the application.
|
||||
* @param args Command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
launch();
|
||||
}
|
||||
|
||||
+59
-23
@@ -1,22 +1,21 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
/**
|
||||
* Controller for the Casono main UI lobby.
|
||||
* Handles UI initialization and user actions.
|
||||
*/
|
||||
public class CasinomainuiController {
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootPane;
|
||||
@FXML
|
||||
@@ -24,26 +23,63 @@ public class CasinomainuiController {
|
||||
@FXML
|
||||
private Label subtitleLabel;
|
||||
@FXML
|
||||
private javafx.scene.image.ImageView logoView;
|
||||
private ImageView logoView;
|
||||
@FXML
|
||||
private javafx.scene.shape.Rectangle greenBox;
|
||||
private Rectangle greenBox;
|
||||
@FXML
|
||||
private Button exitbutton;
|
||||
|
||||
|
||||
@FXML
|
||||
private VBox casinoTable;
|
||||
|
||||
private LobbyButtonTranslationManager translationManager;
|
||||
private LobbyButtonGridManager gridManager;
|
||||
private int nextButtonId = 1;
|
||||
|
||||
public CasinomainuiController() {
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the UI components and sets default values.
|
||||
*/
|
||||
@FXML
|
||||
public void initialize() {
|
||||
titleLabel.setText("Casono");
|
||||
subtitleLabel.setText("Texas Hold'em Poker");
|
||||
// Logo laden
|
||||
logoView.setImage(new javafx.scene.image.Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
||||
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
||||
|
||||
translationManager = new LobbyButtonTranslationManager();
|
||||
gridManager = new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
|
||||
casinoTable.getChildren().clear();
|
||||
casinoTable.getChildren().add(gridManager.getGridPane());
|
||||
gridManager.renderLobbyButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the exit button action to close the application.
|
||||
*/
|
||||
@FXML
|
||||
public void handleexitbutton() {
|
||||
Platform.exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles creation of a new lobby button.
|
||||
*/
|
||||
@FXML
|
||||
public void handleCreateLobbyButton() {
|
||||
if (translationManager.isFull()) {
|
||||
System.out.println("Grid voll! Keine weiteren Lobbys moeglich.");
|
||||
return;
|
||||
}
|
||||
int buttonId = nextButtonId++;
|
||||
int lobbyId = gridManager.createLobby();
|
||||
try {
|
||||
translationManager.addLobbyButton(buttonId, lobbyId);
|
||||
System.out.println("ButtonID: " + buttonId + ", LobbyID: " + lobbyId);
|
||||
gridManager.renderLobbyButtons();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Fehler beim Hinzufügen: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||
|
||||
/**
|
||||
* Manages the grid for lobby buttons and rendering.
|
||||
* Uses LobbyButtonTranslationManager for mapping ButtonID to LobbyID.
|
||||
*/
|
||||
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages the grid for lobby buttons and rendering.
|
||||
* Uses LobbyButtonTranslationManager for mapping ButtonID to LobbyID.
|
||||
*/
|
||||
public class LobbyButtonGridManager {
|
||||
|
||||
/** GridPane for the button grid. */
|
||||
private final GridPane gridPane;
|
||||
/** Manager for mapping ButtonID to LobbyID. */
|
||||
private final LobbyButtonTranslationManager translationManager;
|
||||
/** Number of rows in the grid. */
|
||||
private final int rows = 2;
|
||||
/** Number of columns in the grid. */
|
||||
private final int cols = 4;
|
||||
/** Path to the button image. */
|
||||
private final String buttonImagePath = "/images/logo.png";
|
||||
|
||||
/**
|
||||
* Constructor for the GridManager.
|
||||
*
|
||||
* @param gridPane the GridPane for rendering
|
||||
* @param translationManager the manager for mapping ButtonID to LobbyID
|
||||
*/
|
||||
public LobbyButtonGridManager(GridPane gridPane, LobbyButtonTranslationManager translationManager) {
|
||||
this.gridPane = gridPane;
|
||||
this.translationManager = translationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all lobby buttons in the grid.
|
||||
* Creates a button for each mapping with image and event handler.
|
||||
*/
|
||||
public void renderLobbyButtons() {
|
||||
gridPane.getChildren().clear();
|
||||
int index = 0;
|
||||
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
|
||||
if (mapping.isEmpty()) {
|
||||
// No buttons to render
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) {
|
||||
int buttonId = entry.getKey();
|
||||
Button btn = new Button();
|
||||
btn.setId("lobbyBtn-" + buttonId);
|
||||
btn.setGraphic(new ImageView(new Image(getClass().getResourceAsStream(buttonImagePath))));
|
||||
btn.setOnAction(e -> {
|
||||
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
|
||||
if (lobbyId != null)
|
||||
joinLobby(lobbyId);
|
||||
});
|
||||
int row = index / cols;
|
||||
int col = index % cols;
|
||||
gridPane.add(btn, col, row);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for lobby creation logic. Returns a generated lobbyId.
|
||||
*
|
||||
* @return The generated lobbyId
|
||||
*/
|
||||
public int createLobby() {
|
||||
// TODO: Replace with actual lobby creation logic
|
||||
int lobbyId = (int) (Math.random() * 10000 + 1);
|
||||
System.out.println("Lobby created: " + lobbyId);
|
||||
return lobbyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for joining a lobby.
|
||||
*
|
||||
* @param lobbyId The lobbyId to join
|
||||
*/
|
||||
public void joinLobby(int lobbyId) {
|
||||
// TODO: Replace with actual join logic
|
||||
System.out.println("Joining lobby: " + lobbyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the GridPane.
|
||||
*
|
||||
* @return The GridPane for the button grid
|
||||
*/
|
||||
public javafx.scene.layout.GridPane getGridPane() {
|
||||
return gridPane;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher.
|
||||
* Keine Dateioperationen, nur Laufzeitdatenstruktur.
|
||||
*/
|
||||
public class LobbyButtonTranslationManager {
|
||||
/** Maximale Anzahl an Buttons/Lobbys */
|
||||
private static final int MAX_BUTTONS = 8;
|
||||
/** Zuordnung ButtonID → LobbyID */
|
||||
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Konstruktor: initialisiert die Zuordnung leer.
|
||||
*/
|
||||
public LobbyButtonTranslationManager() {
|
||||
// Zuordnung bleibt leer beim Start
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht).
|
||||
* @return true, wenn Grid voll; sonst false
|
||||
*/
|
||||
public boolean isFull() {
|
||||
return buttonIdToLobbyId.size() >= MAX_BUTTONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt eine Zuordnung ButtonID → LobbyID hinzu.
|
||||
* @param buttonId Die ID des Buttons
|
||||
* @param lobbyId Die ID der Lobby
|
||||
* @throws Exception wenn das Grid voll ist
|
||||
*/
|
||||
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
||||
if (isFull()) throw new Exception("Grid is full!");
|
||||
buttonIdToLobbyId.put(buttonId, lobbyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt eine Zuordnung für die gegebene ButtonID.
|
||||
* @param buttonId Die ID des zu entfernenden Buttons
|
||||
*/
|
||||
public void removeLobbyButton(int buttonId) {
|
||||
buttonIdToLobbyId.remove(buttonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die LobbyID für eine gegebene ButtonID zurück.
|
||||
* @param buttonId Die ButtonID
|
||||
* @return Die zugehoerige LobbyID oder null, falls nicht vorhanden
|
||||
*/
|
||||
public Integer getLobbyIdForButton(int buttonId) {
|
||||
return buttonIdToLobbyId.get(buttonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die gesamte Zuordnung ButtonID → LobbyID zurück.
|
||||
* @return Map aller Zuordnungen
|
||||
*/
|
||||
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
||||
return buttonIdToLobbyId;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,22 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
|
||||
/**
|
||||
* Application class for starting the server.
|
||||
*/
|
||||
public class ServerApp {
|
||||
public static void start(String arg) {
|
||||
int port = Integer.parseInt(arg);
|
||||
System.out.println("You've selected the server. It will accept connections at port " + port);
|
||||
|
||||
EventBus eventBus = new EventBus();
|
||||
SessionManager sessionManager = new SessionManager();
|
||||
eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.removeSession(event.sessionId()));
|
||||
NetworkManager networkManager = new NetworkManager(port, sessionManager, eventBus);
|
||||
networkManager.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport;
|
||||
|
||||
/**
|
||||
* Creates and manages the server socket. Accepts new incoming connections and creates sessions.
|
||||
*/
|
||||
public class NetworkManager implements Runnable {
|
||||
private Integer port;
|
||||
private Logger logger;
|
||||
private Thread thread;
|
||||
private Boolean running;
|
||||
private SessionManager sessionManager;
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Creates a new NetworkManager with the given port, session manager, and event bus.
|
||||
*
|
||||
* @param port the port to listen on
|
||||
* @param sessionManager the session manager to use
|
||||
* @param eventBus the event bus for events
|
||||
*/
|
||||
public NetworkManager(Integer port, SessionManager sessionManager, EventBus eventBus) {
|
||||
this.port = port;
|
||||
this.logger = LogManager.getLogger(NetworkManager.class);
|
||||
this.thread = new Thread(this, "networkManager");
|
||||
this.running = true;
|
||||
this.sessionManager = sessionManager;
|
||||
this.eventBus = eventBus;
|
||||
this.eventBus.subscribe(DisconnectEvent.class, event -> clientDisconnected(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the internal thread to accept new connections.
|
||||
*/
|
||||
public void start() {
|
||||
logger.debug("Starting server at port " + port);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles client disconnection events.
|
||||
*
|
||||
* @param event the disconnect event
|
||||
*/
|
||||
public void clientDisconnected(DisconnectEvent event) {
|
||||
logger.info("Session " + event.sessionId().value() + " disconnected adhasghd");
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the network manager loop, accepting connections.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try (ServerSocket serverSocket = new ServerSocket(port)) {
|
||||
while (running) {
|
||||
Socket clientSocket = serverSocket.accept();
|
||||
|
||||
System.out.println("Accepted connection from " + clientSocket.getRemoteSocketAddress());
|
||||
|
||||
Session session = new Session(new TcpTransport(clientSocket), eventBus);
|
||||
sessionManager.addSession(session);
|
||||
session.start();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.fatal(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||
|
||||
/**
|
||||
* Represents a disconnect event for a session.
|
||||
*/
|
||||
public record DisconnectEvent(SessionId sessionId) implements Event {}
|
||||
@@ -0,0 +1,6 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
||||
|
||||
/**
|
||||
* Marker interface for events in the event bus system.
|
||||
*/
|
||||
interface Event {}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A simple event bus for publishing and subscribing to events.
|
||||
*/
|
||||
public class EventBus {
|
||||
private final Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Subscribes a handler to a specific event type.
|
||||
*
|
||||
* @param eventType the class of the event to subscribe to
|
||||
* @param handler the consumer to handle the event
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // This cast is safe, because handlers only get passed the type they subscribed to
|
||||
public <T extends Event> void subscribe(Class<T> eventType, Consumer<T> handler) {
|
||||
handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
||||
.add((Consumer<Object>) (Consumer<?>) handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an event to all subscribed handlers.
|
||||
*
|
||||
* @param event the event to publish
|
||||
*/
|
||||
public <T extends Event> void publish(T event) {
|
||||
Objects.requireNonNull(event, "event must not be null");
|
||||
List<Consumer<Object>> subscribers = handlers.get(event.getClass());
|
||||
if (subscribers != null) {
|
||||
subscribers.forEach(h -> h.accept(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||
|
||||
/**
|
||||
* Represents a client session in the network server.
|
||||
*/
|
||||
public class Session implements Runnable {
|
||||
private SessionId id;
|
||||
private Thread thread;
|
||||
private TransportLayer transport;
|
||||
private Logger logger;
|
||||
private Boolean running;
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Creates a new Session with the given transport and event bus.
|
||||
*
|
||||
* @param transport the transport layer for communication
|
||||
* @param eventBus the event bus for publishing events
|
||||
* @throws IOException if an I/O error occurs during initialization
|
||||
*/
|
||||
public Session(TransportLayer transport, EventBus eventBus) throws IOException {
|
||||
this.id = new SessionId();
|
||||
this.thread = new Thread(this, "session-" + this.id.value());
|
||||
this.transport = transport;
|
||||
this.running = true;
|
||||
this.eventBus = eventBus;
|
||||
|
||||
this.logger = LogManager.getLogger(Session.class.toString() + id.value());
|
||||
this.logger.info("Created new session");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of this session.
|
||||
*
|
||||
* @return the session ID
|
||||
*/
|
||||
public SessionId getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session thread.
|
||||
*/
|
||||
public void start() {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the session and its transport.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
transport.close();
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the session loop, reading from the transport.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
try {
|
||||
System.out.println("Recieved: " + transport.read());
|
||||
} catch (EOFException e) {
|
||||
logger.info("Client disconnected");
|
||||
eventBus.publish(new DisconnectEvent(id));
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a unique identifier for a session.
|
||||
*/
|
||||
public class SessionId {
|
||||
private final UUID value;
|
||||
|
||||
/**
|
||||
* Creates a new SessionId with a randomly generated UUID.
|
||||
*/
|
||||
public SessionId() {
|
||||
this.value = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SessionId with the specified UUID.
|
||||
*
|
||||
* @param UUID to use for this SessionId
|
||||
*/
|
||||
public SessionId(UUID value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UUID value of this SessionId.
|
||||
*
|
||||
* @return the UUID value
|
||||
*/
|
||||
public UUID value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages active sessions in the server.
|
||||
*/
|
||||
public class SessionManager {
|
||||
private Map<SessionId, Session> sessions;
|
||||
|
||||
/**
|
||||
* Constructs a new SessionManager.
|
||||
*/
|
||||
public SessionManager() {
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a session to the manager.
|
||||
*
|
||||
* @param session the session to add
|
||||
*/
|
||||
public void addSession(Session session) {
|
||||
sessions.put(session.getId(), session);
|
||||
System.out.println("Added session " + session.getId().value() + " to session manager");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a session by its ID.
|
||||
*
|
||||
* @param id the ID of the session to remove
|
||||
* @return the removed session, or null if not found
|
||||
*/
|
||||
public Session removeSession(SessionId id) {
|
||||
System.out.println("Removed session " + id.value() + " from session manager");
|
||||
return sessions.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified session.
|
||||
*
|
||||
* @param session the session to remove
|
||||
* @return the removed session, or null if not found
|
||||
*/
|
||||
public Session removeSession(Session session) {
|
||||
return sessions.remove(session.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a session by its ID.
|
||||
*
|
||||
* @param id the ID of the session to retrieve
|
||||
* @return the session with the specified ID, or null if not found
|
||||
*/
|
||||
public Session getSessionById(SessionId id) {
|
||||
return sessions.get(id);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.transport;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Implements TCP-based transport layer for network communication.
|
||||
*/
|
||||
public class TcpTransport implements TransportLayer {
|
||||
private Socket socket;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
|
||||
/**
|
||||
* Creates a new TcpTransport with the given socket.
|
||||
*
|
||||
* @param socket the socket to use for communication
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public TcpTransport(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
this.in = new DataInputStream(socket.getInputStream());
|
||||
this.out = new DataOutputStream(socket.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string from the socket.
|
||||
*
|
||||
* @return the read string
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public String read() throws IOException {
|
||||
int length = in.readInt();
|
||||
byte[] payload = new byte[length];
|
||||
in.readFully(payload);
|
||||
return new String(payload, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the socket.
|
||||
*
|
||||
* @param payload the string to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void write(String payload) throws IOException {
|
||||
byte[] rawPayload = payload.getBytes(StandardCharsets.UTF_8);
|
||||
out.writeInt(rawPayload.length);
|
||||
out.write(rawPayload);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the socket.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for transport layer implementations.
|
||||
*/
|
||||
public interface TransportLayer {
|
||||
/**
|
||||
* Reads data from the transport layer.
|
||||
*
|
||||
* @return the read data as a string
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
String read() throws IOException;
|
||||
|
||||
/**
|
||||
* Writes data to the transport layer.
|
||||
*
|
||||
* @param data the data to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void write(String data) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the transport layer.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
Reference in New Issue
Block a user