diff options
| author | ilotterytea <iltsu@alright.party> | 2025-01-22 04:11:41 +0500 |
|---|---|---|
| committer | ilotterytea <iltsu@alright.party> | 2025-01-22 04:11:41 +0500 |
| commit | 449c70cbf900a80042f70dd8929f302f0f0016d7 (patch) | |
| tree | cd958299e1a421165c370cb31500315f0635560c | |
| parent | b96a6d7005fcb90a21aeb21ddebe8bb7b82a7ef5 (diff) | |
feat: MULTIPLAYER!!!!!!!!!!!!!!! LETS FUCKING GOOOOOOOO!!!!!
22 files changed, 573 insertions, 72 deletions
diff --git a/core/src/main/java/kz/ilotterytea/frogartha/entities/PlayerEntity.java b/core/src/main/java/kz/ilotterytea/frogartha/entities/PlayerEntity.java index 8cc70bc..8264c8f 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/entities/PlayerEntity.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/entities/PlayerEntity.java @@ -1,10 +1,9 @@ package kz.ilotterytea.frogartha.entities; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector3; import kz.ilotterytea.frogartha.FrogarthaConstants; +import kz.ilotterytea.frogartha.FrogarthaGame; public class PlayerEntity extends RenderableEntity { protected Vector3 startPosition, endPosition; @@ -13,7 +12,7 @@ public class PlayerEntity extends RenderableEntity { protected boolean isJumping; public PlayerEntity() { - super(new Texture(Gdx.files.internal("sprites/player/player.png"))); + super(FrogarthaGame.getInstance().getAssetManager().get("sprites/player/player.png")); this.endPosition = Vector3.Zero; this.startPosition = Vector3.Zero; } diff --git a/core/src/main/java/kz/ilotterytea/frogartha/screens/GameScreen.java b/core/src/main/java/kz/ilotterytea/frogartha/screens/GameScreen.java index 3da9d55..421a458 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/screens/GameScreen.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/screens/GameScreen.java @@ -11,7 +11,9 @@ import com.badlogic.gdx.graphics.g3d.decals.CameraGroupStrategy; import com.badlogic.gdx.graphics.g3d.decals.DecalBatch; import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; import com.badlogic.gdx.math.Vector3; +import kz.ilotterytea.frogartha.FrogarthaGame; import kz.ilotterytea.frogartha.entities.LocalPlayerEntity; +import kz.ilotterytea.frogartha.entities.PlayerEntity; import net.mgsx.gltf.scene3d.attributes.PBRCubemapAttribute; import net.mgsx.gltf.scene3d.attributes.PBRTextureAttribute; import net.mgsx.gltf.scene3d.lights.DirectionalShadowLight; @@ -21,19 +23,34 @@ import net.mgsx.gltf.scene3d.scene.SceneManager; import net.mgsx.gltf.scene3d.scene.SceneSkybox; import net.mgsx.gltf.scene3d.utils.IBLBuilder; +import java.util.HashMap; +import java.util.Map; + public class GameScreen implements Screen { + private FrogarthaGame game; + private PerspectiveCamera camera; private SceneManager sceneManager; private DecalBatch decalBatch; private LocalPlayerEntity playerEntity; + private HashMap<Integer, PlayerEntity> playerEntityMap; + @Override public void show() { + game = FrogarthaGame.getInstance(); + create3D(); playerEntity = new LocalPlayerEntity(camera); playerEntity.setPosition(0f, 1f, 0f); + + playerEntityMap = new HashMap<>(); + + for (Integer id : game.getSessionClient().getPlayerDataMap().keySet()) { + playerEntityMap.put(id, new PlayerEntity()); + } } @Override @@ -45,6 +62,11 @@ public class GameScreen implements Screen { playerEntity.update(delta); playerEntity.render(decalBatch); + for (Map.Entry<Integer, PlayerEntity> player : playerEntityMap.entrySet()) { + player.getValue().update(delta); + player.getValue().render(decalBatch); + } + decalBatch.flush(); } @@ -130,4 +152,8 @@ public class GameScreen implements Screen { public LocalPlayerEntity getPlayerEntity() { return playerEntity; } + + public HashMap<Integer, PlayerEntity> getPlayerEntityMap() { + return playerEntityMap; + } } diff --git a/core/src/main/java/kz/ilotterytea/frogartha/screens/MenuScreen.java b/core/src/main/java/kz/ilotterytea/frogartha/screens/MenuScreen.java index 761835e..9f716f6 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/screens/MenuScreen.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/screens/MenuScreen.java @@ -37,7 +37,7 @@ public class MenuScreen implements Screen { stage.act(delta); stage.draw(); - if (game.getIdentityClient().isAuthorized()) { + if (game.getIdentityClient().isInRoom()) { game.setScreen(new GameScreen()); } } diff --git a/core/src/main/java/kz/ilotterytea/frogartha/sessions/IdentityClient.java b/core/src/main/java/kz/ilotterytea/frogartha/sessions/IdentityClient.java index 1e09e8e..64e7270 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/sessions/IdentityClient.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/sessions/IdentityClient.java @@ -8,7 +8,7 @@ public class IdentityClient { private final FrogarthaGame game; private String username; - private boolean isProcessing, isAuthorized; + private boolean isProcessing, isAuthorized, inRoom; public IdentityClient() { this.log = new Logger(IdentityClient.class); @@ -18,6 +18,7 @@ public class IdentityClient { this.isProcessing = false; this.isAuthorized = false; + this.inRoom = false; } @@ -38,6 +39,14 @@ public class IdentityClient { isAuthorized = authorized; } + public boolean isInRoom() { + return inRoom; + } + + public void setInRoom(boolean inRoom) { + this.inRoom = inRoom; + } + public boolean isProcessing() { return isProcessing; } diff --git a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java index 738ee3a..e8b32e3 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java @@ -8,18 +8,22 @@ import com.github.czyzby.websocket.serialization.impl.ManualSerializer; import kz.ilotterytea.frogartha.FrogarthaConstants; import kz.ilotterytea.frogartha.FrogarthaGame; import kz.ilotterytea.frogartha.domain.Identity; -import kz.ilotterytea.frogartha.domain.events.ChangedDirectionEvent; -import kz.ilotterytea.frogartha.domain.events.PlayerJumpEvent; -import kz.ilotterytea.frogartha.domain.server.Acknowledge; +import kz.ilotterytea.frogartha.domain.PlayerData; +import kz.ilotterytea.frogartha.domain.events.*; import kz.ilotterytea.frogartha.exceptions.PlayerKickException; import kz.ilotterytea.frogartha.utils.Logger; import kz.ilotterytea.frogartha.utils.SerializerUtils; +import java.util.HashMap; + public class SessionClient implements WebSocketListener { private final Logger log; private final FrogarthaGame game; private final WebSocket connection; private final ManualSerializer serializer; + private final HashMap<Integer, PlayerData> playerDataMap; + + private int connectionId; public SessionClient() { this.connection = WebSockets.newSocket(FrogarthaConstants.URLS.SESSION_WSS); @@ -31,6 +35,7 @@ public class SessionClient implements WebSocketListener { this.log = new Logger(SessionClient.class); this.game = FrogarthaGame.getInstance(); + this.playerDataMap = new HashMap<>(); } @Override @@ -61,12 +66,20 @@ public class SessionClient implements WebSocketListener { throw new RuntimeException("Deserialized packet is null"); } - if (obj instanceof Acknowledge) { - SessionHandlers.handleAcknowledge((Acknowledge) obj); + if (obj instanceof IdentifiedEvent) { + SessionHandlers.handleIdentifiedEvent((IdentifiedEvent) obj); } else if (obj instanceof PlayerJumpEvent) { SessionHandlers.handlePlayerJumpEvent((PlayerJumpEvent) obj); } else if (obj instanceof ChangedDirectionEvent) { SessionHandlers.handleChangedDirectionEvent((ChangedDirectionEvent) obj); + } else if (obj instanceof SenderJoinedRoomEvent) { + SessionHandlers.handleSenderJoinedRoomEvent((SenderJoinedRoomEvent) obj); + } else if (obj instanceof SenderLeftRoomEvent) { + SessionHandlers.handleSenderLeftRoomEvent((SenderLeftRoomEvent) obj); + } else if (obj instanceof PlayerJoinedRoomEvent) { + SessionHandlers.handlePlayerJoinedRoomEvent((PlayerJoinedRoomEvent) obj); + } else if (obj instanceof PlayerLeftRoomEvent) { + SessionHandlers.handlePlayerLeftRoomEvent((PlayerLeftRoomEvent) obj); } else if (obj instanceof PlayerKickException) { throw (PlayerKickException) obj; } @@ -103,4 +116,16 @@ public class SessionClient implements WebSocketListener { public void updateIdentity() { send(new Identity(game.getIdentityClient().getUsername())); } + + public int getConnectionId() { + return connectionId; + } + + public void setConnectionId(int connectionId) { + this.connectionId = connectionId; + } + + public HashMap<Integer, PlayerData> getPlayerDataMap() { + return playerDataMap; + } } diff --git a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java index 2fc50b8..c7abe3e 100644 --- a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java +++ b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java @@ -2,24 +2,29 @@ package kz.ilotterytea.frogartha.sessions; import com.badlogic.gdx.math.Vector3; import kz.ilotterytea.frogartha.FrogarthaGame; -import kz.ilotterytea.frogartha.domain.events.ChangedDirectionEvent; -import kz.ilotterytea.frogartha.domain.events.PlayerJumpEvent; -import kz.ilotterytea.frogartha.domain.server.Acknowledge; +import kz.ilotterytea.frogartha.domain.PlayerData; +import kz.ilotterytea.frogartha.domain.RoomTopic; +import kz.ilotterytea.frogartha.domain.actions.JoinRoomAction; +import kz.ilotterytea.frogartha.domain.events.*; import kz.ilotterytea.frogartha.entities.PlayerEntity; import kz.ilotterytea.frogartha.screens.GameScreen; +import kz.ilotterytea.frogartha.screens.MenuScreen; import kz.ilotterytea.frogartha.utils.Logger; +import java.util.HashMap; +import java.util.Map; + public class SessionHandlers { private static final Logger log = new Logger(SessionHandlers.class); private static final FrogarthaGame game = FrogarthaGame.getInstance(); private static final SessionClient client = game.getSessionClient(); - public static void handleAcknowledge(Acknowledge acknowledge) { - log.log("The server was acknowledged: {}", acknowledge); + public static void handleIdentifiedEvent(IdentifiedEvent event) { + log.log("The server identified me!"); - if (acknowledge.getCode() == Acknowledge.AcknowledgeCode.IDENTIFIED) { - game.getIdentityClient().setAuthorized(true); - } + client.setConnectionId(event.getPlayerId()); + game.getIdentityClient().setAuthorized(true); + client.send(new JoinRoomAction(RoomTopic.POND)); } public static void handlePlayerJumpEvent(PlayerJumpEvent event) { @@ -29,7 +34,22 @@ public class SessionHandlers { } GameScreen screen = (GameScreen) game.getScreen(); - PlayerEntity entity = screen.getPlayerEntity(); + PlayerEntity entity = null; + + if (event.getPlayerId() == client.getConnectionId()) { + entity = screen.getPlayerEntity(); + } else { + for (Map.Entry<Integer, PlayerEntity> entityEntry : screen.getPlayerEntityMap().entrySet()) { + if (entityEntry.getKey() != event.getPlayerId()) continue; + entity = entityEntry.getValue(); + break; + } + } + + if (entity == null) { + log.log("No player ID {}", event.getPlayerId()); + return; + } entity.setJumpStrength(event.getJumpStrength()); entity.setStartPosition(event.getStartPosition()); @@ -44,10 +64,73 @@ public class SessionHandlers { } GameScreen screen = (GameScreen) game.getScreen(); - PlayerEntity entity = screen.getPlayerEntity(); + PlayerEntity entity = null; + + if (event.getPlayerId() == client.getConnectionId()) { + entity = screen.getPlayerEntity(); + } else { + for (Map.Entry<Integer, PlayerEntity> entityEntry : screen.getPlayerEntityMap().entrySet()) { + if (entityEntry.getKey() != event.getPlayerId()) continue; + entity = entityEntry.getValue(); + break; + } + } + + if (entity == null) { + log.log("No player ID {}", event.getPlayerId()); + return; + } Vector3 direction = event.getDirection(); entity.setDirection(direction.x, entity.getPosition().y, direction.z); } + + public static void handleSenderJoinedRoomEvent(SenderJoinedRoomEvent event) { + if (game.getScreen().getClass().equals(GameScreen.class)) { + log.log("The screen is already GameScreen, but the session received SenderJoinedRoomEvent"); + return; + } + + HashMap<Integer, PlayerData> map = new HashMap<>(); + + for (int i = 0; i < event.getPlayers().size(); i++) { + Integer id = event.getIds().get(i); + PlayerData data = event.getPlayers().get(i); + map.put(id, data); + } + + client.getPlayerDataMap().putAll(map); + game.getIdentityClient().setInRoom(true); + } + + public static void handleSenderLeftRoomEvent(SenderLeftRoomEvent event) { + if (!game.getScreen().getClass().equals(GameScreen.class)) { + log.log("The screen is not GameScreen, but the session received SenderLeftRoomEvent"); + return; + } + + client.getPlayerDataMap().clear(); + game.setScreen(new MenuScreen()); + } + + public static void handlePlayerJoinedRoomEvent(PlayerJoinedRoomEvent event) { + if (!game.getScreen().getClass().equals(GameScreen.class)) { + log.log("The screen is not GameScreen, but the session received PlayerJoinedRoomEvent"); + return; + } + + GameScreen screen = (GameScreen) game.getScreen(); + screen.getPlayerEntityMap().put(event.getPlayerId(), new PlayerEntity()); + } + + public static void handlePlayerLeftRoomEvent(PlayerLeftRoomEvent event) { + if (!game.getScreen().getClass().equals(GameScreen.class)) { + log.log("The screen is not GameScreen, but the session received PlayerLeftRoomEvent"); + return; + } + + GameScreen screen = (GameScreen) game.getScreen(); + screen.getPlayerEntityMap().remove(event.getPlayerId()); + } } diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java b/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java index 24a607d..dca0afd 100644 --- a/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java +++ b/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java @@ -3,6 +3,8 @@ package kz.ilotterytea.frogartha.server; import com.github.czyzby.websocket.serialization.impl.ManualSerializer; import kz.ilotterytea.frogartha.domain.Identity; import kz.ilotterytea.frogartha.domain.actions.ChangedDirectionAction; +import kz.ilotterytea.frogartha.domain.actions.JoinRoomAction; +import kz.ilotterytea.frogartha.domain.actions.LeaveRoomAction; import kz.ilotterytea.frogartha.domain.actions.PlayerJumpAction; import kz.ilotterytea.frogartha.exceptions.PlayerKickException; import kz.ilotterytea.frogartha.utils.Logger; @@ -21,14 +23,18 @@ public class FrogarthaServer extends WebSocketServer { private static FrogarthaServer instance; private final Logger log; + private final ArrayList<PlayerConnection> players; + private final ArrayList<Room> rooms; private final ManualSerializer serializer; private FrogarthaServer() { super(new InetSocketAddress(20015)); this.log = new Logger(FrogarthaServer.class); + this.players = new ArrayList<>(); + this.rooms = new ArrayList<>(); this.serializer = new ManualSerializer(); SerializerUtils.registerTypes(serializer); @@ -46,6 +52,7 @@ public class FrogarthaServer extends WebSocketServer { Optional<PlayerConnection> player = this.players.stream().filter((x) -> x.getConnection().equals(webSocket)).findFirst(); if (player.isPresent()) { + kickPlayerFromRoom(player.get()); log.log("{} has left! Reason: {} {}", player.get(), i, s); this.players.remove(player.get()); } else { @@ -55,7 +62,12 @@ public class FrogarthaServer extends WebSocketServer { @Override public void onMessage(WebSocket webSocket, String s) { - this.players.removeIf((x) -> x.getConnection().equals(webSocket)); + Optional<PlayerConnection> optionalPlayerConnection = this.players.stream() + .filter((x) -> x.getConnection().equals(webSocket)) + .findFirst(); + + optionalPlayerConnection.ifPresent((x) -> kickConnection(x, PlayerKickException.internalServerError())); + webSocket.send("Invalid input."); webSocket.close(CloseFrame.UNEXPECTED_CONDITION); } @@ -84,6 +96,10 @@ public class FrogarthaServer extends WebSocketServer { ServerHandlers.handlePlayerJumpAction(player, (PlayerJumpAction) obj); } else if (obj instanceof ChangedDirectionAction) { ServerHandlers.handleChangedDirectionAction(player, (ChangedDirectionAction) obj); + } else if (obj instanceof JoinRoomAction) { + ServerHandlers.handleJoinRoomAction(player, (JoinRoomAction) obj); + } else if (obj instanceof LeaveRoomAction) { + ServerHandlers.handleLeaveRoomAction(player, (LeaveRoomAction) obj); } else { throw PlayerKickException.internalServerError(); } @@ -108,12 +124,22 @@ public class FrogarthaServer extends WebSocketServer { } public void kickConnection(PlayerConnection player, PlayerKickException exception) { + kickPlayerFromRoom(player); player.send(exception); player.getConnection().close(); players.remove(player); log.log("Kicked out {}! Reason: {}", player, exception.getMessage()); } + public void kickPlayerFromRoom(PlayerConnection player) { + if (player.getRoom() == null) return; + Room room = player.getRoom(); + room.removePlayer(player); + if (room.getPlayers().isEmpty()) { + this.rooms.remove(room); + } + } + public static FrogarthaServer getInstance() { if (instance == null) instance = new FrogarthaServer(); return instance; @@ -123,6 +149,10 @@ public class FrogarthaServer extends WebSocketServer { return players; } + public ArrayList<Room> getRooms() { + return rooms; + } + public ManualSerializer getSerializer() { return serializer; } diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java b/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java index f30dcfe..56dd695 100644 --- a/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java +++ b/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java @@ -10,6 +10,7 @@ public class PlayerConnection extends PlayerData { private final int id; private final WebSocket connection; + private Room room; private final Timestamp connectedTimestamp; @@ -18,6 +19,7 @@ public class PlayerConnection extends PlayerData { this.id = TOTAL_CONNECTION_IDS; TOTAL_CONNECTION_IDS++; + this.room = null; this.connectedTimestamp = new Timestamp(System.currentTimeMillis()); } @@ -38,6 +40,14 @@ public class PlayerConnection extends PlayerData { return connection; } + public Room getRoom() { + return room; + } + + public void setRoom(Room room) { + this.room = room; + } + public Timestamp getConnectedTimestamp() { return connectedTimestamp; } diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/Room.java b/server/src/main/java/kz/ilotterytea/frogartha/server/Room.java new file mode 100644 index 0000000..01f92d4 --- /dev/null +++ b/server/src/main/java/kz/ilotterytea/frogartha/server/Room.java @@ -0,0 +1,58 @@ +package kz.ilotterytea.frogartha.server; + +import kz.ilotterytea.frogartha.domain.PlayerData; +import kz.ilotterytea.frogartha.domain.RoomTopic; +import kz.ilotterytea.frogartha.domain.events.PlayerJoinedRoomEvent; +import kz.ilotterytea.frogartha.domain.events.PlayerLeftRoomEvent; +import kz.ilotterytea.frogartha.domain.events.SenderJoinedRoomEvent; +import kz.ilotterytea.frogartha.domain.events.SenderLeftRoomEvent; + +import java.util.ArrayList; +import java.util.stream.Collectors; + +public class Room { + private final RoomTopic topic; + private final ArrayList<PlayerConnection> players; + + public Room(RoomTopic topic) { + this.topic = topic; + this.players = new ArrayList<>(); + } + + public ArrayList<PlayerConnection> getPlayers() { + return players; + } + + public void addPlayer(PlayerConnection player) { + this.players.forEach((x) -> x.send(new PlayerJoinedRoomEvent(player.getId(), player))); + player.send(new SenderJoinedRoomEvent( + player.getId(), + this.players.stream().map(PlayerConnection::getId).collect(Collectors.toList()), + this.players.stream().map((x) -> (PlayerData) x).collect(Collectors.toList()) + )); + player.setRoom(this); + this.players.add(player); + } + + public void removePlayer(PlayerConnection player) { + player.setRoom(null); + this.players.remove(player); + + this.players.forEach((x) -> x.send(new PlayerLeftRoomEvent(player.getId()))); + if (player.getConnection().isOpen()) { + player.send(new SenderLeftRoomEvent(player.getId())); + } + } + + public RoomTopic getTopic() { + return topic; + } + + @Override + public String toString() { + return "Room{" + + "topic=" + topic + + ", playerSize=" + players.size() + + '}'; + } +} diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java index 1cc0e57..c1bdbbe 100644 --- a/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java +++ b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java @@ -5,13 +5,17 @@ import kz.ilotterytea.frogartha.FrogarthaConstants; import kz.ilotterytea.frogartha.domain.Identity; import kz.ilotterytea.frogartha.domain.PlayerState; import kz.ilotterytea.frogartha.domain.actions.ChangedDirectionAction; +import kz.ilotterytea.frogartha.domain.actions.JoinRoomAction; +import kz.ilotterytea.frogartha.domain.actions.LeaveRoomAction; import kz.ilotterytea.frogartha.domain.actions.PlayerJumpAction; import kz.ilotterytea.frogartha.domain.events.ChangedDirectionEvent; +import kz.ilotterytea.frogartha.domain.events.IdentifiedEvent; import kz.ilotterytea.frogartha.domain.events.PlayerJumpEvent; -import kz.ilotterytea.frogartha.domain.server.Acknowledge; import kz.ilotterytea.frogartha.exceptions.PlayerKickException; import kz.ilotterytea.frogartha.utils.Logger; +import java.util.Optional; + public class ServerHandlers { private static final Logger log = new Logger(ServerHandlers.class); private static final FrogarthaServer server = FrogarthaServer.getInstance(); @@ -26,11 +30,15 @@ public class ServerHandlers { } player.setIdentity(identity); - player.send(new Acknowledge(Acknowledge.AcknowledgeCode.IDENTIFIED)); + player.send(new IdentifiedEvent(player.getId())); log.log("Successfully identified {} for {}", identity, player); } public static void handlePlayerJumpAction(PlayerConnection player, PlayerJumpAction action) { + if (player.getRoom() == null) { + throw PlayerKickException.notInRoom(); + } + PlayerState state = player.getState(); float nowTimestamp = System.currentTimeMillis() / 1000f; @@ -56,18 +64,75 @@ public class ServerHandlers { state.setPosition(endPosition.x, endPosition.y, endPosition.z); - player.send(new PlayerJumpEvent( + PlayerJumpEvent event = new PlayerJumpEvent( player.getId(), startPosition, endPosition, action.getJumpStrength() - )); + ); + player.send(event); log.log("{} jumped from {} to {} with strength {}", player, startPosition, endPosition, action.getJumpStrength()); + + // broadcasting + Room room = player.getRoom(); + room.getPlayers().forEach((x) -> { + if (x.getId() == player.getId()) return; + x.send(event); + }); } public static void handleChangedDirectionAction(PlayerConnection player, ChangedDirectionAction action) { + if (player.getRoom() == null) { + throw PlayerKickException.notInRoom(); + } + PlayerState state = player.getState(); Vector3 direction = action.getDirection(); state.setDirection(direction.x, state.getPosition().y, direction.z); - player.send(new ChangedDirectionEvent(player.getId(), state.getDirection())); + ChangedDirectionEvent event = new ChangedDirectionEvent(player.getId(), state.getDirection()); + player.send(event); + + // broadcasting + Room room = player.getRoom(); + room.getPlayers().forEach((x) -> { + if (x.getId() == player.getId()) return; + x.send(event); + }); + } + + public static void handleJoinRoomAction(PlayerConnection player, JoinRoomAction action) { + Optional<Room> optionalRoom = server.getRooms().stream() + .filter((x) -> + x.getPlayers().size() < FrogarthaConstants.Room.MAX_PLAYERS && + x.getTopic().equals(action.getTopic()) + ) + .findFirst(); + + Room room = optionalRoom.orElse(new Room(action.getTopic())); + room.addPlayer(player); + + if (optionalRoom.isEmpty()) { + server.getRooms().add(room); + } + + log.log("{} joined {}", player, room); + } + + public static void handleLeaveRoomAction(PlayerConnection player, LeaveRoomAction action) { + Optional<Room> optionalRoom = server.getRooms().stream() + .filter((x) -> x.getPlayers().contains(player)) + .findFirst(); + + if (optionalRoom.isEmpty()) { + throw PlayerKickException.illegalOperation(); + } + + Room room = optionalRoom.get(); + room.removePlayer(player); + + if (room.getPlayers().isEmpty()) { + server.getRooms().remove(room); + } + + log.log("{} left {}", player, room); } } diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java b/shared/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java index d5cdb06..84dc6b0 100644 --- a/shared/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java +++ b/shared/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java @@ -10,4 +10,8 @@ public class FrogarthaConstants { public static final float MAX_JUMP_HEIGHT = 2f; public static final float JUMP_SPEED = 10f; } + + public static class Room { + public static final int MAX_PLAYERS = 30; + } } diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/RoomTopic.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/RoomTopic.java new file mode 100644 index 0000000..5b2f47e --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/RoomTopic.java @@ -0,0 +1,5 @@ +package kz.ilotterytea.frogartha.domain; + +public enum RoomTopic { + POND +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/JoinRoomAction.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/JoinRoomAction.java new file mode 100644 index 0000000..fa19d27 --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/JoinRoomAction.java @@ -0,0 +1,32 @@ +package kz.ilotterytea.frogartha.domain.actions; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; +import kz.ilotterytea.frogartha.domain.RoomTopic; + +public class JoinRoomAction implements Transferable<JoinRoomAction> { + private RoomTopic topic; + + public JoinRoomAction() { + } + + public JoinRoomAction(RoomTopic topic) { + this.topic = topic; + } + + public RoomTopic getTopic() { + return topic; + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer.serializeInt(topic.ordinal()); + } + + @Override + public JoinRoomAction deserialize(Deserializer deserializer) throws SerializationException { + return new JoinRoomAction(RoomTopic.values()[deserializer.deserializeInt()]); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/LeaveRoomAction.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/LeaveRoomAction.java new file mode 100644 index 0000000..2ae304a --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/actions/LeaveRoomAction.java @@ -0,0 +1,20 @@ +package kz.ilotterytea.frogartha.domain.actions; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; + +public class LeaveRoomAction implements Transferable<LeaveRoomAction> { + public LeaveRoomAction() { + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + } + + @Override + public LeaveRoomAction deserialize(Deserializer deserializer) throws SerializationException { + return new LeaveRoomAction(); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/IdentifiedEvent.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/IdentifiedEvent.java new file mode 100644 index 0000000..d2d9e1e --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/IdentifiedEvent.java @@ -0,0 +1,25 @@ +package kz.ilotterytea.frogartha.domain.events; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; + +public class IdentifiedEvent extends Event implements Transferable<IdentifiedEvent> { + public IdentifiedEvent() { + } + + public IdentifiedEvent(int playerId) { + this.playerId = playerId; + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer.serializeInt(playerId); + } + + @Override + public IdentifiedEvent deserialize(Deserializer deserializer) throws SerializationException { + return new IdentifiedEvent(deserializer.deserializeInt()); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerJoinedRoomEvent.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerJoinedRoomEvent.java new file mode 100644 index 0000000..f9389a3 --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerJoinedRoomEvent.java @@ -0,0 +1,35 @@ +package kz.ilotterytea.frogartha.domain.events; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; +import kz.ilotterytea.frogartha.domain.PlayerData; + +public class PlayerJoinedRoomEvent extends Event implements Transferable<PlayerJoinedRoomEvent> { + private PlayerData data; + + public PlayerJoinedRoomEvent() { + } + + public PlayerJoinedRoomEvent(int playerId, PlayerData data) { + super(playerId); + this.data = data; + } + + public PlayerData getData() { + return data; + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer + .serializeInt(playerId) + .serializeTransferable(data); + } + + @Override + public PlayerJoinedRoomEvent deserialize(Deserializer deserializer) throws SerializationException { + return new PlayerJoinedRoomEvent(deserializer.deserializeInt(), deserializer.deserializeTransferable(new PlayerData())); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerLeftRoomEvent.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerLeftRoomEvent.java new file mode 100644 index 0000000..5472e95 --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/PlayerLeftRoomEvent.java @@ -0,0 +1,25 @@ +package kz.ilotterytea.frogartha.domain.events; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; + +public class PlayerLeftRoomEvent extends Event implements Transferable<PlayerLeftRoomEvent> { + public PlayerLeftRoomEvent() { + } + + public PlayerLeftRoomEvent(int playerId) { + super(playerId); + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer.serializeInt(playerId); + } + + @Override + public PlayerLeftRoomEvent deserialize(Deserializer deserializer) throws SerializationException { + return new PlayerLeftRoomEvent(deserializer.deserializeInt()); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderJoinedRoomEvent.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderJoinedRoomEvent.java new file mode 100644 index 0000000..b032c47 --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderJoinedRoomEvent.java @@ -0,0 +1,51 @@ +package kz.ilotterytea.frogartha.domain.events; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; +import kz.ilotterytea.frogartha.domain.PlayerData; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class SenderJoinedRoomEvent extends Event implements Transferable<SenderJoinedRoomEvent> { + private List<Integer> ids; + private List<PlayerData> players; + + public SenderJoinedRoomEvent() { + } + + public SenderJoinedRoomEvent(int playerId, List<Integer> ids, List<PlayerData> players) { + super(playerId); + this.ids = ids; + this.players = players; + } + + public List<Integer> getIds() { + return ids; + } + + public List<PlayerData> getPlayers() { + return players; + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer.serializeInt(playerId) + .serializeIntArray(ids.stream().mapToInt(Integer::intValue).toArray()) + .serializeTransferableArray(players.toArray(new PlayerData[]{})); + } + + @Override + public SenderJoinedRoomEvent deserialize(Deserializer deserializer) throws SerializationException { + List<Integer> ids = Arrays.stream(deserializer.deserializeIntArray()).boxed().collect(Collectors.toList()); + return new SenderJoinedRoomEvent( + deserializer.deserializeInt(), + new ArrayList<>(ids), + new ArrayList<>(Arrays.asList(deserializer.deserializeTransferableArray(new PlayerData(), size -> new PlayerData[ids.size()]))) + ); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderLeftRoomEvent.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderLeftRoomEvent.java new file mode 100644 index 0000000..2c2b35b --- /dev/null +++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/events/SenderLeftRoomEvent.java @@ -0,0 +1,25 @@ +package kz.ilotterytea.frogartha.domain.events; + +import com.github.czyzby.websocket.serialization.SerializationException; +import com.github.czyzby.websocket.serialization.Transferable; +import com.github.czyzby.websocket.serialization.impl.Deserializer; +import com.github.czyzby.websocket.serialization.impl.Serializer; + +public class SenderLeftRoomEvent extends Event implements Transferable<SenderLeftRoomEvent> { + public SenderLeftRoomEvent() { + } + + public SenderLeftRoomEvent(int playerId) { + super(playerId); + } + + @Override + public void serialize(Serializer serializer) throws SerializationException { + serializer.serializeInt(playerId); + } + + @Override + public SenderLeftRoomEvent deserialize(Deserializer deserializer) throws SerializationException { + return new SenderLeftRoomEvent(deserializer.deserializeInt()); + } +} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/server/Acknowledge.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/server/Acknowledge.java deleted file mode 100644 index 8ca0f66..0000000 --- a/shared/src/main/java/kz/ilotterytea/frogartha/domain/server/Acknowledge.java +++ /dev/null @@ -1,42 +0,0 @@ -package kz.ilotterytea.frogartha.domain.server; - -import com.github.czyzby.websocket.serialization.SerializationException; -import com.github.czyzby.websocket.serialization.Transferable; -import com.github.czyzby.websocket.serialization.impl.Deserializer; -import com.github.czyzby.websocket.serialization.impl.Serializer; - -public class Acknowledge implements Transferable<Acknowledge> { - public enum AcknowledgeCode { - IDENTIFIED - } - - private AcknowledgeCode code; - - public Acknowledge() { - } - - public Acknowledge(AcknowledgeCode code) { - this.code = code; - } - - public AcknowledgeCode getCode() { - return code; - } - - @Override - public String toString() { - return "Acknowledge{" + - "code=" + code.toString() + - '}'; - } - - @Override - public void serialize(Serializer serializer) throws SerializationException { - serializer.serializeInt(code.ordinal()); - } - - @Override - public Acknowledge deserialize(Deserializer deserializer) throws SerializationException { - return new Acknowledge(AcknowledgeCode.values()[deserializer.deserializeInt()]); - } -} diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java b/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java index ec0deb4..ad9c408 100644 --- a/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java +++ b/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java @@ -25,6 +25,14 @@ public class PlayerKickException extends RuntimeException implements Transferabl return new PlayerKickException("Kicked for jump spamming"); } + public static PlayerKickException illegalOperation() { + return new PlayerKickException("Illegal operation"); + } + + public static PlayerKickException notInRoom() { + return new PlayerKickException("You are not in room"); + } + @Override public void serialize(Serializer serializer) throws SerializationException { serializer.serializeString(getMessage()); diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/utils/SerializerUtils.java b/shared/src/main/java/kz/ilotterytea/frogartha/utils/SerializerUtils.java index 2df2acf..e005937 100644 --- a/shared/src/main/java/kz/ilotterytea/frogartha/utils/SerializerUtils.java +++ b/shared/src/main/java/kz/ilotterytea/frogartha/utils/SerializerUtils.java @@ -3,20 +3,28 @@ package kz.ilotterytea.frogartha.utils; import com.github.czyzby.websocket.serialization.impl.ManualSerializer; import kz.ilotterytea.frogartha.domain.Identity; import kz.ilotterytea.frogartha.domain.actions.ChangedDirectionAction; +import kz.ilotterytea.frogartha.domain.actions.JoinRoomAction; +import kz.ilotterytea.frogartha.domain.actions.LeaveRoomAction; import kz.ilotterytea.frogartha.domain.actions.PlayerJumpAction; -import kz.ilotterytea.frogartha.domain.events.ChangedDirectionEvent; -import kz.ilotterytea.frogartha.domain.events.PlayerJumpEvent; -import kz.ilotterytea.frogartha.domain.server.Acknowledge; +import kz.ilotterytea.frogartha.domain.events.*; import kz.ilotterytea.frogartha.exceptions.PlayerKickException; public class SerializerUtils { public static void registerTypes(ManualSerializer serializer) { - serializer.register(new Acknowledge()); serializer.register(new Identity()); serializer.register(new PlayerKickException()); serializer.register(new PlayerJumpAction()); serializer.register(new PlayerJumpEvent()); serializer.register(new ChangedDirectionAction()); serializer.register(new ChangedDirectionEvent()); + serializer.register(new IdentifiedEvent()); + + serializer.register(new JoinRoomAction()); + serializer.register(new PlayerJoinedRoomEvent()); + serializer.register(new SenderJoinedRoomEvent()); + + serializer.register(new LeaveRoomAction()); + serializer.register(new PlayerLeftRoomEvent()); + serializer.register(new SenderLeftRoomEvent()); } } |
