diff options
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' + |
