docs: Add javadoc
This commit is contained in:
@@ -10,7 +10,18 @@ import org.reflections.Reflections;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Main entry point for the game server.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class Main {
|
||||
/**
|
||||
* Starts the server and initializes the application context.
|
||||
*
|
||||
* @param args Command line arguments
|
||||
* @throws DeploymentException If server deployment fails
|
||||
*/
|
||||
public static void main(String[] args) throws DeploymentException {
|
||||
GlobalContext globalContext = new GlobalContext();
|
||||
AppContext.set(globalContext);
|
||||
|
||||
@@ -7,8 +7,18 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark a class as an event handler for a specific SocketMessage.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface EventHandler {
|
||||
/**
|
||||
* The class of the SocketMessage this handler handles.
|
||||
*
|
||||
* @return The message class
|
||||
*/
|
||||
Class<? extends SocketMessage> value();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
package cz.jzitnik.server.context;
|
||||
|
||||
/**
|
||||
* Global application context holder.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class AppContext {
|
||||
private static GlobalContext globalContext;
|
||||
|
||||
/**
|
||||
* Sets the global context.
|
||||
*
|
||||
* @param context The context to set
|
||||
*/
|
||||
public static void set(GlobalContext context) {
|
||||
globalContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global context.
|
||||
*
|
||||
* @return The global context
|
||||
*/
|
||||
public static GlobalContext get() {
|
||||
return globalContext;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
package cz.jzitnik.server.context;
|
||||
|
||||
/**
|
||||
* Manages game instances (currently empty).
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class GameManager {
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Holds global server state and configuration.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class GlobalContext {
|
||||
@Getter
|
||||
private final HashMap<Session, Client> sessions = new HashMap<>();
|
||||
@@ -25,14 +30,28 @@ public class GlobalContext {
|
||||
@Getter
|
||||
private final Properties properties;
|
||||
|
||||
/**
|
||||
* Registers a client with their session.
|
||||
*
|
||||
* @param client The client to register
|
||||
*/
|
||||
public void registerClient(Client client) {
|
||||
sessions.put(client.getSession().getSession(), client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a game by its password.
|
||||
*
|
||||
* @param pass The game password
|
||||
* @return An Optional containing the game if found
|
||||
*/
|
||||
public Optional<Game> getGame(String pass) {
|
||||
return games.stream().filter(game -> game.getPassword().equals(pass)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a GlobalContext and loads configuration properties.
|
||||
*/
|
||||
public GlobalContext() {
|
||||
Properties props = new Properties();
|
||||
|
||||
|
||||
@@ -5,8 +5,21 @@ import cz.jzitnik.server.context.GlobalContext;
|
||||
import cz.jzitnik.server.game.Client;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Base class for all socket event handlers.
|
||||
*
|
||||
* @param <T> The type of SocketMessage this handler handles
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractEventHandler<T extends SocketMessage> {
|
||||
protected final GlobalContext globalContext;
|
||||
|
||||
/**
|
||||
* Handles the received socket event.
|
||||
*
|
||||
* @param event The event message
|
||||
* @param client The client who sent the event
|
||||
*/
|
||||
public abstract void handle(T event, Client client);
|
||||
}
|
||||
|
||||
@@ -14,18 +14,38 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Manages the dispatching and handling of socket events.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Slf4j
|
||||
public class EventManager extends Thread {
|
||||
private ExecutorService eventExecutor;
|
||||
private final HashMap<Class<? extends SocketMessage>, AbstractEventHandler<?>> handlers = new HashMap<>();
|
||||
private final BlockingQueue<Registry> eventQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
/**
|
||||
* Internal registry to store event and associated client.
|
||||
*/
|
||||
private record Registry(SocketMessage event, Client client) {}
|
||||
|
||||
/**
|
||||
* Emits an event to be handled by the manager.
|
||||
*
|
||||
* @param event The socket event message
|
||||
* @param client The client who sent the event
|
||||
*/
|
||||
public void emitEvent(SocketMessage event, Client client) {
|
||||
eventQueue.add(new Registry(event, client));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an EventManager by discovering handlers using reflection.
|
||||
*
|
||||
* @param reflections The reflection instance to scan for handlers
|
||||
* @param globalContext The global context to pass to handlers
|
||||
*/
|
||||
public EventManager(Reflections reflections, GlobalContext globalContext) {
|
||||
setDaemon(true);
|
||||
|
||||
@@ -43,6 +63,9 @@ public class EventManager extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main execution loop for processing events in the queue.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
eventExecutor = Executors.newFixedThreadPool(6);
|
||||
@@ -58,11 +81,24 @@ public class EventManager extends Thread {
|
||||
//eventExecutor.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a handler for a specific message type.
|
||||
*
|
||||
* @param type The type of the message
|
||||
* @param <T> The message type
|
||||
* @return The associated event handler
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends SocketMessage> AbstractEventHandler<T> getHandler(Class<T> type) {
|
||||
return (AbstractEventHandler<T>) handlers.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an individual event using its associated handler.
|
||||
*
|
||||
* @param event The event message
|
||||
* @param client The client who sent it
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void handleEvent(SocketMessage event, Client client) {
|
||||
eventExecutor.submit(() -> {
|
||||
|
||||
@@ -15,12 +15,28 @@ import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.ObjectReader;
|
||||
import tools.jackson.dataformat.yaml.YAMLFactory;
|
||||
|
||||
/**
|
||||
* Handler for the ConnectToAGame event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(ConnectToAGame.class)
|
||||
public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame> {
|
||||
/**
|
||||
* Constructs a ConnectToAGameHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public ConnectToAGameHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the ConnectToAGame event by validating the game password and adding the player to the game.
|
||||
*
|
||||
* @param event The ConnectToAGame event message
|
||||
* @param client The client who wants to connect
|
||||
*/
|
||||
@Override
|
||||
public void handle(ConnectToAGame event, Client client) {
|
||||
var gameOptional = globalContext.getGame(event.gamePass().toUpperCase());
|
||||
|
||||
@@ -19,12 +19,28 @@ import tools.jackson.dataformat.yaml.YAMLFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handler for the CreateGame event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(CreateGame.class)
|
||||
public class CreateGameHandler extends AbstractEventHandler<CreateGame> {
|
||||
/**
|
||||
* Constructs a CreateGameHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public CreateGameHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the CreateGame event by creating a new game instance and initializing the owner player.
|
||||
*
|
||||
* @param event The CreateGame event message
|
||||
* @param client The client who requested game creation
|
||||
*/
|
||||
@Override
|
||||
public void handle(CreateGame event, Client client) {
|
||||
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
|
||||
|
||||
@@ -6,12 +6,28 @@ import cz.jzitnik.server.context.GlobalContext;
|
||||
import cz.jzitnik.server.events.AbstractEventHandler;
|
||||
import cz.jzitnik.server.game.Client;
|
||||
|
||||
/**
|
||||
* Handler for the GameWin event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(GameWin.class)
|
||||
public class GameWinHandler extends AbstractEventHandler<GameWin> {
|
||||
/**
|
||||
* Constructs a GameWinHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public GameWinHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the GameWin event by notifying all players in the game.
|
||||
*
|
||||
* @param event The GameWin event message
|
||||
* @param client The client who triggered the win
|
||||
*/
|
||||
@Override
|
||||
public void handle(GameWin event, Client client) {
|
||||
for (Client player : client.getGame().getPlayers()) {
|
||||
|
||||
@@ -6,12 +6,28 @@ import cz.jzitnik.server.context.GlobalContext;
|
||||
import cz.jzitnik.server.events.AbstractEventHandler;
|
||||
import cz.jzitnik.server.game.Client;
|
||||
|
||||
/**
|
||||
* Handler for the ItemTookFromChest event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(ItemTookFromChest.class)
|
||||
public class ItemTookFromChestHandler extends AbstractEventHandler<ItemTookFromChest> {
|
||||
/**
|
||||
* Constructs an ItemTookFromChestHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public ItemTookFromChestHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the ItemTookFromChest event by recording the modifier and notifying all players.
|
||||
*
|
||||
* @param event The ItemTookFromChest event message
|
||||
* @param client The client who took the item
|
||||
*/
|
||||
@Override
|
||||
public void handle(ItemTookFromChest event, Client client) {
|
||||
client.getGame().getItemModifiers().add(event);
|
||||
|
||||
@@ -11,13 +11,29 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Handler for the MovePlayerRoom event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Slf4j
|
||||
@EventHandler(MovePlayerRoom.class)
|
||||
public class MovePlayerRoomHandler extends AbstractEventHandler<MovePlayerRoom> {
|
||||
/**
|
||||
* Constructs a MovePlayerRoomHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public MovePlayerRoomHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the MovePlayerRoom event by moving the player to a new room and notifying other players.
|
||||
*
|
||||
* @param event The MovePlayerRoom event message
|
||||
* @param client The client who is moving
|
||||
*/
|
||||
@Override
|
||||
public void handle(MovePlayerRoom event, Client client) {
|
||||
String oldRoomId = client.getPlayer().getCurrentRoom();
|
||||
|
||||
@@ -6,12 +6,28 @@ import cz.jzitnik.server.context.GlobalContext;
|
||||
import cz.jzitnik.server.events.AbstractEventHandler;
|
||||
import cz.jzitnik.server.game.Client;
|
||||
|
||||
/**
|
||||
* Handler for the PlayerDeath event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(PlayerDeath.class)
|
||||
public class PlayerDeathHandler extends AbstractEventHandler<PlayerDeath> {
|
||||
/**
|
||||
* Constructs a PlayerDeathHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public PlayerDeathHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the PlayerDeath event by notifying all players in the game.
|
||||
*
|
||||
* @param event The PlayerDeath event message
|
||||
* @param client The client who died (or reported the death)
|
||||
*/
|
||||
@Override
|
||||
public void handle(PlayerDeath event, Client client) {
|
||||
for (Client player : client.getGame().getPlayers()) {
|
||||
|
||||
@@ -7,12 +7,28 @@ import cz.jzitnik.server.context.GlobalContext;
|
||||
import cz.jzitnik.server.events.AbstractEventHandler;
|
||||
import cz.jzitnik.server.game.Client;
|
||||
|
||||
/**
|
||||
* Handler for the PlayerMove event.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@EventHandler(PlayerMove.class)
|
||||
public class PlayerMoveHandler extends AbstractEventHandler<PlayerMove> {
|
||||
/**
|
||||
* Constructs a PlayerMoveHandler.
|
||||
*
|
||||
* @param globalContext The global context
|
||||
*/
|
||||
public PlayerMoveHandler(GlobalContext globalContext) {
|
||||
super(globalContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the PlayerMove event by updating the player's position and notifying others in the same room.
|
||||
*
|
||||
* @param event The PlayerMove event message
|
||||
* @param client The client who moved
|
||||
*/
|
||||
@Override
|
||||
public void handle(PlayerMove event, Client client) {
|
||||
client.getPlayer().getCords().updateCords(event.newCords());
|
||||
|
||||
@@ -4,6 +4,11 @@ import cz.jzitnik.server.socket.SocketSession;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Represents a connected client in the server.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Getter
|
||||
public final class Client {
|
||||
private final SocketSession session;
|
||||
@@ -12,12 +17,25 @@ public final class Client {
|
||||
@Setter
|
||||
private Game game;
|
||||
|
||||
/**
|
||||
* Constructs a Client with session, player and game.
|
||||
*
|
||||
* @param session The socket session
|
||||
* @param player The player associated with the client
|
||||
* @param game The game the client is part of
|
||||
*/
|
||||
public Client(SocketSession session, Player player, Game game) {
|
||||
this.session = session;
|
||||
this.player = player;
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Client with session and player.
|
||||
*
|
||||
* @param session The socket session
|
||||
* @param player The player associated with the client
|
||||
*/
|
||||
public Client(SocketSession session, Player player) {
|
||||
this(session, player, null);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@ import lombok.Getter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a game instance on the server.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class Game {
|
||||
|
||||
@@ -6,6 +6,11 @@ import cz.jzitnik.common.socket.messages.player.PlayerRotation;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Represents a player in the game.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Getter
|
||||
public class Player {
|
||||
private final int id;
|
||||
@@ -15,12 +20,22 @@ public class Player {
|
||||
@Setter
|
||||
private String currentRoom;
|
||||
|
||||
/**
|
||||
* Constructs a Player from a PlayerCreation object.
|
||||
*
|
||||
* @param creation The player creation data
|
||||
*/
|
||||
public Player(PlayerCreation creation) {
|
||||
id = creation.getId();
|
||||
cords = creation.getPlayerCords();
|
||||
playerRotation = PlayerRotation.FRONT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the player state to a PlayerCreation object.
|
||||
*
|
||||
* @return A new PlayerCreation instance
|
||||
*/
|
||||
public PlayerCreation toPlayerCreation() {
|
||||
return new PlayerCreation(cords, null);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,20 @@ import jakarta.websocket.HandshakeResponse;
|
||||
import jakarta.websocket.server.HandshakeRequest;
|
||||
import jakarta.websocket.server.ServerEndpointConfig;
|
||||
|
||||
/**
|
||||
* Configurator for the WebSocket endpoint to inject the GlobalContext.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class GlobalContextConfigurator extends ServerEndpointConfig.Configurator {
|
||||
|
||||
/**
|
||||
* Modifies the handshake to include the GlobalContext in user properties.
|
||||
*
|
||||
* @param sec The server endpoint configuration
|
||||
* @param request The handshake request
|
||||
* @param response The handshake response
|
||||
*/
|
||||
@Override
|
||||
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
|
||||
sec.getUserProperties().put("globalContext", AppContext.get());
|
||||
|
||||
@@ -9,11 +9,21 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* Wrapper for a WebSocket session to facilitate sending messages.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class SocketSession {
|
||||
private final Session session;
|
||||
|
||||
/**
|
||||
* Sends a SocketMessage to the client.
|
||||
*
|
||||
* @param message The message to send
|
||||
*/
|
||||
public void sendMessage(SocketMessage message) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
@@ -11,12 +11,23 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* WebSocket endpoint for handling game socket communication.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
@Slf4j
|
||||
@ServerEndpoint(value = "/ws", configurator = GlobalContextConfigurator.class)
|
||||
public class WebSocket {
|
||||
|
||||
private GlobalContext globalContext;
|
||||
|
||||
/**
|
||||
* Called when a new WebSocket connection is opened.
|
||||
*
|
||||
* @param session The WebSocket session
|
||||
* @param config The endpoint configuration
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
this.globalContext = (GlobalContext) config.getUserProperties().get("globalContext");
|
||||
@@ -25,6 +36,12 @@ public class WebSocket {
|
||||
log.debug("Client connected: {}", session.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a binary message is received.
|
||||
*
|
||||
* @param bytes The received bytes
|
||||
* @param session The WebSocket session
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(byte[] bytes, Session session) {
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
@@ -37,6 +54,12 @@ public class WebSocket {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a WebSocket connection is closed.
|
||||
*
|
||||
* @param session The WebSocket session
|
||||
* @param reason The reason for closing
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(Session session, CloseReason reason) {
|
||||
Client client = globalContext.getSessions().get(session);
|
||||
@@ -59,6 +82,12 @@ public class WebSocket {
|
||||
log.debug("Connection closed: {}", reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a WebSocket error occurs.
|
||||
*
|
||||
* @param session The WebSocket session
|
||||
* @param throwable The error that occurred
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
|
||||
@@ -2,10 +2,21 @@ package cz.jzitnik.server.utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Utility class for generating random passwords.
|
||||
*
|
||||
* @author Jakub Žitník (jzitnik)
|
||||
*/
|
||||
public class PasswordGenerator {
|
||||
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* Generates a random alphanumeric password of specified length.
|
||||
*
|
||||
* @param length The length of the password to generate
|
||||
* @return The generated password string
|
||||
*/
|
||||
public static String generatePassword(int length) {
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Password length must be greater than 0");
|
||||
|
||||
Reference in New Issue
Block a user