feat: Multiplayer #3

Merged
jzitnik merged 14 commits from multiplayer into main 2026-02-04 10:37:42 +00:00
8 changed files with 163 additions and 14 deletions
Showing only changes of commit 9ca3a8d34c - Show all commits

View File

@@ -1,6 +1,8 @@
package cz.jzitnik.common.socket.messages.player; package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage; import cz.jzitnik.common.socket.SocketMessage;
public record PlayerArrivalChange(int id, boolean arrived) implements SocketMessage { public record PlayerArrivalChange(int id, RoomCords playerCords, boolean arrived,
boolean rerender) implements SocketMessage {
} }

View File

@@ -0,0 +1,8 @@
package cz.jzitnik.common.socket.messages.room;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage;
public record MovePlayerRoom(String newRoomId, RoomCords oldCords, RoomCords newCords) implements SocketMessage {
}

View File

@@ -0,0 +1,8 @@
package cz.jzitnik.common.socket.messages.room;
import cz.jzitnik.common.socket.SocketMessage;
import java.util.Set;
public record MovePlayerRoomResponse(Set<Integer> players) implements SocketMessage {
}

View File

@@ -3,14 +3,15 @@ package cz.jzitnik.client.events.handlers;
import cz.jzitnik.client.annotations.EventHandler; import cz.jzitnik.client.annotations.EventHandler;
import cz.jzitnik.client.annotations.injectors.InjectDependency; import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState; import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.FullRoomDraw;
import cz.jzitnik.client.events.RoomChangeEvent; import cz.jzitnik.client.events.RoomChangeEvent;
import cz.jzitnik.client.events.SendSocketMessageEvent;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.utils.events.AbstractEventHandler; import cz.jzitnik.client.utils.events.AbstractEventHandler;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler; import cz.jzitnik.client.utils.roomtasks.RoomTaskScheduler;
import cz.jzitnik.common.socket.messages.room.MovePlayerRoom;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -31,6 +32,7 @@ public class RoomChangeEventHandler extends AbstractEventHandler<RoomChangeEvent
@Override @Override
public void handle(RoomChangeEvent event) { public void handle(RoomChangeEvent event) {
RoomCords playerCords = gameState.getPlayer().getPlayerCords(); RoomCords playerCords = gameState.getPlayer().getPlayerCords();
RoomCords oldCords = playerCords.clone();
GameRoom currentRoom = gameState.getCurrentRoom(); GameRoom currentRoom = gameState.getCurrentRoom();
GameRoom newRoom = switch (event.door()) { GameRoom newRoom = switch (event.door()) {
case LEFT -> currentRoom.getLeft(); case LEFT -> currentRoom.getLeft();
@@ -50,10 +52,9 @@ public class RoomChangeEventHandler extends AbstractEventHandler<RoomChangeEvent
case BOTTOM -> playerCords.updateCords(playerCords.getX(), 10); case BOTTOM -> playerCords.updateCords(playerCords.getX(), 10);
} }
eventManager.emitEvent(new SendSocketMessageEvent(new MovePlayerRoom(newRoom.getId(), oldCords, playerCords)));
gameState.setCurrentRoom(newRoom); gameState.setCurrentRoom(newRoom);
scheduler.schedule(() -> { scheduler.schedule(() -> roomTaskScheduler.setupNewSchedulers(newRoom), 200, TimeUnit.MILLISECONDS);
roomTaskScheduler.setupNewSchedulers(newRoom);
}, 200, TimeUnit.MILLISECONDS);
eventManager.emitEvent(new FullRoomDraw());
} }
} }

View File

@@ -0,0 +1,29 @@
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.OtherPlayer;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.room.MovePlayerRoomResponse;
@SocketEventHandler(MovePlayerRoomResponse.class)
public class MovePlayerRoomResponseHandler extends AbstractSocketEventHandler<MovePlayerRoomResponse> {
@InjectDependency
private EventManager eventManager;
@InjectState
private GameState gameState;
@Override
public void handle(MovePlayerRoomResponse event) {
for (OtherPlayer player : gameState.getAllOtherPlayers()) {
player.setVisible(event.players().contains(player.getId()));
}
eventManager.emitEvent(new FullRoomDraw());
}
}

View File

@@ -1,24 +1,86 @@
package cz.jzitnik.client.socket.events; package cz.jzitnik.client.socket.events;
import com.googlecode.lanterna.TerminalPosition;
import cz.jzitnik.client.annotations.SocketEventHandler; import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectConfig;
import cz.jzitnik.client.annotations.injectors.InjectDependency;
import cz.jzitnik.client.annotations.injectors.InjectState; import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.events.MouseMoveEvent;
import cz.jzitnik.client.events.RerenderScreen;
import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.OtherPlayer; import cz.jzitnik.client.game.OtherPlayer;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.socket.AbstractSocketEventHandler; import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.states.ScreenBuffer;
import cz.jzitnik.client.states.TerminalState;
import cz.jzitnik.client.ui.Stats;
import cz.jzitnik.client.utils.RerenderUtils;
import cz.jzitnik.client.utils.events.Event;
import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange; import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@Slf4j
@SocketEventHandler(PlayerArrivalChange.class) @SocketEventHandler(PlayerArrivalChange.class)
public class PlayerArrivalChangeHandler extends AbstractSocketEventHandler<PlayerArrivalChange> { public class PlayerArrivalChangeHandler extends AbstractSocketEventHandler<PlayerArrivalChange> {
@InjectState @InjectState
private GameState gameState; private GameState gameState;
@InjectDependency
private ResourceManager resourceManager;
@InjectDependency
private EventManager eventManager;
@InjectState
private TerminalState terminalState;
@InjectState
private ScreenBuffer screenBuffer;
@InjectConfig
private Debugging debugging;
@Override @Override
public void handle(PlayerArrivalChange event) { public void handle(PlayerArrivalChange event) {
var allPlayers = gameState.getAllOtherPlayers(); log.debug("Player appear change: {}", event.id());
for (OtherPlayer player : allPlayers) { OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.id()).findFirst().get();
if (player.getId() == event.id()) { otherPlayer.setVisible(event.arrived());
player.setVisible(event.arrived()); otherPlayer.getPlayerCords().updateCords(event.playerCords());
}
} GameRoom currentRoom = gameState.getCurrentRoom();
BufferedImage playerTexture = otherPlayer.getTexture(resourceManager);
BufferedImage room = resourceManager.getResource(currentRoom.getTexture());
int forStartX = event.playerCords().getX();
int forStartY = event.playerCords().getY();
int forEndX = forStartX + playerTexture.getWidth();
int forEndY = forStartY + playerTexture.getHeight();
var start = RerenderUtils.getStart(room, terminalState.getTerminalScreen().getTerminalSize());
int startX = start.getX();
int startY = start.getY();
RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, gameState.getPlayer(), screenBuffer, resourceManager, debugging, gameState.getOtherPlayers());
eventManager.emitEvent(new Event[]{
new MouseMoveEvent(null),
new RerenderScreen(
new RerenderScreen.ScreenPart[]{
new RerenderScreen.ScreenPart(
new TerminalPosition(forStartX + startX, forStartY + startY),
new TerminalPosition(forEndX + 1 + startX, forEndY + startY)
),
new RerenderScreen.ScreenPart(
new TerminalPosition(Stats.OFFSET_X, Stats.OFFSET_X),
new TerminalPosition(Stats.OFFSET_X + Stats.WIDTH, Stats.OFFSET_Y + Stats.HEIGHT)
)
}
)
});
} }
} }

View File

@@ -49,8 +49,8 @@ public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame>
cl.getSession().sendMessage(new PlayerJoined(player)); cl.getSession().sendMessage(new PlayerJoined(player));
if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) { if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) {
cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), true)); cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), player.getPlayerCords(), true, true));
client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), true)); client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), cl.getPlayer().getCords(), true, false));
} }
} }

View File

@@ -0,0 +1,39 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange;
import cz.jzitnik.common.socket.messages.room.MovePlayerRoom;
import cz.jzitnik.common.socket.messages.room.MovePlayerRoomResponse;
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 lombok.extern.slf4j.Slf4j;
import java.util.stream.Collectors;
@Slf4j
@EventHandler(MovePlayerRoom.class)
public class MovePlayerRoomHandler extends AbstractEventHandler<MovePlayerRoom> {
public MovePlayerRoomHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(MovePlayerRoom event, Client client) {
String oldRoomId = client.getPlayer().getCurrentRoom();
client.getSession().sendMessage(new MovePlayerRoomResponse(client.getGame().getPlayers().stream().filter(player -> player.getPlayer().getCurrentRoom().equals(event.newRoomId())).map(client1 -> client1.getPlayer().getId()).collect(Collectors.toSet())));
client.getPlayer().setCurrentRoom(event.newRoomId());
for (Client player : client.getGame().getPlayers()) {
if (player.getPlayer().getId() != client.getPlayer().getId()) {
if (player.getPlayer().getCurrentRoom().equals(oldRoomId)) {
log.debug("{}", event.oldCords());
player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.oldCords(), false, true));
} else if (player.getPlayer().getCurrentRoom().equals(event.newRoomId())) {
player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.newCords(), true, true));
}
}
}
}
}