summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/build.gradle3
-rw-r--r--core/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java7
-rw-r--r--core/src/main/java/kz/ilotterytea/frogartha/FrogarthaGame.java10
-rw-r--r--core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java81
-rw-r--r--core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java15
-rw-r--r--gradle.properties1
-rw-r--r--server/build.gradle3
-rw-r--r--server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java110
-rw-r--r--server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java67
-rw-r--r--server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java26
-rw-r--r--server/src/main/java/kz/ilotterytea/frogartha/server/ServerLauncher.java9
-rw-r--r--shared/src/main/java/kz/ilotterytea/frogartha/domain/Acknowledge.java22
-rw-r--r--shared/src/main/java/kz/ilotterytea/frogartha/domain/Identity.java30
-rw-r--r--shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java15
14 files changed, 394 insertions, 5 deletions
diff --git a/core/build.gradle b/core/build.gradle
index bb73187..e142af4 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -4,9 +4,10 @@ eclipse.project.name = appName + '-core'
dependencies {
api "com.badlogicgames.gdx:gdx:$gdxVersion"
api "com.github.mgsx-dev.gdx-gltf:gltf:$gdxGltfVersion"
+ api "org.java-websocket:Java-WebSocket:$javaWebsocketVersion"
api project(':shared')
- if(enableGraalNative == 'true') {
+ if (enableGraalNative == 'true') {
implementation "io.github.berstanio:gdx-svmhelper-annotations:$graalHelperVersion"
}
}
diff --git a/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java b/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java
new file mode 100644
index 0000000..f80948a
--- /dev/null
+++ b/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaConstants.java
@@ -0,0 +1,7 @@
+package kz.ilotterytea.frogartha;
+
+public class FrogarthaConstants {
+ public static class URLS {
+ public static String SESSION_WSS = "ws://localhost:20015";
+ }
+}
diff --git a/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaGame.java b/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaGame.java
index 149dcc7..ea227ec 100644
--- a/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaGame.java
+++ b/core/src/main/java/kz/ilotterytea/frogartha/FrogarthaGame.java
@@ -2,6 +2,7 @@ package kz.ilotterytea.frogartha;
import com.badlogic.gdx.Game;
import kz.ilotterytea.frogartha.screens.GameScreen;
+import kz.ilotterytea.frogartha.sessions.SessionClient;
/**
* {@link com.badlogic.gdx.ApplicationListener} implementation shared by all platforms.
@@ -9,8 +10,13 @@ import kz.ilotterytea.frogartha.screens.GameScreen;
public class FrogarthaGame extends Game {
private static FrogarthaGame instance;
+ private SessionClient sessionClient;
+
@Override
public void create() {
+ sessionClient = new SessionClient();
+ sessionClient.connect();
+
setScreen(new GameScreen());
}
@@ -18,4 +24,8 @@ public class FrogarthaGame extends Game {
if (instance == null) instance = new FrogarthaGame();
return instance;
}
+
+ public SessionClient getSessionClient() {
+ return sessionClient;
+ }
}
diff --git a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java
new file mode 100644
index 0000000..c775fdf
--- /dev/null
+++ b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionClient.java
@@ -0,0 +1,81 @@
+package kz.ilotterytea.frogartha.sessions;
+
+import kz.ilotterytea.frogartha.FrogarthaConstants;
+import kz.ilotterytea.frogartha.FrogarthaGame;
+import kz.ilotterytea.frogartha.domain.Acknowledge;
+import kz.ilotterytea.frogartha.domain.Identity;
+import kz.ilotterytea.frogartha.exceptions.PlayerKickException;
+import org.java_websocket.client.WebSocketClient;
+import org.java_websocket.handshake.ServerHandshake;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+import java.nio.ByteBuffer;
+
+public class SessionClient extends WebSocketClient {
+ private final Logger log;
+ private final FrogarthaGame game;
+
+ public SessionClient() {
+ super(URI.create(FrogarthaConstants.URLS.SESSION_WSS));
+ this.log = LoggerFactory.getLogger(SessionClient.class);
+ this.game = FrogarthaGame.getInstance();
+ }
+
+ @Override
+ public void onOpen(ServerHandshake handshakedata) {
+ log.info("Connected");
+ updateIdentity();
+ }
+
+ @Override
+ public void onMessage(String message) {
+ }
+
+ @Override
+ public void onMessage(ByteBuffer bytes) {
+ try {
+ // Deserialize the object
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes.array());
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ Object obj = ois.readObject();
+
+ if (obj instanceof Acknowledge) SessionHandlers.handleAcknowledge((Acknowledge) obj);
+ else if (obj instanceof PlayerKickException) throw (PlayerKickException) obj;
+ } catch (PlayerKickException e) {
+ log.info("Kicked out: {}", e.getMessage());
+ } catch (Exception e) {
+ log.error("An exception was thrown while processing message", e);
+ }
+ }
+
+ @Override
+ public void onClose(int code, String reason, boolean remote) {
+ log.info("Connection closed! Reason: {} {}", code, reason);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ log.error("Failed to connect", ex);
+ }
+
+ public void send(Object object) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(object);
+ send(baos.toByteArray());
+ } catch (Exception e) {
+ log.error("Failed to serialize and send an object", e);
+ }
+ }
+
+ public void updateIdentity() {
+ send(new Identity("Playerxd"));
+ }
+}
diff --git a/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java
new file mode 100644
index 0000000..8c7487b
--- /dev/null
+++ b/core/src/main/java/kz/ilotterytea/frogartha/sessions/SessionHandlers.java
@@ -0,0 +1,15 @@
+package kz.ilotterytea.frogartha.sessions;
+
+import kz.ilotterytea.frogartha.FrogarthaGame;
+import kz.ilotterytea.frogartha.domain.Acknowledge;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SessionHandlers {
+ private static final Logger log = LoggerFactory.getLogger(SessionHandlers.class);
+ private static final SessionClient client = FrogarthaGame.getInstance().getSessionClient();
+
+ public static void handleAcknowledge(Acknowledge acknowledge) {
+ log.info("The server was acknowledged: {}", acknowledge);
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 8d571e2..e4e9538 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,4 +7,5 @@ gwtFrameworkVersion=2.11.0
gwtPluginVersion=1.1.29
enableGraalNative=false
gdxVersion=1.13.1
+javaWebsocketVersion=1.6.0
projectVersion=1.0.0
diff --git a/server/build.gradle b/server/build.gradle
index 1aaf851..d4acfbd 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'application'
java.sourceCompatibility = 11
java.targetCompatibility = 11
if (JavaVersion.current().isJava9Compatible()) {
- compileJava.options.release.set(11)
+ compileJava.options.release.set(11)
}
mainClassName = 'kz.ilotterytea.frogartha.server.ServerLauncher'
@@ -12,6 +12,7 @@ application.setMainClass(mainClassName)
eclipse.project.name = appName + '-server'
dependencies {
+ api "org.java-websocket:Java-WebSocket:$javaWebsocketVersion"
implementation project(':shared')
}
diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java b/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java
new file mode 100644
index 0000000..f6a9847
--- /dev/null
+++ b/server/src/main/java/kz/ilotterytea/frogartha/server/FrogarthaServer.java
@@ -0,0 +1,110 @@
+package kz.ilotterytea.frogartha.server;
+
+import kz.ilotterytea.frogartha.domain.Identity;
+import kz.ilotterytea.frogartha.exceptions.PlayerKickException;
+import org.java_websocket.WebSocket;
+import org.java_websocket.framing.CloseFrame;
+import org.java_websocket.handshake.ClientHandshake;
+import org.java_websocket.server.WebSocketServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ObjectInputStream;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Optional;
+
+public class FrogarthaServer extends WebSocketServer {
+ private static FrogarthaServer instance;
+
+ private final Logger log;
+ private final ArrayList<PlayerConnection> players;
+
+ private FrogarthaServer() {
+ super(new InetSocketAddress(20015));
+ this.log = LoggerFactory.getLogger(FrogarthaServer.class);
+ this.players = new ArrayList<>();
+ }
+
+ @Override
+ public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
+ PlayerConnection player = new PlayerConnection(webSocket);
+ log.info("{} ({}) connected!", player, webSocket.getRemoteSocketAddress().getAddress().getHostAddress());
+ this.players.add(player);
+ }
+
+ @Override
+ public void onClose(WebSocket webSocket, int i, String s, boolean b) {
+ Optional<PlayerConnection> player = this.players.stream().filter((x) -> x.getConnection().equals(webSocket)).findFirst();
+
+ if (player.isPresent()) {
+ log.info("{} has let! Reason: {} {}", player.get(), i, s);
+ } else {
+ log.info("Unknown connection was closed! Reason: {} {}", i, s);
+ }
+ }
+
+ @Override
+ public void onMessage(WebSocket webSocket, String s) {
+ this.players.removeIf((x) -> x.getConnection().equals(webSocket));
+ webSocket.send("Invalid input.");
+ webSocket.close(CloseFrame.UNEXPECTED_CONDITION);
+ }
+
+ @Override
+ public void onMessage(WebSocket conn, ByteBuffer message) {
+ Optional<PlayerConnection> optionalPlayer = this.players.stream().filter((x) -> x.getConnection().equals(conn)).findFirst();
+
+ if (optionalPlayer.isEmpty()) {
+ conn.close();
+ return;
+ }
+
+ PlayerConnection player = optionalPlayer.get();
+
+ try {
+ // Deserializing the object
+ ByteArrayInputStream bais = new ByteArrayInputStream(message.array());
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ Object obj = ois.readObject();
+
+ if (obj instanceof Identity) ServerHandlers.handleIdentity(player, (Identity) obj);
+ else throw PlayerKickException.internalServerError();
+ } catch (PlayerKickException e) {
+ kickConnection(player, e);
+ } catch (Exception e) {
+ log.error("An exception was thrown while processing message", e);
+ kickConnection(player, PlayerKickException.internalServerError());
+ }
+ }
+
+ @Override
+ public void onError(WebSocket webSocket, Exception e) {
+ log.error("Something went error", e);
+ }
+
+ @Override
+ public void onStart() {
+ log.info("Running the server on port {}", getPort());
+ setConnectionLostTimeout(0);
+ setConnectionLostTimeout(100);
+ }
+
+ public void kickConnection(PlayerConnection player, PlayerKickException exception) {
+ player.send(exception);
+ player.getConnection().close();
+ players.remove(player);
+ log.debug("Kicked out {}! Reason: {}", player, exception.getMessage());
+ }
+
+ public static FrogarthaServer getInstance() {
+ if (instance == null) instance = new FrogarthaServer();
+ return instance;
+ }
+
+ public ArrayList<PlayerConnection> getPlayers() {
+ return players;
+ }
+}
diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java b/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java
new file mode 100644
index 0000000..c62dce5
--- /dev/null
+++ b/server/src/main/java/kz/ilotterytea/frogartha/server/PlayerConnection.java
@@ -0,0 +1,67 @@
+package kz.ilotterytea.frogartha.server;
+
+import kz.ilotterytea.frogartha.domain.Identity;
+import org.java_websocket.WebSocket;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.sql.Timestamp;
+
+public class PlayerConnection {
+ private static int TOTAL_CONNECTION_IDS = 0;
+
+ private final int id;
+ private final WebSocket connection;
+ private Identity identity;
+
+ private final Timestamp connectedTimestamp;
+
+ public PlayerConnection(WebSocket connection) {
+ this.connection = connection;
+ this.id = TOTAL_CONNECTION_IDS;
+ TOTAL_CONNECTION_IDS++;
+
+ this.connectedTimestamp = new Timestamp(System.currentTimeMillis());
+ this.identity = null;
+ }
+
+ public void send(Object object) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(object);
+ } catch (IOException ignored) {
+
+ }
+
+ this.connection.send(baos.toByteArray());
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public WebSocket getConnection() {
+ return connection;
+ }
+
+ public Timestamp getConnectedTimestamp() {
+ return connectedTimestamp;
+ }
+
+ public Identity getIdentity() {
+ return identity;
+ }
+
+ public void setIdentity(Identity identity) {
+ this.identity = identity;
+ }
+
+ @Override
+ public String toString() {
+ return "PlayerConnection{" +
+ "id=" + id +
+ '}';
+ }
+}
diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java
new file mode 100644
index 0000000..87dc17d
--- /dev/null
+++ b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerHandlers.java
@@ -0,0 +1,26 @@
+package kz.ilotterytea.frogartha.server;
+
+import kz.ilotterytea.frogartha.domain.Acknowledge;
+import kz.ilotterytea.frogartha.domain.Identity;
+import kz.ilotterytea.frogartha.exceptions.PlayerKickException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ServerHandlers {
+ private static final Logger log = LoggerFactory.getLogger(ServerHandlers.class);
+ private static final FrogarthaServer server = FrogarthaServer.getInstance();
+
+ public static void handleIdentity(PlayerConnection player, Identity identity) {
+ if (server.getPlayers()
+ .stream()
+ .filter((x) -> x.getIdentity() != null)
+ .anyMatch((x) -> x.getIdentity().equals(identity) && x.getId() != player.getId())) {
+ server.kickConnection(player, PlayerKickException.loggedIn());
+ return;
+ }
+
+ player.setIdentity(identity);
+ player.send(new Acknowledge(player));
+ log.debug("Successfully identified {} for {}", identity, player);
+ }
+}
diff --git a/server/src/main/java/kz/ilotterytea/frogartha/server/ServerLauncher.java b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerLauncher.java
index fc7c1e5..8c07f64 100644
--- a/server/src/main/java/kz/ilotterytea/frogartha/server/ServerLauncher.java
+++ b/server/src/main/java/kz/ilotterytea/frogartha/server/ServerLauncher.java
@@ -1,8 +1,11 @@
package kz.ilotterytea.frogartha.server;
-/** Launches the server application. */
+/**
+ * Launches the server application.
+ */
public class ServerLauncher {
public static void main(String[] args) {
- // TODO Implement server application.
+ FrogarthaServer server = FrogarthaServer.getInstance();
+ server.start();
}
-} \ No newline at end of file
+}
diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/Acknowledge.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/Acknowledge.java
new file mode 100644
index 0000000..3460cbc
--- /dev/null
+++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/Acknowledge.java
@@ -0,0 +1,22 @@
+package kz.ilotterytea.frogartha.domain;
+
+import java.io.Serializable;
+
+public class Acknowledge implements Serializable {
+ private final Object payload;
+
+ public Acknowledge(Object payload) {
+ this.payload = payload;
+ }
+
+ public Object getPayload() {
+ return payload;
+ }
+
+ @Override
+ public String toString() {
+ return "Acknowledge{" +
+ "payload=" + payload +
+ '}';
+ }
+}
diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/domain/Identity.java b/shared/src/main/java/kz/ilotterytea/frogartha/domain/Identity.java
new file mode 100644
index 0000000..9a72268
--- /dev/null
+++ b/shared/src/main/java/kz/ilotterytea/frogartha/domain/Identity.java
@@ -0,0 +1,30 @@
+package kz.ilotterytea.frogartha.domain;
+
+import java.io.Serializable;
+
+public class Identity implements Serializable {
+ private final String username;
+
+ public Identity(String username) {
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Identity) {
+ return ((Identity) obj).username.equals(username);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Identity{" +
+ "username='" + username + '\'' +
+ '}';
+ }
+}
diff --git a/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java b/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java
new file mode 100644
index 0000000..a6d8c0a
--- /dev/null
+++ b/shared/src/main/java/kz/ilotterytea/frogartha/exceptions/PlayerKickException.java
@@ -0,0 +1,15 @@
+package kz.ilotterytea.frogartha.exceptions;
+
+public class PlayerKickException extends RuntimeException {
+ private PlayerKickException(String message) {
+ super(message);
+ }
+
+ public static PlayerKickException loggedIn() {
+ return new PlayerKickException("You logged in from another location");
+ }
+
+ public static PlayerKickException internalServerError() {
+ return new PlayerKickException("Internal Server Error");
+ }
+}