├── client ├── settings.gradle ├── android │ ├── ic_launcher-web.png │ ├── assets │ │ └── fonts │ │ │ └── NotoSans-Regular.ttf │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── color.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── drawable-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_foreground.xml │ ├── project.properties │ ├── src │ │ └── ir │ │ │ └── doorbash │ │ │ └── agar │ │ │ └── io │ │ │ └── AndroidLauncher.java │ ├── AndroidManifest.xml │ ├── proguard-rules.pro │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── core │ ├── build.gradle │ └── src │ │ └── ir │ │ └── doorbash │ │ └── agar │ │ └── io │ │ ├── classes │ │ ├── GameState.java │ │ ├── Fruit.java │ │ └── Player.java │ │ └── Game.java ├── desktop │ ├── src │ │ └── ir │ │ │ └── doorbash │ │ │ └── agar │ │ │ └── io │ │ │ └── desktop │ │ │ └── DesktopLauncher.java │ └── build.gradle ├── .gitignore └── build.gradle ├── server ├── .gitignore ├── src │ ├── entities │ │ ├── Fruit.ts │ │ ├── GameState.ts │ │ └── Player.ts │ ├── index.ts │ ├── util │ │ └── Constants.ts │ └── rooms │ │ └── FreeForAll.ts ├── tsconfig.json ├── package.json ├── .vscode │ └── launch.json └── package-lock.json ├── screenshot.png ├── README.md └── LICENSE /client/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'desktop', 'core', 'android' -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.js 4 | lib/ -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/screenshot.png -------------------------------------------------------------------------------- /client/android/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/ic_launcher-web.png -------------------------------------------------------------------------------- /client/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=true 2 | org.gradle.jvmargs=-Xms128m -Xmx1500m 3 | org.gradle.configureondemand=false 4 | -------------------------------------------------------------------------------- /client/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /client/android/assets/fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/assets/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /client/android/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doorbash/agar.io-clone/HEAD/client/android/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /client/android/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFFFF 4 | 5 | -------------------------------------------------------------------------------- /client/android/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | agar.io 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 11 06:09:05 IRDT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /server/src/entities/Fruit.ts: -------------------------------------------------------------------------------- 1 | import { Schema, type } from "@colyseus/schema"; 2 | 3 | class Fruit extends Schema { 4 | @type("float32") 5 | x: number; 6 | 7 | @type("float32") 8 | y: number; 9 | 10 | @type("int32") 11 | color: number; 12 | } 13 | 14 | export default Fruit; -------------------------------------------------------------------------------- /client/android/res/drawable-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /server/src/entities/GameState.ts: -------------------------------------------------------------------------------- 1 | import { type, Schema, MapSchema } from "@colyseus/schema"; 2 | import Player from "./Player"; 3 | import Fruit from "./Fruit"; 4 | 5 | class GameState extends Schema { 6 | @type({ map: Player }) 7 | players = new MapSchema(); 8 | 9 | @type({ map: Fruit }) 10 | fruits = new MapSchema(); 11 | } 12 | 13 | export default GameState; -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "target": "es2016", 7 | "declaration": true, 8 | "noImplicitAny": false, 9 | "experimentalDecorators": true, 10 | "sourceMap": false, 11 | }, 12 | "include": [ 13 | "**/*.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /client/core/build.gradle: -------------------------------------------------------------------------------- 1 | targetCompatibility = 1.8 2 | apply plugin: "java" 3 | 4 | sourceCompatibility = 1.8 5 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 6 | 7 | sourceSets.main.java.srcDirs = [ "src/" ] 8 | 9 | 10 | eclipse.project { 11 | name = appName + "-core" 12 | } 13 | 14 | dependencies { 15 | implementation fileTree(dir: 'libs', include: ['*.jar']) 16 | implementation 'ir.doorbash:colyseus-java:2.3.5' 17 | } -------------------------------------------------------------------------------- /server/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import { Server } from "colyseus"; 3 | import { createServer } from "http"; 4 | import FreeForAll from './rooms/FreeForAll'; 5 | 6 | const port = 2560 7 | 8 | const app = express(); 9 | app.use(express.json()); 10 | 11 | const gameServer = new Server({ 12 | server: createServer(app), 13 | express: app, 14 | }); 15 | 16 | gameServer.define("ffa", FreeForAll); 17 | 18 | gameServer.listen(port); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Agar.io Clone (Colyseus Java Example) 2 | This is an example project to show how to work with [Colyseus Java Client](https://github.com/doorbash/colyseus-java) 3 | 4 | **Client:** 5 | [Libgdx](https://libgdx.badlogicgames.com/) + [Colyseus client for Java](https://github.com/doorbash/colyseus-java) 6 | 7 | **Server:** 8 | [Colyseus](https://colyseus.io/) 9 | 10 | 11 | 12 | ## License 13 | 14 | MIT 15 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon --exec ts-node src/index.ts" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "colyseus": "0.11.10", 13 | "cors": "2.8.5", 14 | "express": "4.17.1" 15 | }, 16 | "devDependencies": { 17 | "@types/express": "^4.17.1", 18 | "@types/node": "^12.7.2", 19 | "ts-node": "^8.3.0", 20 | "typescript": "^3.5.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/desktop/src/ir/doorbash/agar/io/desktop/DesktopLauncher.java: -------------------------------------------------------------------------------- 1 | package ir.doorbash.agar.io.desktop; 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication; 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; 5 | import ir.doorbash.agar.io.Game; 6 | 7 | public class DesktopLauncher { 8 | public static void main (String[] arg) { 9 | LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); 10 | config.width = 800; 11 | config.height = 480; 12 | new LwjglApplication(new Game(), config); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/android/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/src/entities/Player.ts: -------------------------------------------------------------------------------- 1 | import { type, Schema } from "@colyseus/schema"; 2 | import Constants from "../util/Constants"; 3 | 4 | class Player extends Schema { 5 | @type("float32") 6 | x: number; 7 | 8 | @type("float32") 9 | y: number; 10 | 11 | @type("float32") 12 | radius: number = Constants.PLAYER_INIT_RADIUS; 13 | 14 | @type("int32") 15 | color: number; 16 | 17 | speed: number = Constants.PLAYER_INIT_SPEED; 18 | angle = Math.PI * (Math.random() * 2 - 1); 19 | online: boolean; 20 | } 21 | 22 | export default Player; -------------------------------------------------------------------------------- /client/android/project.properties: -------------------------------------------------------------------------------- 1 | # This file is used by the Eclipse ADT plugin. It is unnecessary for IDEA and Android Studio projects, which 2 | # configure Proguard and the Android target via the build.gradle file. 3 | 4 | # To enable ProGuard to work with Eclipse ADT, uncomment this (available properties: sdk.dir, user.home) 5 | # and ensure proguard.jar in the Android SDK is up to date (or alternately reduce the android target to 23 or lower): 6 | # proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-rules.pro 7 | 8 | # Project target. 9 | target=android-19 10 | -------------------------------------------------------------------------------- /client/android/src/ir/doorbash/agar/io/AndroidLauncher.java: -------------------------------------------------------------------------------- 1 | package ir.doorbash.agar.io; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.badlogic.gdx.backends.android.AndroidApplication; 6 | import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; 7 | import com.badlogic.gdx.backends.android.surfaceview.ResolutionStrategy; 8 | 9 | public class AndroidLauncher extends AndroidApplication { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | initialize(new Game()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}\\lib\\index.js", 12 | "preLaunchTask": "tsc: build - tsconfig.json", 13 | "outFiles": [ 14 | "${workspaceFolder}/lib/**/*.js" 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /client/core/src/ir/doorbash/agar/io/classes/GameState.java: -------------------------------------------------------------------------------- 1 | // 2 | // THIS FILE HAS BEEN GENERATED AUTOMATICALLY 3 | // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING 4 | // 5 | // GENERATED USING @colyseus/schema 0.4.41 6 | // 7 | 8 | package ir.doorbash.agar.io.classes; 9 | 10 | import io.colyseus.serializer.schema.Schema; 11 | import io.colyseus.serializer.schema.annotations.SchemaClass; 12 | import io.colyseus.serializer.schema.annotations.SchemaField; 13 | 14 | @SchemaClass 15 | public class GameState extends Schema { 16 | @SchemaField("0/map/ref") 17 | public MapSchema players = new MapSchema<>(Player.class); 18 | 19 | @SchemaField("1/map/ref") 20 | public MapSchema fruits = new MapSchema<>(Fruit.class); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /server/src/util/Constants.ts: -------------------------------------------------------------------------------- 1 | class Constants { 2 | static PLAYER_COLORS: number[] = [ 3 | 0x4cb050FF, 4 | 0xe6194bFF, 0x3cb44bFF, 0xffe119FF, 0x4363d8FF, 5 | 0xf58231FF, 0x911eb4FF, 0x46f0f0FF, 0xf032e6FF, 6 | 0xbcf60cFF, 0xfabebeFF, 0x008080FF, 0xe6beffFF, 7 | 0x9a6324FF, 0xfffac8FF, 0x800000FF, 0xaaffc3FF, 8 | ]; 9 | 10 | static FRUIT_COLORS: number[] = [ 11 | 0xFF0000FF, 12 | 0x00FF00FF, 13 | 0x0000FFFF 14 | ]; 15 | 16 | static WORLD_UPDATE_INTERVAL = 16; // ms 17 | static INIT_FRUITS = 50; 18 | static FRUIT_RADIUS = 10; 19 | static PLAYER_MIN_SPEED = 40; 20 | static PLAYER_INIT_SPEED = 120; 21 | static PLAYER_INIT_RADIUS = 40; 22 | } 23 | 24 | export default Constants; -------------------------------------------------------------------------------- /client/core/src/ir/doorbash/agar/io/classes/Fruit.java: -------------------------------------------------------------------------------- 1 | // 2 | // THIS FILE HAS BEEN GENERATED AUTOMATICALLY 3 | // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING 4 | // 5 | // GENERATED USING @colyseus/schema 0.4.41 6 | // 7 | 8 | package ir.doorbash.agar.io.classes; 9 | 10 | import com.badlogic.gdx.graphics.Color; 11 | import com.badlogic.gdx.math.Vector2; 12 | import io.colyseus.serializer.schema.Schema; 13 | import io.colyseus.serializer.schema.annotations.SchemaClass; 14 | import io.colyseus.serializer.schema.annotations.SchemaField; 15 | 16 | @SchemaClass 17 | public class Fruit extends Schema { 18 | @SchemaField("0/float32") 19 | public float x = 0; 20 | 21 | @SchemaField("1/float32") 22 | public float y = 0; 23 | 24 | @SchemaField("2/int32") 25 | public int color = 0; 26 | 27 | public Vector2 position = new Vector2(); 28 | public Color _color; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /client/core/src/ir/doorbash/agar/io/classes/Player.java: -------------------------------------------------------------------------------- 1 | // 2 | // THIS FILE HAS BEEN GENERATED AUTOMATICALLY 3 | // DO NOT CHANGE IT MANUALLY UNLESS YOU KNOW WHAT YOU'RE DOING 4 | // 5 | // GENERATED USING @colyseus/schema 0.4.41 6 | // 7 | 8 | package ir.doorbash.agar.io.classes; 9 | 10 | import com.badlogic.gdx.graphics.Color; 11 | import com.badlogic.gdx.math.Vector2; 12 | import io.colyseus.serializer.schema.Schema; 13 | import io.colyseus.serializer.schema.annotations.SchemaClass; 14 | import io.colyseus.serializer.schema.annotations.SchemaField; 15 | 16 | @SchemaClass 17 | public class Player extends Schema { 18 | @SchemaField("0/float32") 19 | public float x = 0; 20 | 21 | @SchemaField("1/float32") 22 | public float y = 0; 23 | 24 | @SchemaField("2/float32") 25 | public float radius = 0; 26 | 27 | @SchemaField("3/int32") 28 | public int color = 0; 29 | 30 | public Vector2 position = new Vector2(); 31 | public Color _color; 32 | public Color _strokeColor; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License: 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /client/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /client/desktop/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "java" 2 | 3 | sourceCompatibility = 1.8 4 | sourceSets.main.java.srcDirs = [ "src/" ] 5 | 6 | project.ext.mainClassName = "ir.doorbash.agar.io.desktop.DesktopLauncher" 7 | project.ext.assetsDir = new File("../android/assets"); 8 | 9 | task run(dependsOn: classes, type: JavaExec) { 10 | main = project.mainClassName 11 | classpath = sourceSets.main.runtimeClasspath 12 | standardInput = System.in 13 | workingDir = project.assetsDir 14 | ignoreExitValue = true 15 | } 16 | 17 | task debug(dependsOn: classes, type: JavaExec) { 18 | main = project.mainClassName 19 | classpath = sourceSets.main.runtimeClasspath 20 | standardInput = System.in 21 | workingDir = project.assetsDir 22 | ignoreExitValue = true 23 | debug = true 24 | } 25 | 26 | task dist(type: Jar) { 27 | from files(sourceSets.main.output.classesDirs) 28 | from files(sourceSets.main.output.resourcesDir) 29 | from {configurations.compile.collect {zipTree(it)}} 30 | from files(project.assetsDir); 31 | 32 | manifest { 33 | attributes 'Main-Class': project.mainClassName 34 | } 35 | } 36 | 37 | dist.dependsOn classes 38 | 39 | eclipse { 40 | project { 41 | name = appName + "-desktop" 42 | linkedResource name: 'assets', type: '2', location: 'PARENT-1-PROJECT_LOC/android/assets' 43 | } 44 | } 45 | 46 | task afterEclipseImport(description: "Post processing after project generation", group: "IDE") { 47 | doLast { 48 | def classpath = new XmlParser().parse(file(".classpath")) 49 | new Node(classpath, "classpathentry", [ kind: 'src', path: 'assets' ]); 50 | def writer = new FileWriter(file(".classpath")) 51 | def printer = new XmlNodePrinter(new PrintWriter(writer)) 52 | printer.setPreserveWhitespace(true) 53 | printer.print(classpath) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | 22 | -verbose 23 | 24 | -dontwarn android.support.** 25 | -dontwarn com.badlogic.gdx.backends.android.AndroidFragmentApplication 26 | -dontwarn com.badlogic.gdx.utils.GdxBuild 27 | -dontwarn com.badlogic.gdx.physics.box2d.utils.Box2DBuild 28 | -dontwarn com.badlogic.gdx.jnigen.BuildTarget* 29 | -dontwarn com.badlogic.gdx.graphics.g2d.freetype.FreetypeBuild 30 | 31 | -keep class com.badlogic.gdx.controllers.android.AndroidControllers 32 | 33 | -keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* { 34 | (com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration); 35 | } 36 | 37 | -keepclassmembers class com.badlogic.gdx.physics.box2d.World { 38 | boolean contactFilter(long, long); 39 | void beginContact(long); 40 | void endContact(long); 41 | void preSolve(long, long); 42 | void postSolve(long, long); 43 | boolean reportFixture(long); 44 | float reportRayFixture(long, float, float, float, float, float); 45 | } 46 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | ## Java 2 | 3 | *.class 4 | *.war 5 | *.ear 6 | hs_err_pid* 7 | 8 | ## Robovm 9 | /ios/robovm-build/ 10 | 11 | ## GWT 12 | /html/war/ 13 | /html/gwt-unitCache/ 14 | .apt_generated/ 15 | .gwt/ 16 | gwt-unitCache/ 17 | www-test/ 18 | .gwt-tmp/ 19 | 20 | ## Android Studio and Intellij and Android in general 21 | /android/libs/armeabi/ 22 | /android/libs/armeabi-v7a/ 23 | /android/libs/arm64-v8a/ 24 | /android/libs/x86/ 25 | /android/libs/x86_64/ 26 | /android/gen/ 27 | .idea/ 28 | *.ipr 29 | *.iws 30 | *.iml 31 | /android/out/ 32 | com_crashlytics_export_strings.xml 33 | 34 | ## Eclipse 35 | 36 | .classpath 37 | .project 38 | .metadata/ 39 | /android/bin/ 40 | /core/bin/ 41 | /desktop/bin/ 42 | /html/bin/ 43 | /ios/bin/ 44 | /ios-moe/bin/ 45 | *.tmp 46 | *.bak 47 | *.swp 48 | *~.nib 49 | .settings/ 50 | .loadpath 51 | .externalToolBuilders/ 52 | *.launch 53 | 54 | ## NetBeans 55 | 56 | /nbproject/private/ 57 | /android/nbproject/private/ 58 | /core/nbproject/private/ 59 | /desktop/nbproject/private/ 60 | /html/nbproject/private/ 61 | /ios/nbproject/private/ 62 | /ios-moe/nbproject/private/ 63 | 64 | /build/ 65 | /android/build/ 66 | /core/build/ 67 | /desktop/build/ 68 | /html/build/ 69 | /ios/build/ 70 | /ios-moe/build/ 71 | 72 | /nbbuild/ 73 | /android/nbbuild/ 74 | /core/nbbuild/ 75 | /desktop/nbbuild/ 76 | /html/nbbuild/ 77 | /ios/nbbuild/ 78 | /ios-moe/nbbuild/ 79 | 80 | /dist/ 81 | /android/dist/ 82 | /core/dist/ 83 | /desktop/dist/ 84 | /html/dist/ 85 | /ios/dist/ 86 | /ios-moe/dist/ 87 | 88 | /nbdist/ 89 | /android/nbdist/ 90 | /core/nbdist/ 91 | /desktop/nbdist/ 92 | /html/nbdist/ 93 | /ios/nbdist/ 94 | /ios-moe/nbdist/ 95 | 96 | nbactions.xml 97 | nb-configuration.xml 98 | 99 | ## Gradle 100 | 101 | /local.properties 102 | .gradle/ 103 | gradle-app.setting 104 | /build/ 105 | /android/build/ 106 | /core/build/ 107 | /desktop/build/ 108 | /html/build/ 109 | /ios/build/ 110 | /ios-moe/build/ 111 | 112 | ## OS Specific 113 | .DS_Store 114 | Thumbs.db 115 | 116 | ## iOS 117 | /ios/xcode/*.xcodeproj/* 118 | !/ios/xcode/*.xcodeproj/xcshareddata 119 | !/ios/xcode/*.xcodeproj/project.pbxproj 120 | /ios/xcode/native/ 121 | 122 | /ios-moe/xcode/*.xcodeproj/* 123 | !/ios-moe/xcode/*.xcodeproj/xcshareddata 124 | !/ios-moe/xcode/*.xcodeproj/project.pbxproj 125 | /ios-moe/xcode/native/ 126 | -------------------------------------------------------------------------------- /client/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 21 | 26 | 31 | 36 | 40 | 41 | -------------------------------------------------------------------------------- /client/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | 3 | 4 | repositories { 5 | mavenLocal() 6 | mavenCentral() 7 | maven { url "https://plugins.gradle.org/m2/" } 8 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 9 | jcenter() 10 | google() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:3.2.0' 14 | 15 | 16 | } 17 | } 18 | 19 | allprojects { 20 | apply plugin: "eclipse" 21 | apply plugin: "idea" 22 | 23 | version = '1.0' 24 | ext { 25 | appName = "agar.io" 26 | gdxVersion = '1.9.9' 27 | roboVMVersion = '2.3.6' 28 | box2DLightsVersion = '1.4' 29 | ashleyVersion = '1.7.0' 30 | aiVersion = '1.8.0' 31 | } 32 | 33 | repositories { 34 | mavenLocal() 35 | mavenCentral() 36 | jcenter() 37 | google() 38 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 39 | maven { url "https://oss.sonatype.org/content/repositories/releases/" } 40 | } 41 | } 42 | 43 | project(":desktop") { 44 | apply plugin: "java" 45 | 46 | 47 | dependencies { 48 | compile project(":core") 49 | compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" 50 | compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" 51 | compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" 52 | implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" 53 | implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" 54 | } 55 | } 56 | 57 | project(":core") { 58 | apply plugin: "java" 59 | 60 | 61 | dependencies { 62 | compile "com.badlogicgames.gdx:gdx:$gdxVersion" 63 | compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" 64 | implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" 65 | } 66 | } 67 | 68 | project(":android") { 69 | apply plugin: "android" 70 | 71 | configurations { natives } 72 | 73 | dependencies { 74 | compile project(":core") 75 | compile "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" 76 | natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi" 77 | natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" 78 | natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a" 79 | natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86" 80 | natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64" 81 | compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" 82 | natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-armeabi" 83 | natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-armeabi-v7a" 84 | natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-arm64-v8a" 85 | natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-x86" 86 | natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-x86_64" 87 | implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" 88 | natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi" 89 | natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a" 90 | natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a" 91 | natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86" 92 | natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64" 93 | 94 | } 95 | } 96 | 97 | tasks.eclipse.doLast { 98 | delete ".project" 99 | } -------------------------------------------------------------------------------- /client/android/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildToolsVersion "28.0.3" 3 | compileSdkVersion 28 4 | sourceSets { 5 | main { 6 | manifest.srcFile 'AndroidManifest.xml' 7 | java.srcDirs = ['src'] 8 | aidl.srcDirs = ['src'] 9 | renderscript.srcDirs = ['src'] 10 | res.srcDirs = ['res'] 11 | assets.srcDirs = ['assets'] 12 | jniLibs.srcDirs = ['libs'] 13 | } 14 | 15 | } 16 | packagingOptions { 17 | exclude 'META-INF/robovm/ios/robovm.xml' 18 | } 19 | defaultConfig { 20 | applicationId "ir.doorbash.agar.io" 21 | minSdkVersion 19 22 | targetSdkVersion 28 23 | versionCode 1 24 | versionName "1.0" 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility 1.8 34 | targetCompatibility 1.8 35 | } 36 | } 37 | 38 | 39 | // called every time gradle gets executed, takes the native dependencies of 40 | // the natives configuration, and extracts them to the proper libs/ folders 41 | // so they get packed with the APK. 42 | task copyAndroidNatives { 43 | doFirst { 44 | file("libs/armeabi/").mkdirs() 45 | file("libs/armeabi-v7a/").mkdirs() 46 | file("libs/arm64-v8a/").mkdirs() 47 | file("libs/x86_64/").mkdirs() 48 | file("libs/x86/").mkdirs() 49 | 50 | configurations.natives.files.each { jar -> 51 | def outputDir = null 52 | if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a") 53 | if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a") 54 | if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi") 55 | if(jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64") 56 | if(jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86") 57 | if(outputDir != null) { 58 | copy { 59 | from zipTree(jar) 60 | into outputDir 61 | include "*.so" 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | tasks.whenTaskAdded { packageTask -> 69 | if (packageTask.name.contains("package")) { 70 | packageTask.dependsOn 'copyAndroidNatives' 71 | } 72 | } 73 | 74 | task run(type: Exec) { 75 | def path 76 | def localProperties = project.file("../local.properties") 77 | if (localProperties.exists()) { 78 | Properties properties = new Properties() 79 | localProperties.withInputStream { instr -> 80 | properties.load(instr) 81 | } 82 | def sdkDir = properties.getProperty('sdk.dir') 83 | if (sdkDir) { 84 | path = sdkDir 85 | } else { 86 | path = "$System.env.ANDROID_HOME" 87 | } 88 | } else { 89 | path = "$System.env.ANDROID_HOME" 90 | } 91 | 92 | def adb = path + "/platform-tools/adb" 93 | commandLine "$adb", 'shell', 'am', 'start', '-n', 'ir.doorbash.agar.io/ir.doorbash.agar.io.AndroidLauncher' 94 | } 95 | 96 | // sets up the Android Eclipse project, using the old Ant based build. 97 | eclipse { 98 | // need to specify Java source sets explicitly, SpringSource Gradle Eclipse plugin 99 | // ignores any nodes added in classpath.file.withXml 100 | sourceSets { 101 | main { 102 | java.srcDirs "src", 'gen' 103 | } 104 | } 105 | 106 | jdt { 107 | sourceCompatibility = 1.8 108 | targetCompatibility = 1.8 109 | } 110 | 111 | classpath { 112 | plusConfigurations += [ project.configurations.compile ] 113 | containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES' 114 | } 115 | 116 | project { 117 | name = appName + "-android" 118 | natures 'com.android.ide.eclipse.adt.AndroidNature' 119 | buildCommands.clear(); 120 | buildCommand "com.android.ide.eclipse.adt.ResourceManagerBuilder" 121 | buildCommand "com.android.ide.eclipse.adt.PreCompilerBuilder" 122 | buildCommand "org.eclipse.jdt.core.javabuilder" 123 | buildCommand "com.android.ide.eclipse.adt.ApkBuilder" 124 | } 125 | } 126 | 127 | // sets up the Android Idea project, using the old Ant based build. 128 | idea { 129 | module { 130 | sourceDirs += file("src"); 131 | scopes = [ COMPILE: [plus:[project.configurations.compile]]] 132 | 133 | iml { 134 | withXml { 135 | def node = it.asNode() 136 | def builder = NodeBuilder.newInstance(); 137 | builder.current = node; 138 | builder.component(name: "FacetManager") { 139 | facet(type: "android", name: "Android") { 140 | configuration { 141 | option(name: "UPDATE_PROPERTY_FILES", value:"true") 142 | } 143 | } 144 | } 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /server/src/rooms/FreeForAll.ts: -------------------------------------------------------------------------------- 1 | import { Room, Client } from "colyseus" 2 | import { IncomingMessage } from "http" 3 | import GameState from "../entities/GameState"; 4 | import Player from "../entities/Player"; 5 | import Fruit from "../entities/Fruit"; 6 | import Constants from "../util/Constants"; 7 | 8 | class FreeForAll extends Room { 9 | 10 | maxClients = 20; 11 | autoDispose = true; 12 | fruitId = 0; 13 | 14 | onCreate?(options: any): void { 15 | this.setState(new GameState()); 16 | 17 | for (var i = 0; i < Constants.INIT_FRUITS; i++) 18 | this.generateFruit(); 19 | 20 | this.setSimulationInterval(() => { 21 | this.updateWorld(); 22 | }, Constants.WORLD_UPDATE_INTERVAL); 23 | } 24 | 25 | onAuth(client: Client, options: any, request?: IncomingMessage): any | Promise { 26 | console.log("onAuth(" + client.id + ")"); 27 | return true; 28 | } 29 | 30 | onJoin?(client: Client, options?: any, auth?: any): void | Promise { 31 | console.log('onJoin(', client.id, ')', options); 32 | if (!this.state.players[client.id]) { 33 | var player: Player = new Player(); 34 | player.x = Math.floor(Math.random() * 1200); 35 | player.y = Math.floor(Math.random() * 1200); 36 | player.color = Constants.PLAYER_COLORS[Math.floor(Math.random() * Constants.PLAYER_COLORS.length)]; 37 | player.online = true; 38 | this.state.players[client.id] = player; 39 | console.log("new player added " + client.id); 40 | } 41 | } 42 | 43 | onMessage(client: Client, data: any): void { 44 | console.log("Room received message from", client.id, ":", data); 45 | var player = this.state.players[client.id]; 46 | switch (data.op) { 47 | case 'angle': { 48 | player.angle = data.angle * 0.0174533; 49 | } break; 50 | case 'ping': { 51 | this.send(client, 'pong'); 52 | } break; 53 | } 54 | } 55 | 56 | async onLeave?(client: Client, consented?: boolean): Promise { 57 | console.log("onLeave(" + client.id + ")"); 58 | let player = this.state.players[client.id]; 59 | if (!player) return; 60 | player.online = false; 61 | try { 62 | if (consented) { 63 | throw new Error("consented leave"); 64 | } 65 | console.log("await this.allowReconnection(client, 30);") 66 | await this.allowReconnection(client, 30); 67 | player.online = true; 68 | console.log("player " + client.id + " is back! player.online = true;") 69 | } catch (e) { 70 | if (player && !player.online) { 71 | delete this.state.players[client.id]; 72 | } 73 | } 74 | } 75 | 76 | onDispose?(): void | Promise { 77 | console.log("Dispose Room"); 78 | } 79 | 80 | updateWorld() { 81 | Object.keys(this.state.players).forEach(key => { 82 | // update player position 83 | var player = this.state.players[key]; 84 | var newX = player.x + Math.cos(player.angle) * player.speed * Constants.WORLD_UPDATE_INTERVAL / 1000; 85 | var newY = player.y + Math.sin(player.angle) * player.speed * Constants.WORLD_UPDATE_INTERVAL / 1000; 86 | if ((newX - player.radius) < 0) newX = player.radius; else if ((newX + player.radius) > 1200) newX = 1200 - player.radius; 87 | if ((newY - player.radius) < 0) newY = player.radius; else if ((newY + player.radius) > 1200) newY = 1200 - player.radius; 88 | player.x = newX; 89 | player.y = newY; 90 | 91 | // check if player eat something 92 | this.checkIfPlayerIsEatingFruit(player); 93 | this.checkIfPlayerIsEatingAnotherPlayer(key, player); 94 | }); 95 | } 96 | 97 | checkIfPlayerIsEatingFruit(player) { 98 | var eatenFruitKeys = []; 99 | Object.keys(this.state.fruits).forEach(key => { 100 | var fruit = this.state.fruits[key]; 101 | if ((Math.pow(fruit.x - player.x, 2) + Math.pow(fruit.y - player.y, 2)) < Math.pow(player.radius + Constants.FRUIT_RADIUS, 2)) { 102 | eatenFruitKeys.push(key); 103 | } 104 | }); 105 | eatenFruitKeys.forEach(key => { 106 | this.eat(player, key); 107 | }); 108 | } 109 | 110 | eat(player, fruitKey) { 111 | delete this.state.fruits[fruitKey]; 112 | player.radius += Constants.FRUIT_RADIUS / 10; 113 | var newSpeed = player.speed - Constants.FRUIT_RADIUS / 30; 114 | if (newSpeed > Constants.PLAYER_MIN_SPEED) player.speed = newSpeed; 115 | console.log('yum yum yummm'); 116 | this.generateFruit(); 117 | } 118 | 119 | generateFruit() { 120 | var fr: Fruit = new Fruit(); 121 | fr.x = Constants.FRUIT_RADIUS + Math.random() * (1200 - 2 * Constants.FRUIT_RADIUS); 122 | fr.y = Constants.FRUIT_RADIUS + Math.random() * (1200 - 2 * Constants.FRUIT_RADIUS); 123 | fr.color = Constants.FRUIT_COLORS[Math.floor(Math.random() * Constants.FRUIT_COLORS.length)]; 124 | var key = "fr_" + (this.fruitId++); 125 | this.state.fruits[key] = fr; 126 | } 127 | 128 | checkIfPlayerIsEatingAnotherPlayer(clientId, player) { 129 | Object.keys(this.state.players).forEach(key => { 130 | if (key == clientId) return; 131 | var p = this.state.players[key]; 132 | if (p.radius < player.radius && (Math.pow(p.x - player.x, 2) + Math.pow(p.y - player.y, 2)) < Math.pow(player.radius + p.radius, 2)) { 133 | this.eatPlayer(player, p); 134 | } 135 | }); 136 | } 137 | 138 | eatPlayer(player, player2) { 139 | player.radius += player2.radius / 10; 140 | var newSpeed = player.speed - player2.radius / 20; 141 | if (newSpeed > Constants.PLAYER_MIN_SPEED) player.speed = newSpeed; 142 | console.log('oh nooooo'); 143 | player2.x = Math.floor(Math.random() * 1200); 144 | player2.y = Math.floor(Math.random() * 1200); 145 | player2.radius = Constants.PLAYER_INIT_RADIUS; 146 | } 147 | 148 | } 149 | 150 | export default FreeForAll; -------------------------------------------------------------------------------- /client/core/src/ir/doorbash/agar/io/Game.java: -------------------------------------------------------------------------------- 1 | package ir.doorbash.agar.io; 2 | 3 | import com.badlogic.gdx.ApplicationAdapter; 4 | import com.badlogic.gdx.Gdx; 5 | import com.badlogic.gdx.graphics.Color; 6 | import com.badlogic.gdx.graphics.GL20; 7 | import com.badlogic.gdx.graphics.OrthographicCamera; 8 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 9 | import com.badlogic.gdx.graphics.g2d.SpriteBatch; 10 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; 11 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 12 | import com.badlogic.gdx.math.MathUtils; 13 | 14 | import java.util.HashMap; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map.Entry; 17 | 18 | import io.colyseus.Client; 19 | import io.colyseus.Client.MatchMakeException; 20 | import io.colyseus.Room; 21 | import ir.doorbash.agar.io.classes.Fruit; 22 | import ir.doorbash.agar.io.classes.GameState; 23 | import ir.doorbash.agar.io.classes.Player; 24 | 25 | public class Game extends ApplicationAdapter { 26 | 27 | /* *************************************** CONSTANTS *****************************************/ 28 | 29 | private static final String ENDPOINT = "ws://127.0.0.1:2560"; 30 | private static final String PATH_FONT_NOTO = "fonts/NotoSans-Regular.ttf"; 31 | private static final int SEND_DIRECTION_INTERVAL = 200; 32 | private static final int PING_INTERVAL = 5000; 33 | private static final int CHECK_CONNECTION_INTERVAL = 3000; 34 | private static final int GRID_SIZE = 60; 35 | private static final int FRUIT_RADIUS = 10; 36 | private static final int LATENCY_MIN = 100; // ms 37 | private static final int LATENCY_MAX = 500; // ms 38 | private static final int CONNECTION_STATE_DISCONNECTED = 0; 39 | private static final int CONNECTION_STATE_CONNECTING = 1; 40 | private static final int CONNECTION_STATE_CONNECTED = 2; 41 | private static final float LERP_MIN = 0.1f; 42 | private static final float LERP_MAX = 0.5f; 43 | private static final float OTHER_PLAYERS_LERP = 0.5f; 44 | 45 | /* **************************************** FIELDS *******************************************/ 46 | 47 | private int connectionState = CONNECTION_STATE_DISCONNECTED; 48 | private int lastAngle = -1000; 49 | private int mapWidth = 1200; 50 | private int mapHeight = 1200; 51 | private long lastSendDirectionTime; 52 | private long lastPingSentTime; 53 | private long lastPingReplyTime; 54 | private long currentPing = -1; 55 | private long lastConnectionCheckTime; 56 | private float lerp = LERP_MAX; 57 | 58 | private String sessionId; 59 | private String roomId; 60 | 61 | private SpriteBatch batch; 62 | private ShapeRenderer shapeRenderer; 63 | private OrthographicCamera camera; 64 | private OrthographicCamera guiCamera; 65 | private FreeTypeFontGenerator freetypeGeneratorNoto; 66 | private BitmapFont logFont; 67 | 68 | private Room room; 69 | private LinkedHashMap message; 70 | private final HashMap fruits = new HashMap<>(); 71 | private final HashMap players = new HashMap<>(); 72 | 73 | /* *************************************** OVERRIDE *****************************************/ 74 | 75 | @Override 76 | public void create() { 77 | batch = new SpriteBatch(); 78 | shapeRenderer = new ShapeRenderer(); 79 | camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 80 | camera.update(); 81 | guiCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 82 | guiCamera.update(); 83 | message = new LinkedHashMap<>(); 84 | message.put("op", "angle"); 85 | 86 | freetypeGeneratorNoto = new FreeTypeFontGenerator(Gdx.files.internal(PATH_FONT_NOTO)); 87 | FreeTypeFontGenerator.FreeTypeFontParameter logFontParams = new FreeTypeFontGenerator.FreeTypeFontParameter(); 88 | logFontParams.size = 14; 89 | logFontParams.color = Color.BLACK; 90 | logFontParams.flip = false; 91 | logFontParams.incremental = true; 92 | logFont = freetypeGeneratorNoto.generateFont(logFontParams); 93 | } 94 | 95 | @Override 96 | public void render() { 97 | Gdx.gl.glClearColor(0.98f, 0.99f, 1, 1); 98 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 99 | 100 | updatePositions(); 101 | adjustCamera(); 102 | 103 | if (connectionState == CONNECTION_STATE_CONNECTED) { 104 | shapeRenderer.setProjectionMatrix(camera.combined); 105 | shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); 106 | drawGrid(); 107 | drawFruits(); 108 | drawPlayers(); 109 | shapeRenderer.end(); 110 | 111 | batch.setProjectionMatrix(guiCamera.combined); 112 | batch.begin(); 113 | drawPing(); 114 | batch.end(); 115 | } 116 | 117 | long now = System.currentTimeMillis(); 118 | if (now - lastSendDirectionTime >= SEND_DIRECTION_INTERVAL) { 119 | lastSendDirectionTime = now; 120 | sendDirection(); 121 | } 122 | if (now - lastPingSentTime >= PING_INTERVAL) { 123 | lastPingSentTime = now; 124 | sendPing(); 125 | } 126 | if (now - lastConnectionCheckTime >= CHECK_CONNECTION_INTERVAL) { 127 | lastConnectionCheckTime = now; 128 | checkConnection(); 129 | } 130 | } 131 | 132 | @Override 133 | public void dispose() { 134 | if (logFont != null) logFont.dispose(); 135 | if (batch != null) batch.dispose(); 136 | if (shapeRenderer != null) shapeRenderer.dispose(); 137 | if (freetypeGeneratorNoto != null) freetypeGeneratorNoto.dispose(); 138 | if (room != null) room.leave(); 139 | } 140 | 141 | @Override 142 | public void resize(int width, int height) { 143 | camera.viewportWidth = width; 144 | camera.viewportHeight = height; 145 | camera.update(); 146 | guiCamera.viewportWidth = width; 147 | guiCamera.viewportHeight = height; 148 | guiCamera.update(); 149 | } 150 | 151 | /* ***************************************** DRAW *******************************************/ 152 | 153 | private void drawGrid() { 154 | Player player; 155 | int leftBottomX; 156 | int leftBottomY; 157 | int rightTopX; 158 | int rightTopY; 159 | 160 | float width = camera.zoom * camera.viewportWidth; 161 | float height = camera.zoom * camera.viewportHeight; 162 | 163 | if (room != null && (player = room.state.players.get(room.getSessionId())) != null) { 164 | leftBottomX = (int) (player.position.x - width / 2f); 165 | leftBottomY = (int) (player.position.y - height / 2f); 166 | } else { 167 | leftBottomX = (int) (-width / 2f); 168 | leftBottomY = (int) (-height / 2f); 169 | } 170 | 171 | leftBottomX -= leftBottomX % GRID_SIZE + GRID_SIZE; 172 | leftBottomY -= leftBottomY % GRID_SIZE + GRID_SIZE; 173 | rightTopX = (int) (leftBottomX + width + 2 * GRID_SIZE); 174 | rightTopY = (int) (leftBottomY + height + 2 * GRID_SIZE); 175 | 176 | shapeRenderer.setColor(Color.BLACK); 177 | for (int i = leftBottomX; i < rightTopX; i += GRID_SIZE) { 178 | shapeRenderer.line(i, leftBottomY, i, rightTopY); 179 | } 180 | 181 | for (int i = leftBottomY; i < rightTopY; i += GRID_SIZE) { 182 | shapeRenderer.line(leftBottomX, i, rightTopX, i); 183 | } 184 | 185 | shapeRenderer.end(); 186 | shapeRenderer.begin(ShapeRenderer.ShapeType.Line); 187 | shapeRenderer.setColor(Color.RED); 188 | shapeRenderer.rect(0, 0, mapWidth, mapHeight); 189 | shapeRenderer.end(); 190 | shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); 191 | } 192 | 193 | private void drawFruits() { 194 | if (room == null) return; 195 | Player thisPlayer = room.state.players.get(room.getSessionId()); 196 | if (thisPlayer == null) return; 197 | synchronized (fruits) { 198 | for (Fruit fr : fruits.values()) { 199 | if (fr == null || fr._color == null) continue; 200 | if (!objectIsInViewport(thisPlayer, fr.position.x, fr.position.y, FRUIT_RADIUS)) 201 | continue; 202 | shapeRenderer.setColor(fr._color); 203 | shapeRenderer.circle(fr.position.x, fr.position.y, FRUIT_RADIUS); 204 | } 205 | } 206 | } 207 | 208 | private void drawPlayers() { 209 | if (room == null) return; 210 | Player thisPlayer = room.state.players.get(room.getSessionId()); 211 | if (thisPlayer == null) return; 212 | synchronized (players) { 213 | for (Entry keyValue : players.entrySet()) { 214 | String clientId = keyValue.getKey(); 215 | Player player = keyValue.getValue(); 216 | if (player == null) continue; 217 | if (clientId.equals(room.getSessionId()) || objectIsInViewport(thisPlayer, player.position.x, player.position.y, player.radius)) { 218 | shapeRenderer.setColor(player._strokeColor); 219 | shapeRenderer.circle(player.position.x, player.position.y, player.radius); 220 | shapeRenderer.setColor(player._color); 221 | shapeRenderer.circle(player.position.x, player.position.y, player.radius - 3); 222 | } 223 | } 224 | } 225 | } 226 | 227 | private void drawPing() { 228 | String logText = "fps: " + Gdx.graphics.getFramesPerSecond(); 229 | if (currentPing >= 0) { 230 | logText += " - ping: " + currentPing; 231 | } 232 | logFont.draw(batch, logText, -guiCamera.viewportWidth / 2f + 8, -guiCamera.viewportHeight / 2f + 2 + logFont.getLineHeight()); 233 | } 234 | 235 | /* ***************************************** LOGIC *******************************************/ 236 | 237 | private void updatePositions() { 238 | if (room == null) return; 239 | synchronized (players) { 240 | for (Entry keyValue : players.entrySet()) { 241 | String clientId = keyValue.getKey(); 242 | Player player = keyValue.getValue(); 243 | if (player == null) continue; 244 | if (clientId.equals(room.getSessionId())) 245 | player.position.set(MathUtils.lerp(player.position.x, player.x, lerp), MathUtils.lerp(player.position.y, player.y, lerp)); 246 | else 247 | player.position.set(MathUtils.lerp(player.position.x, player.x, OTHER_PLAYERS_LERP), MathUtils.lerp(player.position.y, player.y, OTHER_PLAYERS_LERP)); 248 | } 249 | } 250 | } 251 | 252 | private boolean objectIsInViewport(Player player, float x, float y, float radius) { 253 | if (x + radius < player.position.x - camera.zoom * camera.viewportWidth / 2) return false; 254 | if (x - radius > player.position.x + camera.zoom * camera.viewportWidth / 2) return false; 255 | if (y + radius < player.position.y - camera.zoom * camera.viewportHeight / 2) return false; 256 | if (y - radius > player.position.y + camera.zoom * camera.viewportHeight / 2) return false; 257 | return true; 258 | } 259 | 260 | private void adjustCamera() { 261 | if (room == null) return; 262 | Player player = room.state.players.get(room.getSessionId()); 263 | if (player == null) return; 264 | camera.position.x = player.position.x; 265 | camera.position.y = player.position.y; 266 | camera.zoom = player.radius / 60f; 267 | camera.update(); 268 | } 269 | 270 | /* **************************************** NETWORK ******************************************/ 271 | 272 | private void checkConnection() { 273 | if (connectionState == CONNECTION_STATE_CONNECTED && lastPingReplyTime > 0 && System.currentTimeMillis() - lastPingReplyTime > 15000) { 274 | connectionState = CONNECTION_STATE_DISCONNECTED; 275 | } 276 | 277 | if (connectionState == CONNECTION_STATE_DISCONNECTED) { 278 | connectToServer(); 279 | } 280 | } 281 | 282 | private void connectToServer() { 283 | System.out.println("connectToServer()"); 284 | if (connectionState != CONNECTION_STATE_DISCONNECTED) return; 285 | connectionState = CONNECTION_STATE_CONNECTING; 286 | Client client = new Client(ENDPOINT); 287 | 288 | if (sessionId == null) { 289 | client.joinOrCreate("ffa", GameState.class, this::updateRoom, e -> { 290 | e.printStackTrace(); 291 | connectionState = CONNECTION_STATE_DISCONNECTED; 292 | }); 293 | } else { 294 | client.reconnect(roomId, sessionId, GameState.class, this::updateRoom, e -> { 295 | if (e instanceof MatchMakeException) { 296 | this.roomId = null; 297 | this.sessionId = null; 298 | } 299 | e.printStackTrace(); 300 | connectionState = CONNECTION_STATE_DISCONNECTED; 301 | }); 302 | } 303 | } 304 | 305 | private void updateRoom(Room room) { 306 | this.room = room; 307 | this.roomId = room.getId(); 308 | this.sessionId = room.getSessionId(); 309 | System.out.println("joined chat"); 310 | 311 | synchronized (players) { 312 | players.clear(); 313 | } 314 | synchronized (fruits) { 315 | fruits.clear(); 316 | } 317 | 318 | connectionState = CONNECTION_STATE_CONNECTED; 319 | lastPingReplyTime = 0; 320 | 321 | room.setListener(new Room.Listener() { 322 | 323 | @Override 324 | protected void onLeave(int code) { 325 | System.out.println("left public, code = " + code); 326 | if (code > 1000) { 327 | // abnormal disconnection! 328 | connectionState = CONNECTION_STATE_DISCONNECTED; 329 | } else { 330 | // the client has initiated the disconnection 331 | } 332 | } 333 | 334 | @Override 335 | protected void onError(Exception e) { 336 | connectionState = CONNECTION_STATE_DISCONNECTED; 337 | System.out.println("onError()"); 338 | e.printStackTrace(); 339 | } 340 | 341 | @Override 342 | protected void onMessage(Object message) { 343 | if (message.equals("pong")) { 344 | lastPingReplyTime = System.currentTimeMillis(); 345 | currentPing = lastPingReplyTime - lastPingSentTime; 346 | calculateLerp(currentPing); 347 | } 348 | } 349 | }); 350 | room.state.players.onAdd = (player, key) -> { 351 | if (connectionState != CONNECTION_STATE_CONNECTED) return; 352 | synchronized (players) { 353 | players.put(key, player); 354 | } 355 | // System.out.println("new player added >> clientId: " + key); 356 | player.position.x = player.x; 357 | player.position.y = player.y; 358 | player._color = new Color(player.color); 359 | player._strokeColor = new Color(player.color); 360 | player._strokeColor.mul(0.9f); 361 | }; 362 | 363 | room.state.players.onRemove = (player, key) -> { 364 | if (connectionState != CONNECTION_STATE_CONNECTED) return; 365 | synchronized (players) { 366 | players.remove(key); 367 | } 368 | }; 369 | 370 | room.state.fruits.onAdd = (fruit, key) -> { 371 | if (connectionState != CONNECTION_STATE_CONNECTED) return; 372 | synchronized (fruits) { 373 | fruits.put(key, fruit); 374 | } 375 | // System.out.println("new fruit added >> key: " + key); 376 | fruit.position.x = fruit.x; 377 | fruit.position.y = fruit.y; 378 | fruit._color = new Color(fruit.color); 379 | }; 380 | 381 | room.state.fruits.onRemove = (fruit, key) -> { 382 | if (connectionState != CONNECTION_STATE_CONNECTED) return; 383 | synchronized (fruits) { 384 | fruits.remove(key); 385 | } 386 | }; 387 | } 388 | 389 | private void sendDirection() { 390 | if (room == null) return; 391 | float dx = Gdx.input.getX() - camera.viewportWidth / 2; 392 | float dy = Gdx.input.getY() - camera.viewportHeight / 2; 393 | int angle = (int) Math.toDegrees(Math.atan2(-dy, dx)); 394 | if (lastAngle != angle) { 395 | message.put("angle", angle); 396 | room.send(message); 397 | lastAngle = angle; 398 | } 399 | } 400 | 401 | private void sendPing() { 402 | if (room == null) return; 403 | LinkedHashMap data = new LinkedHashMap<>(); 404 | data.put("op", "ping"); 405 | room.send(data); 406 | } 407 | 408 | private void calculateLerp(float currentLatency) { 409 | float latency; 410 | if (currentLatency < LATENCY_MIN) latency = LATENCY_MIN; 411 | else if (currentLatency > LATENCY_MAX) latency = LATENCY_MAX; 412 | else latency = currentLatency; 413 | lerp = LERP_MAX + ((latency - LATENCY_MIN) / (LATENCY_MAX - LATENCY_MIN)) * (LERP_MIN - LERP_MAX); 414 | System.out.println("current latency: " + currentLatency + " ms"); 415 | System.out.println("lerp : " + lerp); 416 | } 417 | } -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@colyseus/schema": { 8 | "version": "0.4.54", 9 | "resolved": "https://registry.npmjs.org/@colyseus/schema/-/schema-0.4.54.tgz", 10 | "integrity": "sha512-uJXPHgFXY1GEQwvvnp9/7URvrnZDwkDySMnHSUxBSkGKK2wOlXr5RXPDjZWIvXTcARA1o/mvBmCo+y0VxYEbvQ==" 11 | }, 12 | "@gamestdio/clock": { 13 | "version": "1.1.9", 14 | "resolved": "https://registry.npmjs.org/@gamestdio/clock/-/clock-1.1.9.tgz", 15 | "integrity": "sha1-d+wMAROR+7/ctPMhEkKVNv2GgMs=" 16 | }, 17 | "@gamestdio/timer": { 18 | "version": "1.3.1", 19 | "resolved": "https://registry.npmjs.org/@gamestdio/timer/-/timer-1.3.1.tgz", 20 | "integrity": "sha512-WEucGxBEYBaja/YCCGCAeHkPpkZYkybJ4BfLH0HMJ2+n8RKmpjeYbx0j2TevADkk5mJ7gO48hbE+hcL/y5V45g==", 21 | "requires": { 22 | "@gamestdio/clock": "^1.1.9" 23 | } 24 | }, 25 | "@types/body-parser": { 26 | "version": "1.17.1", 27 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", 28 | "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", 29 | "dev": true, 30 | "requires": { 31 | "@types/connect": "*", 32 | "@types/node": "*" 33 | } 34 | }, 35 | "@types/connect": { 36 | "version": "3.4.32", 37 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", 38 | "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", 39 | "dev": true, 40 | "requires": { 41 | "@types/node": "*" 42 | } 43 | }, 44 | "@types/express": { 45 | "version": "4.17.1", 46 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", 47 | "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", 48 | "dev": true, 49 | "requires": { 50 | "@types/body-parser": "*", 51 | "@types/express-serve-static-core": "*", 52 | "@types/serve-static": "*" 53 | } 54 | }, 55 | "@types/express-serve-static-core": { 56 | "version": "4.16.9", 57 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", 58 | "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", 59 | "dev": true, 60 | "requires": { 61 | "@types/node": "*", 62 | "@types/range-parser": "*" 63 | } 64 | }, 65 | "@types/mime": { 66 | "version": "2.0.1", 67 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", 68 | "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", 69 | "dev": true 70 | }, 71 | "@types/node": { 72 | "version": "12.7.2", 73 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", 74 | "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==" 75 | }, 76 | "@types/range-parser": { 77 | "version": "1.2.3", 78 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 79 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", 80 | "dev": true 81 | }, 82 | "@types/redis": { 83 | "version": "2.8.13", 84 | "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.13.tgz", 85 | "integrity": "sha512-p86cm5P6DMotUqCS6odQRz0JJwc5QXZw9eyH0ALVIqmq12yqtex5ighWyGFHKxak9vaA/GF/Ilu0KZ0MuXXUbg==", 86 | "requires": { 87 | "@types/node": "*" 88 | } 89 | }, 90 | "@types/serve-static": { 91 | "version": "1.13.3", 92 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", 93 | "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", 94 | "dev": true, 95 | "requires": { 96 | "@types/express-serve-static-core": "*", 97 | "@types/mime": "*" 98 | } 99 | }, 100 | "@types/ws": { 101 | "version": "6.0.3", 102 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.3.tgz", 103 | "integrity": "sha512-yBTM0P05Tx9iXGq00BbJPo37ox68R5vaGTXivs6RGh/BQ6QP5zqZDGWdAO6JbRE/iR1l80xeGAwCQS2nMV9S/w==", 104 | "requires": { 105 | "@types/node": "*" 106 | } 107 | }, 108 | "accepts": { 109 | "version": "1.3.7", 110 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 111 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 112 | "requires": { 113 | "mime-types": "~2.1.24", 114 | "negotiator": "0.6.2" 115 | } 116 | }, 117 | "arg": { 118 | "version": "4.1.1", 119 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", 120 | "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", 121 | "dev": true 122 | }, 123 | "array-flatten": { 124 | "version": "1.1.1", 125 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 126 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 127 | }, 128 | "async": { 129 | "version": "2.6.2", 130 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", 131 | "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", 132 | "optional": true, 133 | "requires": { 134 | "lodash": "^4.17.11" 135 | } 136 | }, 137 | "async-limiter": { 138 | "version": "1.0.1", 139 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 140 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" 141 | }, 142 | "bluebird": { 143 | "version": "3.5.1", 144 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 145 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", 146 | "optional": true 147 | }, 148 | "body-parser": { 149 | "version": "1.19.0", 150 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 151 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 152 | "requires": { 153 | "bytes": "3.1.0", 154 | "content-type": "~1.0.4", 155 | "debug": "2.6.9", 156 | "depd": "~1.1.2", 157 | "http-errors": "1.7.2", 158 | "iconv-lite": "0.4.24", 159 | "on-finished": "~2.3.0", 160 | "qs": "6.7.0", 161 | "raw-body": "2.4.0", 162 | "type-is": "~1.6.17" 163 | } 164 | }, 165 | "bson": { 166 | "version": "1.1.1", 167 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", 168 | "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" 169 | }, 170 | "buffer-from": { 171 | "version": "1.1.1", 172 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 173 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 174 | "dev": true 175 | }, 176 | "bytes": { 177 | "version": "3.1.0", 178 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 179 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 180 | }, 181 | "colyseus": { 182 | "version": "0.11.9", 183 | "resolved": "https://registry.npmjs.org/colyseus/-/colyseus-0.11.9.tgz", 184 | "integrity": "sha512-BlZi1J6vtGJTMVxX+M2Sd3EznTrKuGYnYsLDzqDoqlD9GWQFN4AW2cgHlmFD1eQycS/cFfIXAHM+qU3+PX+0SA==", 185 | "requires": { 186 | "@colyseus/schema": "^0.4.0", 187 | "@gamestdio/timer": "^1.3.0", 188 | "@types/redis": "^2.8.12", 189 | "@types/ws": "^6.0.1", 190 | "debug": "^4.0.1", 191 | "fast-json-patch": "^2.0.5", 192 | "fossil-delta": "^1.0.1", 193 | "mongoose": "^5.6.9", 194 | "nanoid": "^2.0.0", 195 | "nonenumerable": "^1.0.1", 196 | "notepack.io": "^2.2.0", 197 | "redis": "^2.8.0", 198 | "ws": "^7.1.0" 199 | }, 200 | "dependencies": { 201 | "debug": { 202 | "version": "4.1.1", 203 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 204 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 205 | "requires": { 206 | "ms": "^2.1.1" 207 | } 208 | }, 209 | "ms": { 210 | "version": "2.1.2", 211 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 212 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 213 | } 214 | } 215 | }, 216 | "content-disposition": { 217 | "version": "0.5.3", 218 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 219 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 220 | "requires": { 221 | "safe-buffer": "5.1.2" 222 | } 223 | }, 224 | "content-type": { 225 | "version": "1.0.4", 226 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 227 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 228 | }, 229 | "cookie": { 230 | "version": "0.4.0", 231 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 232 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 233 | }, 234 | "cookie-signature": { 235 | "version": "1.0.6", 236 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 237 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 238 | }, 239 | "cors": { 240 | "version": "2.8.5", 241 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 242 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 243 | "requires": { 244 | "object-assign": "^4", 245 | "vary": "^1" 246 | } 247 | }, 248 | "debug": { 249 | "version": "2.6.9", 250 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 251 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 252 | "requires": { 253 | "ms": "2.0.0" 254 | } 255 | }, 256 | "depd": { 257 | "version": "1.1.2", 258 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 259 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 260 | }, 261 | "destroy": { 262 | "version": "1.0.4", 263 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 264 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 265 | }, 266 | "diff": { 267 | "version": "4.0.1", 268 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", 269 | "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", 270 | "dev": true 271 | }, 272 | "double-ended-queue": { 273 | "version": "2.1.0-0", 274 | "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", 275 | "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", 276 | "optional": true 277 | }, 278 | "ee-first": { 279 | "version": "1.1.1", 280 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 281 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 282 | }, 283 | "encodeurl": { 284 | "version": "1.0.2", 285 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 286 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 287 | }, 288 | "escape-html": { 289 | "version": "1.0.3", 290 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 291 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 292 | }, 293 | "etag": { 294 | "version": "1.8.1", 295 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 296 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 297 | }, 298 | "express": { 299 | "version": "4.17.1", 300 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 301 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 302 | "requires": { 303 | "accepts": "~1.3.7", 304 | "array-flatten": "1.1.1", 305 | "body-parser": "1.19.0", 306 | "content-disposition": "0.5.3", 307 | "content-type": "~1.0.4", 308 | "cookie": "0.4.0", 309 | "cookie-signature": "1.0.6", 310 | "debug": "2.6.9", 311 | "depd": "~1.1.2", 312 | "encodeurl": "~1.0.2", 313 | "escape-html": "~1.0.3", 314 | "etag": "~1.8.1", 315 | "finalhandler": "~1.1.2", 316 | "fresh": "0.5.2", 317 | "merge-descriptors": "1.0.1", 318 | "methods": "~1.1.2", 319 | "on-finished": "~2.3.0", 320 | "parseurl": "~1.3.3", 321 | "path-to-regexp": "0.1.7", 322 | "proxy-addr": "~2.0.5", 323 | "qs": "6.7.0", 324 | "range-parser": "~1.2.1", 325 | "safe-buffer": "5.1.2", 326 | "send": "0.17.1", 327 | "serve-static": "1.14.1", 328 | "setprototypeof": "1.1.1", 329 | "statuses": "~1.5.0", 330 | "type-is": "~1.6.18", 331 | "utils-merge": "1.0.1", 332 | "vary": "~1.1.2" 333 | } 334 | }, 335 | "fast-deep-equal": { 336 | "version": "2.0.1", 337 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 338 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 339 | }, 340 | "fast-json-patch": { 341 | "version": "2.2.1", 342 | "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", 343 | "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", 344 | "requires": { 345 | "fast-deep-equal": "^2.0.1" 346 | } 347 | }, 348 | "finalhandler": { 349 | "version": "1.1.2", 350 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 351 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 352 | "requires": { 353 | "debug": "2.6.9", 354 | "encodeurl": "~1.0.2", 355 | "escape-html": "~1.0.3", 356 | "on-finished": "~2.3.0", 357 | "parseurl": "~1.3.3", 358 | "statuses": "~1.5.0", 359 | "unpipe": "~1.0.0" 360 | } 361 | }, 362 | "forwarded": { 363 | "version": "0.1.2", 364 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 365 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 366 | }, 367 | "fossil-delta": { 368 | "version": "1.0.2", 369 | "resolved": "https://registry.npmjs.org/fossil-delta/-/fossil-delta-1.0.2.tgz", 370 | "integrity": "sha512-fkgQUnBaJjhDq0DN1ajBlWWRH99NQgwVoReMEr0oXLqYXAx8X9mD5gGaoTYDdVVbLJYsMNrnkJ028Ld0UgWrGA==" 371 | }, 372 | "fresh": { 373 | "version": "0.5.2", 374 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 375 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 376 | }, 377 | "http-errors": { 378 | "version": "1.7.2", 379 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 380 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 381 | "requires": { 382 | "depd": "~1.1.2", 383 | "inherits": "2.0.3", 384 | "setprototypeof": "1.1.1", 385 | "statuses": ">= 1.5.0 < 2", 386 | "toidentifier": "1.0.0" 387 | } 388 | }, 389 | "iconv-lite": { 390 | "version": "0.4.24", 391 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 392 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 393 | "requires": { 394 | "safer-buffer": ">= 2.1.2 < 3" 395 | } 396 | }, 397 | "inherits": { 398 | "version": "2.0.3", 399 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 400 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 401 | }, 402 | "ipaddr.js": { 403 | "version": "1.9.0", 404 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 405 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 406 | }, 407 | "kareem": { 408 | "version": "2.3.1", 409 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", 410 | "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==", 411 | "optional": true 412 | }, 413 | "lodash": { 414 | "version": "4.17.15", 415 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 416 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", 417 | "optional": true 418 | }, 419 | "make-error": { 420 | "version": "1.3.5", 421 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", 422 | "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", 423 | "dev": true 424 | }, 425 | "media-typer": { 426 | "version": "0.3.0", 427 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 428 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 429 | }, 430 | "memory-pager": { 431 | "version": "1.5.0", 432 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 433 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 434 | "optional": true 435 | }, 436 | "merge-descriptors": { 437 | "version": "1.0.1", 438 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 439 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 440 | }, 441 | "methods": { 442 | "version": "1.1.2", 443 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 444 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 445 | }, 446 | "mime": { 447 | "version": "1.6.0", 448 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 449 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 450 | }, 451 | "mime-db": { 452 | "version": "1.40.0", 453 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 454 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 455 | }, 456 | "mime-types": { 457 | "version": "2.1.24", 458 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 459 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 460 | "requires": { 461 | "mime-db": "1.40.0" 462 | } 463 | }, 464 | "mongodb": { 465 | "version": "3.2.7", 466 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", 467 | "integrity": "sha512-2YdWrdf1PJgxcCrT1tWoL6nHuk6hCxhddAAaEh8QJL231ci4+P9FLyqopbTm2Z2sAU6mhCri+wd9r1hOcHdoMw==", 468 | "optional": true, 469 | "requires": { 470 | "mongodb-core": "3.2.7", 471 | "safe-buffer": "^5.1.2" 472 | } 473 | }, 474 | "mongodb-core": { 475 | "version": "3.2.7", 476 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.7.tgz", 477 | "integrity": "sha512-WypKdLxFNPOH/Jy6i9z47IjG2wIldA54iDZBmHMINcgKOUcWJh8og+Wix76oGd7EyYkHJKssQ2FAOw5Su/n4XQ==", 478 | "requires": { 479 | "bson": "^1.1.1", 480 | "require_optional": "^1.0.1", 481 | "safe-buffer": "^5.1.2", 482 | "saslprep": "^1.0.0" 483 | } 484 | }, 485 | "mongoose": { 486 | "version": "5.6.11", 487 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.6.11.tgz", 488 | "integrity": "sha512-+Mxfmu2Jcspmdqk1111BLUj5mzSLHWKVovImkjzO9GMymTwkHshsuSFmUN7ou4dWy2WR8DPSJcwU52HhnnNk8Q==", 489 | "optional": true, 490 | "requires": { 491 | "async": "2.6.2", 492 | "bson": "~1.1.1", 493 | "kareem": "2.3.1", 494 | "mongodb": "3.2.7", 495 | "mongodb-core": "3.2.7", 496 | "mongoose-legacy-pluralize": "1.0.2", 497 | "mpath": "0.6.0", 498 | "mquery": "3.2.1", 499 | "ms": "2.1.2", 500 | "regexp-clone": "1.0.0", 501 | "safe-buffer": "5.1.2", 502 | "sift": "7.0.1", 503 | "sliced": "1.0.1" 504 | }, 505 | "dependencies": { 506 | "ms": { 507 | "version": "2.1.2", 508 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 509 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 510 | "optional": true 511 | } 512 | } 513 | }, 514 | "mongoose-legacy-pluralize": { 515 | "version": "1.0.2", 516 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 517 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", 518 | "optional": true 519 | }, 520 | "mpath": { 521 | "version": "0.6.0", 522 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", 523 | "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==", 524 | "optional": true 525 | }, 526 | "mquery": { 527 | "version": "3.2.1", 528 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.1.tgz", 529 | "integrity": "sha512-kY/K8QToZWTTocm0U+r8rqcJCp5PRl6e8tPmoDs5OeSO3DInZE2rAL6AYH+V406JTo8305LdASOQcxRDqHojyw==", 530 | "optional": true, 531 | "requires": { 532 | "bluebird": "3.5.1", 533 | "debug": "3.1.0", 534 | "regexp-clone": "^1.0.0", 535 | "safe-buffer": "5.1.2", 536 | "sliced": "1.0.1" 537 | }, 538 | "dependencies": { 539 | "debug": { 540 | "version": "3.1.0", 541 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 542 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 543 | "optional": true, 544 | "requires": { 545 | "ms": "2.0.0" 546 | } 547 | } 548 | } 549 | }, 550 | "ms": { 551 | "version": "2.0.0", 552 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 553 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 554 | }, 555 | "nanoid": { 556 | "version": "2.1.0", 557 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.0.tgz", 558 | "integrity": "sha512-g5WwS+p6Cm+zQhO2YOpRbQThZVnNb7DDq74h8YDCLfAGynrEOrbx2E16dc8ciENiP1va5sqaAruqn2sN+xpkWg==" 559 | }, 560 | "negotiator": { 561 | "version": "0.6.2", 562 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 563 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 564 | }, 565 | "nonenumerable": { 566 | "version": "1.1.1", 567 | "resolved": "https://registry.npmjs.org/nonenumerable/-/nonenumerable-1.1.1.tgz", 568 | "integrity": "sha512-ptUD9w9D8WqW6fuJJkZNCImkf+0vdbgUTbRK3i7jsy3olqtH96hYE6Q/S3Tx9NWbcB/ocAjYshXCAUP0lZ9B4Q==" 569 | }, 570 | "notepack.io": { 571 | "version": "2.2.0", 572 | "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.2.0.tgz", 573 | "integrity": "sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw==" 574 | }, 575 | "object-assign": { 576 | "version": "4.1.1", 577 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 578 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 579 | }, 580 | "on-finished": { 581 | "version": "2.3.0", 582 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 583 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 584 | "requires": { 585 | "ee-first": "1.1.1" 586 | } 587 | }, 588 | "parseurl": { 589 | "version": "1.3.3", 590 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 591 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 592 | }, 593 | "path-to-regexp": { 594 | "version": "0.1.7", 595 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 596 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 597 | }, 598 | "proxy-addr": { 599 | "version": "2.0.5", 600 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 601 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 602 | "requires": { 603 | "forwarded": "~0.1.2", 604 | "ipaddr.js": "1.9.0" 605 | } 606 | }, 607 | "qs": { 608 | "version": "6.7.0", 609 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 610 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 611 | }, 612 | "range-parser": { 613 | "version": "1.2.1", 614 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 615 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 616 | }, 617 | "raw-body": { 618 | "version": "2.4.0", 619 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 620 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 621 | "requires": { 622 | "bytes": "3.1.0", 623 | "http-errors": "1.7.2", 624 | "iconv-lite": "0.4.24", 625 | "unpipe": "1.0.0" 626 | } 627 | }, 628 | "redis": { 629 | "version": "2.8.0", 630 | "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", 631 | "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", 632 | "optional": true, 633 | "requires": { 634 | "double-ended-queue": "^2.1.0-0", 635 | "redis-commands": "^1.2.0", 636 | "redis-parser": "^2.6.0" 637 | } 638 | }, 639 | "redis-commands": { 640 | "version": "1.5.0", 641 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", 642 | "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==", 643 | "optional": true 644 | }, 645 | "redis-parser": { 646 | "version": "2.6.0", 647 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", 648 | "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", 649 | "optional": true 650 | }, 651 | "regexp-clone": { 652 | "version": "1.0.0", 653 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", 654 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" 655 | }, 656 | "require_optional": { 657 | "version": "1.0.1", 658 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 659 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 660 | "requires": { 661 | "resolve-from": "^2.0.0", 662 | "semver": "^5.1.0" 663 | } 664 | }, 665 | "resolve-from": { 666 | "version": "2.0.0", 667 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 668 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 669 | }, 670 | "safe-buffer": { 671 | "version": "5.1.2", 672 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 673 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 674 | }, 675 | "safer-buffer": { 676 | "version": "2.1.2", 677 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 678 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 679 | }, 680 | "saslprep": { 681 | "version": "1.0.3", 682 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 683 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 684 | "optional": true, 685 | "requires": { 686 | "sparse-bitfield": "^3.0.3" 687 | } 688 | }, 689 | "semver": { 690 | "version": "5.7.1", 691 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 692 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 693 | }, 694 | "send": { 695 | "version": "0.17.1", 696 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 697 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 698 | "requires": { 699 | "debug": "2.6.9", 700 | "depd": "~1.1.2", 701 | "destroy": "~1.0.4", 702 | "encodeurl": "~1.0.2", 703 | "escape-html": "~1.0.3", 704 | "etag": "~1.8.1", 705 | "fresh": "0.5.2", 706 | "http-errors": "~1.7.2", 707 | "mime": "1.6.0", 708 | "ms": "2.1.1", 709 | "on-finished": "~2.3.0", 710 | "range-parser": "~1.2.1", 711 | "statuses": "~1.5.0" 712 | }, 713 | "dependencies": { 714 | "ms": { 715 | "version": "2.1.1", 716 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 717 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 718 | } 719 | } 720 | }, 721 | "serve-static": { 722 | "version": "1.14.1", 723 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 724 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 725 | "requires": { 726 | "encodeurl": "~1.0.2", 727 | "escape-html": "~1.0.3", 728 | "parseurl": "~1.3.3", 729 | "send": "0.17.1" 730 | } 731 | }, 732 | "setprototypeof": { 733 | "version": "1.1.1", 734 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 735 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 736 | }, 737 | "sift": { 738 | "version": "7.0.1", 739 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", 740 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==", 741 | "optional": true 742 | }, 743 | "sliced": { 744 | "version": "1.0.1", 745 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 746 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 747 | }, 748 | "source-map-support": { 749 | "version": "0.5.12", 750 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", 751 | "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", 752 | "dev": true, 753 | "requires": { 754 | "buffer-from": "^1.0.0", 755 | "source-map": "^0.6.0" 756 | }, 757 | "dependencies": { 758 | "source-map": { 759 | "version": "0.6.1", 760 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 761 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 762 | "dev": true 763 | } 764 | } 765 | }, 766 | "sparse-bitfield": { 767 | "version": "3.0.3", 768 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 769 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 770 | "optional": true, 771 | "requires": { 772 | "memory-pager": "^1.0.2" 773 | } 774 | }, 775 | "statuses": { 776 | "version": "1.5.0", 777 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 778 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 779 | }, 780 | "toidentifier": { 781 | "version": "1.0.0", 782 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 783 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 784 | }, 785 | "ts-node": { 786 | "version": "8.3.0", 787 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", 788 | "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", 789 | "dev": true, 790 | "requires": { 791 | "arg": "^4.1.0", 792 | "diff": "^4.0.1", 793 | "make-error": "^1.1.1", 794 | "source-map-support": "^0.5.6", 795 | "yn": "^3.0.0" 796 | } 797 | }, 798 | "type-is": { 799 | "version": "1.6.18", 800 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 801 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 802 | "requires": { 803 | "media-typer": "0.3.0", 804 | "mime-types": "~2.1.24" 805 | } 806 | }, 807 | "typescript": { 808 | "version": "3.5.3", 809 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", 810 | "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", 811 | "dev": true 812 | }, 813 | "unpipe": { 814 | "version": "1.0.0", 815 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 816 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 817 | }, 818 | "utils-merge": { 819 | "version": "1.0.1", 820 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 821 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 822 | }, 823 | "vary": { 824 | "version": "1.1.2", 825 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 826 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 827 | }, 828 | "ws": { 829 | "version": "7.1.2", 830 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", 831 | "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", 832 | "requires": { 833 | "async-limiter": "^1.0.0" 834 | } 835 | }, 836 | "yn": { 837 | "version": "3.1.0", 838 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", 839 | "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", 840 | "dev": true 841 | } 842 | } 843 | } 844 | --------------------------------------------------------------------------------