feat: Multiplayer #2

Merged
jzitnik merged 14 commits from multiplayer into main 2026-02-04 10:33:25 +00:00
11 changed files with 129 additions and 10 deletions
Showing only changes of commit 24f54b8b7a - Show all commits

View File

@@ -0,0 +1,6 @@
package cz.jzitnik.common.socket.messages.game.connection;
import cz.jzitnik.common.socket.SocketMessage;
public record ConnectToAGame(String gamePass) implements SocketMessage {
}

View File

@@ -0,0 +1,23 @@
package cz.jzitnik.common.socket.messages.game.connection;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.SocketMessage;
public record ConnectToAGameResponse(ResponseType responseType, PlayerCreation playerCreation) implements SocketMessage {
private enum ResponseType {
GAME_DOES_NOT_EXIST,
SUCCESS
}
public ConnectToAGameResponse() {
this(ResponseType.GAME_DOES_NOT_EXIST, null);
}
public ConnectToAGameResponse(PlayerCreation playerCreation) {
this(ResponseType.SUCCESS, playerCreation);
}
public boolean success() {
return responseType == ResponseType.SUCCESS;
}
}

View File

@@ -23,6 +23,7 @@ import cz.jzitnik.client.ui.utils.Input;
import cz.jzitnik.client.utils.DependencyManager; import cz.jzitnik.client.utils.DependencyManager;
import cz.jzitnik.client.utils.TextRenderer; import cz.jzitnik.client.utils.TextRenderer;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGame;
import cz.jzitnik.common.socket.messages.game.creation.CreateGame; import cz.jzitnik.common.socket.messages.game.creation.CreateGame;
import jakarta.websocket.DeploymentException; import jakarta.websocket.DeploymentException;
import cz.jzitnik.client.ui.utils.Button; import cz.jzitnik.client.ui.utils.Button;
@@ -432,6 +433,7 @@ public class ServerChoose extends Scene {
if (event.getKeyStroke().getKeyType() == KeyType.Enter) { if (event.getKeyStroke().getKeyType() == KeyType.Enter) {
connecting = true; connecting = true;
String pass = passBuffer.toString(); String pass = passBuffer.toString();
eventManager.emitEvent(new SendSocketMessageEvent(new ConnectToAGame(pass)));
return; return;
} }

View File

@@ -0,0 +1,33 @@
package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.FullRoomDraw;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SocketEventHandler(ConnectToAGameResponse.class)
public class ConnectGameHandler extends AbstractSocketEventHandler<ConnectToAGameResponse> {
@InjectState
private GameState gameState;
@InjectDependency
private EventManager eventManager;
@Override
public void handle(ConnectToAGameResponse event) {
if (!event.success()) {
log.debug("Error");
return;
}
gameState.setPlayer(new Player(event.playerCreation()));
gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw());
}
}

View File

@@ -9,7 +9,9 @@ import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.socket.AbstractSocketEventHandler; import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse; import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SocketEventHandler(CreateGameResponse.class) @SocketEventHandler(CreateGameResponse.class)
public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResponse> { public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResponse> {
@InjectState @InjectState
@@ -19,6 +21,7 @@ public class CreateGameHandler extends AbstractSocketEventHandler<CreateGameResp
@Override @Override
public void handle(CreateGameResponse event) { public void handle(CreateGameResponse event) {
log.debug("Game code: {}", event.getGamePassword());
gameState.setPlayer(new Player(event.getOwnerPlayer())); gameState.setPlayer(new Player(event.getOwnerPlayer()));
gameState.setScreen(null); gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw()); eventManager.emitEvent(new FullRoomDraw());

View File

@@ -9,10 +9,7 @@ import lombok.Setter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap; import java.util.*;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
public class GlobalContext { public class GlobalContext {
@Getter @Getter
@@ -32,6 +29,10 @@ public class GlobalContext {
sessions.put(client.getSession().getSession(), client); sessions.put(client.getSession().getSession(), client);
} }
public Optional<Game> getGame(String pass) {
return games.stream().filter(game -> game.getPassword().equals(pass)).findFirst();
}
public GlobalContext() { public GlobalContext() {
Properties props = new Properties(); Properties props = new Properties();

View File

@@ -1,8 +1,12 @@
package cz.jzitnik.server.events; package cz.jzitnik.server.events;
import cz.jzitnik.common.socket.SocketMessage; import cz.jzitnik.common.socket.SocketMessage;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public abstract class AbstractEventHandler<T extends SocketMessage> { public abstract class AbstractEventHandler<T extends SocketMessage> {
protected final GlobalContext globalContext;
public abstract void handle(T event, Client client); public abstract void handle(T event, Client client);
} }

View File

@@ -0,0 +1,46 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGame;
import cz.jzitnik.common.socket.messages.game.connection.ConnectToAGameResponse;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.game.Player;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader;
import tools.jackson.dataformat.yaml.YAMLFactory;
@Slf4j
@EventHandler(ConnectToAGame.class)
public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame> {
public ConnectToAGameHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(ConnectToAGame event, Client client) {
log.debug("Pepa");
var gameOptional = globalContext.getGame(event.gamePass().toUpperCase());
if (gameOptional.isEmpty()) {
client.getSession().sendMessage(new ConnectToAGameResponse());
return;
}
var game = gameOptional.get();
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class);
PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml"));
client.setPlayer(new Player(game.getPlayers().size(), player.getPlayerCords()));
client.setGame(game);
client.getPlayer().setCurrentRoom(globalContext.getProperties().getProperty("rooms.default"));
game.getPlayers().add(client);
client.getSession().sendMessage(new ConnectToAGameResponse(player));
}
}

View File

@@ -19,10 +19,11 @@ import tools.jackson.dataformat.yaml.YAMLFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@RequiredArgsConstructor
@EventHandler(CreateGame.class) @EventHandler(CreateGame.class)
public class CreateGameHandler extends AbstractEventHandler<CreateGame> { public class CreateGameHandler extends AbstractEventHandler<CreateGame> {
private final GlobalContext globalContext; public CreateGameHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override @Override
public void handle(CreateGame event, Client client) { public void handle(CreateGame event, Client client) {

View File

@@ -6,12 +6,12 @@ import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext; import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler; import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@EventHandler(PlayerMove.class) @EventHandler(PlayerMove.class)
public class PlayerMoveHandler extends AbstractEventHandler<PlayerMove> { public class PlayerMoveHandler extends AbstractEventHandler<PlayerMove> {
private final GlobalContext globalContext; public PlayerMoveHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override @Override
public void handle(PlayerMove event, Client client) { public void handle(PlayerMove event, Client client) {

View File

@@ -3,7 +3,7 @@ package cz.jzitnik.server.utils;
import java.security.SecureRandom; import java.security.SecureRandom;
public class PasswordGenerator { public class PasswordGenerator {
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final SecureRandom RANDOM = new SecureRandom(); private static final SecureRandom RANDOM = new SecureRandom();
public static String generatePassword(int length) { public static String generatePassword(int length) {