summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/build.gradle8
-rw-r--r--api/src/main/java/akkamon/api/AkkamonSession.java12
-rw-r--r--api/src/main/java/akkamon/api/App.java46
-rw-r--r--api/src/main/java/akkamon/api/EventSocket.java31
-rw-r--r--api/src/main/java/akkamon/api/MessagingEngine.java117
-rw-r--r--api/src/main/java/akkamon/api/models/Event.java14
-rw-r--r--api/src/main/java/akkamon/api/models/GameState.java33
-rw-r--r--api/src/main/java/akkamon/api/models/Player.java13
-rw-r--r--api/src/main/java/akkamon/api/models/Position.java11
-rw-r--r--api/src/main/java/akkamon/api/models/User.java11
-rw-r--r--api/src/test/java/akkamon/api/EventSocketTest.java7
-rw-r--r--api/src/test/java/akkamon/api/MessagingEngineTest.java7
-rw-r--r--client/dist/index.html2
-rw-r--r--client/rollup.config.dev.js4
-rw-r--r--client/src/GameState.ts31
-rw-r--r--client/src/app.ts44
-rw-r--r--client/src/client.ts58
-rw-r--r--client/src/events.ts9
-rw-r--r--client/src/game.ts80
-rw-r--r--client/src/messageTypes.ts4
-rw-r--r--client/src/player.ts33
-rw-r--r--client/src/scene.ts1
-rw-r--r--client/src/session.ts10
-rw-r--r--client/src/socket.ts40
-rw-r--r--client/tsconfig.json2
-rw-r--r--domain/build.gradle20
-rw-r--r--domain/src/main/java/akkamon/domain/Akkamon.java6
-rw-r--r--domain/src/main/java/akkamon/domain/AkkamonImpl.java39
-rw-r--r--domain/src/main/java/akkamon/domain/Trainer.java28
-rw-r--r--domain/src/test/java/akkamon/domain/AkkamonImplTest.java28
-rw-r--r--settings.gradle2
31 files changed, 692 insertions, 59 deletions
diff --git a/api/build.gradle b/api/build.gradle
index 3db517e..28ec919 100644
--- a/api/build.gradle
+++ b/api/build.gradle
@@ -29,16 +29,18 @@ dependencies {
// In our simple use case, the logger gets automatically configured by simply existing.
implementation 'org.slf4j:slf4j-simple:+'
+ implementation 'com.google.code.gson:gson:2.8.7'
+
// Reference the domain subproject.
- // implementation project(':domain')
+ implementation project(':domain')
// Use JUnit Jupiter API for testing.
- testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
// Also use the Mockito mocking framework to mock simple server functionality.
testImplementation "org.mockito:mockito-core:2.+"
// Use JUnit Jupiter Engine for testing.
- testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
application {
diff --git a/api/src/main/java/akkamon/api/AkkamonSession.java b/api/src/main/java/akkamon/api/AkkamonSession.java
new file mode 100644
index 0000000..217c8d8
--- /dev/null
+++ b/api/src/main/java/akkamon/api/AkkamonSession.java
@@ -0,0 +1,12 @@
+package akkamon.api;
+
+import akkamon.api.models.User;
+
+public interface AkkamonSession {
+
+ void receiveGameState(String gameState);
+
+ void disconnect(int statusCode, String message);
+
+ void setCurrentUser(User user);
+}
diff --git a/api/src/main/java/akkamon/api/App.java b/api/src/main/java/akkamon/api/App.java
index bc462d0..989562b 100644
--- a/api/src/main/java/akkamon/api/App.java
+++ b/api/src/main/java/akkamon/api/App.java
@@ -3,21 +3,18 @@ package akkamon.api;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.glassfish.jersey.servlet.ServletContainer;
public class App {
public static void main(String[] args) {
- Server server = new Server();
- ServerConnector connector = new ServerConnector(server);
- connector.setPort(8080);
- server.addConnector(connector);
+ Server server = startServer(8080);
+ ServletContextHandler context = createStatefulContext(server);
- // application "context" ?
- ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
- context.setContextPath("/");
- server.setHandler(context);
// websocket behaviour
// Configure specific websocket behavior
@@ -30,8 +27,13 @@ public class App {
wsContainer.addMapping("/", EventSocket.class);
});
+ // registerServlets(context);
+
try {
server.start();
+ System.out.println("Started server.");
+ System.out.println("Listening on http://localhost:8080/");
+ System.out.println("Press CTRL+C to exit.");
server.join();
}
catch (Throwable t) {
@@ -39,4 +41,32 @@ public class App {
}
}
+
+ private static ServletContextHandler createStatefulContext(Server server) {
+ ServletContextHandler context =
+ new ServletContextHandler(ServletContextHandler.SESSIONS);
+ context.setContextPath("/");
+ server.setHandler(context);
+ return context;
+ }
+
+ private static Server startServer(int port) {
+ Server server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(port);
+ server.addConnector(connector);
+ return server;
+ }
+
+ private static void registerServlets(ServletContextHandler context) {
+ // Use the Jersey framework to translate the classes in the
+ // mancala.api package to server endpoints (servlets).
+ // For example, the StartMancala class will become an endpoint at
+ // http://localost:8080/mancala/api/start
+ ServletHolder serverHolder = context.addServlet(JettyWebSocketServlet.class, "/");
+ serverHolder.setInitOrder(1);
+ serverHolder.setInitParameter("jersey.config.server.provider.packages",
+ "akkamon.api");
+ }
+
}
diff --git a/api/src/main/java/akkamon/api/EventSocket.java b/api/src/main/java/akkamon/api/EventSocket.java
index 1c7bafc..5a2693e 100644
--- a/api/src/main/java/akkamon/api/EventSocket.java
+++ b/api/src/main/java/akkamon/api/EventSocket.java
@@ -1,16 +1,18 @@
package akkamon.api;
-import java.util.Locale;
+import java.io.IOException;
import java.util.concurrent.CountDownLatch;
+import akkamon.api.models.User;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.StatusCode;
-public class EventSocket extends WebSocketAdapter {
+public class EventSocket extends WebSocketAdapter implements AkkamonSession {
private final CountDownLatch closureLatch = new CountDownLatch(1);
+ public User user;
+
@Override
public void onWebSocketConnect(Session sess)
{
@@ -23,10 +25,8 @@ public class EventSocket extends WebSocketAdapter {
{
super.onWebSocketText(message);
System.out.println("Received TEXT message: " + message);
+ MessagingEngine.getInstance().incoming(this, message);
- if (message.toLowerCase(Locale.US).contains("bye")) {
- getSession().close(StatusCode.NORMAL, "Thanks");
- }
}
@Override
@@ -49,4 +49,23 @@ public class EventSocket extends WebSocketAdapter {
System.out.println("Awaiting closure from remote");
closureLatch.await();
}
+
+ @Override
+ public void receiveGameState(String gameState) {
+ try {
+ getRemote().sendString(gameState);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void disconnect(int statusCode, String message) {
+ getSession().close(statusCode, message);
+ }
+
+ @Override
+ public void setCurrentUser(User user) {
+ this.user = user;
+ }
}
diff --git a/api/src/main/java/akkamon/api/MessagingEngine.java b/api/src/main/java/akkamon/api/MessagingEngine.java
new file mode 100644
index 0000000..a3cff1d
--- /dev/null
+++ b/api/src/main/java/akkamon/api/MessagingEngine.java
@@ -0,0 +1,117 @@
+package akkamon.api;
+
+import akkamon.api.models.*;
+import akkamon.domain.AkkamonImpl;
+import akkamon.domain.Trainer;
+import com.google.gson.Gson;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class MessagingEngine {
+
+ private HashMap<String, AkkamonSession> akkamonSessions = new HashMap<>();
+ private static MessagingEngine instance;
+ private Gson gson = new Gson();
+
+ public static MessagingEngine getInstance() {
+ if (instance == null) {
+ instance = new MessagingEngine();
+ return instance;
+ }
+ return instance;
+ }
+
+ public MessagingEngine() {
+ ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
+ executor.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ emitGameState();
+ }
+ }, 0, 200, TimeUnit.MILLISECONDS);
+
+ }
+
+ void emitGameState() {
+ HashMap<String, Trainer> trainers = AkkamonImpl.getInstance().getDummyTrainersCollection();
+
+ if (akkamonSessions.size() == 1) {
+ AkkamonSession session = akkamonSessions.get("Ash");
+
+ GameState gameState = new GameState();
+ // dummy
+ gameState.setCurrentPlayer("Ash", trainers);
+
+ Event event = new Event("updatePos", gameState);
+
+ session.receiveGameState(gson.toJson(event));
+
+ } else if (akkamonSessions.size() == 2) {
+ for (String name: akkamonSessions.keySet()) {
+ AkkamonSession session = akkamonSessions.get(name);
+
+ GameState gameState = new GameState();
+ // dummy
+ gameState.setCurrentPlayer(name, trainers);
+ gameState.setRemotePlayers(trainers);
+
+ Event event = new Event("updatePos", gameState);
+
+ session.receiveGameState(gson.toJson(event));
+ }
+ }
+
+ // for (Map.Entry<User, AkkamonSession> sess: akkamonSessions.entrySet()) {
+
+ // User user = sess.getKey();
+ // AkkamonSession session = sess.getValue();
+
+ // GameState gameState = new GameState();
+ // // dummy
+ // gameState.setCurrentPlayer(user.name, trainers);
+
+ // session.receiveGameState(gson.toJson(gameState));
+ // }
+ }
+
+ void incoming(AkkamonSession session, String message) {
+ Event event = gson.fromJson(message, Event.class);
+ switch (event.type) {
+ case "login":
+ login(session, event.user);
+ break;
+ case "posUpdate":
+ updatePositions(event.gameState);
+ break;
+ }
+ }
+
+ private void updatePositions(GameState gameState) {
+ Player current = gameState.currentPlayer;
+ if (gameState.currentPlayer != null) {
+ AkkamonImpl.getInstance().updateTrainerPosition(current.name, current.position.x, current.position.y);
+ }
+ }
+
+ private void login(AkkamonSession session, User user) {
+ if (user == null) {
+ session.disconnect(401, "Give username and password");
+ }
+ System.out.println("Currrent connections: " + akkamonSessions.size());
+ if (akkamonSessions.size() == 0) {
+ akkamonSessions.put("Ash", session);
+ System.out.println("After adding ash!: " + akkamonSessions.size());
+ session.setCurrentUser(new User("Ash", ""));
+ } else if (akkamonSessions.size() == 1) {
+ akkamonSessions.put("Misty", session);
+ session.setCurrentUser(new User("Misty", ""));
+ }
+ AkkamonImpl.getInstance().newPlayerConnected(user.name, user.password);
+ System.out.println("Emitting gameState!");
+ emitGameState();
+ }
+
+}
diff --git a/api/src/main/java/akkamon/api/models/Event.java b/api/src/main/java/akkamon/api/models/Event.java
new file mode 100644
index 0000000..50fc849
--- /dev/null
+++ b/api/src/main/java/akkamon/api/models/Event.java
@@ -0,0 +1,14 @@
+package akkamon.api.models;
+
+import org.eclipse.jetty.server.Authentication;
+
+public class Event {
+ public String type;
+ public GameState gameState;
+ public User user;
+
+ public Event(String type, GameState gameState) {
+ this.type = type;
+ this.gameState = gameState;
+ }
+}
diff --git a/api/src/main/java/akkamon/api/models/GameState.java b/api/src/main/java/akkamon/api/models/GameState.java
new file mode 100644
index 0000000..b303d24
--- /dev/null
+++ b/api/src/main/java/akkamon/api/models/GameState.java
@@ -0,0 +1,33 @@
+package akkamon.api.models;
+
+import akkamon.domain.Trainer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GameState {
+ public Player currentPlayer;
+ public HashMap<String, Player> remotePlayers;
+
+
+ public void setCurrentPlayer(String name, HashMap<String, Trainer> trainers) {
+ Trainer trainer = trainers.get(name);
+ Position position = new Position(trainer.getX(), trainer.getY());
+ currentPlayer = new Player(name, position);
+ }
+
+ public void setRemotePlayers(HashMap<String, Trainer> trainers) {
+ for (Map.Entry<String, Trainer> trainer: trainers.entrySet()) {
+ if (trainer.getValue().getName().equals(currentPlayer.name)) {
+ continue;
+ }
+
+ String name = trainer.getKey();
+ Trainer obj = trainer.getValue();
+ remotePlayers.put(name, new Player(name, new Position(
+ obj.getX(),
+ obj.getY()
+ )));
+ }
+ }
+}
diff --git a/api/src/main/java/akkamon/api/models/Player.java b/api/src/main/java/akkamon/api/models/Player.java
new file mode 100644
index 0000000..3aebb4d
--- /dev/null
+++ b/api/src/main/java/akkamon/api/models/Player.java
@@ -0,0 +1,13 @@
+package akkamon.api.models;
+
+import java.util.HashMap;
+
+public class Player {
+ public String name;
+ public Position position;
+
+ public Player(String name, Position position) {
+ this.name = name;
+ this.position = position;
+ }
+}
diff --git a/api/src/main/java/akkamon/api/models/Position.java b/api/src/main/java/akkamon/api/models/Position.java
new file mode 100644
index 0000000..abe11dd
--- /dev/null
+++ b/api/src/main/java/akkamon/api/models/Position.java
@@ -0,0 +1,11 @@
+package akkamon.api.models;
+
+public class Position {
+ public int x;
+ public int y;
+
+ public Position(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+}
diff --git a/api/src/main/java/akkamon/api/models/User.java b/api/src/main/java/akkamon/api/models/User.java
new file mode 100644
index 0000000..5f26ea0
--- /dev/null
+++ b/api/src/main/java/akkamon/api/models/User.java
@@ -0,0 +1,11 @@
+package akkamon.api.models;
+
+public class User {
+ public String name;
+ public String password;
+
+ public User(String name, String password) {
+ this.name = name;
+ this.password = password;
+ }
+}
diff --git a/api/src/test/java/akkamon/api/EventSocketTest.java b/api/src/test/java/akkamon/api/EventSocketTest.java
new file mode 100644
index 0000000..e9a6cc0
--- /dev/null
+++ b/api/src/test/java/akkamon/api/EventSocketTest.java
@@ -0,0 +1,7 @@
+package akkamon.api;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class EventSocketTest {
+
+} \ No newline at end of file
diff --git a/api/src/test/java/akkamon/api/MessagingEngineTest.java b/api/src/test/java/akkamon/api/MessagingEngineTest.java
new file mode 100644
index 0000000..97d556c
--- /dev/null
+++ b/api/src/test/java/akkamon/api/MessagingEngineTest.java
@@ -0,0 +1,7 @@
+package akkamon.api;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class MessagingEngineTest {
+
+} \ No newline at end of file
diff --git a/client/dist/index.html b/client/dist/index.html
index 71c49a2..f68bd5d 100644
--- a/client/dist/index.html
+++ b/client/dist/index.html
@@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
- <script src="game.js"></script>
+ <script src="app.js"></script>
</head>
<body>
</body>
diff --git a/client/rollup.config.dev.js b/client/rollup.config.dev.js
index 09d0552..2a265da 100644
--- a/client/rollup.config.dev.js
+++ b/client/rollup.config.dev.js
@@ -8,7 +8,7 @@ export default {
// Our games entry point (edit as required)
input: [
- './src/game.ts'
+ './src/app.ts'
],
// Where the build file is to be generated.
@@ -16,7 +16,7 @@ export default {
// You can also use 'umd' if you need to ingest your game into another system.
// The 'intro' property can be removed if using Phaser 3.21 or above. Keep it for earlier versions.
output: {
- file: './dist/game.js',
+ file: './dist/app.js',
name: 'MyGame',
format: 'iife',
sourcemap: true,
diff --git a/client/src/GameState.ts b/client/src/GameState.ts
new file mode 100644
index 0000000..def867f
--- /dev/null
+++ b/client/src/GameState.ts
@@ -0,0 +1,31 @@
+import Player from './player';
+import type { Event } from './events';
+
+export default class GameState {
+
+ static instance: GameState;
+
+ currentPlayer: Player | undefined;
+ remotePlayers: { [name: string]: Player } | undefined
+
+ static getInstance() {
+ if (GameState.instance) return GameState.instance;
+ else {
+ GameState.instance = new GameState();
+ return GameState.instance;
+ }
+ }
+
+ setCurrentPlayer(player: Player) {
+ this.currentPlayer = player;
+ }
+
+ posUpdate(receivedState: GameState) {
+ console.log("--> Game is updating positions");
+ if (this.currentPlayer === undefined) {
+ console.log("--> getting current player object");
+ console.log(receivedState.currentPlayer!);
+ this.currentPlayer = new Player(receivedState.currentPlayer!);
+ }
+ }
+}
diff --git a/client/src/app.ts b/client/src/app.ts
new file mode 100644
index 0000000..784fed8
--- /dev/null
+++ b/client/src/app.ts
@@ -0,0 +1,44 @@
+// import Phaser from 'phaser';
+import GameState from './GameState';
+import Socket from './socket';
+import Client from './client';
+import Player from './player';
+
+const url = 'ws://localhost:8080';
+const session = Socket.getInstance('ws://localhost:8080', {name: "", password: ""});
+const client = Client.getInstance();
+client.setSession(session);
+
+import AkkamonStartScene from './game';
+
+const config: Phaser.Types.Core.GameConfig & Phaser.Types.Core.RenderConfig = {
+ type: Phaser.AUTO,
+ backgroundColor: '#125555',
+ width: 800,
+ height: 600,
+ pixelArt: true,
+ scene: AkkamonStartScene,
+ physics: {
+ default: "arcade",
+ arcade: {
+ gravity: { y: 0 }
+ }
+ }
+};
+
+function delay(ms: number) {
+ return new Promise( resolve => setTimeout(resolve, ms) );
+}
+
+async function startGame() {
+ while (true) {
+ console.log(GameState.getInstance().currentPlayer);
+ if (GameState.getInstance().currentPlayer) {
+ const game: Phaser.Game = new Phaser.Game(config);
+ break;
+ }
+ await delay(1000);
+ }
+}
+
+startGame();
diff --git a/client/src/client.ts b/client/src/client.ts
new file mode 100644
index 0000000..ecd15fc
--- /dev/null
+++ b/client/src/client.ts
@@ -0,0 +1,58 @@
+import type {
+ Event,
+} from './events';
+import type AkkamonSession from './session';
+import GameState from './GameState';
+
+
+export default class Client
+{
+ static instance: Client
+ session: AkkamonSession | undefined;
+
+ static getInstance() {
+ if (Client.instance) return Client.instance;
+ else {
+ Client.instance = new Client();
+ return Client.instance;
+ }
+ }
+
+ setSession(akkamonSession: AkkamonSession) {
+ this.session = akkamonSession;
+ }
+
+ in(eventString: string) {
+ let event: Event = JSON.parse(eventString);
+ console.log("-> client is handling incoming event:");
+ console.log(event);
+ switch (event.type) {
+ case 'updatePos':
+ GameState.getInstance().posUpdate(event.gameState!);
+ break;
+ }
+ }
+
+ out(event: Event) {
+ console.log("-> client is now sending out message:");
+ console.log(event)
+ if (this.session) {
+ this.session.send(JSON.stringify(event));
+ }
+ }
+
+ login(user: {name:string, password: string}) {
+ console.log("Sending the login message");
+ if (this.session) {
+ this.session.send(JSON.stringify(
+ {
+ type: 'login',
+ user: {
+ name: user.name,
+ password: user.password
+ }
+ }
+ ));
+ }
+ }
+}
diff --git a/client/src/events.ts b/client/src/events.ts
new file mode 100644
index 0000000..89cadaf
--- /dev/null
+++ b/client/src/events.ts
@@ -0,0 +1,9 @@
+import Phaser from 'phaser';
+import type Player from './player';
+import type GameState from './GameState';
+
+export interface Event {
+ type: string
+ gameState?: GameState
+}
+
diff --git a/client/src/game.ts b/client/src/game.ts
index daaf5d9..369e7e6 100644
--- a/client/src/game.ts
+++ b/client/src/game.ts
@@ -1,10 +1,21 @@
import Phaser from 'phaser';
+import type Player from './player';
+import Client from './client';
+import GameState from './GameState';
-export default class Demo extends Phaser.Scene
+type Sprite = Phaser.Types.Physics.Arcade.SpriteWithDynamicBody;
+
+type Input = {
+ cursors: Phaser.Types.Input.Keyboard.CursorKeys,
+}
+
+
+export default class AkkamonStartScene extends Phaser.Scene
{
+ remotePlayers: Array<Player> = new Array();
constructor ()
{
- super('demo');
+ super('akkamonStartScene');
}
preload ()
@@ -49,12 +60,15 @@ export default class Demo extends Phaser.Scene
// Create a sprite with physics enabled via the physics system. The image used for the sprite has
// a bit of whitespace, so I'm using setSize & setOffset to control the size of the player's body.
- player = this.physics.add
+
+ let player = this.physics.add
.sprite(spawnPoint.x as number, spawnPoint.y as number, "atlas", "misa-front")
.setSize(30, 40)
.setOffset(0, 24);
- this.physics.add.collider(player, worldLayer);
+ GameState.getInstance().currentPlayer!.setSprite(player);
+
+ // this.physics.add.collider(player, worldLayer);
// Create the player's walking animations from the texture atlas. These are stored in the global
// animation manager so any sprite can access them.
@@ -90,7 +104,8 @@ export default class Demo extends Phaser.Scene
camera.startFollow(player);
camera.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
- cursors = this.input.keyboard.createCursorKeys();
+ let cursors = this.input.keyboard.createCursorKeys();
+ GameState.getInstance().currentPlayer!.input = { cursors };
// Debug graphics
this.input.keyboard.once("keydown_D", (event: Event) => {
@@ -117,23 +132,36 @@ export default class Demo extends Phaser.Scene
}
update(time: Number, delta: Number) {
+ let player = GameState.getInstance().currentPlayer!.sprite as Sprite;
+ let input = GameState.getInstance().currentPlayer!.input;
+
+
const speed = 175;
const prevVelocity = player.body.velocity.clone();
// Stop any previous movement from the last frame
player.body.setVelocity(0);
+ if (input) {
+ this.moveSprite(player, input, speed, prevVelocity)
+ }
+
+ // this.drawRemotePlayers();
+ }
+
+ moveSprite(player: Sprite, input: Input, speed: number, prevVelocity: {x: number, y:number}) {
+
// Horizontal movement
- if (cursors.left.isDown) {
+ if (input.cursors.left.isDown) {
player.body.setVelocityX(-speed);
- } else if (cursors.right.isDown) {
+ } else if (input.cursors.right.isDown) {
player.body.setVelocityX(speed);
}
// Vertical movement
- if (cursors.up.isDown) {
+ if (input.cursors.up.isDown) {
player.body.setVelocityY(-speed);
- } else if (cursors.down.isDown) {
+ } else if (input.cursors.down.isDown) {
player.body.setVelocityY(speed);
}
@@ -141,13 +169,13 @@ export default class Demo extends Phaser.Scene
player.body.velocity.normalize().scale(speed);
// Update the animation last and give left/right animations precedence over up/down animations
- if (cursors.left.isDown) {
+ if (input.cursors.left.isDown) {
player.anims.play("misa-left-walk", true);
- } else if (cursors.right.isDown) {
+ } else if (input.cursors.right.isDown) {
player.anims.play("misa-right-walk", true);
- } else if (cursors.up.isDown) {
+ } else if (input.cursors.up.isDown) {
player.anims.play("misa-back-walk", true);
- } else if (cursors.down.isDown) {
+ } else if (input.cursors.down.isDown) {
player.anims.play("misa-front-walk", true);
} else {
player.anims.stop();
@@ -158,29 +186,5 @@ export default class Demo extends Phaser.Scene
else if (prevVelocity.y > 0) player.setTexture("atlas", "misa-front");
}
}
-}
-
-const config = {
- type: Phaser.AUTO,
- backgroundColor: '#125555',
- width: 800,
- height: 600,
- pixelArt: true,
- scene: Demo,
- physics: {
- default: "arcade",
- arcade: {
- gravity: { y: 0 }
- }
- }
-};
-let cursors: Phaser.Types.Input.Keyboard.CursorKeys;
-let player: Phaser.Types.Physics.Arcade.SpriteWithDynamicBody;
-
-const socket = new WebSocket('ws://localhost:8080');
-socket.addEventListener('open', (e: Event) => {socket.send('Hello Server!');});
-socket.addEventListener('close', (e: Event) => {socket.send('Goodbye!');});
-// socket.send("bye");
-
-const game = new Phaser.Game(config);
+}
diff --git a/client/src/messageTypes.ts b/client/src/messageTypes.ts
new file mode 100644
index 0000000..eae45dc
--- /dev/null
+++ b/client/src/messageTypes.ts
@@ -0,0 +1,4 @@
+export interface Message {
+ type: 'login'
+}
+
diff --git a/client/src/player.ts b/client/src/player.ts
new file mode 100644
index 0000000..1be85dd
--- /dev/null
+++ b/client/src/player.ts
@@ -0,0 +1,33 @@
+import type Phaser from 'phaser';
+
+type Sprite = Phaser.Types.Physics.Arcade.SpriteWithDynamicBody;
+
+type PlayerConfig = {
+ name: string,
+ position: {x: number, y: number}
+}
+
+type Input = {
+ cursors: Phaser.Types.Input.Keyboard.CursorKeys,
+}
+
+export default class Player
+{
+ name: string
+ position: {x:number, y: number}
+ sprite: Sprite | undefined;
+ input: Input | undefined;
+
+ constructor({name, position}: PlayerConfig) {
+ this.name = name;
+ this.position = position
+ }
+
+ setSprite(sprite: Sprite) {
+ this.sprite = sprite;
+ }
+
+ setInput(input: Input) {
+ this.input = input;
+ }
+}
diff --git a/client/src/scene.ts b/client/src/scene.ts
new file mode 100644
index 0000000..4ac2a3f
--- /dev/null
+++ b/client/src/scene.ts
@@ -0,0 +1 @@
+import type Player from './player';
diff --git a/client/src/session.ts b/client/src/session.ts
new file mode 100644
index 0000000..bbefd66
--- /dev/null
+++ b/client/src/session.ts
@@ -0,0 +1,10 @@
+import type Player from './player';
+
+export default interface AkkamonSession extends WebSocket {
+ user?: User
+}
+
+interface User {
+ name: string
+ password: string
+}
diff --git a/client/src/socket.ts b/client/src/socket.ts
new file mode 100644
index 0000000..cfd8ce3
--- /dev/null
+++ b/client/src/socket.ts
@@ -0,0 +1,40 @@
+import Phaser from 'phaser';
+import Client from './client'
+import type AkkamonSession from './session'
+
+export default class Socket extends WebSocket implements AkkamonSession
+{
+ static instance: AkkamonSession;
+
+ static getInstance(url: string, user: {name: string, password: string}) {
+ if (Socket.instance) return Socket.instance;
+ else {
+ Socket.instance = new Socket(url, user);
+ return Socket.instance;
+ }
+ }
+
+ constructor(url: string, user: {name: string, password: string}) {
+ super(url);
+
+ let client = Client.getInstance();
+
+ let session = this;
+
+ this.onopen = function echo(this: WebSocket, ev: Event) {
+ console.log("opening socket");
+ console.log("this is the websocket");
+ console.log(this);
+ console.log("logging in the session to the server");
+ client.login(user);
+ }
+
+ this.onmessage = function incomingMessage(this: WebSocket, ev: MessageEvent) {
+ console.log("received message from the server!");
+ console.log("-> " + ev.data);
+ console.log("calling client.in:");
+ client.in(ev.data);
+ }
+ }
+
+}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 4da9d17..7e0909c 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -3,7 +3,7 @@
"./src/**/*"
],
"compilerOptions": {
- "target": "es5",
+ "target": "es6",
"moduleResolution": "node",
"noEmit": true,
"strict": true,
diff --git a/domain/build.gradle b/domain/build.gradle
new file mode 100644
index 0000000..2f385d0
--- /dev/null
+++ b/domain/build.gradle
@@ -0,0 +1,20 @@
+plugins {
+ id 'java'
+ id 'java-library'
+}
+
+version 'unspecified'
+
+repositories {
+ jcenter()
+ mavenCentral()
+}
+
+dependencies {
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
+}
+
+test {
+ useJUnitPlatform()
+} \ No newline at end of file
diff --git a/domain/src/main/java/akkamon/domain/Akkamon.java b/domain/src/main/java/akkamon/domain/Akkamon.java
new file mode 100644
index 0000000..db5c3e2
--- /dev/null
+++ b/domain/src/main/java/akkamon/domain/Akkamon.java
@@ -0,0 +1,6 @@
+package akkamon.domain;
+
+public interface Akkamon {
+ void newPlayerConnected(String name, String password);
+ void updateTrainerPosition(String name, int x, int y);
+}
diff --git a/domain/src/main/java/akkamon/domain/AkkamonImpl.java b/domain/src/main/java/akkamon/domain/AkkamonImpl.java
new file mode 100644
index 0000000..a5f563b
--- /dev/null
+++ b/domain/src/main/java/akkamon/domain/AkkamonImpl.java
@@ -0,0 +1,39 @@
+package akkamon.domain;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class AkkamonImpl implements Akkamon {
+
+ private static AkkamonImpl instance;
+
+ private static HashMap<String, Trainer> dummyTrainersCollection = new HashMap<>();
+
+ public static AkkamonImpl getInstance() {
+ if (instance == null) {
+ instance = new AkkamonImpl();
+ }
+ return instance;
+ }
+
+ @Override
+ public void newPlayerConnected(String name, String password) {
+ switch (dummyTrainersCollection.size()) {
+ case 0:
+ dummyTrainersCollection.put("Ash", new Trainer("Ash"));
+ break;
+ case 1:
+ dummyTrainersCollection.put("Misty", new Trainer("Misty"));
+ break;
+ }
+ }
+
+ public void updateTrainerPosition(String name, int x, int y) {
+ Trainer trainer = dummyTrainersCollection.get(name);
+ trainer.newPosition(x, y);
+ }
+
+ public HashMap<String, Trainer> getDummyTrainersCollection() {
+ return dummyTrainersCollection;
+ }
+}
diff --git a/domain/src/main/java/akkamon/domain/Trainer.java b/domain/src/main/java/akkamon/domain/Trainer.java
new file mode 100644
index 0000000..7490431
--- /dev/null
+++ b/domain/src/main/java/akkamon/domain/Trainer.java
@@ -0,0 +1,28 @@
+package akkamon.domain;
+
+public class Trainer {
+ private String name;
+ private int x;
+ private int y;
+
+ public Trainer(String name) {
+ this.name = name;
+ }
+
+ public void newPosition(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/domain/src/test/java/akkamon/domain/AkkamonImplTest.java b/domain/src/test/java/akkamon/domain/AkkamonImplTest.java
new file mode 100644
index 0000000..6570c2a
--- /dev/null
+++ b/domain/src/test/java/akkamon/domain/AkkamonImplTest.java
@@ -0,0 +1,28 @@
+package akkamon.domain;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AkkamonImplTest {
+
+ @BeforeEach
+ void setUp() {
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @Nested
+ class getInstance_behaviour {
+ @Test
+ void given_there_is_no_instance_yet_when_getInstance_is_called_then_give_class_property_instance() {
+ assertNotNull(AkkamonImpl.getInstance());
+ }
+ }
+
+} \ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index c353050..951c56b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,3 +9,5 @@
rootProject.name = 'mvink_akkamons'
include('model', 'api')
+include 'domain'
+