Basic multiplayer stuff

This commit is contained in:
thepaperpilot 2022-09-05 12:35:37 -05:00
parent e754b01dca
commit 5d2b8ecef9
10 changed files with 876 additions and 185 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "alkatest-common"]
path = src/alkatest-common
url = https://github.com/thepaperpilot/alkatest-common

371
package-lock.json generated
View file

@ -19,6 +19,9 @@
"is-plain-object": "^5.0.0",
"lz-string": "^1.4.4",
"nanoevents": "^6.0.2",
"semver": "^7.3.7",
"socket.io-client": "^4.5.2",
"unique-names-generator": "^4.7.1",
"vite": "^2.9.12",
"vite-plugin-pwa": "^0.12.0",
"vite-tsconfig-paths": "^3.5.0",
@ -34,6 +37,7 @@
"@ivanv/vue-collapse-transition": "^1.0.2",
"@rushstack/eslint-patch": "^1.1.0",
"@types/lz-string": "^1.3.34",
"@types/semver": "^7.3.12",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"eslint": "^8.6.0",
@ -107,6 +111,14 @@
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/generator": {
"version": "7.18.12",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz",
@ -173,6 +185,14 @@
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz",
@ -224,6 +244,14 @@
"@babel/core": "^7.4.0-0"
}
},
"node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
@ -1525,6 +1553,14 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/preset-modules": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz",
@ -2058,6 +2094,11 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"node_modules/@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@ -2134,6 +2175,12 @@
"@types/node": "*"
}
},
"node_modules/@types/semver": {
"version": "7.3.12",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz",
"integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==",
"dev": true
},
"node_modules/@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
@ -2172,21 +2219,6 @@
}
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.33.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.33.1.tgz",
@ -2297,21 +2329,6 @@
}
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.33.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.33.1.tgz",
@ -2754,6 +2771,14 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/babel-plugin-polyfill-corejs3": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz",
@ -3240,6 +3265,46 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
"integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw=="
},
"node_modules/engine.io-client": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/entities": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz",
@ -3847,22 +3912,6 @@
"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/eslint-plugin-vue/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"peer": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -5261,7 +5310,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -5967,11 +6015,17 @@
}
},
"node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/serialize-javascript": {
@ -6024,6 +6078,32 @@
"node": ">=8"
}
},
"node_modules/socket.io-client": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.2.tgz",
"integrity": "sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.2.1",
"socket.io-parser": "~4.2.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
@ -6428,6 +6508,14 @@
"node": ">=4"
}
},
"node_modules/unique-names-generator": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz",
"integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==",
"engines": {
"node": ">=8"
}
},
"node_modules/unique-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
@ -6698,21 +6786,6 @@
"node": ">=4.0"
}
},
"node_modules/vue-eslint-parser/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vue-next-select": {
"version": "2.10.4",
"resolved": "https://registry.npmjs.org/vue-next-select/-/vue-next-select-2.10.4.tgz",
@ -7211,11 +7284,18 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yocto-queue": {
"version": "0.1.0",
@ -7273,6 +7353,13 @@
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.1",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"@babel/generator": {
@ -7323,6 +7410,13 @@
"@babel/helper-validator-option": "^7.18.6",
"browserslist": "^4.20.2",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"@babel/helper-create-class-features-plugin": {
@ -7359,6 +7453,13 @@
"lodash.debounce": "^4.0.8",
"resolve": "^1.14.2",
"semver": "^6.1.2"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"@babel/helper-environment-visitor": {
@ -8213,6 +8314,13 @@
"babel-plugin-polyfill-regenerator": "^0.4.0",
"core-js-compat": "^3.22.1",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"@babel/preset-modules": {
@ -8609,6 +8717,11 @@
"integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==",
"dev": true
},
"@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"@surma/rollup-plugin-off-main-thread": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
@ -8682,6 +8795,12 @@
"@types/node": "*"
}
},
"@types/semver": {
"version": "7.3.12",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz",
"integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==",
"dev": true
},
"@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
@ -8702,17 +8821,6 @@
"regexpp": "^3.2.0",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"dependencies": {
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"@typescript-eslint/parser": {
@ -8767,17 +8875,6 @@
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"dependencies": {
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"@typescript-eslint/utils": {
@ -9140,6 +9237,13 @@
"@babel/compat-data": "^7.17.7",
"@babel/helper-define-polyfill-provider": "^0.3.2",
"semver": "^6.1.1"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"babel-plugin-polyfill-corejs3": {
@ -9499,6 +9603,31 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz",
"integrity": "sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw=="
},
"engine.io-client": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.2.tgz",
"integrity": "sha512-8ZQmx0LQGRTYkHuogVZuGSpDqYZtCM/nv8zQ68VZ+JkOpazJ7ICdsSpaO6iXwvaU30oFg5QJOJWj8zWqhbKjkQ==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3",
"xmlhttprequest-ssl": "~2.0.0"
},
"dependencies": {
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"requires": {}
}
}
},
"engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
},
"entities": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz",
@ -9927,18 +10056,6 @@
"postcss-selector-parser": "^6.0.9",
"semver": "^7.3.5",
"vue-eslint-parser": "^8.0.1"
},
"dependencies": {
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"peer": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"eslint-scope": {
@ -10866,7 +10983,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
@ -11357,9 +11473,12 @@
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"serialize-javascript": {
"version": "4.0.0",
@ -11399,6 +11518,26 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"socket.io-client": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.2.tgz",
"integrity": "sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.2.1",
"socket.io-parser": "~4.2.0"
}
},
"socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
}
},
"sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
@ -11685,6 +11824,11 @@
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz",
"integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ=="
},
"unique-names-generator": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz",
"integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow=="
},
"unique-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
@ -11847,15 +11991,6 @@
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
@ -12279,11 +12414,15 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
"xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yocto-queue": {
"version": "0.1.0",

View file

@ -23,6 +23,9 @@
"is-plain-object": "^5.0.0",
"lz-string": "^1.4.4",
"nanoevents": "^6.0.2",
"semver": "^7.3.7",
"socket.io-client": "^4.5.2",
"unique-names-generator": "^4.7.1",
"vite": "^2.9.12",
"vite-plugin-pwa": "^0.12.0",
"vite-tsconfig-paths": "^3.5.0",
@ -38,6 +41,7 @@
"@ivanv/vue-collapse-transition": "^1.0.2",
"@rushstack/eslint-patch": "^1.1.0",
"@types/lz-string": "^1.3.34",
"@types/semver": "^7.3.12",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"eslint": "^8.6.0",

View file

@ -41,6 +41,11 @@
<span class="material-icons">library_books</span>
</Tooltip>
</div>
<div @click="roomsDialog?.open()">
<Tooltip display="Multiplayer" :direction="Direction.Down" xoffset="-20px">
<span class="material-icons">group</span>
</Tooltip>
</div>
<div @click="options?.open()">
<Tooltip display="Options" :direction="Direction.Down" xoffset="-66px">
<span class="material-icons">settings</span>
@ -58,6 +63,11 @@
<span class="material-icons">library_books</span>
</Tooltip>
</div>
<div @click="roomsDialog?.open()">
<Tooltip display="Multiplayer" :direction="Direction.Right">
<span class="material-icons">group</span>
</Tooltip>
</div>
<div @click="options?.open()">
<Tooltip display="Options" :direction="Direction.Right">
<span class="material-icons">settings</span>
@ -96,12 +106,15 @@
<SavesManager ref="savesManager" />
<Options ref="options" />
<Changelog ref="changelog" />
<RoomsDialog ref="roomsDialog" />
</template>
<script setup lang="ts">
import Changelog from "data/Changelog.vue";
import projInfo from "data/projInfo.json";
import RoomsDialog from "data/RoomsDialog.vue";
import Tooltip from "features/tooltips/Tooltip.vue";
import { globalBus } from "game/events";
import { Direction } from "util/common";
import type { ComponentPublicInstance } from "vue";
import { ref } from "vue";
@ -115,6 +128,9 @@ const options = ref<ComponentPublicInstance<typeof Options> | null>(null);
// For some reason Info won't accept the changelog unless I do this:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const changelog = ref<ComponentPublicInstance<any> | null>(null);
const roomsDialog = ref<ComponentPublicInstance<typeof RoomsDialog> | null>(null);
globalBus.on("openMultiplayer", () => roomsDialog.value?.open());
const { useHeader, banner, title, discordName, discordLink, versionNumber } = projInfo;
@ -205,8 +221,8 @@ function openDiscord() {
position: fixed;
top: 45px;
padding: 20px;
right: -280px;
width: 200px;
right: -340px;
width: 300px;
transition: right 0.25s ease;
background: var(--raised-background);
z-index: 10;

154
src/data/Room.vue Normal file
View file

@ -0,0 +1,154 @@
<template>
<div class="room">
<div class="actions" v-if="enteringPassword">
<button @pointerdown="submitPassword" class="button">
<Tooltip display="Join" :direction="Direction.Left" class="info">
<span class="material-icons">check</span>
</Tooltip>
</button>
<button @pointerdown="enteringPassword = !enteringPassword" class="button">
<Tooltip display="Cancel" :direction="Direction.Left" class="info">
<span class="material-icons">close</span>
</Tooltip>
</button>
</div>
<div class="details" v-if="!enteringPassword">
<span class="material-icons" v-if="room.hasPassword">lock</span>
<span class="material-icons" v-if="isPrivate">visibility_off</span>
<button class="button open" @click="startConnecting">
<h3>{{ room.name }}</h3>
</button>
<span class="room-host">Hosted by {{ room.host }}</span
><br />
<div>{{ room.numContentPacks }} active content packs</div>
</div>
<div v-else class="details" style="display: flex">
<span>Password:</span>
<Text
v-model="password"
class="editname"
@submit="submitPassword"
@cancel="enteringPassword = !enteringPassword"
:submitOnBlur="false"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, toRefs, watch } from "vue";
import Text from "components/fields/Text.vue";
import { Direction } from "util/common";
import Tooltip from "features/tooltips/Tooltip.vue";
const _props = defineProps<{
isPrivate: boolean;
room: ClientRoomData;
}>();
const { room } = toRefs(_props);
const emit = defineEmits<{
(e: "connect", password?: string): void;
}>();
const enteringPassword = ref(false);
const password = ref("");
watch(enteringPassword, enteringPassword => {
if (enteringPassword) {
password.value = "";
}
});
function startConnecting() {
if (room.value.hasPassword) {
enteringPassword.value = true;
} else {
emit("connect");
}
}
function submitPassword() {
emit("connect", password.value);
enteringPassword.value = false;
}
</script>
<style scoped>
.room {
position: relative;
border: solid 4px var(--outline);
padding: 4px;
background: var(--raised-background);
margin: var(--feature-margin);
display: flex;
align-items: center;
min-height: 30px;
}
.room.active {
border-color: var(--bought);
}
.open {
display: inline;
margin: 0;
padding-left: 0;
}
.details {
margin: 0;
flex-grow: 1;
margin-right: 80px;
}
.details .material-icons {
font-size: 20px;
margin-right: 4px;
transform: translateY(2px);
}
.room-host {
margin-left: 4px;
font-size: 0.7em;
opacity: 0.7;
}
.actions {
position: absolute;
top: 0;
bottom: 0;
right: 4px;
display: flex;
padding: 4px;
z-index: 1;
}
.editname {
margin: 0;
}
</style>
<style>
.room button {
transition-duration: 0s;
}
.room .actions button {
display: flex;
font-size: 1.2em;
}
.room .actions button .material-icons {
font-size: unset;
}
.room .button.danger {
display: flex;
align-items: center;
padding: 4px;
}
.room .field {
margin: 0;
}
</style>

234
src/data/RoomsDialog.vue Normal file
View file

@ -0,0 +1,234 @@
<template>
<Modal v-model="isOpen" ref="modal">
<template v-slot:header>
<h2>Multiplayer</h2>
</template>
<template #body="{ shown }">
<template v-if="shown">
<template v-if="connected">
<template v-if="currentRoom">
<div style="text-align: center">
<h3>Connected to {{ currentRoom }}</h3>
</div>
<br />
<h4>Connected Players</h4>
<ul class="players-list">
<div v-for="(nickname, i) in nicknames" :key="i" style="display: flex">
<span>{{ nickname }}</span>
<span
v-if="nickname === settings.nickname"
style="font-size: small; color: grey; margin-left: 10px"
>(YOU)</span
>
<div style="flex-grow: 1"></div>
<button
v-if="isHosting && nickname !== settings.nickname"
class="button"
style="color: red; display: inline"
@click="emitToServer('kick user', nickname)"
>
KICK
</button>
</div>
</ul>
</template>
<template v-else>
<Room
v-for="(room, i) in rooms ?? []"
:key="i"
:room="room"
:isPrivate="false"
@connect="password => join(room.name, password)"
/>
<div v-if="rooms != null && rooms.length === 0" style="text-align: center">
No public rooms found
</div>
<div v-if="rooms == null" style="text-align: center">
Loading public rooms list...
</div>
<br />
<button
class="button"
style="float: right; display: inline-block"
@click="refresh()"
>
Refresh
</button>
</template>
</template>
<div v-else>
Not connected to a server. Please set up networking in the options modal.
<br />
</div>
</template>
</template>
<template v-slot:footer>
<div class="modal-footer">
<div v-if="connected && !currentRoom">
<br />
<hr />
<div style="margin-top: 10px; margin-bottom: -10px">Direct Connect</div>
<div class="direct-connect field">
<Text v-model="directRoomName" placeholder="Room Name" />
<Text v-model="directRoomPassword" placeholder="Room Password" />
<div class="field-buttons">
<button
class="button"
@click="join(directRoomName, directRoomPassword)"
>
Connect
</button>
</div>
</div>
<div style="margin-top: 10px; margin-bottom: -10px">Host current world</div>
<div class="direct-connect field">
<Text v-model="hostRoomName" placeholder="Room Name" />
<Text v-model="hostRoomPassword" placeholder="Room Password" />
<Toggle
v-model="hostPrivate"
title="Private"
style="width: 320px; margin-right: 10px"
/>
<div class="field-buttons">
<button
class="button"
@click="host(hostRoomName, hostRoomPassword, hostPrivate)"
>
Host
</button>
</div>
</div>
</div>
<div class="footer">
<div style="flex-grow: 1"></div>
<button
v-if="currentRoom"
class="button modal-default-button"
@click="emitToServer('leave room')"
>
Leave room
</button>
<button class="button modal-default-button" @click="isOpen = false">
Close
</button>
</div>
</div>
</template>
</Modal>
</template>
<script setup lang="ts">
import Text from "components/fields/Text.vue";
import Toggle from "components/fields/Toggle.vue";
import Modal from "components/Modal.vue";
import {
connected,
emit as emitToServer,
getGameState,
isHosting,
nicknames,
room as currentRoom
} from "data/socket";
import { globalBus } from "game/events";
import { PlayerData } from "game/player";
import settings from "game/settings";
import type { ComponentPublicInstance } from "vue";
import { ref, watch } from "vue";
import { main } from "./projEntry";
import Room from "./Room.vue";
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
const isOpen = ref(false);
const modal = ref<ComponentPublicInstance<typeof Modal> | null>(null);
defineExpose({
open() {
isOpen.value = true;
}
});
const rooms = ref<ClientRoomData[] | null>(null);
const directRoomName = ref("");
const directRoomPassword = ref("");
const hostRoomName = ref("");
const hostRoomPassword = ref("");
const hostPrivate = ref<boolean>(false);
watch(isOpen, isOpen => {
if (isOpen) {
refresh();
}
});
watch(currentRoom, room => {
if (!room) {
directRoomName.value = "";
directRoomPassword.value = "";
hostRoomName.value = "";
hostRoomPassword.value = "";
refresh();
}
});
globalBus.on("setRooms", r => (rooms.value = r));
globalBus.on("serverSentInfo", () => {
if (isOpen.value) {
refresh();
}
});
function refresh() {
rooms.value = null;
emitToServer("get rooms");
}
function join(room: string, password?: string) {
emitToServer("connect to room", room, password, settings.nickname);
}
function host(room: string, password?: string, privateRoom?: boolean) {
emitToServer("create room", {
name: room,
password,
nickname: settings.nickname,
contentPacks: main.contentPacks.value,
privateRoom: privateRoom === true,
state: getGameState()
});
}
</script>
<style scoped>
.field form,
.field .field-title,
.field .field-buttons {
margin: 0;
}
.field-buttons {
display: flex;
}
.field-buttons .field {
margin: 0;
margin-left: 8px;
}
.modal-footer {
margin-top: -20px;
}
.footer {
display: flex;
margin-top: 20px;
}
.field-title {
white-space: nowrap;
}
.direct-connect :deep(input[type="text"]) {
margin-right: 10px;
}
</style>

View file

@ -1,75 +1,25 @@
import Spacer from "components/layout/Spacer.vue";
import { jsx } from "features/feature";
import { createResource, trackBest, trackOOMPS, trackTotal } from "features/resources/resource";
import type { GenericTree } from "features/trees/tree";
import { branchedResetPropagation, createTree } from "features/trees/tree";
import { globalBus } from "game/events";
import type { BaseLayer, GenericLayer } from "game/layers";
import { createLayer } from "game/layers";
import { persistent } from "game/persistence";
import type { PlayerData } from "game/player";
import player from "game/player";
import type { DecimalSource } from "util/bignum";
import Decimal, { format, formatTime } from "util/bignum";
import { render } from "util/vue";
import { computed, toRaw } from "vue";
import prestige from "./layers/prestige";
import { computed } from "vue";
/**
* @hidden
*/
export const main = createLayer("main", function (this: BaseLayer) {
const points = createResource<DecimalSource>(10);
const best = trackBest(points);
const total = trackTotal(points);
const pointGain = computed(() => {
// eslint-disable-next-line prefer-const
let gain = new Decimal(1);
return gain;
});
globalBus.on("update", diff => {
points.value = Decimal.add(points.value, Decimal.times(pointGain.value, diff));
});
const oomps = trackOOMPS(points, pointGain);
const tree = createTree(() => ({
nodes: [[prestige.treeNode]],
branches: [],
onReset() {
points.value = toRaw(this.resettingNode.value) === toRaw(prestige.treeNode) ? 0 : 10;
best.value = points.value;
total.value = points.value;
},
resetPropagation: branchedResetPropagation
})) as GenericTree;
const contentPacks = persistent<(ContentPack | string)[]>(["core"]);
return {
name: "Tree",
links: tree.links,
name: "Main",
minimizable: false,
contentPacks,
display: jsx(() => (
<>
{player.devSpeed === 0 ? <div>Game Paused</div> : null}
{player.devSpeed && player.devSpeed !== 1 ? (
<div>Dev Speed: {format(player.devSpeed)}x</div>
) : null}
{player.offlineTime ? (
<div>Offline Time: {formatTime(player.offlineTime)}</div>
) : null}
<div>
{Decimal.lt(points.value, "1e1000") ? <span>You have </span> : null}
<h2>{format(points.value)}</h2>
{Decimal.lt(points.value, "1e1e6") ? <span> points</span> : null}
</div>
{Decimal.gt(pointGain.value, 0) ? <div>({oomps.value})</div> : null}
<Spacer />
{render(tree)}
<div>placeholder</div>
</>
)),
points,
best,
total,
oomps,
tree
))
};
});
@ -80,7 +30,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
export const getInitialLayers = (
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
player: Partial<PlayerData>
): Array<GenericLayer> => [main, prestige];
): Array<GenericLayer> => [main];
/**
* A computed ref whose value is true whenever the game is over.

View file

@ -1,15 +1,15 @@
{
"title": "Profectus",
"description": "A project made in Profectus",
"id": "",
"author": "",
"title": "Alkatest",
"description": "A test",
"id": "alkatest",
"author": "thepaperpilot",
"discordName": "",
"discordLink": "",
"versionNumber": "0.0",
"versionNumber": "0.0.0",
"versionTitle": "Initial Commit",
"allowGoBack": true,
"allowGoBack": false,
"defaultShowSmall": false,
"defaultDecimalsShown": 2,
"useHeader": true,

190
src/data/socket.tsx Normal file
View file

@ -0,0 +1,190 @@
import Text from "components/fields/Text.vue";
import { jsx, setDefault } from "features/feature";
import { globalBus } from "game/events";
import settings, { registerSettingField } from "game/settings";
import { io, Socket } from "socket.io-client";
import { load } from "util/save";
import { ref, watch } from "vue";
import { uniqueNamesGenerator, adjectives, colors, animals } from "unique-names-generator";
import player from "game/player";
import { useToast } from "vue-toastification";
import { ProxyState } from "util/proxies";
import satisfies from "semver/functions/satisfies";
import projInfo from "data/projInfo.json";
export const connected = ref<boolean>(false);
export const room = ref<string | null>(null);
export const isHosting = ref<boolean>(false);
export const nicknames = ref<Record<string, string>>({});
const toast = useToast();
const socket = ref<Socket<ServerToClientEvents, ClientToServerEvents> | null>();
const connectionError = ref<string>("");
export function emit<T extends keyof ClientToServerEvents>(
event: T,
...args: Parameters<ClientToServerEvents[T]>
): void {
if (!connected.value) {
return;
}
socket.value?.emit(event, ...args);
}
export function getGameState(): GameState {
return player.layers.main[ProxyState] as unknown as GameState;
}
globalBus.on("loadSettings", settings => {
setDefault(settings, "server", window.location.origin);
setDefault(settings, "nickname", randomName());
watch(
() => settings.server,
server => {
if (socket.value) {
socket.value.close();
}
socket.value = io(server);
setupSocket(socket.value);
connected.value = false;
connectionError.value = "";
socket.value.connect();
},
{ immediate: true }
);
watch(
() => settings.nickname,
nickname => {
if (room.value) {
emit("set nickname", nickname);
}
}
);
registerSettingField(
jsx(() => (
<>
<Text
title="Server URL"
onUpdate:modelValue={value => (settings.server = value)}
modelValue={settings.server}
/>
<div style="font-style: italic; font-size: small; margin-top: -10px;">
{connected.value ? (
<span>Connected!</span>
) : connectionError.value ? (
<span style="color: red">{connectionError.value}</span>
) : (
<span>Connecting...</span>
)}
</div>
</>
))
);
registerSettingField(
jsx(() => (
<>
<Text
title={jsx(() => (
<span>
Nickname
<button
class="button"
style="position: absolute; right: 0px; top: 2px;"
onClick={() => (settings.nickname = randomName())}
>
<span class="material-icons">casino</span>
</button>
</span>
))}
onUpdate:modelValue={value => (settings.nickname = value)}
modelValue={settings.nickname}
/>
</>
))
);
});
function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>) {
socket.on("connect", () => {
connectionError.value = "";
connected.value = true;
});
socket.on("connect_error", error => {
connectionError.value = `${error.name}: ${error.message}`;
});
socket.on("disconnect", (reason, details) => {
connectionError.value =
details instanceof Error
? `${details.name}: ${details.message}`
: details?.description ?? reason;
connected.value = false;
});
socket.on("server version", semver => {
if (!satisfies(projInfo.versionNumber, semver)) {
toast.info("Server only accepts game versions in range: " + semver);
socket.disconnect();
}
});
socket.on("info", message => {
toast.info(message);
globalBus.emit("serverSentInfo");
});
socket.on("set rooms", rooms => {
globalBus.emit("setRooms", rooms);
});
socket.on("joined room", (r, hosting) => {
room.value = r;
isHosting.value = hosting;
});
socket.on("left room", () => {
room.value = null;
});
socket.on("set nicknames", n => {
nicknames.value = n;
});
}
function randomName(): string {
return uniqueNamesGenerator({
dictionaries: [adjectives, colors, animals],
length: 3,
separator: " ",
style: "capital"
});
}
watch(connected, connected => {
if (!connected && room.value) {
stopMultiplayer();
}
});
function stopMultiplayer() {
if (!isHosting.value) {
load();
}
room.value = null;
isHosting.value = false;
}
declare module "game/settings" {
interface Settings {
server: string;
nickname: string;
}
}
declare module "game/events" {
interface GlobalEvents {
openMultiplayer: VoidFunction;
setRooms: (rooms: ClientRoomData[]) => void;
serverSentInfo: VoidFunction;
}
}

View file

@ -1,4 +1,5 @@
import projInfo from "data/projInfo.json";
import { isHosting, room } from "data/socket";
import { globalBus } from "game/events";
import type { Player, PlayerData } from "game/player";
import player, { stringifySave } from "game/player";
@ -118,12 +119,12 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
}
setInterval(() => {
if (player.autosave) {
if (player.autosave && (!room.value || isHosting.value)) {
save();
}
}, 1000);
window.onbeforeunload = () => {
if (player.autosave) {
if (player.autosave && (!room.value || isHosting.value)) {
save();
}
};