Compare commits

..

10 Commits

Author SHA1 Message Date
cdd002ea20 feat: Synchronize game items
kinda
2026-02-03 21:40:13 +01:00
faadc92dbc feat: Player disconnect handler 2026-02-03 20:52:36 +01:00
f6de75afd9 feat: Store player rotation 2026-02-03 09:54:23 +01:00
1f54e933ad chore: Remove todo 2026-02-02 22:04:54 +01:00
9ca3a8d34c feat: Multiplayer player moving between rooms 2026-02-02 21:59:55 +01:00
e3f5c12b43 feat: Player movement multiplayer 2026-02-02 21:06:46 +01:00
24f54b8b7a feat: Connecting to a game 2026-02-02 19:18:25 +01:00
92eb93815d feat: Some more multiplayer stuff
Hello miss Meitnerova... If you are not Meitnerova but some random
person stalking my projects go get a life.

Anyways have a great time reading this awful code :)
2026-02-02 18:51:51 +01:00
f987b2bed6 chore: Add logs to gitignore 2026-02-01 15:23:37 +01:00
b79c2f3373 chore: Remove logs 2026-02-01 15:23:20 +01:00
58 changed files with 1360 additions and 380 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
logs

3
.idea/compiler.xml generated
View File

@@ -14,7 +14,6 @@
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" /> <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
</processorPath> </processorPath>
<module name="common" /> <module name="common" />
</profile> </profile>
@@ -24,7 +23,6 @@
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" /> <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
</processorPath> </processorPath>
<module name="server" /> <module name="server" />
</profile> </profile>
@@ -34,7 +32,6 @@
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false"> <processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" /> <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" />
</processorPath> </processorPath>
<module name="game (1)" /> <module name="game (1)" />
</profile> </profile>

2
.idea/misc.xml generated
View File

@@ -8,5 +8,5 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK" />
</project> </project>

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.common;
public class Config {
public static final int WORLD_PASSWORD_LENGTH = 5;
}

View File

@@ -29,6 +29,10 @@ public class RoomCords implements Cloneable, Serializable {
this.y = y; this.y = y;
} }
public void updateCords(RoomCords roomCords) {
updateCords(roomCords.getX(), roomCords.getY());
}
public void updateCordsWithColliders(List<RoomPart> colliders, int x, int y, RoomPart playerCollider) { public void updateCordsWithColliders(List<RoomPart> colliders, int x, int y, RoomPart playerCollider) {
var normalizedPlayerCollider = new RoomPart( var normalizedPlayerCollider = new RoomPart(
new RoomCords(playerCollider.getStart().getX() + x, playerCollider.getStart().getY() + y), new RoomCords(playerCollider.getStart().getX() + x, playerCollider.getStart().getY() + y),

View File

@@ -5,11 +5,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.coordinates.RoomPart; import cz.jzitnik.common.models.coordinates.RoomPart;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import java.io.Serializable; import java.io.Serializable;
@Getter @Getter
public final class PlayerCreation implements Serializable { public final class PlayerCreation implements Serializable {
@Setter
private int id;
private final RoomCords playerCords; private final RoomCords playerCords;
private final RoomPart collider; private final RoomPart collider;

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,25 @@
package cz.jzitnik.common.socket.messages.game.connection;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.SocketMessage;
import java.util.List;
public record ConnectToAGameResponse(ResponseType responseType, PlayerCreation playerCreation, List<PlayerCreation> existingPlayers) implements SocketMessage {
private enum ResponseType {
GAME_DOES_NOT_EXIST,
SUCCESS
}
public ConnectToAGameResponse() {
this(ResponseType.GAME_DOES_NOT_EXIST, null, null);
}
public ConnectToAGameResponse(PlayerCreation playerCreation, List<PlayerCreation> existingPlayers) {
this(ResponseType.SUCCESS, playerCreation, existingPlayers);
}
public boolean success() {
return responseType == ResponseType.SUCCESS;
}
}

View File

@@ -0,0 +1,9 @@
package cz.jzitnik.common.socket.messages.items;
import cz.jzitnik.common.socket.SocketMessage;
public record ItemTookFromChest(
String roomId, // For faster lookup i guess
int id
) implements SocketMessage {
}

View File

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

View File

@@ -0,0 +1,6 @@
package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.socket.SocketMessage;
public record PlayerDisconnected(int playerId) implements SocketMessage {
}

View File

@@ -0,0 +1,7 @@
package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.SocketMessage;
public record PlayerJoined(PlayerCreation playerCreation) implements SocketMessage {
}

View File

@@ -0,0 +1,7 @@
package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage;
public record PlayerMove(RoomCords newCords, PlayerRotation playerRotation) implements SocketMessage {
}

View File

@@ -0,0 +1,14 @@
package cz.jzitnik.common.socket.messages.player;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class PlayerMovedInUrRoom implements SocketMessage {
private int playerId;
private RoomCords cords;
private PlayerRotation playerRotation;
}

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.common.socket.messages.player;
public enum PlayerRotation {
FRONT, BACK, LEFT, RIGHT
}

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,22 @@
package cz.jzitnik.common.socket.messages.room;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.socket.SocketMessage;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import java.io.Serializable;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public record MovePlayerRoomResponse(Set<Registry> players) implements SocketMessage {
public record Registry(int id, RoomCords cords, PlayerRotation playerRotation) implements Serializable {}
public Optional<Registry> getById(int id) {
return players.stream().filter(registry -> registry.id == id).findFirst();
}
public Set<Integer> getIds() {
return players.stream().map(Registry::id).collect(Collectors.toSet());
}
}

View File

@@ -75,7 +75,7 @@ public class FullRoomDrawHandler extends AbstractEventHandler<FullRoomDraw> {
int startX = start.getX(); int startX = start.getX();
int startY = start.getY(); int startY = start.getY();
RerenderUtils.rerenderPart(0, width - 1, 0, height - 1, startX, startY, currentRoom, room, player, playerTexture, screenBuffer, resourceManager, debugging); RerenderUtils.rerenderPart(0, width - 1, 0, height - 1, startX, startY, currentRoom, room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers());
if (event.isFullRerender()) { if (event.isFullRerender()) {
globalIOHandlerRepository.renderAll(); globalIOHandlerRepository.renderAll();
} }

View File

@@ -177,10 +177,10 @@ public class MouseMoveEventHandler extends AbstractEventHandler<MouseMoveEvent>
currentRoom, currentRoom,
room, room,
player, player,
playerTexture,
screenBuffer, screenBuffer,
resourceManager, resourceManager,
debugging debugging,
gameState.getOtherPlayers()
); );
parts.add(new RerenderScreen.ScreenPart( parts.add(new RerenderScreen.ScreenPart(

View File

@@ -8,14 +8,8 @@ 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.config.Debugging;
import cz.jzitnik.client.config.PlayerConfig; import cz.jzitnik.client.config.PlayerConfig;
import cz.jzitnik.client.events.MouseMoveEvent; import cz.jzitnik.client.events.*;
import cz.jzitnik.client.events.PlayerMoveEvent; import cz.jzitnik.client.game.*;
import cz.jzitnik.client.events.RerenderScreen;
import cz.jzitnik.client.events.RoomChangeEvent;
import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.states.RenderState; import cz.jzitnik.client.states.RenderState;
import cz.jzitnik.client.states.ScreenBuffer; import cz.jzitnik.client.states.ScreenBuffer;
@@ -26,6 +20,8 @@ import cz.jzitnik.client.utils.RerenderUtils;
import cz.jzitnik.client.utils.events.AbstractEventHandler; import cz.jzitnik.client.utils.events.AbstractEventHandler;
import cz.jzitnik.client.utils.events.Event; import cz.jzitnik.client.utils.events.Event;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.player.PlayerMove;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -79,46 +75,46 @@ public class PlayerMoveEventHandler extends AbstractEventHandler<PlayerMoveEvent
case 'w' -> { case 'w' -> {
if (originalPlayerY <= 10) { if (originalPlayerY <= 10) {
if (originalPlayerX >= 80 && originalPlayerX <= 105) { if (originalPlayerX >= 80 && originalPlayerX <= 105) {
player.setPlayerRotation(Player.PlayerRotation.BACK); player.setPlayerRotation(PlayerRotation.BACK);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.TOP)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.TOP));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() - moveStep, player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() - moveStep, player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.BACK); player.setPlayerRotation(PlayerRotation.BACK);
} }
case 'a' -> { case 'a' -> {
if (originalPlayerX <= 30) { if (originalPlayerX <= 30) {
if (originalPlayerY >= 35 && originalPlayerY <= 65) { if (originalPlayerY >= 35 && originalPlayerY <= 65) {
player.setPlayerRotation(Player.PlayerRotation.LEFT); player.setPlayerRotation(PlayerRotation.LEFT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.LEFT)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.LEFT));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() - moveStep, playerCords.getY(), player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() - moveStep, playerCords.getY(), player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.LEFT); player.setPlayerRotation(PlayerRotation.LEFT);
} }
case 's' -> { case 's' -> {
if (originalPlayerY >= 110) { if (originalPlayerY >= 110) {
if (originalPlayerX >= 75 && originalPlayerX <= 105) { if (originalPlayerX >= 75 && originalPlayerX <= 105) {
player.setPlayerRotation(Player.PlayerRotation.FRONT); player.setPlayerRotation(PlayerRotation.FRONT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.BOTTOM)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.BOTTOM));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() + moveStep, player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX(), playerCords.getY() + moveStep, player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.FRONT); player.setPlayerRotation(PlayerRotation.FRONT);
} }
case 'd' -> { case 'd' -> {
if (originalPlayerX >= 155) { if (originalPlayerX >= 155) {
if (originalPlayerY >= 40 && originalPlayerY <= 60) { if (originalPlayerY >= 40 && originalPlayerY <= 60) {
player.setPlayerRotation(Player.PlayerRotation.RIGHT); player.setPlayerRotation(PlayerRotation.RIGHT);
eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.RIGHT)); eventManager.emitEvent(new RoomChangeEvent(FullRoomDrawHandler.DoorPosition.RIGHT));
} }
return; return;
} }
playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() + moveStep, playerCords.getY(), player.getCollider()); playerCords.updateCordsWithColliders(currentRoom.getColliders(), player.getPlayerCords().getX() + moveStep, playerCords.getY(), player.getCollider());
player.setPlayerRotation(Player.PlayerRotation.RIGHT); player.setPlayerRotation(PlayerRotation.RIGHT);
} }
} }
playerMovementState.setLastMovement(System.currentTimeMillis()); playerMovementState.setLastMovement(System.currentTimeMillis());
@@ -143,9 +139,10 @@ public class PlayerMoveEventHandler extends AbstractEventHandler<PlayerMoveEvent
int startX = start.getX(); int startX = start.getX();
int startY = start.getY(); int startY = start.getY();
RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, player, playerTexture, screenBuffer, resourceManager, debugging); RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, currentRoom, room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers());
eventManager.emitEvent(new Event[]{ eventManager.emitEvent(new Event[]{
new SendSocketMessageEvent(new PlayerMove(playerCords, player.getPlayerRotation())),
new MouseMoveEvent(null), new MouseMoveEvent(null),
new RerenderScreen( new RerenderScreen(
new RerenderScreen.ScreenPart[]{ new RerenderScreen.ScreenPart[]{

View File

@@ -62,10 +62,10 @@ public class RerenderPartHandler extends AbstractEventHandler<RerenderPart> {
currentRoom, currentRoom,
room, room,
gameState.getPlayer(), gameState.getPlayer(),
gameState.getPlayer().getTexture(resourceManager),
screenBuffer, screenBuffer,
resourceManager, resourceManager,
debugging debugging,
gameState.getOtherPlayers()
); );
eventManager.emitEvent( eventManager.emitEvent(

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,10 @@
package cz.jzitnik.client.game;
import cz.jzitnik.common.models.coordinates.RoomCords;
import java.awt.image.BufferedImage;
public interface GamePlayer {
RoomCords getPlayerCords();
BufferedImage getTexture(ResourceManager resourceManager);
}

View File

@@ -8,6 +8,9 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
@State @State
public class GameState { public class GameState {
@@ -17,10 +20,24 @@ public class GameState {
@Setter @Setter
private GameRoom currentRoom; private GameRoom currentRoom;
@Getter
@Setter
private List<GameRoom> allRooms;
@Getter @Getter
@Setter @Setter
private Player player; private Player player;
private final List<OtherPlayer> otherPlayers = new ArrayList<>();
public List<OtherPlayer> getOtherPlayers() {
return otherPlayers.stream().filter(OtherPlayer::isVisible).toList();
}
public List<OtherPlayer> getAllOtherPlayers() {
return otherPlayers;
}
@Getter @Getter
@Setter @Setter
private Interactable interacting; private Interactable interacting;

View File

@@ -0,0 +1,41 @@
package cz.jzitnik.client.game;
import cz.jzitnik.client.game.mobs.HittableMob;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.Getter;
import lombok.Setter;
import java.awt.image.BufferedImage;
@Getter
public class OtherPlayer implements GamePlayer {
private final int id;
private boolean hitAnimationOn = false;
private final RoomCords playerCords;
@Setter
private PlayerRotation playerRotation = PlayerRotation.FRONT;
@Setter
private boolean visible;
public OtherPlayer(PlayerCreation playerCreation) {
this.id = playerCreation.getId();
this.playerCords = playerCreation.getPlayerCords();
}
public BufferedImage getTexture(ResourceManager resourceManager) {
BufferedImage resource = resourceManager.getResource(switch (playerRotation) {
case FRONT -> ResourceManager.Resource.PLAYER_FRONT;
case BACK -> ResourceManager.Resource.PLAYER_BACK;
case LEFT -> ResourceManager.Resource.PLAYER_LEFT;
case RIGHT -> ResourceManager.Resource.PLAYER_RIGHT;
});
if (hitAnimationOn) {
return HittableMob.applyRedFactor(resource);
}
return resource;
}
}

View File

@@ -1,7 +1,5 @@
package cz.jzitnik.client.game; package cz.jzitnik.client.game;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.client.events.RerenderPart; import cz.jzitnik.client.events.RerenderPart;
import cz.jzitnik.client.game.items.GameItem; import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.items.types.interfaces.WeaponInterface; import cz.jzitnik.client.game.items.types.interfaces.WeaponInterface;
@@ -13,6 +11,7 @@ import cz.jzitnik.client.utils.DependencyManager;
import cz.jzitnik.client.utils.events.Event; import cz.jzitnik.client.utils.events.Event;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -25,7 +24,8 @@ import java.util.concurrent.TimeUnit;
@Getter @Getter
@Slf4j @Slf4j
public class Player { public class Player implements GamePlayer {
private final int id;
public static final int MAX_STAMINA = 20; public static final int MAX_STAMINA = 20;
public static final int MAX_HEALTH = 30; public static final int MAX_HEALTH = 30;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@@ -43,18 +43,10 @@ public class Player {
private boolean hitAnimationOn = false; private boolean hitAnimationOn = false;
private ScheduledFuture<?> currentTimeoutHitAnimation = null; private ScheduledFuture<?> currentTimeoutHitAnimation = null;
@JsonCreator
public Player(
@JsonProperty("playerCords") RoomCords playerCords,
@JsonProperty("collider") RoomPart collider
) {
this.playerCords = playerCords;
this.collider = collider;
}
public Player(PlayerCreation playerCreation) { public Player(PlayerCreation playerCreation) {
this.playerCords = playerCreation.getPlayerCords(); this.playerCords = playerCreation.getPlayerCords();
this.collider = playerCreation.getCollider(); this.collider = playerCreation.getCollider();
this.id = playerCreation.getId();
} }
public void increaseStamina() { public void increaseStamina() {
@@ -164,7 +156,4 @@ public class Player {
}, delayMs, TimeUnit.MILLISECONDS); }, delayMs, TimeUnit.MILLISECONDS);
} }
public enum PlayerRotation {
FRONT, BACK, LEFT, RIGHT
}
} }

View File

@@ -14,14 +14,17 @@ public class GameItem implements Renderable {
@JsonIgnore @JsonIgnore
private final BufferedImage texture; private final BufferedImage texture;
private final String name; private final String name;
private final int id;
@JsonCreator @JsonCreator
public GameItem( public GameItem(
@JsonProperty("id") int id,
@JsonProperty("name") String name, @JsonProperty("name") String name,
@JsonProperty("type") ItemType<?> type, @JsonProperty("type") ItemType<?> type,
@JsonProperty("texture") ResourceManager.Resource resource, @JsonProperty("texture") ResourceManager.Resource resource,
@JacksonInject ResourceManager resourceManager @JacksonInject ResourceManager resourceManager
) { ) {
this.id = id;
this.name = name; this.name = name;
this.type = type; this.type = type;
this.texture = resourceManager.getResource(resource); this.texture = resourceManager.getResource(resource);

View File

@@ -96,7 +96,7 @@ public class MobFollowingPlayerTask extends MobRoomTask {
Player player = gameState.getPlayer(); Player player = gameState.getPlayer();
BufferedImage playerTexture = player.getTexture(resourceManager); BufferedImage playerTexture = player.getTexture(resourceManager);
RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, gameState.getCurrentRoom(), room, player, playerTexture, screenBuffer, resourceManager, debugging); RerenderUtils.rerenderPart(forStartX, forEndX, forStartY, forEndY, startX, startY, gameState.getCurrentRoom(), room, player, screenBuffer, resourceManager, debugging, gameState.getOtherPlayers());
eventManager.emitEvent(new Event[]{ eventManager.emitEvent(new Event[]{
new MouseMoveEvent(null), new MouseMoveEvent(null),

View File

@@ -10,10 +10,7 @@ import cz.jzitnik.client.annotations.injectors.InjectConfig;
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.config.Debugging; import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.events.InventoryRerender; import cz.jzitnik.client.events.*;
import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.events.RerenderPart;
import cz.jzitnik.client.events.RerenderScreen;
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.client.game.Player; import cz.jzitnik.client.game.Player;
@@ -30,9 +27,12 @@ import cz.jzitnik.client.ui.pixels.Pixel;
import cz.jzitnik.client.utils.RerenderUtils; import cz.jzitnik.client.utils.RerenderUtils;
import cz.jzitnik.client.utils.UIRoomClickHandlerRepository; import cz.jzitnik.client.utils.UIRoomClickHandlerRepository;
import cz.jzitnik.client.utils.events.EventManager; import cz.jzitnik.client.utils.events.EventManager;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.net.Socket;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -40,8 +40,11 @@ import java.util.List;
public final class Chest extends GameObject implements UIClickHandler { public final class Chest extends GameObject implements UIClickHandler {
private static final int RENDER_PADDING = 1; private static final int RENDER_PADDING = 1;
@Getter
private final List<GameItem> items; private final List<GameItem> items;
private boolean rendered;
private int listenerHashCode; private int listenerHashCode;
private int actualDisplayStartX; private int actualDisplayStartX;
@@ -101,7 +104,6 @@ public final class Chest extends GameObject implements UIClickHandler {
Player player = gameState.getPlayer(); Player player = gameState.getPlayer();
BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture()); BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture());
BufferedImage playerTexture = player.getTexture(resourceManager);
BufferedImage chestTexture = getTexture(); BufferedImage chestTexture = getTexture();
var buffer = screenBuffer.getRenderedBuffer(); var buffer = screenBuffer.getRenderedBuffer();
@@ -141,7 +143,7 @@ public final class Chest extends GameObject implements UIClickHandler {
renderMaxX = Math.max(renderMaxX, prevDisplayStartX + prevWidth); renderMaxX = Math.max(renderMaxX, prevDisplayStartX + prevWidth);
clearPreviousUI( clearPreviousUI(
currentRoom, roomTexture, player, playerTexture, currentRoom, roomTexture, player, resourceManager,
buffer, overrideBuffer, start, buffer, overrideBuffer, start,
guiStartY, guiStartY,
prevGuiStartX, prevGuiStartX,
@@ -165,13 +167,14 @@ public final class Chest extends GameObject implements UIClickHandler {
guiStart.getRow() - start.getY(), guiStart.getRow() - start.getY(),
guiEnd.getRow() - start.getY() guiEnd.getRow() - start.getY()
)); ));
rendered = true;
} }
private void clearPreviousUI( private void clearPreviousUI(
GameRoom room, GameRoom room,
BufferedImage roomTexture, BufferedImage roomTexture,
Player player, Player player,
BufferedImage playerTexture, ResourceManager resourceManager,
Pixel[][] buffer, Pixel[][] buffer,
Pixel[][] overrideBuffer, Pixel[][] overrideBuffer,
RoomCords start, RoomCords start,
@@ -188,10 +191,11 @@ public final class Chest extends GameObject implements UIClickHandler {
null, null,
new HashSet<>(), new HashSet<>(),
player, player,
playerTexture, resourceManager,
x, x,
y, y,
debugging debugging,
gameState.getOtherPlayers()
).pixel(); ).pixel();
buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel); buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel);
@@ -257,15 +261,23 @@ public final class Chest extends GameObject implements UIClickHandler {
if (!added) { if (!added) {
return true; return true;
} }
eventManager.emitEvent(new InventoryRerender());
eventManager.emitEvent(new SendSocketMessageEvent(new ItemTookFromChest(gameState.getCurrentRoom().getId(), item.getId())));
return true;
}
public void handleItemRemoval(GameItem item) {
items.remove(item); items.remove(item);
if (!rendered) {
return;
}
eventManager.emitEvent(new InventoryRerender());
if (items.isEmpty()) { if (items.isEmpty()) {
uiRoomClickHandlerRepository.removeHandlerForCurrentRoom(listenerHashCode); uiRoomClickHandlerRepository.removeHandlerForCurrentRoom(listenerHashCode);
} }
render(true); render(true);
return true;
} }
} }

View File

@@ -47,5 +47,6 @@ public class GameSetup {
); );
gameState.setCurrentRoom(rooms.getFirst()); gameState.setCurrentRoom(rooms.getFirst());
gameState.setAllRooms(rooms);
} }
} }

View File

@@ -2,6 +2,7 @@ package cz.jzitnik.client.game.setup.scenes.connect;
import com.googlecode.lanterna.TerminalSize; import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor; import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.input.KeyType; import com.googlecode.lanterna.input.KeyType;
import com.googlecode.lanterna.screen.TerminalScreen; import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.client.annotations.injectors.InjectDependency; import cz.jzitnik.client.annotations.injectors.InjectDependency;
@@ -9,23 +10,29 @@ import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.events.KeyboardPressEvent; import cz.jzitnik.client.events.KeyboardPressEvent;
import cz.jzitnik.client.events.MouseAction; import cz.jzitnik.client.events.MouseAction;
import cz.jzitnik.client.events.SendSocketMessageEvent; import cz.jzitnik.client.events.SendSocketMessageEvent;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.screens.Screen; import cz.jzitnik.client.screens.Screen;
import cz.jzitnik.client.screens.scenes.Scene; import cz.jzitnik.client.screens.scenes.Scene;
import cz.jzitnik.client.socket.Client; import cz.jzitnik.client.socket.Client;
import cz.jzitnik.client.sound.SoundPlayer; import cz.jzitnik.client.sound.SoundPlayer;
import cz.jzitnik.client.states.TerminalState; import cz.jzitnik.client.states.TerminalState;
import cz.jzitnik.client.ui.Inventory;
import cz.jzitnik.client.ui.pixels.AlphaPixel; import cz.jzitnik.client.ui.pixels.AlphaPixel;
import cz.jzitnik.client.ui.pixels.Empty; import cz.jzitnik.client.ui.pixels.Empty;
import cz.jzitnik.client.ui.utils.Input; 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 lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
@Slf4j
public class ServerChoose extends Scene { public class ServerChoose extends Scene {
public ServerChoose(DependencyManager dependencyManager) { public ServerChoose(DependencyManager dependencyManager) {
GameMenuAudioScreen gameMenuScreen = new GameMenuAudioScreen(); GameMenuAudioScreen gameMenuScreen = new GameMenuAudioScreen();
@@ -78,6 +85,12 @@ public class ServerChoose extends Scene {
@InjectDependency @InjectDependency
private TextRenderer textRenderer; private TextRenderer textRenderer;
@InjectState
private GameState gameState;
@InjectDependency
private DependencyManager dependencyManager;
private void renderInput(boolean refresh) { private void renderInput(boolean refresh) {
var tg = terminalState.getTextGraphics(); var tg = terminalState.getTextGraphics();
TerminalScreen screen = terminalState.getTerminalScreen(); TerminalScreen screen = terminalState.getTerminalScreen();
@@ -136,32 +149,12 @@ public class ServerChoose extends Scene {
AlphaPixel[][] selectServer = textRenderer.renderText("Enter server IP", terminalSize.getColumns(), 20, Color.WHITE, 15f, true); AlphaPixel[][] selectServer = textRenderer.renderText("Enter server IP", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
int padY = 10; render(selectServer, 0, 10, tg);
for (int y = 0; y < selectServer.length; y += 2) {
for (int x = 0; x < selectServer[y].length; x++) {
AlphaPixel topPixel = selectServer[y][x];
AlphaPixel bottomPixel;
if (y + 1 < selectServer.length) {
bottomPixel = selectServer[y + 1][x];
} else {
bottomPixel = new Empty();
}
int termX = x;
int termY = padY / 2 + y / 2;
tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
tg.setCharacter(termX, termY, '▄');
}
}
renderInput(false); renderInput(false);
try { try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA); screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -178,7 +171,10 @@ public class ServerChoose extends Scene {
connecting = true; connecting = true;
client.connect(ipBuffer.toString()); client.connect(ipBuffer.toString());
eventManager.emitEvent(new SendSocketMessageEvent(new CreateGame())); Screen screen = new ActionSelector();
dependencyManager.inject(screen);
gameState.setScreen(screen);
screen.fullRender();
} catch (DeploymentException | IOException e) { } catch (DeploymentException | IOException e) {
connecting = false; connecting = false;
} }
@@ -195,7 +191,7 @@ public class ServerChoose extends Scene {
return; return;
} }
if (event.getKeyStroke().getKeyType() == KeyType.Character) { if (event.getKeyStroke().getKeyType() == KeyType.Character && !event.getKeyStroke().isCtrlDown()) {
ipBuffer.append(event.getKeyStroke().getCharacter()); ipBuffer.append(event.getKeyStroke().getCharacter());
renderInput(true); renderInput(true);
} }
@@ -205,4 +201,283 @@ public class ServerChoose extends Scene {
public void handleMouseAction(MouseAction event) { public void handleMouseAction(MouseAction event) {
} }
} }
private static final class ActionSelector extends Screen {
@InjectDependency
private TextRenderer textRenderer;
@InjectState
private TerminalState terminalState;
@InjectDependency
private EventManager eventManager;
@InjectState
private GameState gameState;
private int selectedIndex = -1;
@Override
public void fullRender() {
TerminalScreen screen = terminalState.getTerminalScreen();
var tg = terminalState.getTextGraphics();
screen.clear();
TerminalSize terminalSize = screen.getTerminalSize();
for (int y = 0; y < terminalSize.getRows(); y += 1) {
for (int x = 0; x < terminalSize.getColumns(); x++) {
tg.setBackgroundColor(TextColor.ANSI.BLACK);
tg.setForegroundColor(TextColor.ANSI.BLACK);
tg.setCharacter(x, y, '▄');
}
}
AlphaPixel[][] selectAction = textRenderer.renderText("Select action", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
render(selectAction, 0, 10, tg);
renderButtons();
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static final int BUTTON_HEIGHT = 20;
private static final int BUTTON_WIDTH = 200;
private static final int BUTTON_GAP = 10;
private static final int BUTTON_COUNT = 2;
private static final int BUTTONS_HEIGHT = BUTTON_HEIGHT * BUTTON_COUNT + (BUTTON_COUNT - 1) * BUTTON_GAP;
private void renderButtons() {
var tg = terminalState.getTextGraphics();
TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize();
final int BUTTON_PAD_X = terminalSize.getColumns() / 2 - BUTTON_WIDTH / 2;
final int BUTTON_PAD_Y = terminalSize.getRows() - BUTTONS_HEIGHT / 2;
Button button = new Button(
Inventory.BORDER_COLOR,
Inventory.BACKGROUND_COLOR,
Inventory.BACKGROUND_COLOR_HOVERED,
Color.WHITE,
BUTTON_HEIGHT,
BUTTON_WIDTH,
1,
15f,
textRenderer
);
render(button.render("Create a world", selectedIndex == 0), BUTTON_PAD_X, BUTTON_PAD_Y, tg);
render(button.render("Connect to an existing world", selectedIndex == 1), BUTTON_PAD_X, BUTTON_PAD_Y + (BUTTON_HEIGHT + BUTTON_GAP), tg);
}
@Override
public void handleMouseAction(MouseAction event) {
TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize();
final int BUTTON_START_X = terminalSize.getColumns() / 2 - BUTTON_WIDTH / 2;
final int BUTTON_START_Y = terminalSize.getRows() - BUTTONS_HEIGHT / 2;
final int BUTTON_END_X = BUTTON_START_X + BUTTON_WIDTH;
final int BUTTON_END_Y = BUTTON_START_Y + BUTTONS_HEIGHT;
final int TERMINAL_X = event.getPosition().getColumn();
final int TERMINAL_Y = event.getPosition().getRow() * 2;
final int SINGLE_BUTTON_HEIGHT = BUTTON_HEIGHT + BUTTON_GAP;
int index = (TERMINAL_Y - BUTTON_START_Y) / SINGLE_BUTTON_HEIGHT;
int rest = (TERMINAL_Y - BUTTON_START_Y) % SINGLE_BUTTON_HEIGHT;
if (!(TERMINAL_X >= BUTTON_START_X && TERMINAL_Y >= BUTTON_START_Y && TERMINAL_X < BUTTON_END_X && TERMINAL_Y < BUTTON_END_Y) || rest > BUTTON_HEIGHT) {
if (selectedIndex != -1) {
selectedIndex = -1;
renderButtons();
refresh();
}
return;
}
switch (event.getActionType()) {
case MOVE -> {
selectedIndex = index;
renderButtons();
refresh();
}
case CLICK_RELEASE -> {
switch (index) {
case 0 -> eventManager.emitEvent(new SendSocketMessageEvent(new CreateGame()));
case 1 -> {
Screen screen = new ConnectWorld();
gameState.setScreen(screen);
screen.fullRender();
}
}
}
}
}
private void refresh() {
try {
terminalState.getTerminalScreen().refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
}
}
private static final class ConnectWorld extends Screen {
private final StringBuilder passBuffer = new StringBuilder();
private boolean connecting = false;
@InjectDependency
private EventManager eventManager;
@InjectDependency
private Client client;
@InjectState
private TerminalState terminalState;
@InjectDependency
private TextRenderer textRenderer;
@InjectState
private GameState gameState;
@InjectDependency
private DependencyManager dependencyManager;
private void renderInput(boolean refresh) {
var tg = terminalState.getTextGraphics();
TerminalScreen screen = terminalState.getTerminalScreen();
Input input = new Input(passBuffer.toString(), 18, 100);
var inputBuffer = input.render(textRenderer);
TerminalSize termSize = screen.getTerminalSize();
int renderPixelWidth = inputBuffer[0].length;
int renderPixelHeight = inputBuffer.length;
int renderCharWidth = renderPixelWidth;
int renderCharHeight = (renderPixelHeight + 1) / 2;
int startX = (termSize.getColumns() - renderCharWidth) / 2;
int startY = (termSize.getRows() - renderCharHeight) / 2;
for (int y = 0; y < inputBuffer.length; y += 2) {
for (int x = 0; x < inputBuffer[y].length; x++) {
AlphaPixel bottomPixel;
AlphaPixel topPixel = inputBuffer[y][x];
if (y + 1 < inputBuffer.length) {
bottomPixel = inputBuffer[y + 1][x];
} else {
bottomPixel = new Empty();
}
int termX = startX + x;
int termY = startY + (y / 2);
tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
tg.setCharacter(termX, termY, '▄');
}
}
if (refresh) {
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.DELTA);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void fullRender() {
TerminalScreen screen = terminalState.getTerminalScreen();
var tg = terminalState.getTextGraphics();
screen.clear();
TerminalSize terminalSize = screen.getTerminalSize();
for (int y = 0; y < terminalSize.getRows(); y += 1) {
for (int x = 0; x < terminalSize.getColumns(); x++) {
tg.setBackgroundColor(TextColor.ANSI.BLACK);
tg.setForegroundColor(TextColor.ANSI.BLACK);
tg.setCharacter(x, y, '▄');
}
}
AlphaPixel[][] selectServer = textRenderer.renderText("Enter world password", terminalSize.getColumns(), 20, Color.WHITE, 15f, true);
render(selectServer, 0, 10, tg);
renderInput(false);
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
if (connecting) {
return;
}
if (event.getKeyStroke().getKeyType() == KeyType.Enter) {
connecting = true;
String pass = passBuffer.toString();
eventManager.emitEvent(new SendSocketMessageEvent(new ConnectToAGame(pass)));
return;
}
if (event.getKeyStroke().getKeyType() == KeyType.Backspace) {
if (passBuffer.isEmpty()) {
return;
}
passBuffer.deleteCharAt(passBuffer.length() - 1);
renderInput(true);
return;
}
if (event.getKeyStroke().getKeyType() == KeyType.Character && !event.getKeyStroke().isCtrlDown()) {
passBuffer.append(event.getKeyStroke().getCharacter());
renderInput(true);
}
}
@Override
public void handleMouseAction(MouseAction event) {
}
}
private static void render(AlphaPixel[][] buffer, int padX, int padY, TextGraphics tg) {
for (int y = 0; y < buffer.length; y += 2) {
for (int x = 0; x < buffer[y].length; x++) {
AlphaPixel topPixel = buffer[y][x];
AlphaPixel bottomPixel;
if (y + 1 < buffer.length) {
bottomPixel = buffer[y + 1][x];
} else {
bottomPixel = new Empty();
}
int termX = padX + x;
int termY = padY / 2 + y / 2;
tg.setBackgroundColor(topPixel instanceof Empty ? TextColor.ANSI.BLACK : topPixel.getColor());
tg.setForegroundColor(bottomPixel instanceof Empty ? TextColor.ANSI.BLACK : bottomPixel.getColor());
tg.setCharacter(termX, termY, '▄');
}
}
}
} }

View File

@@ -0,0 +1,38 @@
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.TerminalResizeEvent;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.OtherPlayer;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.states.TerminalState;
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;
@InjectState
private TerminalState terminalState;
@Override
public void handle(ConnectToAGameResponse event) {
if (!event.success()) {
log.debug("Error");
return;
}
gameState.setPlayer(new Player(event.playerCreation()));
gameState.getAllOtherPlayers().addAll(event.existingPlayers().stream().map(OtherPlayer::new).toList());
gameState.setScreen(null);
eventManager.emitEvent(new TerminalResizeEvent(terminalState.getTerminalScreen().getTerminalSize()));
}
}

View File

@@ -3,24 +3,30 @@ package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler; import cz.jzitnik.client.annotations.SocketEventHandler;
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.TerminalResizeEvent;
import cz.jzitnik.client.game.GameState; import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.Player; import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.socket.AbstractSocketEventHandler; import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.client.states.TerminalState;
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
private GameState gameState; private GameState gameState;
@InjectState
private TerminalState terminalState;
@InjectDependency @InjectDependency
private EventManager eventManager; private EventManager eventManager;
@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 TerminalResizeEvent(terminalState.getTerminalScreen().getTerminalSize()));
} }
} }

View File

@@ -0,0 +1,43 @@
package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.game.GameRoom;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.objects.Chest;
import cz.jzitnik.client.game.objects.GameObject;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
@SocketEventHandler(ItemTookFromChest.class)
public class ItemTookFromChestHandler extends AbstractSocketEventHandler<ItemTookFromChest> {
@InjectState
private GameState gameState;
@Override
public void handle(ItemTookFromChest event) {
for (GameRoom room : gameState.getAllRooms()) {
if (!room.getId().equals(event.roomId())) {
continue;
}
for (GameObject object : room.getObjects()) {
if (object instanceof Chest chest) {
var items = chest.getItems();
for (GameItem item : items) {
if (item.getId() != event.id()) {
continue;
}
items.remove(item);
chest.handleItemRemoval(item);
return;
}
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
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;
import java.util.Set;
@SocketEventHandler(MovePlayerRoomResponse.class)
public class MovePlayerRoomResponseHandler extends AbstractSocketEventHandler<MovePlayerRoomResponse> {
@InjectDependency
private EventManager eventManager;
@InjectState
private GameState gameState;
@Override
public void handle(MovePlayerRoomResponse event) {
Set<Integer> ids = event.getIds();
for (OtherPlayer player : gameState.getAllOtherPlayers()) {
player.setVisible(ids.contains(player.getId()));
var playerRegistryOptional = event.getById(player.getId());
if (player.isVisible() && playerRegistryOptional.isPresent()) {
player.getPlayerCords().updateCords(playerRegistryOptional.get().cords());
player.setPlayerRotation(playerRegistryOptional.get().playerRotation());
}
}
eventManager.emitEvent(new FullRoomDraw());
}
}

View File

@@ -0,0 +1,87 @@
package cz.jzitnik.client.socket.events;
import com.googlecode.lanterna.TerminalPosition;
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.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.OtherPlayer;
import cz.jzitnik.client.game.ResourceManager;
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 lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@Slf4j
@SocketEventHandler(PlayerArrivalChange.class)
public class PlayerArrivalChangeHandler extends AbstractSocketEventHandler<PlayerArrivalChange> {
@InjectState
private GameState gameState;
@InjectDependency
private ResourceManager resourceManager;
@InjectDependency
private EventManager eventManager;
@InjectState
private TerminalState terminalState;
@InjectState
private ScreenBuffer screenBuffer;
@InjectConfig
private Debugging debugging;
@Override
public void handle(PlayerArrivalChange event) {
log.debug("Player appear change: {}", event.id());
OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.id()).findFirst().get();
otherPlayer.setVisible(event.arrived());
otherPlayer.getPlayerCords().updateCords(event.playerCords());
otherPlayer.setPlayerRotation(event.playerRotation());
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

@@ -0,0 +1,18 @@
package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.player.PlayerDisconnected;
@SocketEventHandler(PlayerDisconnected.class)
public class PlayerDisconnectedHandler extends AbstractSocketEventHandler<PlayerDisconnected> {
@InjectState
private GameState gameState;
@Override
public void handle(PlayerDisconnected event) {
gameState.getAllOtherPlayers().removeIf(player -> player.getId() == event.playerId());
}
}

View File

@@ -0,0 +1,22 @@
package cz.jzitnik.client.socket.events;
import cz.jzitnik.client.annotations.SocketEventHandler;
import cz.jzitnik.client.annotations.injectors.InjectState;
import cz.jzitnik.client.game.GameState;
import cz.jzitnik.client.game.OtherPlayer;
import cz.jzitnik.client.socket.AbstractSocketEventHandler;
import cz.jzitnik.common.socket.messages.player.PlayerJoined;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SocketEventHandler(PlayerJoined.class)
public class PlayerJoinedHandler extends AbstractSocketEventHandler<PlayerJoined> {
@InjectState
private GameState gameState;
@Override
public void handle(PlayerJoined event) {
log.debug("Player joined: {}", event.playerCreation().getId());
gameState.getAllOtherPlayers().add(new OtherPlayer(event.playerCreation()));
}
}

View File

@@ -0,0 +1,94 @@
package cz.jzitnik.client.socket.events;
import com.googlecode.lanterna.TerminalPosition;
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.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.OtherPlayer;
import cz.jzitnik.client.game.ResourceManager;
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.PlayerMovedInUrRoom;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@Slf4j
@SocketEventHandler(PlayerMovedInUrRoom.class)
public class PlayerMovedInUrRoomHandler extends AbstractSocketEventHandler<PlayerMovedInUrRoom> {
@InjectState
private GameState gameState;
@InjectDependency
private EventManager eventManager;
@InjectDependency
private ResourceManager resourceManager;
@InjectState
private ScreenBuffer screenBuffer;
@InjectConfig
private Debugging debugging;
@InjectState
private TerminalState terminalState;
@Override
public void handle(PlayerMovedInUrRoom event) {
log.debug("Player moved: {}", event.getPlayerId());
OtherPlayer otherPlayer = gameState.getAllOtherPlayers().stream().filter(otherPlayer1 -> otherPlayer1.getId() == event.getPlayerId()).findFirst().get();
otherPlayer.setPlayerRotation(event.getPlayerRotation());
var oldCords = otherPlayer.getPlayerCords();
var newCords = event.getCords();
GameRoom currentRoom = gameState.getCurrentRoom();
BufferedImage playerTexture = otherPlayer.getTexture(resourceManager);
BufferedImage room = resourceManager.getResource(currentRoom.getTexture());
int originalPlayerX = oldCords.getX();
int originalPlayerY = oldCords.getY();
int newPlayerX = newCords.getX();
int newPlayerY = newCords.getY();
int forStartX = Math.min(originalPlayerX, newPlayerX);
int forStartY = Math.min(originalPlayerY, newPlayerY);
int forEndX = Math.max(originalPlayerX, newPlayerX) + playerTexture.getWidth();
int forEndY = Math.max(originalPlayerY, newPlayerY) + playerTexture.getHeight();
var start = RerenderUtils.getStart(room, terminalState.getTerminalScreen().getTerminalSize());
int startX = start.getX();
int startY = start.getY();
otherPlayer.getPlayerCords().updateCords(newCords);
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

@@ -18,6 +18,7 @@ import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.game.items.GameItem; import cz.jzitnik.client.game.items.GameItem;
import cz.jzitnik.client.game.items.types.InteractableItem; import cz.jzitnik.client.game.items.types.InteractableItem;
import cz.jzitnik.client.game.objects.DroppedItem; import cz.jzitnik.client.game.objects.DroppedItem;
import cz.jzitnik.client.ui.pixels.AlphaPixel;
import cz.jzitnik.common.models.coordinates.RoomCords; import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.client.states.ScreenBuffer; import cz.jzitnik.client.states.ScreenBuffer;
import cz.jzitnik.client.states.TerminalState; import cz.jzitnik.client.states.TerminalState;
@@ -50,17 +51,17 @@ public class Inventory {
private static final int INNER_BORDER_WIDTH = 1; private static final int INNER_BORDER_WIDTH = 1;
private static final int ITEM_SIZE = 16; // Characters private static final int ITEM_SIZE = 16; // Characters
private static final int ITEM_PADDING = 2; // padding on each side private static final int ITEM_PADDING = 2; // padding on each side
public static final Pixel BORDER_COLOR = public static final AlphaPixel BORDER_COLOR =
new ColoredPixel(new TextColor.RGB(41, 29, 19)); new ColoredPixel(new TextColor.RGB(41, 29, 19));
public static final Pixel BACKGROUND_COLOR = public static final AlphaPixel BACKGROUND_COLOR =
new ColoredPixel(new TextColor.RGB(61, 45, 29)); new ColoredPixel(new TextColor.RGB(61, 45, 29));
private static final Pixel BACKGROUND_COLOR_HOVERED = public static final AlphaPixel BACKGROUND_COLOR_HOVERED =
new ColoredPixel(new TextColor.RGB(77, 56, 36)); new ColoredPixel(new TextColor.RGB(77, 56, 36));
private static final Pixel BACKGROUND_COLOR_SELECTED = private static final AlphaPixel BACKGROUND_COLOR_SELECTED =
new ColoredPixel(new TextColor.RGB(95, 62, 32)); new ColoredPixel(new TextColor.RGB(95, 62, 32));
private static final Pixel BACKGROUND_COLOR_EQUIPPED = private static final AlphaPixel BACKGROUND_COLOR_EQUIPPED =
new ColoredPixel(new TextColor.RGB(50, 145, 150)); new ColoredPixel(new TextColor.RGB(50, 145, 150));
private static final Pixel BACKGROUND_COLOR_EQUIPPED_SELECTED = private static final AlphaPixel BACKGROUND_COLOR_EQUIPPED_SELECTED =
new ColoredPixel(new TextColor.RGB(58, 170, 176)); new ColoredPixel(new TextColor.RGB(58, 170, 176));
private static final int ITEM_SLOT_SIZE = ITEM_SIZE + ITEM_PADDING * 2; private static final int ITEM_SLOT_SIZE = ITEM_SIZE + ITEM_PADDING * 2;
public static final int INVENTORY_WIDTH = public static final int INVENTORY_WIDTH =

View File

@@ -10,4 +10,8 @@ public sealed abstract class AlphaPixel extends Pixel permits Empty, ColoredPixe
super(color); super(color);
this.alpha = alpha; this.alpha = alpha;
} }
public boolean isTransparent() {
return alpha == 0f;
}
} }

View File

@@ -0,0 +1,50 @@
package cz.jzitnik.client.ui.utils;
import cz.jzitnik.client.ui.pixels.AlphaPixel;
import cz.jzitnik.client.utils.TextRenderer;
import java.awt.*;
public record Button(
AlphaPixel borderColor,
AlphaPixel backgroundColor,
AlphaPixel backgroundSelectedColor,
Color textColor,
int height,
int width,
int borderWidth,
float fontSize,
TextRenderer textRenderer
) {
public AlphaPixel[][] render(String text, boolean selected) {
AlphaPixel[][] buf = new AlphaPixel[height][width];
AlphaPixel[][] textBuf = textRenderer.renderTextSingleLine(text, width - borderWidth * 2, height - borderWidth * 2, textColor, fontSize, true, true);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x < borderWidth || y < borderWidth || x >= width - borderWidth || y >= height - borderWidth) {
buf[y][x] = borderColor;
continue;
}
if (x == borderWidth || y == borderWidth || x - 1 == width - borderWidth || y == height - borderWidth) {
buf[y][x] = selected ? backgroundSelectedColor : backgroundColor;
continue;
}
int textActualX = x - borderWidth - 1;
int textActualY = y - borderWidth - 1;
AlphaPixel textPixel = textBuf[textActualY][textActualX];
if (!textPixel.isTransparent()) {
buf[y][x] = textPixel;
continue;
}
buf[y][x] = selected ? backgroundSelectedColor : backgroundColor;
}
}
return buf;
}
}

View File

@@ -4,9 +4,7 @@ import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor; import com.googlecode.lanterna.TextColor;
import cz.jzitnik.client.config.Debugging; import cz.jzitnik.client.config.Debugging;
import cz.jzitnik.client.events.handlers.FullRoomDrawHandler; import cz.jzitnik.client.events.handlers.FullRoomDrawHandler;
import cz.jzitnik.client.game.GameRoom; import cz.jzitnik.client.game.*;
import cz.jzitnik.client.game.Player;
import cz.jzitnik.client.game.ResourceManager;
import cz.jzitnik.client.game.mobs.Mob; import cz.jzitnik.client.game.mobs.Mob;
import cz.jzitnik.client.game.objects.DroppedItem; import cz.jzitnik.client.game.objects.DroppedItem;
import cz.jzitnik.client.game.objects.GameObject; import cz.jzitnik.client.game.objects.GameObject;
@@ -19,8 +17,11 @@ import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j @Slf4j
public class RerenderUtils { public class RerenderUtils {
@@ -36,14 +37,14 @@ public class RerenderUtils {
return new RoomCords(x, y); return new RoomCords(x, y);
} }
public static void rerenderPart(int startX, int endX, int startY, int endY, int screenStartX, int screenStartY, GameRoom currentRoom, BufferedImage room, Player player, BufferedImage playerTexture, ScreenBuffer screenBuffer, ResourceManager resourceManager, Debugging debugging) { public static void rerenderPart(int startX, int endX, int startY, int endY, int screenStartX, int screenStartY, GameRoom currentRoom, BufferedImage room, Player player, ScreenBuffer screenBuffer, ResourceManager resourceManager, Debugging debugging, List<OtherPlayer> otherPlayers) {
var buffer = screenBuffer.getRenderedBuffer(); var buffer = screenBuffer.getRenderedBuffer();
var overrideBuffer = currentRoom.getOverrideBuffer(); var overrideBuffer = currentRoom.getOverrideBuffer();
BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS); BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS);
Set<FullRoomDrawHandler.DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom); Set<FullRoomDrawHandler.DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom);
for (int x = startX; x <= endX; x++) { for (int x = startX; x <= endX; x++) {
for (int y = startY; y <= endY; y++) { for (int y = startY; y <= endY; y++) {
PixelResult pixelResult = getPixel(currentRoom, room, doors, doorPositions, player, playerTexture, x, y, debugging); PixelResult pixelResult = getPixel(currentRoom, room, doors, doorPositions, player, resourceManager, x, y, debugging, otherPlayers);
int pixel = pixelResult.pixel(); int pixel = pixelResult.pixel();
int red = (pixel >> 16) & 0xff; int red = (pixel >> 16) & 0xff;
@@ -63,26 +64,34 @@ public class RerenderUtils {
public record PixelResult(int pixel, boolean isPlayer) { public record PixelResult(int pixel, boolean isPlayer) {
} }
public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set<FullRoomDrawHandler.DoorPosition> doorPositions, Player player, BufferedImage playerTexture, int x, int y, Debugging debugging) { public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set<FullRoomDrawHandler.DoorPosition> doorPositions, Player player, ResourceManager resourceManager, int x, int y, Debugging debugging, List<OtherPlayer> visiblePlayer) {
float factor = 1.5f; // brightness multiplier float factor = 1.5f; // brightness multiplier
if (debugging.renderColliders() && currentRoom.getColliders().stream().anyMatch(collider -> collider.isWithin(new RoomCords(x, y)))) { if (debugging.renderColliders() && currentRoom.getColliders().stream().anyMatch(collider -> collider.isWithin(new RoomCords(x, y)))) {
return new PixelResult(0xFFFF0000, false); return new PixelResult(0xFFFF0000, false);
} }
if (x >= player.getPlayerCords().getX() && x < player.getPlayerCords().getX() + playerTexture.getWidth() - 1 && y >= player.getPlayerCords().getY() && y <= player.getPlayerCords().getY() + playerTexture.getHeight() - 1) { List<GamePlayer> players = Stream.concat(
int relativeX = x - player.getPlayerCords().getX(); Stream.of(player),
int relativeY = y - player.getPlayerCords().getY(); visiblePlayer.stream()
var playerCollider = player.getCollider(); ).toList();
if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) { var playerCollider = player.getCollider();
return new PixelResult(0xFFFF0000, false); for (GamePlayer player1 : players) {
} var playerTexture = player1.getTexture(resourceManager);
if (x >= player1.getPlayerCords().getX() && x < player1.getPlayerCords().getX() + playerTexture.getWidth() - 1 && y >= player1.getPlayerCords().getY() && y <= player1.getPlayerCords().getY() + playerTexture.getHeight() - 1) {
int relativeX = x - player1.getPlayerCords().getX();
int relativeY = y - player1.getPlayerCords().getY();
int pixel = playerTexture.getRGB(relativeX, relativeY); if (debugging.renderPlayerCollider() && playerCollider.isWithin(new RoomCords(relativeX, relativeY))) {
int alpha = (pixel >> 24) & 0xff; return new PixelResult(0xFFFF0000, false);
}
if (alpha != 0) { int pixel = playerTexture.getRGB(relativeX, relativeY);
return new PixelResult(pixel, true); int alpha = (pixel >> 24) & 0xff;
if (alpha != 0) {
return new PixelResult(pixel, true);
}
} }
} }

View File

@@ -1,33 +1,33 @@
- id: "spawn" - id: "spawn"
texture: "ROOM1" texture: "ROOM1"
mobs: #mobs:
- type: "hittable_drops" # - type: "hittable_drops"
texture: "PLAYER_FRONT" # texture: "PLAYER_FRONT"
cords: # cords:
x: 100 # x: 100
y: 100 # y: 100
collider: # collider:
start: # start:
x: 0 # x: 0
y: 52 # y: 52
end: # end:
x: 44 # x: 44
y: 78 # y: 78
health: 10 # health: 10
itemsDrops: # itemsDrops:
- name: "Apple" # - name: "Apple"
type: # type:
name: "food" # name: "food"
addHealth: 1 # addHealth: 1
texture: "APPLE" # texture: "APPLE"
tasks: # tasks:
- type: "blind_following_player" # - type: "blind_following_player"
speed: 1 # speed: 1
updateRateMs: 100 # updateRateMs: 100
- type: "attacking_player" # - type: "attacking_player"
damage: 5 # damage: 5
reach: 15 # reach: 15
updateRateMs: 500 # updateRateMs: 500
west: "empty" west: "empty"
east: "truhlaright" east: "truhlaright"
north: null north: null
@@ -41,12 +41,14 @@
x: 100 x: 100
y: 45 y: 45
items: items:
- name: "Wooden sword" - id: 1
name: "Wooden sword"
type: type:
name: "weapon_sword" name: "weapon_sword"
dealDamage: 1 dealDamage: 1
texture: "WOODEN_SWORD" texture: "WOODEN_SWORD"
- name: "Apple" - id: 2
name: "Apple"
type: type:
name: "food" name: "food"
addHealth: 1 addHealth: 1
@@ -78,12 +80,14 @@
x: 100 x: 100
y: 45 y: 45
items: items:
- name: "Wooden sword" - id: 3
name: "Wooden sword"
type: type:
name: "weapon_sword" name: "weapon_sword"
dealDamage: 1 dealDamage: 1
texture: "WOODEN_SWORD" texture: "WOODEN_SWORD"
- name: "Apple" - id: 4
name: "Apple"
type: type:
name: "food" name: "food"
addHealth: 1 addHealth: 1
@@ -124,7 +128,8 @@
y: 78 y: 78
health: 10 health: 10
itemsDrops: itemsDrops:
- name: "Apple" - id: 5
name: "Apple"
type: type:
name: "food" name: "food"
addHealth: 1 addHealth: 1
@@ -159,7 +164,8 @@
y: 78 y: 78
health: 10 health: 10
itemsDrops: itemsDrops:
- name: "Apple" - id: 6
name: "Apple"
type: type:
name: "food" name: "food"
addHealth: 1 addHealth: 1
@@ -189,12 +195,14 @@
x: 100 x: 100
y: 45 y: 45
items: items:
- name: "Wooden sword" - id: 7
name: "Wooden sword"
type: type:
name: "weapon_sword" name: "weapon_sword"
dealDamage: 1 dealDamage: 1
texture: "WOODEN_SWORD" texture: "WOODEN_SWORD"
- name: "Apple" - id: 8
name: "Apple"
type: type:
name: "food" name: "food"
addHealth: 1 addHealth: 1

View File

@@ -1,224 +0,0 @@
2026-02-01 12:16:30 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 69 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:16:31 [Thread-6] INFO cz.jzitnik.client.sound.SoundPlayer - [66] Line started.
2026-02-01 12:18:38 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 63 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:18:39 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:18:47 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 66 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:18:47 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:19:59 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 54 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:19:59 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:20:02 [pool-7-thread-5] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:03 [pool-7-thread-8] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:03 [pool-7-thread-3] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:03 [pool-7-thread-6] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:04 [pool-7-thread-8] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:04 [pool-7-thread-4] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:04 [pool-16-thread-2] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 12:20:05 [pool-16-thread-4] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 12:20:05 [pool-16-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:05 [pool-16-thread-6] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 12:20:06 [pool-16-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:07 [pool-7-thread-4] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:20:08 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:08 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:08 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Health: 9
2026-02-01 12:20:08 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:09 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:09 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:09 [pool-7-thread-5] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:09 [pool-7-thread-5] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:09 [pool-7-thread-5] DEBUG c.j.client.game.mobs.HittableMob - Health: 8
2026-02-01 12:20:09 [pool-7-thread-5] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:09 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:10 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:10 [pool-7-thread-8] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:10 [pool-7-thread-8] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:10 [pool-7-thread-8] DEBUG c.j.client.game.mobs.HittableMob - Health: 7
2026-02-01 12:20:10 [pool-7-thread-8] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:10 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:10 [pool-17-thread-2] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 12:20:10 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:10 [pool-7-thread-4] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:10 [pool-7-thread-4] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:10 [pool-7-thread-4] DEBUG c.j.client.game.mobs.HittableMob - Health: 6
2026-02-01 12:20:10 [pool-7-thread-4] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:10 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:11 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Health: 5
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:11 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:11 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:11 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:11 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:11 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Health: 4
2026-02-01 12:20:11 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:11 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:12 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:12 [pool-7-thread-3] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:12 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Health: 3
2026-02-01 12:20:12 [pool-7-thread-3] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:12 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:12 [pool-7-thread-2] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:12 [pool-7-thread-2] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:12 [pool-7-thread-2] DEBUG c.j.client.game.mobs.HittableMob - Health: 2
2026-02-01 12:20:12 [pool-7-thread-2] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:12 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:13 [pool-7-thread-4] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:13 [pool-7-thread-4] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:13 [pool-7-thread-4] DEBUG c.j.client.game.mobs.HittableMob - Health: 1
2026-02-01 12:20:13 [pool-7-thread-4] DEBUG c.j.client.game.mobs.HittableMob - Hitting start
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-8-thread-1] DEBUG c.j.client.game.mobs.HittableMob - Hitting end
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:13 [pool-17-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 12:20:13 [pool-7-thread-7] DEBUG cz.jzitnik.client.game.Player - Started swinging
2026-02-01 12:20:13 [pool-7-thread-7] DEBUG cz.jzitnik.client.game.Player - Selected item: null
2026-02-01 12:20:13 [pool-7-thread-7] DEBUG c.j.client.game.mobs.HittableMob - Health: 0
2026-02-01 12:20:14 [pool-11-thread-1] DEBUG cz.jzitnik.client.game.Player - Swinging done
2026-02-01 12:20:15 [pool-7-thread-7] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: true
2026-02-01 12:20:15 [pool-3-thread-1] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: false
2026-02-01 12:20:15 [pool-7-thread-5] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: true
2026-02-01 12:20:15 [pool-3-thread-1] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: false
2026-02-01 12:20:16 [pool-7-thread-3] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 12:33:53 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 59 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:33:53 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:34:21 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 63 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:34:21 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:34:50 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 65 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:34:51 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:35:59 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 62 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:36:00 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 12:36:27 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 55 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 12:36:28 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:40:58 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 68 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:40:59 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:41:55 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:42:52 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:43:48 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:44:45 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:55:16 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 75 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:55:17 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:56:06 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 53 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:56:07 [pool-7-thread-1] ERROR c.j.client.utils.events.EventManager - Error
java.lang.ArrayIndexOutOfBoundsException: Index 66 out of bounds for length 66
at cz.jzitnik.client.ui.utils.Input.render(Input.java:45)
at cz.jzitnik.client.game.setup.scenes.connect.ServerChoose$ServerSelector.fullRender(ServerChoose.java:111)
at cz.jzitnik.client.screens.scenes.Scene.render(Scene.java:62)
at cz.jzitnik.client.screens.scenes.Scene.fullRender(Scene.java:53)
at cz.jzitnik.client.events.handlers.TerminalResizeEventHandler.handle(TerminalResizeEventHandler.java:54)
at cz.jzitnik.client.events.handlers.TerminalResizeEventHandler.handle(TerminalResizeEventHandler.java:18)
at cz.jzitnik.client.utils.events.EventManager.lambda$handleEvent$0(EventManager.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.base/java.lang.Thread.run(Thread.java:1474)
2026-02-01 14:56:07 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:56:30 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 71 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:56:31 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:56:52 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 63 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:56:53 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:57:50 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 14:58:35 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 60 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 14:58:36 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:01:08 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 64 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:01:09 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:01:23 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 63 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:01:24 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:02:27 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 57 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:02:28 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:02:36 [pool-7-thread-8] ERROR c.j.client.utils.events.EventManager - Error
java.lang.StringIndexOutOfBoundsException: Index -1 out of bounds for length 0
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
at java.base/java.lang.String.checkIndex(String.java:4904)
at java.base/java.lang.AbstractStringBuilder.deleteCharAt(AbstractStringBuilder.java:1081)
at java.base/java.lang.StringBuilder.deleteCharAt(StringBuilder.java:298)
at cz.jzitnik.client.game.setup.scenes.connect.ServerChoose$ServerSelector.handleKeyboardAction(ServerChoose.java:170)
at cz.jzitnik.client.screens.scenes.Scene.handleKeyboardAction(Scene.java:96)
at cz.jzitnik.client.events.handlers.KeyboardPressEventHandler.handle(KeyboardPressEventHandler.java:22)
at cz.jzitnik.client.events.handlers.KeyboardPressEventHandler.handle(KeyboardPressEventHandler.java:11)
at cz.jzitnik.client.utils.events.EventManager.lambda$handleEvent$0(EventManager.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.base/java.lang.Thread.run(Thread.java:1474)
2026-02-01 15:04:50 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 61 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:04:51 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:05:10 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 58 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:05:11 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:05:51 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 66 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:05:52 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:06:48 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:07:44 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:08:41 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:08:49 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 69 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:08:50 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:09:01 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 58 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:09:02 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:09:21 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 57 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:09:22 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:09:33 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 58 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:09:34 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:10:30 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:11:23 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 62 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:11:24 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:11:42 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 73 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:11:42 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:11:49 [pool-7-thread-1] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:50 [pool-7-thread-4] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:50 [pool-7-thread-7] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:51 [pool-7-thread-2] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:51 [pool-7-thread-5] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:51 [pool-7-thread-7] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:52 [pool-16-thread-2] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 15:11:52 [pool-16-thread-6] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 15:11:53 [pool-16-thread-3] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:53 [pool-16-thread-3] DEBUG c.j.c.g.m.t.EnemyPlayerAttackingTask - Is dead: false
2026-02-01 15:11:53 [pool-16-thread-5] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:53 [pool-16-thread-5] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:53 [pool-16-thread-5] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:53 [pool-16-thread-5] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:53 [pool-7-thread-6] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:53 [pool-16-thread-1] DEBUG c.j.c.g.m.t.MobFollowingPlayerTask - Mob is effectively at the target or trapped.
2026-02-01 15:11:56 [pool-7-thread-6] DEBUG c.j.c.e.handlers.FullRoomDrawHandler - Rendering full room
2026-02-01 15:11:57 [pool-7-thread-8] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: true
2026-02-01 15:11:57 [pool-3-thread-1] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: false
2026-02-01 15:11:57 [pool-7-thread-7] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: true
2026-02-01 15:11:57 [pool-3-thread-1] DEBUG cz.jzitnik.client.ui.Inventory - Gonna doubleclick: false
2026-02-01 15:15:04 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 59 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:15:04 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.
2026-02-01 15:16:09 [cz.jzitnik.client.Main.main()] INFO org.reflections.Reflections - Reflections took 65 ms to scan 1 urls, producing 51 keys and 239 values
2026-02-01 15:16:09 [Thread-5] INFO cz.jzitnik.client.sound.SoundPlayer - [62] Line started.

View File

@@ -2,23 +2,53 @@ package cz.jzitnik.server.context;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.events.EventManager; import cz.jzitnik.server.events.EventManager;
import cz.jzitnik.server.socket.SocketSession; import cz.jzitnik.server.game.Game;
import jakarta.websocket.Session; import jakarta.websocket.Session;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.HashMap; import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class GlobalContext { public class GlobalContext {
@Getter @Getter
private final HashMap<Session, Client> sessions = new HashMap<>(); private final HashMap<Session, Client> sessions = new HashMap<>();
@Getter
private final Set<Game> games = new HashSet<>();
@Getter @Getter
@Setter @Setter
private EventManager eventManager; private EventManager eventManager;
public void registerClient(Session session) { @Getter
Client client = new Client(new SocketSession(session)); private final Properties properties;
sessions.put(session, client);
public void registerClient(Client 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() {
Properties props = new Properties();
try (InputStream input = GlobalContext.class
.getClassLoader()
.getResourceAsStream("config.properties")) {
if (input == null) {
throw new RuntimeException("config.properties not found");
}
props.load(input);
this.properties = props;
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }

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,57 @@
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.common.socket.messages.player.PlayerArrivalChange;
import cz.jzitnik.common.socket.messages.player.PlayerJoined;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
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 tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader;
import tools.jackson.dataformat.yaml.YAMLFactory;
@EventHandler(ConnectToAGame.class)
public class ConnectToAGameHandler extends AbstractEventHandler<ConnectToAGame> {
public ConnectToAGameHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(ConnectToAGame event, Client client) {
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"));
player.setId(game.getPlayers().size());
client.setPlayer(new Player(player));
client.setGame(game);
String defaultRoomId = globalContext.getProperties().getProperty("rooms.default");
client.getPlayer().setCurrentRoom(defaultRoomId);
client.getSession().sendMessage(new ConnectToAGameResponse(player, game.getPlayers().stream().map(client1 -> client1.getPlayer().toPlayerCreation()).toList()));
for (Client cl : game.getPlayers()) {
cl.getSession().sendMessage(new PlayerJoined(player));
if (cl.getPlayer().getCurrentRoom().equals(defaultRoomId)) {
cl.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), player.getPlayerCords(), PlayerRotation.FRONT, true, true));
client.getSession().sendMessage(new PlayerArrivalChange(cl.getPlayer().getId(), cl.getPlayer().getCords(), PlayerRotation.FRONT, true, false));
}
}
game.getPlayers().add(client);
}
}

View File

@@ -1,5 +1,6 @@
package cz.jzitnik.server.events.handlers; package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.Config;
import cz.jzitnik.common.models.player.PlayerCreation; import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.game.creation.CreateGame; import cz.jzitnik.common.socket.messages.game.creation.CreateGame;
import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse; import cz.jzitnik.common.socket.messages.game.creation.CreateGameResponse;
@@ -7,26 +8,44 @@ 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 cz.jzitnik.server.game.Game;
import cz.jzitnik.server.game.Player;
import cz.jzitnik.server.utils.PasswordGenerator;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader; import tools.jackson.databind.ObjectReader;
import tools.jackson.dataformat.yaml.YAMLFactory; import tools.jackson.dataformat.yaml.YAMLFactory;
@RequiredArgsConstructor import java.util.ArrayList;
import java.util.List;
@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) {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
String pass = "nevim"; // TODO: Generate String pass = PasswordGenerator.generatePassword(Config.WORLD_PASSWORD_LENGTH);
int id = 0; // Owners id is always 0
ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class); ObjectReader playerReader = objectMapper.readerFor(PlayerCreation.class);
PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml")); PlayerCreation player = playerReader.readValue(getClass().getClassLoader().getResourceAsStream("setup/player.yaml"));
player.setId(id);
client.setPlayer(new Player(player));
CreateGameResponse gameResponse = new CreateGameResponse(pass, player); CreateGameResponse gameResponse = new CreateGameResponse(pass, player);
Game game = new Game(
pass,
new ArrayList<>(List.of(client))
);
client.setGame(game);
client.getPlayer().setCurrentRoom(globalContext.getProperties().getProperty("rooms.default"));
globalContext.getGames().add(game);
client.session().sendMessage(gameResponse); client.getSession().sendMessage(gameResponse);
} }
} }

View File

@@ -0,0 +1,23 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
@EventHandler(ItemTookFromChest.class)
public class ItemTookFromChestHandler extends AbstractEventHandler<ItemTookFromChest> {
public ItemTookFromChestHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(ItemTookFromChest event, Client client) {
client.getGame().getItemModifiers().add(event);
for (Client client1 : client.getGame().getPlayers()) {
client1.getSession().sendMessage(event);
}
}
}

View File

@@ -0,0 +1,50 @@
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 ->
new MovePlayerRoomResponse.Registry(
client1.getPlayer().getId(),
client1.getPlayer().getCords(),
client1.getPlayer().getPlayerRotation()
)
).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(), client.getPlayer().getPlayerRotation(), false, true));
} else if (player.getPlayer().getCurrentRoom().equals(event.newRoomId())) {
player.getSession().sendMessage(new PlayerArrivalChange(client.getPlayer().getId(), event.newCords(), client.getPlayer().getPlayerRotation(), true, true));
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
package cz.jzitnik.server.events.handlers;
import cz.jzitnik.common.socket.messages.player.PlayerMove;
import cz.jzitnik.common.socket.messages.player.PlayerMovedInUrRoom;
import cz.jzitnik.server.annotations.EventHandler;
import cz.jzitnik.server.context.GlobalContext;
import cz.jzitnik.server.events.AbstractEventHandler;
import cz.jzitnik.server.game.Client;
@EventHandler(PlayerMove.class)
public class PlayerMoveHandler extends AbstractEventHandler<PlayerMove> {
public PlayerMoveHandler(GlobalContext globalContext) {
super(globalContext);
}
@Override
public void handle(PlayerMove event, Client client) {
client.getPlayer().getCords().updateCords(event.newCords());
client.getPlayer().setPlayerRotation(event.playerRotation());
for (Client player : client.getGame().getPlayers()) {
if (player.getPlayer().getCurrentRoom().equals(client.getPlayer().getCurrentRoom()) && player.getPlayer().getId() != client.getPlayer().getId()) {
player.getSession().sendMessage(new PlayerMovedInUrRoom(client.getPlayer().getId(), event.newCords(), event.playerRotation()));
}
}
}
}

View File

@@ -1,5 +1,25 @@
package cz.jzitnik.server.game; package cz.jzitnik.server.game;
import cz.jzitnik.server.socket.SocketSession; import cz.jzitnik.server.socket.SocketSession;
import lombok.Getter;
import lombok.Setter;
public record Client(SocketSession session) {} @Getter
public final class Client {
private final SocketSession session;
@Setter
private Player player;
@Setter
private Game game;
public Client(SocketSession session, Player player, Game game) {
this.session = session;
this.player = player;
this.game = game;
}
public Client(SocketSession session, Player player) {
this(session, player, null);
}
}

View File

@@ -1,4 +1,16 @@
package cz.jzitnik.server.game; package cz.jzitnik.server.game;
import cz.jzitnik.common.socket.messages.items.ItemTookFromChest;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
public class Game { public class Game {
private String password;
private List<Client> players;
private final List<ItemTookFromChest> itemModifiers = new ArrayList<>();
} }

View File

@@ -0,0 +1,27 @@
package cz.jzitnik.server.game;
import cz.jzitnik.common.models.coordinates.RoomCords;
import cz.jzitnik.common.models.player.PlayerCreation;
import cz.jzitnik.common.socket.messages.player.PlayerRotation;
import lombok.Getter;
import lombok.Setter;
@Getter
public class Player {
private final int id;
private final RoomCords cords;
@Setter
private PlayerRotation playerRotation;
@Setter
private String currentRoom;
public Player(PlayerCreation creation) {
id = creation.getId();
cords = creation.getPlayerCords();
playerRotation = PlayerRotation.FRONT;
}
public PlayerCreation toPlayerCreation() {
return new PlayerCreation(cords, null);
}
}

View File

@@ -1,6 +1,8 @@
package cz.jzitnik.server.socket; package cz.jzitnik.server.socket;
import cz.jzitnik.common.socket.SocketMessage; import cz.jzitnik.common.socket.SocketMessage;
import cz.jzitnik.common.socket.messages.player.PlayerArrivalChange;
import cz.jzitnik.common.socket.messages.player.PlayerDisconnected;
import cz.jzitnik.server.game.Client; import cz.jzitnik.server.game.Client;
import cz.jzitnik.server.context.GlobalContext; import cz.jzitnik.server.context.GlobalContext;
import jakarta.websocket.*; import jakarta.websocket.*;
@@ -18,7 +20,8 @@ public class WebSocket {
@OnOpen @OnOpen
public void onOpen(Session session, EndpointConfig config) { public void onOpen(Session session, EndpointConfig config) {
this.globalContext = (GlobalContext) config.getUserProperties().get("globalContext"); this.globalContext = (GlobalContext) config.getUserProperties().get("globalContext");
globalContext.registerClient(session);
globalContext.registerClient(new Client(new SocketSession(session), null));
log.debug("Client connected: {}", session.getId()); log.debug("Client connected: {}", session.getId());
} }
@@ -36,7 +39,24 @@ public class WebSocket {
@OnClose @OnClose
public void onClose(Session session, CloseReason reason) { public void onClose(Session session, CloseReason reason) {
System.out.println("Connection closed: " + reason); Client client = globalContext.getSessions().get(session);
client.getGame().getPlayers().remove(client);
for (Client otherClient : client.getGame().getPlayers()) {
if (otherClient.getPlayer().getCurrentRoom().equals(client.getPlayer().getCurrentRoom())) {
otherClient.getSession().sendMessage(new PlayerArrivalChange(
client.getPlayer().getId(),
client.getPlayer().getCords(),
client.getPlayer().getPlayerRotation(),
false,
true
));
}
otherClient.getSession().sendMessage(new PlayerDisconnected(client.getPlayer().getId()));
}
log.debug("Connection closed: {}", reason);
} }
@OnError @OnError

View File

@@ -0,0 +1,23 @@
package cz.jzitnik.server.utils;
import java.security.SecureRandom;
public class PasswordGenerator {
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final SecureRandom RANDOM = new SecureRandom();
public static String generatePassword(int length) {
if (length <= 0) {
throw new IllegalArgumentException("Password length must be greater than 0");
}
StringBuilder password = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int index = RANDOM.nextInt(CHARACTERS.length());
password.append(CHARACTERS.charAt(index));
}
return password.toString();
}
}

View File

@@ -0,0 +1 @@
rooms.default=spawn