dao-game / commit
Experimenting with multiple entry points, getting remote play mostly working, trying to get pages working for all branches.
.gitlab-ci.yml | 3 +- .vscode/settings.json | 1 + package-lock.json | 693 +-------------------------------------- public/index.html | 127 +++---- public/online.html | 108 +++--- public/styles.css | 72 ++++ src/app.ts | 4 - src/local-game.ts | 63 ++-- src/local.ts | 3 + src/online.ts | 3 + src/remote-game.ts | 263 ++++++++------- src/utils.ts | 12 + tools/esbuild-build-and-serve.ts | 4 +- tools/esbuild-watch.ts | 4 +- tools/esbuild.ts | 4 +- 15 files changed, 369 insertions(+), 995 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 590999b..513e1b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,8 +4,7 @@ pages: script: - npm install - npm run build + - cp public/* public/$CI_COMMIT_BRANCH/ -R artifacts: paths: - public - only: - - main diff --git a/.vscode/settings.json b/.vscode/settings.json index 989167c..48c5f7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "esbuild", "Newp", + "outdir", "outfile", "peerjs", "servedir", diff --git a/package-lock.json b/package-lock.json index 59eac04..edb445e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,699 +1,8 @@ { "name": "dao-game", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "dao-game", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "esbuild": "^0.14.25", - "peerjs": "^1.3.2" - }, - "devDependencies": { - "@types/node": "^17.0.21", - "rimraf": "^3.0.2", - "ts-node": "^10.6.0", - "typescript": "^4.6.2" - } - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/esbuild": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.25.tgz", - "integrity": "sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==", - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "esbuild-android-64": "0.14.25", - "esbuild-android-arm64": "0.14.25", - "esbuild-darwin-64": "0.14.25", - "esbuild-darwin-arm64": "0.14.25", - "esbuild-freebsd-64": "0.14.25", - "esbuild-freebsd-arm64": "0.14.25", - "esbuild-linux-32": "0.14.25", - "esbuild-linux-64": "0.14.25", - "esbuild-linux-arm": "0.14.25", - "esbuild-linux-arm64": "0.14.25", - "esbuild-linux-mips64le": "0.14.25", - "esbuild-linux-ppc64le": "0.14.25", - "esbuild-linux-riscv64": "0.14.25", - "esbuild-linux-s390x": "0.14.25", - "esbuild-netbsd-64": "0.14.25", - "esbuild-openbsd-64": "0.14.25", - "esbuild-sunos-64": "0.14.25", - "esbuild-windows-32": "0.14.25", - "esbuild-windows-64": "0.14.25", - "esbuild-windows-arm64": "0.14.25" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz", - "integrity": "sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz", - "integrity": "sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz", - "integrity": "sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz", - "integrity": "sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz", - "integrity": "sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz", - "integrity": "sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz", - "integrity": "sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz", - "integrity": "sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz", - "integrity": "sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz", - "integrity": "sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz", - "integrity": "sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz", - "integrity": "sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz", - "integrity": "sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz", - "integrity": "sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz", - "integrity": "sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz", - "integrity": "sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz", - "integrity": "sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz", - "integrity": "sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz", - "integrity": "sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.25", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz", - "integrity": "sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/peerjs": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.3.2.tgz", - "integrity": "sha512-+PHfmsC7QGUU8Ye3OLi6tKQZGPCNy7QatUVNw4JtE8alkguF3+DdO5W0bzepqP2OtE9FqH/ltXt37qyvHw2CqA==", - "dependencies": { - "@types/node": "^10.14.33", - "eventemitter3": "^3.1.2", - "peerjs-js-binarypack": "1.0.1", - "webrtc-adapter": "^7.7.1" - } - }, - "node_modules/peerjs-js-binarypack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-1.0.1.tgz", - "integrity": "sha512-N6aeia3NhdpV7kiGxJV5xQiZZCVEEVjRz2T2C6UZQiBkHWHzUv/oWA4myQLcwBwO8LUoR1KWW5oStvwVesmfCg==" - }, - "node_modules/peerjs/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rtcpeerconnection-shim": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz", - "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==", - "dependencies": { - "sdp": "^2.6.0" - }, - "engines": { - "node": ">=6.0.0", - "npm": ">=3.10.0" - } - }, - "node_modules/sdp": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz", - "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==" - }, - "node_modules/ts-node": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.6.0.tgz", - "integrity": "sha512-CJen6+dfOXolxudBQXnVjRVvYTmTWbyz7cn+xq2XTsvnaXbHqr4gXSCNbS2Jj8yTZMuGwUoBESLaOkLascVVvg==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true - }, - "node_modules/webrtc-adapter": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz", - "integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==", - "dependencies": { - "rtcpeerconnection-shim": "^1.2.15", - "sdp": "^2.12.0" - }, - "engines": { - "node": ">=6.0.0", - "npm": ">=3.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - } - }, "dependencies": { "@cspotcode/source-map-consumer": { "version": "0.8.0", diff --git a/public/index.html b/public/index.html index 14baf73..4ed5c54 100644 --- a/public/index.html +++ b/public/index.html @@ -1,77 +1,54 @@ <!DOCTYPE html> <html lang="en"> - - <head> - <meta charset="utf-8" /> - <title>Dao Game</title> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <link rel="icon" href="yin-yang.svg" type="image/svg+xml" /> - <link rel="stylesheet" href="styles.css" /> - <script defer src="bundle.js"></script> - </head> - - <body> - <div id="placeholders" class="invisible"> - <p class="piece p1-piece" id="default-p1-piece"></p> - <p class="piece p2-piece" id="default-p2-piece"></p> - <div id="other-mouse"> - <img src="./cursor.png" alt="cursor" /> - </div> - </div> - <main class="invisible"> - <div class="controls"> - <span class="p1-turn-text"> Red's turn </span> - <span class="divider">|</span> - <span class="p2-turn-text"> Blue's turn </span> - </div> - - <table id="table"></table> - - <div class="controls"> - <button type="button" class="button" id="undo-button"> - Undo - </button> - <span class="divider">|</span> - <button type="button" class="button" id="reset-button"> - Reset - </button> - </div> - </main> - - <hr /> - - <div class="title"> - <p>Board game - <a href="https://en.wikipedia.org/wiki/Dao_(game)" - target="_blank" rel="noopener">Dao</a>. - </p> - </div> - - <div class="info"> - The game consists of only one simple rule and three ways to win. - Pieces move in a straight line, including diagonally, as far as - possible with no jumps. The three ways to win are arranging your - pieces in a straight line on the board (not diagonally), - occupying - the four corners, or forming a 2x2 square anywhere on the board. - </div> - <hr /> - - <div class="info"> - <p> - Here is the - <a href="https://gitlab.com/fuzzy-rp/dao-game" - target="_blank" - rel="noopener">source code</a> - </p> - </div> - - <script type="module"> - const bundle = import('./bundle.js'); - debugger; - console.log('Hi!'); - bundle.play(); - </script> - </body> - -</html> \ No newline at end of file + <head> + <meta charset="utf-8" /> + <title>Dao Game</title> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="icon" href="yin-yang.svg" type="image/svg+xml" /> + <link rel="stylesheet" href="styles.css" /> + <script defer src="online.js"></script> + </head> + + <body> + <div id="placeholders" class="invisible"> + <p class="piece p1-piece" id="default-p1-piece"></p> + <p class="piece p2-piece" id="default-p2-piece"></p> + <div id="other-mouse"> + <img src="./cursor.png" alt="cursor" /> + </div> + </div> + <main class="invisible"> + <div class="controls"> + <span class="p1-turn-text"> Red's turn </span> + <span class="divider">|</span> + <span class="p2-turn-text"> Blue's turn </span> + </div> + + <table id="table"></table> + </main> + + <hr /> + <div id="shareLink">Share this link to play with an opponent.</div> + <hr /> + + <div class="title"> + <p>Board game <a href="https://en.wikipedia.org/wiki/Dao_(game)" target="_blank" rel="noopener">Dao</a>.</p> + </div> + + <div class="info"> + The game consists of only one simple rule and three ways to win. Pieces move in a straight line, including diagonally, as far as + possible with no jumps. The three ways to win are arranging your pieces in a straight line on the board (not diagonally), occupying + the four corners, or forming a 2x2 square anywhere on the board. + </div> + <hr /> + + <div class="info"> + <p> + Here is the + <a href="https://gitlab.com/fuzzy-rp/dao-game" target="_blank" rel="noopener">source code</a> + </p> + </div> + + <div id="snackbar"></div> + </body> +</html> diff --git a/public/online.html b/public/online.html index ad554d7..91997b5 100644 --- a/public/online.html +++ b/public/online.html @@ -1,64 +1,50 @@ <!DOCTYPE html> <html lang="en"> - <head> - <meta charset="utf-8" /> - <title>Dao Game</title> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <link rel="icon" href="yin-yang.svg" type="image/svg+xml" /> - <link rel="stylesheet" href="styles.css" /> - <script> - window.daoIsOnline = true; - </script> - <script defer src="bundle.js"></script> - </head> - - <body> - <div class="invisible"> - <p class="piece p1-piece" id="default-p1-piece"></p> - <p class="piece p2-piece" id="default-p2-piece"></p> - </div> - <main> - <div class="controls"> - <span class="p1-turn-text"> Red's turn </span> - - <span class="divider">|</span> - - <span class="p2-turn-text"> Blue's turn </span> - </div> - - <table id="table"></table> - </main> - <hr /> - - <div class="title"> - <p> - Board game - <a - href="https://en.wikipedia.org/wiki/Dao_(game)" - target="_blank" - rel="noopener">Dao</a>. - </p> - </div> - - <div class="info"> - The game consists of only one simple rule and three ways to win. - Pieces move in a straight line, including diagonally, as far as - possible with no jumps. The three ways to win are arranging your - pieces in a straight line on the board (not diagonally), occupying - the four corners, or forming a 2x2 square anywhere on the board. - </div> - <hr /> - - <div class="info"> - <p> - Here is the - <a - href="https://gitlab.com/fuzzy-rp/dao-game" - target="_blank" - rel="noopener">source code</a> - </p> - </div> - - <canvas id="back-canvas"></canvas> - </body> + <head> + <meta charset="utf-8" /> + <title>Dao Game</title> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="icon" href="yin-yang.svg" type="image/svg+xml" /> + <link rel="stylesheet" href="styles.css" /> + <script defer src="online.js"></script> + </head> + + <body> + <div id="placeholders" class="invisible"> + <p class="piece p1-piece" id="default-p1-piece"></p> + <p class="piece p2-piece" id="default-p2-piece"></p> + <div id="other-mouse"> + <img src="./cursor.png" alt="cursor" /> + </div> + </div> + <main class="invisible"> + <div class="controls"> + <span class="p1-turn-text"> Red's turn </span> + <span class="divider">|</span> + <span class="p2-turn-text"> Blue's turn </span> + </div> + + <table id="table"></table> + </main> + + <hr /> + + <div class="title"> + <p>Board game <a href="https://en.wikipedia.org/wiki/Dao_(game)" target="_blank" rel="noopener">Dao</a>.</p> + </div> + + <div class="info"> + The game consists of only one simple rule and three ways to win. Pieces move in a straight line, including diagonally, as far as + possible with no jumps. The three ways to win are arranging your pieces in a straight line on the board (not diagonally), occupying + the four corners, or forming a 2x2 square anywhere on the board. + </div> + <hr /> + + <div class="info"> + <p> + Here is the + <a href="https://gitlab.com/fuzzy-rp/dao-game" target="_blank" rel="noopener">source code</a> + </p> + </div> + </body> </html> diff --git a/public/styles.css b/public/styles.css index 12f0275..ccd4f8e 100644 --- a/public/styles.css +++ b/public/styles.css @@ -214,3 +214,75 @@ td { height: var(--active-size); } } + +/* Snackbar, from https://www.w3schools.com/howto/howto_js_snackbar.asp */ + +/* The snackbar - position it at the bottom and in the middle of the screen */ +#snackbar { + visibility: hidden; /* Hidden by default. Visible on click */ + min-width: 250px; /* Set a default minimum width */ + margin-left: -125px; /* Divide value of min-width by 2 */ + background-color: #333; /* Black background color */ + color: #fff; /* White text color */ + text-align: center; /* Centered text */ + border-radius: 2px; /* Rounded borders */ + padding: 16px; /* Padding */ + position: fixed; /* Sit on top of the screen */ + z-index: 1; /* Add a z-index if needed */ + left: 50%; /* Center the snackbar */ + bottom: 30px; /* 30px from the bottom */ +} + +/* Show the snackbar when clicking on a button (class added with JavaScript) */ +#snackbar.show { + visibility: visible; /* Show the snackbar */ + /* Add animation: Take 0.5 seconds to fade in and out the snackbar. + However, delay the fade out process for 2.5 seconds */ + -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; + animation: fadein 0.5s, fadeout 0.5s 2.5s; +} + +/* Animations to fade the snackbar in and out */ +@-webkit-keyframes fadein { + from { + bottom: 0; + opacity: 0; + } + to { + bottom: 30px; + opacity: 1; + } +} + +@keyframes fadein { + from { + bottom: 0; + opacity: 0; + } + to { + bottom: 30px; + opacity: 1; + } +} + +@-webkit-keyframes fadeout { + from { + bottom: 30px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +@keyframes fadeout { + from { + bottom: 30px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index 515eb31..0000000 --- a/src/app.ts +++ /dev/null @@ -1,4 +0,0 @@ -// import { playGame } from './local-game' -import { playGame } from './remote-game'; - -playGame(); \ No newline at end of file diff --git a/src/local-game.ts b/src/local-game.ts index 396921d..078e0f3 100644 --- a/src/local-game.ts +++ b/src/local-game.ts @@ -120,13 +120,15 @@ export class LocalGame extends Game { dom.p2TurnText().classList.add(classNames.selectedPlayerText); } - dom.undoButton.disabled = canUndoMove; - if (moveHistory.length === 0) { - dom.undoButton.disabled = true; - dom.resetButton.disabled = true; - } else { - dom.undoButton.disabled = false; - dom.resetButton.disabled = false; + if (dom.undoButton || dom.resetButton) { + dom.undoButton.disabled = canUndoMove; + if (moveHistory.length === 0) { + dom.undoButton.disabled = true; + dom.resetButton.disabled = true; + } else { + dom.undoButton.disabled = false; + dom.resetButton.disabled = false; + } } this.selectedPiece = null; @@ -201,31 +203,36 @@ export class LocalGame extends Game { // hook up buttons let blockActions = false; - dom.resetButton.addEventListener('click', () => { - if (blockActions) { - return; - } + if (dom.resetButton) { + dom.resetButton.addEventListener('click', () => { + if (blockActions) { + return; + } - blockActions = true; - if (confirm('Reset?')) { - window.location.reload(); - } - blockActions = false; - }); - dom.undoButton.addEventListener('click', async () => { - if (blockActions) { - return; - } + blockActions = true; + if (confirm('Reset?')) { + window.location.reload(); + } + blockActions = false; + }); + } - blockActions = true; + if (dom.undoButton) { + dom.undoButton.addEventListener('click', async () => { + if (blockActions) { + return; + } - const move = this.undoMove(); - await this.movePiece(move.dest, move.source); - this.resetUi(); - this.setActiveUi(); + blockActions = true; - blockActions = false; - }); + const move = this.undoMove(); + await this.movePiece(move.dest, move.source); + this.resetUi(); + this.setActiveUi(); + + blockActions = false; + }); + } } } diff --git a/src/local.ts b/src/local.ts new file mode 100644 index 0000000..22236bd --- /dev/null +++ b/src/local.ts @@ -0,0 +1,3 @@ +import { playGame } from './local-game'; + +playGame(); \ No newline at end of file diff --git a/src/online.ts b/src/online.ts new file mode 100644 index 0000000..b314e49 --- /dev/null +++ b/src/online.ts @@ -0,0 +1,3 @@ +import { playGame } from './remote-game'; + +playGame(); \ No newline at end of file diff --git a/src/remote-game.ts b/src/remote-game.ts index 5f43230..35cf3b8 100644 --- a/src/remote-game.ts +++ b/src/remote-game.ts @@ -2,158 +2,167 @@ import Peer, { PeerJSOption } from 'peerjs'; import { LocalGame } from './local-game'; import { useSessionStorage } from './storage'; import { Coord, Move, MoveResult } from './types'; -import { delay, generateHash } from './utils'; +import { delay, generateHash, showSnackbar } from './utils'; interface LocalData { - id: string; - peerId: string; + id: string; + peerId: string; } interface Pos { - x: number; - y: number; + x: number; + y: number; } interface PeerData { - mousePos?: Pos; - move?: Move; + mousePos?: Pos; + move?: Move; } const peerOptions: PeerJSOption = { debug: 4 }; const { getter: getData, setter: setData } = useSessionStorage<LocalData>('dao-game', { id: undefined, peerId: undefined }); const otherMouseElement = document.getElementById('other-mouse'); const openInNewTab = (url: string) => { - const openInEdge = true; - if (openInEdge) { - // opening in the same browser causes a copy of the session storage, - // so we clear the session storage, open the url, and restore the session storage - const data = getData(); - sessionStorage.clear(); - try { - window.open(url, '_blank'); - } finally { - setData(data); + const openInSystemDefault = false; + if (openInSystemDefault) { + window.open(`microsoft-edge:${url}`, '_blank'); + } else { + // opening in the same browser causes a copy of the session storage, + // so we clear the session storage, open the url, and restore the session storage + const data = getData(); + sessionStorage.clear(); + try { + window.open(url, '_blank'); + } finally { + setData(data); + } } - } else { - window.open(url, '_blank'); - } }; const minString = (a: string, b: string): string => (a < b ? a : b); const maxString = (a: string, b: string): string => (a > b ? a : b); const getFirstPlayerId = (playerId1: string, playerId2: string) => { - const hash1 = generateHash(playerId1); - const hash2 = generateHash(playerId2); - const prod = hash1 * hash2; - const isEven = prod % 2 == 0; - const id = isEven ? minString(playerId1, playerId2) : maxString(playerId1, playerId2); - return id; + const hash1 = generateHash(playerId1); + const hash2 = generateHash(playerId2); + const prod = hash1 * hash2; + const isEven = prod % 2 == 0; + const id = isEven ? minString(playerId1, playerId2) : maxString(playerId1, playerId2); + return id; }; export class PeerGame extends LocalGame { - public conn: Peer.DataConnection; - public selfId: string; - public constructor(player1Id: string, player2Id: string, selfId: string, conn: Peer.DataConnection) { - super(player1Id, player2Id); - this.selfId = selfId; - this.conn = conn; - - const { dom } = this; - const isLocalP1 = player1Id === selfId; - (dom.p1TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Local turn` : `Remote turn`; - (dom.p2TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Remote turn` : `Local turn`; - } - public override setActiveUi(): void { - const { currentPlayerId, selfId } = this; - - const isLocalTurn = currentPlayerId === selfId; - if (isLocalTurn) { - super.setActiveUi(); + public conn: Peer.DataConnection; + public selfId: string; + public constructor(player1Id: string, player2Id: string, selfId: string, conn: Peer.DataConnection) { + super(player1Id, player2Id); + this.selfId = selfId; + this.conn = conn; + + const { dom } = this; + const isLocalP1 = player1Id === selfId; + (dom.p1TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Local turn` : `Remote turn`; + (dom.p2TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Remote turn` : `Local turn`; } - } - public override makeMove(source: Coord, dest: Coord): Promise<MoveResult> { - const result = super.makeMove(source, dest); - const peerData: PeerData = { move: { source, dest, playerId: this.currentPlayerId } }; - this.conn.send(JSON.stringify(peerData)); - return result; - } - - public static async createPeerGame(): Promise<PeerGame> { - let { id, peerId } = getData(); - const main = document.getElementsByTagName('main')[0]; - let game: PeerGame = undefined; - - const handleConn = (conn: Peer.DataConnection) => { - if (game !== undefined) { - conn.close(); - return; - } - - id = peer.id; - peerId = conn.peer; - setData({ id, peerId }); - - const isLocalP1 = getFirstPlayerId(id, peerId) === id; - game = isLocalP1 ? new PeerGame(id, peerId, id, conn) : new PeerGame(peerId, id, id, conn); - game.resetUi(); - game.setActiveUi(); - main.classList.remove('invisible'); - - main.addEventListener('mousemove', (ev: MouseEvent) => { - const peerData: PeerData = { mousePos: { x: ev.x, y: ev.y } }; - conn.send(JSON.stringify(peerData)); - }); - - conn.on('data', (data: string) => { - const peerData = JSON.parse(data) as PeerData; - const { mousePos, move } = peerData; - - if (mousePos) { - const style = `top:${mousePos.y}px;left:${mousePos.x - otherMouseElement.clientWidth / 2}px;`; - otherMouseElement.setAttribute('style', style); + public override setActiveUi(): void { + const { currentPlayerId, selfId } = this; + + const isLocalTurn = currentPlayerId === selfId; + if (isLocalTurn) { + super.setActiveUi(); } + } + public override makeMove(source: Coord, dest: Coord): Promise<MoveResult> { + const result = super.makeMove(source, dest); + const peerData: PeerData = { move: { source, dest, playerId: this.currentPlayerId } }; + this.conn.send(JSON.stringify(peerData)); + return result; + } - const isWaitingForRemoteMove = peerId === game.currentPlayerId; - if (isWaitingForRemoteMove && move) { - const isMoveLegal = game.getPiece(move.source) === game.currentPlayerId && game.getPiece(move.dest) === null; - if (isMoveLegal) { - game.makeMove(move.source, move.dest); - } else { - console.log({ isMoveLegal, move }); - } + public static async createPeerGame(): Promise<PeerGame> { + let { id, peerId } = getData(); + const main = document.getElementsByTagName('main')[0]; + let game: PeerGame = undefined; + + const handleConn = (conn: Peer.DataConnection) => { + if (game !== undefined) { + conn.close(); + return; + } + + id = peer.id; + peerId = conn.peer; + setData({ id, peerId }); + + const isLocalP1 = getFirstPlayerId(id, peerId) === id; + game = isLocalP1 ? new PeerGame(id, peerId, id, conn) : new PeerGame(peerId, id, id, conn); + game.resetUi(); + game.setActiveUi(); + main.classList.remove('invisible'); + + main.addEventListener('mousemove', (ev: MouseEvent) => { + const peerData: PeerData = { mousePos: { x: ev.x, y: ev.y } }; + conn.send(JSON.stringify(peerData)); + }); + + conn.on('data', (data: string) => { + const peerData = JSON.parse(data) as PeerData; + const { mousePos, move } = peerData; + + if (mousePos) { + const style = `top:${mousePos.y}px;left:${mousePos.x - otherMouseElement.clientWidth / 2}px;`; + otherMouseElement.setAttribute('style', style); + } + + const isWaitingForRemoteMove = peerId === game.currentPlayerId; + if (isWaitingForRemoteMove && move) { + const isMoveLegal = game.getPiece(move.source) === game.currentPlayerId && game.getPiece(move.dest) === null; + if (isMoveLegal) { + game.makeMove(move.source, move.dest); + } else { + console.log({ isMoveLegal, move }); + } + } + }); + }; + + const { host, protocol, pathname, search } = location; + const queryParams = new URLSearchParams(search); + + const peer = id === undefined ? new Peer(peerOptions) : new Peer(id, peerOptions); + window.onunload = () => peer.destroy(); + peer.on('connection', (conn) => handleConn(conn)); + peer.on('error', (err) => console.error({ err })); + peer.on('open', (trueId) => { + id = trueId; + peerId = queryParams.get('peerId') ?? peerId; + setData({ id, peerId }); + + if (peerId) { + console.log(`${id} connecting to peer "${peerId}"`); + const conn = peer.connect(peerId); + handleConn(conn); + } else { + queryParams.set('peerId', id); + const newSearch = queryParams.toString(); + const newUrl = `${protocol}//${host}${pathname}?${newSearch}`; + const link = document.createElement('a'); + link.innerText = newUrl; + // link.href = newUrl; + // link.target = '_blank'; + link.onclick = () => { + navigator.clipboard.writeText(newUrl); + showSnackbar('Url copied to clipboard'); + }; + document.getElementById('shareLink').appendChild(link); + // openInNewTab(newUrl); + } + }); + + while (game === undefined) { + console.log('Waiting to connect to peer'); + await delay(500); } - }); - }; - - const { host, protocol, pathname, search } = location; - const queryParams = new URLSearchParams(search); - - const peer = id === undefined ? new Peer(peerOptions) : new Peer(id, peerOptions); - window.onunload = () => peer.destroy(); - peer.on('connection', (conn) => handleConn(conn)); - peer.on('error', (err) => console.error({ err })); - peer.on('open', (trueId) => { - id = trueId; - peerId = queryParams.get('peerId') ?? peerId; - setData({ id, peerId }); - - if (peerId) { - console.log(`${id} connecting to peer "${peerId}"`); - const conn = peer.connect(peerId); - handleConn(conn); - } else { - queryParams.set('peerId', id); - const newSearch = queryParams.toString(); - const newUrl = `microsoft-edge:${protocol}//${host}${pathname}?${newSearch}`; - openInNewTab(newUrl); - } - }); - - while (game === undefined) { - console.log('Waiting to connect to peer'); - await delay(500); + return game; } - return game; - } } export const playGame = async () => { - document.getElementsByTagName('body')[0].style.setProperty('display', ''); - const game = await PeerGame.createPeerGame(); + // document.getElementsByTagName('body')[0].style.setProperty('display', ''); + const game = await PeerGame.createPeerGame(); }; diff --git a/src/utils.ts b/src/utils.ts index 2ad21ab..b97d40e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -18,3 +18,15 @@ export async function delay(delayInMs: number) { }, delayInMs); }); } + +export function showSnackbar(text) { + // Get the snackbar DIV + var x = document.getElementById("snackbar"); + + // Add the "show" class to DIV + x.className = "show"; + x.innerText = text; + + // After 3 seconds, remove the show class from DIV + setTimeout(function () { x.className = x.className.replace("show", ""); }, 3000); +} \ No newline at end of file diff --git a/tools/esbuild-build-and-serve.ts b/tools/esbuild-build-and-serve.ts index 4632dc5..a936dfc 100644 --- a/tools/esbuild-build-and-serve.ts +++ b/tools/esbuild-build-and-serve.ts @@ -8,9 +8,9 @@ const serveOptions: esbuild.ServeOptions = { servedir: 'public', }; const buildOptions: esbuild.BuildOptions = { - entryPoints: ['src/app.ts'], + entryPoints: ['src/local.ts', 'src/online.ts'], bundle: true, - outfile: 'public/bundle.js', + outdir: 'public', sourcemap: true, }; esbuild.serve(serveOptions, buildOptions).then((result) => { diff --git a/tools/esbuild-watch.ts b/tools/esbuild-watch.ts index 6843d69..40d47f1 100644 --- a/tools/esbuild-watch.ts +++ b/tools/esbuild-watch.ts @@ -1,8 +1,8 @@ import esbuild from 'esbuild'; esbuild.build({ - entryPoints: ['src/app.ts'], + entryPoints: ['src/local.ts', 'src/online.ts'], bundle: true, - outfile: 'public/bundle.js', + outdir: 'public', watch: true, }).catch(() => process.exit(1)) \ No newline at end of file diff --git a/tools/esbuild.ts b/tools/esbuild.ts index dc7e306..7a0a8fc 100644 --- a/tools/esbuild.ts +++ b/tools/esbuild.ts @@ -1,7 +1,7 @@ import esbuild from 'esbuild'; esbuild.build({ - entryPoints: ['src/app.ts'], + entryPoints: ['src/local.ts', 'src/online.ts'], bundle: true, - outfile: 'public/bundle.js', + outdir: 'public' }).catch(() => process.exit(1)) \ No newline at end of file