diff options
| author | Mike Vink <mike1994vink@gmail.com> | 2021-07-13 18:30:12 +0200 |
|---|---|---|
| committer | Mike Vink <mike1994vink@gmail.com> | 2021-07-13 18:30:12 +0200 |
| commit | 9d54842c2f5b084ab1b0de4c4b8978cc43297546 (patch) | |
| tree | df649d72febeee87ec2d400ef04784b2b259ad65 | |
| parent | 818b9970ef7c61e641e285b7f5ca53d69b3010e4 (diff) | |
feat(WebSockets): client server WebSocket connect
| -rw-r--r-- | .gitignore | 8 | ||||
| -rw-r--r-- | api/build.gradle | 52 | ||||
| -rw-r--r-- | api/src/main/java/akkamon/api/App.java | 42 | ||||
| -rw-r--r-- | api/src/main/java/akkamon/api/EventSocket.java | 52 | ||||
| -rw-r--r-- | client/src/game.ts | 78 | ||||
| -rw-r--r-- | client/tsconfig.json | 26 | ||||
| -rw-r--r-- | gradle/wrapper/gradle-wrapper.jar | bin | 0 -> 59536 bytes | |||
| -rw-r--r-- | gradle/wrapper/gradle-wrapper.properties | 5 | ||||
| -rwxr-xr-x | gradlew | 185 | ||||
| -rw-r--r-- | gradlew.bat | 89 | ||||
| -rw-r--r-- | settings.gradle | 11 |
11 files changed, 506 insertions, 42 deletions
@@ -1,3 +1,11 @@ client/dist/*.js.map client/dist/*.js node_modules + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +.gitattributes +.idea/ diff --git a/api/build.gradle b/api/build.gradle new file mode 100644 index 0000000..3db517e --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,52 @@ +plugins { + id 'java' + // This time we're building a command-line executable application. + id 'application' +} + +repositories { + jcenter() + mavenCentral() +} + +dependencies { + + // Use the Jersey framework to make writing and testing servlets easier. + implementation 'org.glassfish.jersey.containers:jersey-container-servlet-core:+' + implementation 'org.glassfish.jersey.containers:jersey-container-jetty-http:+' + implementation 'org.glassfish.jersey.core:jersey-server:+' + implementation 'org.glassfish.jersey.inject:jersey-hk2:+' + implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:+' + // Use Jakarta (Java EE) for the servlet primitives. + implementation 'jakarta.servlet:jakarta.servlet-api:+' + // Use the Jetty server. + implementation 'org.eclipse.jetty:jetty-server:+' + implementation 'org.eclipse.jetty:jetty-servlet:+' + implementation 'org.eclipse.jetty:jetty-webapp:+' + implementation 'org.eclipse.jetty:jetty-websocket:+' + implementation 'org.eclipse.jetty.websocket:websocket-jetty-server:+' + // We want to have some logging output if things go wrong, so use the simple console logger from SLF4J. + // In our simple use case, the logger gets automatically configured by simply existing. + implementation 'org.slf4j:slf4j-simple:+' + + // Reference the domain subproject. + // implementation project(':domain') + + // Use JUnit Jupiter API for testing. + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2' + // 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' +} + +application { + // Define the main class for the application. + mainClassName = 'akkamon.api.App' +} + +test { + useJUnitPlatform() +} + diff --git a/api/src/main/java/akkamon/api/App.java b/api/src/main/java/akkamon/api/App.java new file mode 100644 index 0000000..bc462d0 --- /dev/null +++ b/api/src/main/java/akkamon/api/App.java @@ -0,0 +1,42 @@ +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.websocket.server.config.JettyWebSocketServletContainerInitializer; + + +public class App { + + public static void main(String[] args) { + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(8080); + server.addConnector(connector); + + // application "context" ? + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + // websocket behaviour + // Configure specific websocket behavior + JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> + { + // Configure default max size + wsContainer.setMaxTextMessageSize(65535); + + // Add websockets + wsContainer.addMapping("/", EventSocket.class); + }); + + try { + server.start(); + server.join(); + } + catch (Throwable t) { + t.printStackTrace(System.err); + } + + } +} diff --git a/api/src/main/java/akkamon/api/EventSocket.java b/api/src/main/java/akkamon/api/EventSocket.java new file mode 100644 index 0000000..1c7bafc --- /dev/null +++ b/api/src/main/java/akkamon/api/EventSocket.java @@ -0,0 +1,52 @@ +package akkamon.api; + +import java.util.Locale; +import java.util.concurrent.CountDownLatch; + +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 { + private final CountDownLatch closureLatch = new CountDownLatch(1); + + @Override + public void onWebSocketConnect(Session sess) + { + super.onWebSocketConnect(sess); + System.out.println("Socket Connected: " + sess); + } + + @Override + public void onWebSocketText(String message) + { + super.onWebSocketText(message); + System.out.println("Received TEXT message: " + message); + + if (message.toLowerCase(Locale.US).contains("bye")) { + getSession().close(StatusCode.NORMAL, "Thanks"); + } + } + + @Override + public void onWebSocketClose(int statusCode, String reason) + { + super.onWebSocketClose(statusCode, reason); + System.out.println("Socket Closed: [" + statusCode + "] " + reason); + closureLatch.countDown(); + } + + @Override + public void onWebSocketError(Throwable cause) + { + super.onWebSocketError(cause); + cause.printStackTrace(System.err); + } + + public void awaitClosure() throws InterruptedException + { + System.out.println("Awaiting closure from remote"); + closureLatch.await(); + } +} diff --git a/client/src/game.ts b/client/src/game.ts index ea9ca26..daaf5d9 100644 --- a/client/src/game.ts +++ b/client/src/game.ts @@ -1,4 +1,4 @@ -import 'phaser'; +import Phaser from 'phaser'; export default class Demo extends Phaser.Scene { @@ -24,6 +24,7 @@ export default class Demo extends Phaser.Scene "assets/atlas/atlas.json"); } + create () { const map = this.make.tilemap({ key: "map" }); @@ -49,7 +50,7 @@ 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 - .sprite(spawnPoint.x, spawnPoint.y, "atlas", "misa-front") + .sprite(spawnPoint.x as number, spawnPoint.y as number, "atlas", "misa-front") .setSize(30, 40) .setOffset(0, 24); @@ -59,28 +60,28 @@ export default class Demo extends Phaser.Scene // animation manager so any sprite can access them. const anims = this.anims; anims.create({ - key: "misa-left-walk", - frames: anims.generateFrameNames("atlas", { prefix: "misa-left-walk.", start: 0, end: 3, zeroPad: 3 }), - frameRate: 10, - repeat: -1 + key: "misa-left-walk", + frames: anims.generateFrameNames("atlas", { prefix: "misa-left-walk.", start: 0, end: 3, zeroPad: 3 }), + frameRate: 10, + repeat: -1 }); anims.create({ - key: "misa-right-walk", - frames: anims.generateFrameNames("atlas", { prefix: "misa-right-walk.", start: 0, end: 3, zeroPad: 3 }), - frameRate: 10, - repeat: -1 + key: "misa-right-walk", + frames: anims.generateFrameNames("atlas", { prefix: "misa-right-walk.", start: 0, end: 3, zeroPad: 3 }), + frameRate: 10, + repeat: -1 }); anims.create({ - key: "misa-front-walk", - frames: anims.generateFrameNames("atlas", { prefix: "misa-front-walk.", start: 0, end: 3, zeroPad: 3 }), - frameRate: 10, - repeat: -1 + key: "misa-front-walk", + frames: anims.generateFrameNames("atlas", { prefix: "misa-front-walk.", start: 0, end: 3, zeroPad: 3 }), + frameRate: 10, + repeat: -1 }); anims.create({ - key: "misa-back-walk", - frames: anims.generateFrameNames("atlas", { prefix: "misa-back-walk.", start: 0, end: 3, zeroPad: 3 }), - frameRate: 10, - repeat: -1 + key: "misa-back-walk", + frames: anims.generateFrameNames("atlas", { prefix: "misa-back-walk.", start: 0, end: 3, zeroPad: 3 }), + frameRate: 10, + repeat: -1 }); @@ -92,20 +93,22 @@ export default class Demo extends Phaser.Scene cursors = this.input.keyboard.createCursorKeys(); // Debug graphics - this.input.keyboard.once("keydown_D", event => { - // Turn on physics debugging to show player's hitbox - this.physics.world.createDebugGraphic(); - - // Create worldLayer collision graphic above the player, but below the help text - const graphics = this.add - .graphics() - .setAlpha(0.75) - .setDepth(20); - worldLayer.renderDebug(graphics, { - tileColor: null, // Color of non-colliding tiles - collidingTileColor: new Phaser.Display.Color(243, 134, 48, 255), // Color of colliding tiles - faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Color of colliding face edges - }); + this.input.keyboard.once("keydown_D", (event: Event) => { + // Turn on physics debugging to show player's hitbox + this.physics.world.createDebugGraphic(); + + // Create worldLayer collision graphic above the player, but below the help text + const graphics = this.add + .graphics() + .setAlpha(0.75) + .setDepth(20); + + worldLayer.renderDebug(graphics, { + tileColor: null, // Color of non-colliding tiles + collidingTileColor: new Phaser.Display.Color(243, 134, 48, 255), // Color of colliding tiles + faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Color of colliding face edges + }); + }); // Constrain the camera so that it isn't allowed to move outside the width/height of tilemap @@ -171,8 +174,13 @@ const config = { } } }; -let cursors; -let player; -let showDebug = false; +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/tsconfig.json b/client/tsconfig.json index cecd357..4da9d17 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,9 +1,21 @@ { - "compilerOptions": { - "target": "es5", - "moduleResolution": "node" - }, - "include": [ - "./src/**/*" - ] + "include": [ + "./src/**/*" + ], + "compilerOptions": { + "target": "es5", + "moduleResolution": "node", + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "importsNotUsedAsValues": "error", + "esModuleInterop": true, + "allowJs": true, + "noFallthroughCasesInSwitch": true, + "isolatedModules": true + } } + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..7454180 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..69a9715 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..c353050 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.1/userguide/multi_project_builds.html + */ + +rootProject.name = 'mvink_akkamons' +include('model', 'api') |
