├── 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 |
--------------------------------------------------------------------------------