From 5b80a579d55e40ceecd221bfd4230ba57e6ec480 Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sat, 2 Dec 2023 18:37:43 +0000 Subject: [PATCH 1/5] Switch back to protobuf.js; add and integrate CompactPlayerFrames support --- .dockerignore | 1 + .gitignore | 3 + .vscode/settings.json | 9 +- Dockerfile | 8 +- babel.config.js | 3 + buf.gen.yaml | 5 - jest.config.js | 6 +- package-lock.json | 794 +++- package.json | 7 +- proto/messages.proto | 35 +- src/compact_move.test.ts | 135 + src/compact_move.ts | 323 ++ src/conformance/lobby.test.ts | 341 +- src/gen/messages_pb.ts | 5486 ------------------------ src/pbreflect.ts | 29 + src/protohax/fixtures/generate.sh | 7 +- src/protohax/fixtures/protohax_pb.d.ts | 164 + src/protohax/fixtures/protohax_pb.js | 952 ++++ src/protohax/fixtures/protohax_pb.json | 115 + src/protohax/fixtures/protohax_pb.ts | 211 - src/protohax/protohax.test.ts | 133 +- src/protoutil.test.ts | 233 +- src/protoutil.ts | 182 +- src/state/lobby.ts | 19 +- src/state/room.ts | 29 +- src/state/user.ts | 11 +- src/types.ts | 71 +- src/util.ts | 69 +- src/ws_handlers.ts | 64 +- tsconfig.json | 6 +- 30 files changed, 2952 insertions(+), 6499 deletions(-) create mode 100644 .dockerignore create mode 100644 babel.config.js delete mode 100644 buf.gen.yaml create mode 100644 src/compact_move.test.ts create mode 100644 src/compact_move.ts delete mode 100644 src/gen/messages_pb.ts create mode 100644 src/pbreflect.ts create mode 100644 src/protohax/fixtures/protohax_pb.d.ts create mode 100644 src/protohax/fixtures/protohax_pb.js create mode 100644 src/protohax/fixtures/protohax_pb.json delete mode 100644 src/protohax/fixtures/protohax_pb.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b6fe4a8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +src/gen \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9fe7c1e..46272b9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ /coverage /.env /.vscode/launch.json +/src/gen +ndjson +playermove.ndjson \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c73d90b..4a458cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,12 @@ "prettier.semi": true, "prettier.printWidth": 120, "prettier.trailingComma": "all", - "prettier.tabWidth": 2 + "prettier.tabWidth": 2, + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/*.code-search": true, + "dist/**": true, + "coverage/**": true, + } } diff --git a/Dockerfile b/Dockerfile index a7dc4d3..5205d02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,11 +3,13 @@ FROM node:21-slim AS build WORKDIR /src COPY package.json package-lock.json ./ RUN npm install -COPY buf.gen.yaml . COPY proto/ ./proto/ -RUN npx buf generate proto -COPY tsconfig.json . +COPY tsconfig.json babel.config.js ./ COPY src/ ./src/ +RUN mkdir -p ./src/gen && \ + npx pbjs --es6 -w es6 -t static-module proto/messages.proto > ./src/gen/pbjs_pb.js && \ + npx pbts ./src/gen/pbjs_pb.js > ./src/gen/pbjs_pb.d.ts && \ + npx pbjs -t json proto/messages.proto > ./src/gen/pbjs_pb.json # run tests COPY jest.config.js . diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..18b6e17 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: ['@babel/plugin-transform-modules-commonjs'], +}; diff --git a/buf.gen.yaml b/buf.gen.yaml deleted file mode 100644 index 1c685cf..0000000 --- a/buf.gen.yaml +++ /dev/null @@ -1,5 +0,0 @@ -version: v1 -plugins: - - plugin: es - opt: target=ts - out: src/gen diff --git a/jest.config.js b/jest.config.js index 9ec76a9..2434c3e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,10 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - preset: 'ts-jest', + // preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/*.test.ts'], + transform: { + '\\.js$': 'babel-jest', + '\\.ts$': 'ts-jest', + }, }; diff --git a/package-lock.json b/package-lock.json index afa3976..64baf0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@bufbuild/protobuf": "^1.3.3", "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", @@ -17,8 +16,7 @@ "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0" }, "devDependencies": { - "@bufbuild/buf": "^1.27.0", - "@bufbuild/protoc-gen-es": "^1.4.2", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@types/debug": "^4.1.10", "@types/jest": "^29.5.7", "@types/jsonwebtoken": "^9.0.3", @@ -26,6 +24,8 @@ "jest": "^29.7.0", "nodemon": "^3.0.1", "prettier": "^3.0.3", + "protobufjs": "^7.2.5", + "protobufjs-cli": "^1.1.2", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "ts-node-dev": "^2.0.0", @@ -110,9 +110,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -269,9 +269,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -339,9 +339,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -614,6 +614,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -669,178 +686,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@bufbuild/buf": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.27.0.tgz", - "integrity": "sha512-vR/ke6gUNgGSC3z7WkHLcbO+ur+zvGTC4ohHsLo2dZqEWysWySjNpcU70SKdIN3G5M4fSS1ki6MkZPes3E+83w==", - "dev": true, - "hasInstallScript": true, - "bin": { - "buf": "bin/buf", - "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", - "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@bufbuild/buf-darwin-arm64": "1.27.0", - "@bufbuild/buf-darwin-x64": "1.27.0", - "@bufbuild/buf-linux-aarch64": "1.27.0", - "@bufbuild/buf-linux-x64": "1.27.0", - "@bufbuild/buf-win32-arm64": "1.27.0", - "@bufbuild/buf-win32-x64": "1.27.0" - } - }, - "node_modules/@bufbuild/buf-darwin-arm64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.27.0.tgz", - "integrity": "sha512-Bsdo9BkkIlIgBpQJ2jyIXl9ggSqDdSJ12euxgU1y4pbT5iD11mdiUA7eq5/ssxLJilUrUGj2Gk1h1KbYG/JfVA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/buf-darwin-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.27.0.tgz", - "integrity": "sha512-aJmSZvO6uNxHST8+kN5cukv7/ZLgDnvklp+r6uyokocg5sk1rgWQVBqiVtGmoDWwPbotpMhb3EuqtwN9hdNrOg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/buf-linux-aarch64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.27.0.tgz", - "integrity": "sha512-1NPHARYENNVWOK3bQxbnYsMLU09em4/kdyAnCwyGkNhr+pWUlWdCBu3X5tdrRW+mnhjeagIcomTMhgVjxIAS7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/buf-linux-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.27.0.tgz", - "integrity": "sha512-3LMTSJlwJAeOfjPuB0NBK+1Yfg1Bybadt+c1X/vF8XSXut1u0Ju1/fbRDz75BF4AlMidMQPdGS+vPWmPcb51hA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/buf-win32-arm64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.27.0.tgz", - "integrity": "sha512-Za5d3obNvSfLZAlQW8IAWtv1Yv0gQTFDVMPyYiOh70rKIfKIxrWZxT4E4nzFLZZ54VQDFoUl81bAjOYLOgaspQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/buf-win32-x64": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.27.0.tgz", - "integrity": "sha512-OzVK4Fz162Z6fFHAAZhHPW2GiCXjweCG/hwjOtFt2gza1t3ImYp0CwxJI6ePGY+th3Y9yu8rY0iHiI59ezMa4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@bufbuild/protobuf": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.4.2.tgz", - "integrity": "sha512-JyEH8Z+OD5Sc2opSg86qMHn1EM1Sa+zj/Tc0ovxdwk56ByVNONJSabuCUbLQp+eKN3rWNfrho0X+3SEqEPXIow==" - }, - "node_modules/@bufbuild/protoc-gen-es": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.4.2.tgz", - "integrity": "sha512-/It7M2s8H1zTDvUMJu6vhBmtnzeFL2VS6e78RYIY38602pNXDK/vbteKUo4KrG0O07lOPFu87hHZ0Y+w5Ib6iw==", - "dev": true, - "dependencies": { - "@bufbuild/protobuf": "^1.4.2", - "@bufbuild/protoplugin": "1.4.2" - }, - "bin": { - "protoc-gen-es": "bin/protoc-gen-es" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@bufbuild/protobuf": "1.4.2" - }, - "peerDependenciesMeta": { - "@bufbuild/protobuf": { - "optional": true - } - } - }, - "node_modules/@bufbuild/protoplugin": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.4.2.tgz", - "integrity": "sha512-5IwGC1ZRD2A+KydGXeaSOErwfILLqVtvMH/RkN+cOoHcQd4EYXFStcF7g7aR+yICRDEEjQVi5tQF/qPGBSr9vg==", - "dev": true, - "dependencies": { - "@bufbuild/protobuf": "1.4.2", - "@typescript/vfs": "^1.4.0", - "typescript": "4.5.2" - } - }, - "node_modules/@bufbuild/protoplugin/node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1592,6 +1437,18 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.6.tgz", + "integrity": "sha512-aA+awb5yoml8TQ3CzI5Ue7sM3VMRC4l1zJJW4fgZ8OCL1wshJZhNzaf0PL85DSnOUw6QuFgeHGD/eq/xwwAF2g==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1627,6 +1484,70 @@ "node": ">= 8" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.31.17", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.17.tgz", @@ -1776,6 +1697,28 @@ "@types/node": "*" } }, + "node_modules/@types/linkify-it": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "dev": true + }, "node_modules/@types/ms": { "version": "0.7.32", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", @@ -1830,15 +1773,6 @@ "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", "dev": true }, - "node_modules/@typescript/vfs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz", - "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1" - } - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1857,6 +1791,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -2079,6 +2022,12 @@ "node": ">=8" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2227,6 +2176,18 @@ } ] }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2461,6 +2422,12 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2550,6 +2517,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2614,6 +2590,66 @@ "node": ">=8" } }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2627,6 +2663,24 @@ "node": ">=4" } }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2697,6 +2751,12 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -3764,6 +3824,56 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -3834,6 +3944,15 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -3852,6 +3971,19 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -3867,6 +3999,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -3888,6 +4029,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -3935,6 +4082,12 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3976,6 +4129,56 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4186,6 +4389,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4365,6 +4585,15 @@ } } }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", @@ -4419,6 +4648,98 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs-cli": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.2.tgz", + "integrity": "sha512-8ivXWxT39gZN4mm4ArQyJrRgnIwZqffBWoLDsE21TmMcKI3XwJMV4lEF2WU02C4JAtgYYc2SfJIltelD8to35g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/protobufjs-cli/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -4497,6 +4818,15 @@ "node": ">=0.10.0" } }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4896,6 +5226,33 @@ "node": ">=0.8" } }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/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/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5148,6 +5505,18 @@ "node": ">= 8" } }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5182,12 +5551,36 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, "node_modules/undici-types": { "version": "5.25.3", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", @@ -5312,6 +5705,15 @@ "node": ">= 8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5348,6 +5750,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index ab92576..ebcf74b 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,13 @@ "description": "Noita together lobby and game server", "main": "index.js", "scripts": { + "genproto": "mkdir -p ./src/gen && pbjs --es6 -w es6 -t static-module proto/messages.proto -o ./src/gen/pbjs_pb.js && pbts -o ./src/gen/pbjs_pb.d.ts ./src/gen/pbjs_pb.js && pbjs -t json proto/messages.proto -o ./src/gen/pbjs_pb.json", "test": "tsc --noEmit && npx jest" }, "author": "Kris Reeves", "license": "ISC", "devDependencies": { - "@bufbuild/buf": "^1.27.0", - "@bufbuild/protoc-gen-es": "^1.4.2", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@types/debug": "^4.1.10", "@types/jest": "^29.5.7", "@types/jsonwebtoken": "^9.0.3", @@ -18,6 +18,8 @@ "jest": "^29.7.0", "nodemon": "^3.0.1", "prettier": "^3.0.3", + "protobufjs": "^7.2.5", + "protobufjs-cli": "^1.1.2", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "ts-node-dev": "^2.0.0", @@ -25,7 +27,6 @@ "typescript": "^5.2.2" }, "dependencies": { - "@bufbuild/protobuf": "^1.3.3", "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", diff --git a/proto/messages.proto b/proto/messages.proto index f40e445..3fa5e7b 100644 --- a/proto/messages.proto +++ b/proto/messages.proto @@ -13,8 +13,8 @@ message GameAction { // The c prefix stands for "client", which refers to this application // The s prefix stands for "server", which refers to the online Noita game server - PlayerMove player_move = 1; - PlayerPosition player_position = 2; + CompactPlayerFrames c_player_move = 1; + ServerPlayerMoves s_player_moves = 2; ClientPlayerUpdate c_player_update = 3; ServerPlayerUpdate s_player_update = 4; @@ -63,7 +63,7 @@ message GameAction { ClientPlayerSecretHourglass c_player_secret_hourglass = 33; ServerPlayerSecretHourglass s_player_secret_hourglass = 34; - + ClientCustomModEvent c_custom_mod_event = 35; ServerCustomModEvent s_custom_mod_event = 36; @@ -86,18 +86,33 @@ message PlayerFrame { optional int32 anim = 6; optional int32 held = 7; } - -message PlayerMove { - repeated PlayerFrame frames = 1; - - optional string user_id = 15; +message OldClientPlayerMove { + repeated PlayerFrame frames = 1; } -message PlayerPosition { - PlayerFrame frame = 1; +message OldServerPlayerMove { + string user_id = 1; + repeated PlayerFrame frames = 2; +} + +message CompactPlayerFrames { + float x_init = 1; + float y_init = 2; + repeated sint32 x_deltas = 3; + repeated sint32 y_deltas = 4; + repeated int32 arm_r = 5; + int32 armScaleY = 6; + int32 scaleX = 7; + repeated int32 anim_idx = 8; + repeated int32 anim_val = 9; + repeated int32 held_idx = 10; + repeated int32 held_val = 11; string user_id = 15; } +message ServerPlayerMoves { + repeated CompactPlayerFrames user_frames = 1; +} message ClientPlayerUpdate { optional float cur_hp = 1; diff --git a/src/compact_move.test.ts b/src/compact_move.test.ts new file mode 100644 index 0000000..4e0ff50 --- /dev/null +++ b/src/compact_move.test.ts @@ -0,0 +1,135 @@ +import { + createArmrCoder, + createDeltaCoder, + decodeBitfield, + decodeStable, + encodeBitfield, + encodeStable, +} from './compact_move'; +import { NT } from './gen/pbjs_pb'; + +describe('compact move encoding', () => { + describe('armR encoding', () => { + const tests: { targetBytes: number; maxError: number; value: number }[] = [ + { targetBytes: 1, maxError: (Math.PI * 2 + 1) / 2 ** 7 }, + { targetBytes: 2, maxError: (Math.PI * 2 + 1) / 2 ** 14 }, + { targetBytes: 3, maxError: (Math.PI * 2 + 1) / 2 ** 21 }, + ].flatMap((obj) => + [ + -Math.PI, + -1, + 0, + 1, + Math.PI, + // fake fuzz testing. should complain enough if something is amiss + Math.random() * (Math.PI * 2) - Math.PI, + Math.random() * (Math.PI * 2) - Math.PI, + Math.random() * (Math.PI * 2) - Math.PI, + Math.random() * (Math.PI * 2) - Math.PI, + ].map((value) => ({ ...obj, value })), + ); + + it.each(tests)('bytes<=$targetBytes maxError<=$maxError value=$value', ({ targetBytes, maxError, value }) => { + const { encodeArmR, decodeArmR } = createArmrCoder(targetBytes); + const encoded = encodeArmR(value); + const roundtrip = decodeArmR(encoded); + expect(Math.abs(roundtrip - value)).toBeLessThanOrEqual(maxError); + const serialized = NT.CompactPlayerFrames.encode({ armR: [encoded] }).finish(); + const serializedSize = serialized.length - 2; // [tag][len][varint] + expect(serializedSize).toBeLessThanOrEqual(targetBytes); + }); + }); + describe('delta encoding', () => { + const tests: { fractionalDigits: number; maxError: number; value: number }[] = [ + { fractionalDigits: 1, maxError: 0.1 / 2 }, + { fractionalDigits: 2, maxError: 0.01 / 2 }, + { fractionalDigits: 3, maxError: 0.001 / 2 }, + ].flatMap((obj) => + [ + 5, + 5.49, + 5.51, + 1005, + 1005.49, + 1005.51, + // fake fuzz testing. should complain enough if something is amiss + 1 + Math.random(), + 1 + Math.random(), + 1 + Math.random(), + 1 + Math.random(), + ].map((value) => ({ ...obj, value })), + ); + + it.each(tests)( + 'fractionalDigits=$fractionalDigits maxError<=$maxError value=$value', + ({ fractionalDigits, maxError, value }) => { + const { encodeDelta, decodeDelta } = createDeltaCoder(fractionalDigits); + + const seq = [1, value]; + let i = 0; + + const { init, deltas } = encodeDelta(seq.length, () => seq[i++])!; + + const res: number[] = new Array(2); + decodeDelta(init, deltas, (i, v) => { + res[i] = v; + }); + + expect(res[0]).toEqual(seq[0]); + expect(Math.abs(seq[1] - res[1])).toBeLessThanOrEqual(maxError); + }, + ); + it('avoids cumulative error', () => { + const vs = new Array(30).fill(1).map((v, idx) => idx * 1.05); + const { encodeDelta, decodeDelta } = createDeltaCoder(1); + const { init, deltas } = encodeDelta(30, (i) => vs[i]); + const res: number[] = new Array(30); + decodeDelta(init, deltas, (i, v) => { + res[i] = v; + }); + expect(Math.abs(res[29] - vs[29])).toBeLessThanOrEqual(0.051); + }); + }); + describe('bitfield encoding', () => { + const tests: { name: string; vs: number[] }[] = [ + { name: 'empty', vs: [] }, + { name: 'asymmetrical', vs: [-1, 1, 1, -1, -1] }, + { name: 'max length', vs: new Array(32).fill(1) }, + ]; + it.each(tests)('$name', ({ vs }) => { + const encoded = encodeBitfield(vs.length, (i) => vs[i]); + const decoded = new Array(vs.length); + decodeBitfield(vs.length, encoded, (i, v) => { + decoded[i] = v; + }); + expect(decoded).toStrictEqual(vs); + }); + it('throws on invalid values', () => { + expect(() => encodeBitfield(1, () => 2)).toThrow('Invalid'); + }); + it('throws on too-long arrays', () => { + expect(() => encodeBitfield(33, () => 1)).toThrow('Cannot encode'); + }); + }); + describe('stable value encoding', () => { + const tests: { name: string; vs: number[] }[] = [ + { name: 'empty', vs: [] }, + { name: '0->1 midway', vs: [0, 0, 1, 1] }, + { name: '0->1->0', vs: [0, 1, 0] }, + { name: 'all 1', vs: [1, 1, 1] }, + ]; + it.each(tests)('$name', ({ vs }) => { + const { idxs, vals } = encodeStable(vs.length, (i) => vs[i]); + const decoded = new Array(vs.length); + decodeStable(vs.length, idxs, vals, (i, v) => { + decoded[i] = v; + }); + expect(decoded).toStrictEqual(vs); + }); + it('throws on mismatched inputs', () => { + expect(() => { + decodeStable(1, [], [1], () => {}); + }).toThrow('Invalid'); + }); + }); +}); diff --git a/src/compact_move.ts b/src/compact_move.ts new file mode 100644 index 0000000..a3eec3a --- /dev/null +++ b/src/compact_move.ts @@ -0,0 +1,323 @@ +import PATH from 'node:path'; +import { once } from 'node:events'; +import { createReadStream } from 'node:fs'; +import { createInterface } from 'node:readline'; +import { NT } from './gen/pbjs_pb'; + +const infile = PATH.resolve(__dirname, '../ndjson'); + +type Stat = { min: number; max: number; zero: number; nan: number }; +type StatKey = 'x' | 'y' | 'armR' | 'armScaleY' | 'scaleX' | 'anim' | 'held'; +const stats: { [K in StatKey]: Stat } & { messages: number } = { + messages: 0, + x: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + y: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + armR: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + armScaleY: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + scaleX: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + anim: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, + held: { min: Infinity, max: -Infinity, zero: 0, nan: 0 }, +}; + +const addStat = (key: StatKey, v: number) => { + if (isNaN(v)) { + stats[key].nan++; + return true; + } + stats[key].min = Math.min(stats[key].min, v); + stats[key].max = Math.max(stats[key].max, v); + if (v === 0) stats[key].zero++; + return false; +}; +const addStats = (obj: any) => { + let s = false; + s ||= addStat('x', obj.x); + s ||= addStat('y', obj.y); + s ||= addStat('armR', obj.armR); + s ||= addStat('armScaleY', obj.armScaleY); + s ||= addStat('scaleX', obj.scaleX); + s ||= addStat('anim', obj.anim); + s ||= addStat('held', obj.held); + if (s) { + console.log(obj); + process.exit(1); + } +}; + +let lastStats = Date.now() + 5000; + +const DELTA_FACTOR = 2 ** 6; +const ROT_FACTOR = 2 ** 7; + +/** + * Create an encoder-decoder pair for lossy-encoding radian + * values (`armR`) to integers that can be compactly encoded + * as varints. + * @param targetBytes The size, in bytes, of encoded values + * when serialized as a varint + */ +export const createArmrCoder = (targetBytes: number) => { + const factor = 2 ** (7 * targetBytes); + const pi2 = Math.PI * 2 + 1; + + return { + /** + * Lossily encode `v`, a value in radians between -PI and PI, + * as an unsigned integer to fit within `targetBytes` of + * serialized protobuf output. + * @see {createArmrCoder} + */ + encodeArmR: (v: number) => (((v + Math.PI) * factor) / pi2) | 0, + /** + * Decode a lossily-encoded value `v` to a value in radians + * between -PI and PI. + * @see {createArmrCoder} + */ + decodeArmR: (v: number) => (v * pi2) / factor - Math.PI, + }; +}; + +export const createDeltaCoder = (fractionalDigits: number) => { + const factor = 10 ** fractionalDigits; + return { + encodeDelta: (len: number, get: (i: number) => number): { init: number; deltas: number[] } => { + if (len === 0) return { init: 0, deltas: [] }; + + const init = get(0); + const deltas: number[] = []; + + if (typeof init !== 'number') throw new Error('Invalid value'); + + let last = init; + for (let i = 1; i < len; i++) { + const val = get(i); + if (typeof val !== 'number') throw new Error('Invalid value'); + + const d = Math.round((val - last) * factor); + deltas.push(d); + last += d / factor; // ameliorate rounding errors + } + return { init, deltas }; + }, + decodeDelta: (init: number, deltas: number[], set: (i: number, v: number) => void): void => { + let cum = init; + set(0, cum); + for (let i = 0; i < deltas.length; i++) { + cum += deltas[i] / factor; + set(i + 1, cum); + } + }, + }; +}; + +export const encodeBitfield = (len: number, next: (i: number) => number): number => { + if (len > 32) throw new Error('Cannot encode more than 32 values in a bitfield'); + let res = 0; + for (let i = 0; i < len; i++) { + const val = next(i); + // values must be -1 or 1 + if (val !== -1 && val !== 1) throw new Error('Invalid value: ' + val); + res |= ((val + 1) >>> 1) << i; + // javascript bitwise operations operate on 32-bit signed integers + } + return res >>> 0; // convert to unsigned +}; +export const decodeBitfield = (len: number, val: number, set: (i: number, val: number) => void): void => { + if (len > 32) throw new Error('Cannot encode more than 32 values in a bitfield'); + for (let i = 0; i < len; i++) { + set(i, ((val & 1) << 1) - 1); + val >>>= 1; + } +}; + +export const encodeStable = (len: number, get: (i: number) => number): { idxs: number[]; vals: number[] } => { + let last = 0; + const idxs: number[] = []; + const vals: number[] = []; + for (let i = 0; i < len; i++) { + const val = get(i); + if (val === last) continue; + idxs.push(i); + vals.push(val); + last = val; + } + return { idxs, vals }; +}; +export const decodeStable = ( + len: number, + idxs: number[], + vals: number[], + set: (i: number, val: number) => void, +): void => { + if (idxs.length !== vals.length) throw new Error('Invalid data: arrays must be same length'); + let cur = 0; + for (let i = 0, pos = 0; i < len; i++) { + if (idxs[pos] === i) { + cur = vals[pos]; + pos++; + } + set(i, cur); + } +}; + +const { encodeArmR, decodeArmR } = createArmrCoder(1); +const { encodeDelta, decodeDelta } = createDeltaCoder(1); + +export const encodeFrames = (frames: NT.PlayerFrame[]): NT.CompactPlayerFrames => { + const numFrames = frames.length; + if (numFrames === 0) return new NT.CompactPlayerFrames(); + if (numFrames > 32) throw new Error('cannot compact more than 32 frames'); + + const { init: xInit, deltas: xDeltas } = encodeDelta(numFrames, (i) => frames[i]!.x!); + const { init: yInit, deltas: yDeltas } = encodeDelta(numFrames, (i) => frames[i]!.y!); + const armR: number[] = frames.map((f) => encodeArmR(f.armR!)); + const armScaleY = encodeBitfield(numFrames, (i) => frames[i]!.armScaleY!); + const scaleX = encodeBitfield(numFrames, (i) => frames[i]!.scaleX!); + const { idxs: animIdx, vals: animVal } = encodeStable(numFrames, (i) => frames[i]!.anim!); + const { idxs: heldIdx, vals: heldVal } = encodeStable(numFrames, (i) => frames[i]!.held!); + + return new NT.CompactPlayerFrames({ + xInit, + xDeltas, + yInit, + yDeltas, + armR, + armScaleY, + scaleX, + animIdx, + animVal, + heldIdx, + heldVal, + }); +}; + +const getBit = (v: number, i: number) => (((v >>> i) & 1) << 1) - 1; +export const decodeFrames = (pm: NT.CompactPlayerFrames): NT.PlayerFrame[] => { + const numFrames = pm.armR.length; + const frames: NT.PlayerFrame[] = new Array(numFrames); + + for (let i = 0; i < numFrames; i++) { + frames[i] = new NT.PlayerFrame({ armR: decodeArmR(pm.armR[i]) }); + } + decodeDelta(pm.xInit, pm.xDeltas, (i, v) => { + frames[i].x = v; + }); + decodeDelta(pm.yInit, pm.yDeltas, (i, v) => { + frames[i].y = v; + }); + decodeBitfield(numFrames, pm.armScaleY, (i, v) => { + frames[i].armScaleY = v; + }); + decodeBitfield(numFrames, pm.scaleX, (i, v) => { + frames[i].scaleX = v; + }); + decodeStable(numFrames, pm.animIdx, pm.animVal, (i, v) => (frames[i].anim = v)); + decodeStable(numFrames, pm.heldIdx, pm.heldVal, (i, v) => (frames[i].held = v)); + + return frames; +}; + +const diffFrame = ( + a: NT.PlayerFrame, + b: NT.PlayerFrame, + xyTolerance: number = 0.051, + armrTolerance: number = (Math.PI * 2 + 1) / 2 ** 7, +) => { + let samey = true; + samey &&= Math.abs(a.x! - b.x!) < xyTolerance; + samey &&= Math.abs(a.y! - b.y!) < xyTolerance; + samey &&= Math.abs(a.armR! - b.armR!) < armrTolerance; + samey &&= a.armScaleY === b.armScaleY; + samey &&= a.scaleX === b.scaleX; + samey &&= a.held === b.held; + samey &&= a.anim === b.anim; + if (samey) return; + return { + x: b.x! - a.x!, + y: b.y! - a.y!, + armR: b.armR! - a.armR!, + armScaleY: [a.armScaleY, b.armScaleY], + scaleX: [a.scaleX, b.scaleX], + held: [a.held, b.held], + anim: [a.anim, b.anim], + }; +}; +const diffFrames = (a: NT.PlayerFrame[], b: NT.PlayerFrame[]) => { + for (let i = 0; i < a.length; i++) { + const diff = diffFrame(a[i], b[i]); + if (diff) return { i, ...diff }; + } +}; + +const calcPrecision = ( + { x, y, armR }: { x: number; y: number; armR: number }, + a: NT.PlayerFrame[], + b: NT.PlayerFrame[], +) => { + for (let i = 0; i < a.length; i++) { + x = Math.max(x, Math.abs(a[i].x! - b[i].x!)); + y = Math.max(y, Math.abs(a[i].y! - b[i].y!)); + armR = Math.max(armR, Math.abs(b[i].armR! - a[i].armR!)); + } + return { x, y, armR }; +}; + +const bytesAt = (players: number, bytes: number) => bytes * (players + 1); + +let oldSize = 0; +let newSize = 0; +let lineCount = 0; +if (require.main === module) { + (async function processLineByLine() { + try { + const rl = createInterface({ + input: createReadStream(infile), + crlfDelay: Infinity, + }); + + let prec = { x: 0, y: 0, armR: 0 }; + + rl.on('line', (line) => { + const obj = JSON.parse(line); + if (!obj.frames || !obj.frames.length) return; + oldSize += NT.OldClientPlayerMove.encode(obj).finish().length; + const compact = encodeFrames(obj.frames); + newSize += NT.CompactPlayerFrames.encode(compact).finish().length; + const roundtrip = decodeFrames(compact); + const diff = diffFrames(obj.frames, roundtrip); + if (diff) { + console.log(lineCount); + console.log(diff); + process.exit(); + } + prec = calcPrecision(prec, obj.frames, roundtrip); + lineCount++; + // stats.messages++; + // try { + // obj.frames.forEach((frame: any) => addStats(frame)); + // } catch (e) {} + // if (Date.now() > lastStats) { + // console.log(stats); + // lastStats = Date.now() + 5000; + // } + }); + + await once(rl, 'close'); + + console.log('File processed.'); + const oldSizeAt90 = bytesAt(90, oldSize); + const newSizeAt90 = bytesAt(90, newSize); + console.log({ + oldSize: oldSize.toLocaleString() + 'b', + oldSizeAt90: oldSizeAt90.toLocaleString() + 'b', + newSize: newSize.toLocaleString() + 'b', + newSizeAt90: newSizeAt90.toLocaleString() + 'b', + pctAt90: ((100 * newSizeAt90) / oldSizeAt90).toFixed(2) + '%', + }); + console.log(prec); + // console.log(stats); + } catch (err) { + console.error(err); + } + })(); +} diff --git a/src/conformance/lobby.test.ts b/src/conformance/lobby.test.ts index 47dccb5..930991f 100644 --- a/src/conformance/lobby.test.ts +++ b/src/conformance/lobby.test.ts @@ -1,5 +1,4 @@ -import { PartialMessage } from '@bufbuild/protobuf'; -import { Envelope } from '../gen/messages_pb'; +import { NT } from '../gen/pbjs_pb'; import { createJwtFns } from '../jwt'; import { AuthProvider, ClientAuth } from '../runtypes/client_auth'; import { LobbyState, SYSTEM_USER } from '../state/lobby'; @@ -10,7 +9,7 @@ import { RecognizedString } from 'uWebSockets.js'; type sentMessage = { topic: string | null; user: ClientAuthWebSocket | null; - message: PartialMessage; + message: NT.Envelope; }; const createTestEnv = ( @@ -25,10 +24,31 @@ const createTestEnv = ( const closedSockets: { socket: ClientAuthWebSocket; code?: number; shortMessage?: RecognizedString }[] = []; const subscribed: Map> = new Map(); + const expectLobbyAction = (key: K) => { + const env = sentMessages.shift()?.message!; + expect(env).toBeDefined(); + const la = env.lobbyAction! as NT.LobbyAction; + expect(la).toBeDefined(); + expect(la.action).toEqual(key); + expect(la[key]).not.toBeUndefined(); + expect(la[key]).not.toBeNull(); + return la[key] as Exclude<(typeof la)[K], undefined | null>; + }; + const expectGameAction = (key: K) => { + const env = sentMessages.shift()?.message!; + expect(env).toBeDefined(); + const ga = env.gameAction! as NT.GameAction; + expect(ga).toBeDefined(); + expect(ga.action).toEqual(key); + expect(ga[key]).not.toBeUndefined(); + expect(ga[key]).not.toBeNull(); + return ga[key] as Exclude<(typeof ga)[K], undefined | null>; + }; + const publishers = BindPublishers( { - publish: (topic: string, message: Uint8Array | Envelope) => { - const decoded = message instanceof Uint8Array ? Envelope.fromBinary(message) : message; + publish: (topic: string, message: Uint8Array | NT.Envelope) => { + const decoded = message instanceof Uint8Array ? NT.Envelope.decode(message) : message; sentMessages.push({ topic, message: decoded, user: null }); }, } as any, @@ -61,7 +81,7 @@ const createTestEnv = ( const user: ClientAuthWebSocket & { toJSON: () => any; toString: () => string } = { send(msg: Uint8Array): number { - sentMessages.push({ topic: null, message: Envelope.fromBinary(msg), user }); + sentMessages.push({ topic: null, message: NT.Envelope.decode(msg), user }); return 1; }, getUserData(): TaggedClientAuth { @@ -74,7 +94,7 @@ const createTestEnv = ( closedSockets.push({ socket: user }); }, publish(topic: string, msg: Uint8Array) { - sentMessages.push({ topic, message: Envelope.fromBinary(msg), user }); + sentMessages.push({ topic, message: NT.Envelope.decode(msg), user }); return true; }, subscribe(topic: string) { @@ -105,6 +125,8 @@ const createTestEnv = ( subscribed, lobby, users, + expectGameAction, + expectLobbyAction, testSocket, handleUpgrade, handleOpen, @@ -158,32 +180,27 @@ describe('lobby conformance tests', () => { describe('non-mocked ids', () => { it('generates uuids like normal', () => { - const { testSocket, handleOpen, handleMessage, sentMessages } = createTestEnv(false); + const { testSocket, handleOpen, handleMessage, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(false); const user = testSocket('id', 'name'); handleOpen(user); // create a room - should have a uuid for its id - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); + handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); - const sRoomCreated = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreated?.case).toEqual('sRoomCreated'); - if (sRoomCreated?.case === 'sRoomCreated') { - expect(sRoomCreated.value.id).toMatch(uuidRE); - } + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); + + expectLobbyAction('sRoomAddToList'); - const sRoomAddToList = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomAddToList?.case).toEqual('sRoomAddToList'); expect(sentMessages).toEqual([]); // send a chat - should have a uuid for its id - handleMessage(user, M.cChat({ message: 'hi' }).toBinary(), true); + handleMessage(user, M.cChat({ message: 'hi' }, true), true); - const sChat = sentMessages.shift()?.message.kind?.value?.action; - expect(sChat?.case).toEqual('sChat'); - if (sChat?.case === 'sChat') { - expect(sChat.value.id).toMatch(uuidRE); - } + const sChat = expectGameAction('sChat'); + expect(sChat.id).toMatch(uuidRE); expect(sentMessages).toEqual([]); }); @@ -191,37 +208,32 @@ describe('lobby conformance tests', () => { describe('disconnect handling', () => { it('cleans up a room when all active users have disconnected', () => { - const { testSocket, handleOpen, handleMessage, handleClose, sentMessages } = createTestEnv(false); + const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(false); const user = testSocket('id', 'name'); handleOpen(user); // create a room - should have a uuid for its id - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); + handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); - const sRoomCreated = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreated?.case).toEqual('sRoomCreated'); - if (sRoomCreated?.case === 'sRoomCreated') { - expect(sRoomCreated.value.id).toMatch(uuidRE); - } + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); - const sRoomAddToList = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomAddToList?.case).toEqual('sRoomAddToList'); + expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); handleClose(user, 1006, Buffer.from('test')); - const sRoomDeleted = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomDeleted?.case).toEqual('sRoomDeleted'); - if (sRoomDeleted?.case === 'sRoomDeleted') { - expect(sRoomDeleted.value.id).toMatch(uuidRE); - } + const sRoomDeleted = expectLobbyAction('sRoomDeleted'); + expect(sRoomDeleted.id).toMatch(uuidRE); expect(sentMessages).toEqual([]); }); it('leaves a room active when at least one connected user is present', () => { - const { testSocket, handleOpen, handleMessage, handleClose, sentMessages } = createTestEnv(false); + const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(false); const user = testSocket('id', 'name'); const user2 = testSocket('id2', 'name2'); @@ -229,38 +241,40 @@ describe('lobby conformance tests', () => { handleOpen(user2); // create a room - should have a uuid for its id - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); - - const sRoomCreated = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreated?.case).toEqual('sRoomCreated'); - let roomId: string; - if (sRoomCreated?.case === 'sRoomCreated') { - expect(sRoomCreated.value.id).toMatch(uuidRE); - roomId = sRoomCreated.value.id!; - } else { - throw new Error('abort'); - } + handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); + + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); + const roomId = sRoomCreated.id; - const sRoomAddToList = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomAddToList?.case).toEqual('sRoomAddToList'); + expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); - handleMessage(user2, M.cJoinRoom({ id: roomId }).toBinary(), true); + handleMessage(user2, M.cJoinRoom({ id: roomId }, true), true); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sUserJoinedRoom'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sJoinRoomSuccess'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sRoomFlagsUpdated'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sUserReadyState'); + expectLobbyAction('sUserJoinedRoom'); + expectLobbyAction('sJoinRoomSuccess'); + expectLobbyAction('sRoomFlagsUpdated'); + expectLobbyAction('sUserReadyState'); handleClose(user, 1006, Buffer.from('test')); // rooms are deleted when the host disconnects - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sRoomDeleted'); + expectLobbyAction('sRoomDeleted'); expect(sentMessages).toEqual([]); }); it('allows disconnected users to rejoin locked rooms. owner retains ownership', () => { - const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, lobby } = createTestEnv(false); + const { + testSocket, + handleOpen, + handleMessage, + handleClose, + sentMessages, + lobby, + expectGameAction, + expectLobbyAction, + } = createTestEnv(false); const owner = testSocket('ownerid', 'owner'); const player = testSocket('playerid', 'player'); @@ -268,32 +282,25 @@ describe('lobby conformance tests', () => { handleOpen(player); // create a room - should have a uuid for its id - handleMessage(owner, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); - - const sRoomCreated = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreated?.case).toEqual('sRoomCreated'); - let roomId: string; - if (sRoomCreated?.case === 'sRoomCreated') { - expect(sRoomCreated.value.id).toMatch(uuidRE); - roomId = sRoomCreated.value.id!; - } else { - throw new Error('abort'); - } + handleMessage(owner, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); + + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); + const roomId = sRoomCreated.id!; - const sRoomAddToList = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomAddToList?.case).toEqual('sRoomAddToList'); + const sRoomAddToList = expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); - handleMessage(player, M.cJoinRoom({ id: roomId }).toBinary(), true); + handleMessage(player, M.cJoinRoom({ id: roomId }, true), true); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sUserJoinedRoom'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sJoinRoomSuccess'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sRoomFlagsUpdated'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sUserReadyState'); + expectLobbyAction('sUserJoinedRoom'); + expectLobbyAction('sJoinRoomSuccess'); + expectLobbyAction('sRoomFlagsUpdated'); + expectLobbyAction('sUserReadyState'); expect(sentMessages).toEqual([]); - handleMessage(owner, M.cRoomUpdate({ locked: true }).toBinary(), true); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sRoomUpdated'); + handleMessage(owner, M.cRoomUpdate({ locked: true }, true), true); + expectLobbyAction('sRoomUpdated'); expect(sentMessages).toEqual([]); handleClose(player, 1006, Buffer.from('test')); @@ -301,18 +308,19 @@ describe('lobby conformance tests', () => { // player can rejoin const player2 = testSocket('playerid', 'player'); handleOpen(player2); - handleMessage(player2, M.cJoinRoom({ id: roomId }).toBinary(), true); + handleMessage(player2, M.cJoinRoom({ id: roomId }, true), true); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sJoinRoomSuccess'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sRoomFlagsUpdated'); - expect(sentMessages.shift()?.message.kind?.value?.action?.case).toEqual('sUserReadyState'); + expectLobbyAction('sJoinRoomSuccess'); + expectLobbyAction('sRoomFlagsUpdated'); + expectLobbyAction('sUserReadyState'); expect(sentMessages).toEqual([]); }); }); describe('dev mode', () => { it('rejects room creation from non-devs', () => { - const { testSocket, handleOpen, handleMessage, handleClose, sentMessages } = createTestEnv(true); + const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(true); const user = testSocket('id', 'name'); const dev = testSocket('devid', 'myndzi'); @@ -320,22 +328,17 @@ describe('lobby conformance tests', () => { handleOpen(dev); // create a room - should have a uuid for its id - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); + handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); - const sRoomCreateFailed = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreateFailed?.case).toEqual('sRoomCreateFailed'); + expectLobbyAction('sRoomCreateFailed'); expect(sentMessages).toEqual([]); - handleMessage(dev, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: 'dev room' }).toBinary(), true); + handleMessage(dev, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: 'dev room' }, true), true); - const sRoomCreated = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomCreated?.case).toEqual('sRoomCreated'); - if (sRoomCreated?.case === 'sRoomCreated') { - expect(sRoomCreated.value.id).toMatch(uuidRE); - } + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); - const sRoomAddToList = sentMessages.shift()?.message.kind?.value?.action; - expect(sRoomAddToList?.case).toEqual('sRoomAddToList'); + expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); }); }); @@ -355,30 +358,30 @@ describe('lobby conformance tests', () => { handleOpen(host); handleOpen(player); handleOpen(player2); - handleMessage(host, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "host's room" }).toBinary(), true); - handleMessage(player, M.cJoinRoom({ id: 'room' }).toBinary(), true); - handleMessage(host, M.cStartRun({ forced: false }).toBinary(), true); + handleMessage(host, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "host's room" }, true), true); + handleMessage(player, M.cJoinRoom({ id: 'room' }, true), true); + handleMessage(host, M.cStartRun({ forced: false }, true), true); - handleMessage(host, M.cPlayerPickup({ kind: { case: 'heart', value: { hpPerk: true } } }).toBinary(), true); - handleMessage(host, M.cPlayerPickup({ kind: { case: 'heart', value: { hpPerk: true } } }).toBinary(), true); + handleMessage(host, M.cPlayerPickup({ heart: { hpPerk: true } }, true), true); + handleMessage(host, M.cPlayerPickup({ heart: { hpPerk: true } }, true), true); // support users joining mid-run - handleMessage(player2, M.cJoinRoom({ id: 'room' }).toBinary(), true); - handleMessage(host, M.cStartRun({ forced: false }).toBinary(), true); + handleMessage(player2, M.cJoinRoom({ id: 'room' }, true), true); + handleMessage(host, M.cStartRun({ forced: false }, true), true); - handleMessage(player, M.cPlayerPickup({ kind: { case: 'orb', value: { id: 3 } } }).toBinary(), true); - handleMessage(player, M.cPlayerPickup({ kind: { case: 'orb', value: { id: 7 } } }).toBinary(), true); - handleMessage(player, M.cAngerySteve().toBinary(), true); - handleMessage(player, M.cPlayerDeath({ isWin: false }).toBinary(), true); + handleMessage(player, M.cPlayerPickup({ orb: { id: 3 } }, true), true); + handleMessage(player, M.cPlayerPickup({ orb: { id: 7 } }, true), true); + handleMessage(player, M.cAngerySteve({}, true), true); + handleMessage(player, M.cPlayerDeath({ isWin: false }, true), true); // win deaths don't count - handleMessage(host, M.cPlayerDeath({ isWin: true }).toBinary(), true); - handleMessage(player, M.cPlayerDeath({ isWin: true }).toBinary(), true); + handleMessage(host, M.cPlayerDeath({ isWin: true }, true), true); + handleMessage(player, M.cPlayerDeath({ isWin: true }, true), true); - handleMessage(host, M.cRunOver({}).toBinary(), true); + handleMessage(host, M.cRunOver({}, true), true); // stats after game is over don't accrue - handleMessage(host, M.cPlayerPickup({ kind: { case: 'heart', value: { hpPerk: true } } }).toBinary(), true); + handleMessage(host, M.cPlayerPickup({ heart: { hpPerk: true } }, true), true); expect(JSON.parse(lobby.getStats('room', 'stats')!)).toEqual({ id: 'stats', @@ -406,22 +409,18 @@ describe('lobby conformance tests', () => { const user = testSocket('id', 'name'); handleOpen(user); - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }).toBinary(), true); - handleMessage(user, M.cStartRun({ forced: false }).toBinary(), true); - handleMessage(user, M.cRunOver({}).toBinary(), true); - handleMessage(user, M.cRoomDelete({ id: 'room' }).toBinary(), true); + handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); + handleMessage(user, M.cStartRun({ forced: false }, true), true); + handleMessage(user, M.cRunOver({}, true), true); + handleMessage(user, M.cRoomDelete({ id: 'room' }, true), true); expect(lobby.getStats('room', 'stats')).toBeUndefined(); }); }); describe('message handlers', () => { - type clientMessage = [user: ClientAuthWebSocket, message: Envelope]; - const sm = >( - topic: string | null, - user: ClientAuthWebSocket | null, - action: E, - ) => ({ + type clientMessage = [user: ClientAuthWebSocket, message: NT.Envelope]; + const sm = (topic: string | null, user: ClientAuthWebSocket | null, action: E) => ({ topic, message: action, user, @@ -2509,20 +2508,20 @@ describe('lobby conformance tests', () => { ///// game messages ///// - type gmTest = ['all' | 'others' | 'toHost' | 'byHost' | 'never', Envelope]; + type gmTest = ['all' | 'others' | 'toHost' | 'byHost' | 'never', NT.Envelope]; const gameMessages: gmTest[] = [ ['others', M.cPlayerUpdate({ curHp: 123 })], ['never', M.cPlayerUpdateInventory({ spells: [{ index: 1 }], wands: [], items: [] })], ['byHost', M.cHostItemBank({ gold: 1, items: [], objects: [], spells: [], wands: [] })], - ['byHost', M.cHostUserTake({ id: '1', success: true, userId: '123' })], - ['byHost', M.cHostUserTakeGold({ amount: 1, success: true, userId: '123' })], + ['byHost', M.cHostUserTake({ id: '123', success: true, userId: '1' })], + ['byHost', M.cHostUserTakeGold({ amount: 1, success: true, userId: '1' })], ['all', M.cPlayerAddGold({ amount: 1 })], ['toHost', M.cPlayerTakeGold({ amount: 1 })], - ['all', M.cPlayerAddItem({ item: { case: 'spells', value: { list: [] } } })], - ['toHost', M.cPlayerTakeItem({ id: '1' })], - ['others', M.cPlayerPickup({ kind: { case: 'heart', value: { hpPerk: true } } })], - ['others', M.cPlayerPickup({ kind: { case: 'orb', value: { id: 1 } } })], + ['all', M.cPlayerAddItem({ spells: { list: [] } })], + ['toHost', M.cPlayerTakeItem({ id: '123' })], + ['others', M.cPlayerPickup({ heart: { hpPerk: true } })], + ['others', M.cPlayerPickup({ orb: { id: 1 } })], ['others', M.cNemesisAbility({ gameId: '1' })], ['others', M.cNemesisPickupItem({ gameId: '1' })], ['others', M.cPlayerNewGamePlus({ amount: 1 })], @@ -2532,46 +2531,38 @@ describe('lobby conformance tests', () => { ['others', M.cRespawnPenalty({ deaths: 1 })], ['others', M.cPlayerDeath({ isWin: true })], ['others', M.cPlayerDeath({ isWin: false })], - ['others', M.playerMove({ frames: [{ x: 1, y: 2 }] })], - ['never', M.playerMove({ userId: 'disallowed from client', frames: [{ x: 1, y: 2 }] })], + ['others', M.cPlayerMove({ xInit: 1, yInit: 2 })], + ['never', M.cPlayerMove({ userId: 'disallowed from client', xInit: 1, yInit: 2 })], ]; - const transformC2S = (env: Envelope, userId: string) => { - const action = env.kind.value!.action; - if (action.case === 'playerMove') { - return new Envelope({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { - ...action.value, - userId, - }, - }, + const transformC2S = (env: NT.Envelope, userId: string) => { + if (env.gameAction?.cPlayerMove) { + const frames = env.gameAction!.cPlayerMove!; + return NT.Envelope.fromObject({ + gameAction: { + sPlayerMoves: { + userFrames: [{ ...frames, userId }], }, }, }); - } - return new Envelope({ - kind: { - case: env.kind.case!, - value: { - action: { - case: action.case!.replace(/^c/, 's'), - value: { - userId, - ...action.value!, - }, + } else { + const payload = env.gameAction! as NT.GameAction; + const cAction = payload.action!; + const sAction = cAction.replace(/^c/, 's'); + const res = NT.Envelope.fromObject({ + gameAction: { + [sAction]: { + ...payload.toJSON()[cAction], + userId, }, }, - }, - } as any); + }); + return res; + } }; for (const [type, env] of gameMessages) { - const name = env.kind.value!.action.case; + const name = (env.gameAction! as NT.GameAction).action; tests.push({ name: `${name} - silent failure (inactive run)`, clientMessages: (users) => [...u1create_u2join_no_password.clientMessages(users), [users.user1, env]], @@ -2612,6 +2603,34 @@ describe('lobby conformance tests', () => { } } + const mergePrototype = (obj: object) => { + const res: any = {}; + for (const [k, v] of Object.entries(Object.getPrototypeOf(obj))) { + if (typeof v === 'function') continue; + res[k] = v; + } + return { ...res, ...obj }; + }; + const getAction = (env: NT.Envelope) => { + let action: string | undefined = undefined; + let oneofValue: NT.GameAction[keyof NT.GameAction] | NT.LobbyAction[keyof NT.LobbyAction] | undefined = undefined; + + switch (env.kind) { + case 'gameAction': + const gameAction = NT.GameAction.fromObject(env.gameAction!); + action = gameAction.action!; + oneofValue = gameAction[gameAction.action!]; + return { kind: action, [action]: mergePrototype(oneofValue!) }; + case 'lobbyAction': + const lobbyAction = NT.LobbyAction.fromObject(env.lobbyAction!); + action = lobbyAction.action!; + oneofValue = lobbyAction[lobbyAction.action!]; + return { kind: action, [action]: mergePrototype(oneofValue!) }; + default: + throw new Error(`Unknown Envelope.kind: ${env.kind}`); + } + }; + it.each(tests)('$name', ({ clientMessages, serverMessages }) => { let roomId = 1; let chatId = 1; @@ -2637,20 +2656,20 @@ describe('lobby conformance tests', () => { const toSend = clientMessages(users); for (const [user, message] of toSend) { - handleMessage(user, message.toBinary(), true); + handleMessage(user, NT.Envelope.encode(message).finish(), true); } const expectedMessages = serverMessages(users); - expect(sentMessages.map((v) => v.message.kind?.value?.action?.case)).toEqual( - expectedMessages.map((v) => v.message.kind?.value?.action?.case), + expect(sentMessages.map((env) => getAction(env.message))).toEqual( + expectedMessages.map((env) => getAction(env.message)), ); if (sentMessages.length === expectedMessages.length) { for (const [idx, sent] of sentMessages.entries()) { const exp = expectedMessages[idx]; - const sentM = sent.message.kind?.value?.action?.case; - const expM = sent.message.kind?.value?.action?.case; + const sentM = getAction(sent.message); + const expM = getAction(exp.message); try { - expect(sent).toStrictEqual(exp); + expect(sentM).toStrictEqual(expM); } catch (e) { if (e instanceof Error) { e.message = `Expected: ${expM} Got: ${sentM}\n\n${e.message}`; diff --git a/src/gen/messages_pb.ts b/src/gen/messages_pb.ts deleted file mode 100644 index 1543e23..0000000 --- a/src/gen/messages_pb.ts +++ /dev/null @@ -1,5486 +0,0 @@ -// @generated by protoc-gen-es v1.3.3 with parameter "target=ts" -// @generated from file messages.proto (package NT, syntax proto3) -/* eslint-disable */ -// @ts-nocheck - -import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; -import { Message, proto3 } from "@bufbuild/protobuf"; - -/** - * @generated from message NT.Envelope - */ -export class Envelope extends Message { - /** - * @generated from oneof NT.Envelope.kind - */ - kind: { - /** - * @generated from field: NT.GameAction game_action = 1; - */ - value: GameAction; - case: "gameAction"; - } | { - /** - * @generated from field: NT.LobbyAction lobby_action = 50; - */ - value: LobbyAction; - case: "lobbyAction"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Envelope"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "game_action", kind: "message", T: GameAction, oneof: "kind" }, - { no: 50, name: "lobby_action", kind: "message", T: LobbyAction, oneof: "kind" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Envelope { - return new Envelope().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Envelope { - return new Envelope().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Envelope { - return new Envelope().fromJsonString(jsonString, options); - } - - static equals(a: Envelope | PlainMessage | undefined, b: Envelope | PlainMessage | undefined): boolean { - return proto3.util.equals(Envelope, a, b); - } -} - -/** - * @generated from message NT.GameAction - */ -export class GameAction extends Message { - /** - * The c prefix stands for "client", which refers to this application - * The s prefix stands for "server", which refers to the online Noita game server - * - * @generated from oneof NT.GameAction.action - */ - action: { - /** - * @generated from field: NT.PlayerMove player_move = 1; - */ - value: PlayerMove; - case: "playerMove"; - } | { - /** - * @generated from field: NT.PlayerPosition player_position = 2; - */ - value: PlayerPosition; - case: "playerPosition"; - } | { - /** - * @generated from field: NT.ClientPlayerUpdate c_player_update = 3; - */ - value: ClientPlayerUpdate; - case: "cPlayerUpdate"; - } | { - /** - * @generated from field: NT.ServerPlayerUpdate s_player_update = 4; - */ - value: ServerPlayerUpdate; - case: "sPlayerUpdate"; - } | { - /** - * @generated from field: NT.ClientPlayerUpdateInventory c_player_update_inventory = 5; - */ - value: ClientPlayerUpdateInventory; - case: "cPlayerUpdateInventory"; - } | { - /** - * @generated from field: NT.ServerPlayerUpdateInventory s_player_update_inventory = 6; - */ - value: ServerPlayerUpdateInventory; - case: "sPlayerUpdateInventory"; - } | { - /** - * @generated from field: NT.ClientHostItemBank c_host_item_bank = 7; - */ - value: ClientHostItemBank; - case: "cHostItemBank"; - } | { - /** - * @generated from field: NT.ServerHostItemBank s_host_item_bank = 8; - */ - value: ServerHostItemBank; - case: "sHostItemBank"; - } | { - /** - * @generated from field: NT.ClientHostUserTake c_host_user_take = 9; - */ - value: ClientHostUserTake; - case: "cHostUserTake"; - } | { - /** - * @generated from field: NT.ServerHostUserTake s_host_user_take = 10; - */ - value: ServerHostUserTake; - case: "sHostUserTake"; - } | { - /** - * @generated from field: NT.ClientHostUserTakeGold c_host_user_take_gold = 11; - */ - value: ClientHostUserTakeGold; - case: "cHostUserTakeGold"; - } | { - /** - * @generated from field: NT.ServerHostUserTakeGold s_host_user_take_gold = 12; - */ - value: ServerHostUserTakeGold; - case: "sHostUserTakeGold"; - } | { - /** - * @generated from field: NT.ClientPlayerAddGold c_player_add_gold = 13; - */ - value: ClientPlayerAddGold; - case: "cPlayerAddGold"; - } | { - /** - * @generated from field: NT.ServerPlayerAddGold s_player_add_gold = 14; - */ - value: ServerPlayerAddGold; - case: "sPlayerAddGold"; - } | { - /** - * @generated from field: NT.ClientPlayerTakeGold c_player_take_gold = 15; - */ - value: ClientPlayerTakeGold; - case: "cPlayerTakeGold"; - } | { - /** - * @generated from field: NT.ServerPlayerTakeGold s_player_take_gold = 16; - */ - value: ServerPlayerTakeGold; - case: "sPlayerTakeGold"; - } | { - /** - * @generated from field: NT.ClientPlayerAddItem c_player_add_item = 17; - */ - value: ClientPlayerAddItem; - case: "cPlayerAddItem"; - } | { - /** - * @generated from field: NT.ServerPlayerAddItem s_player_add_item = 18; - */ - value: ServerPlayerAddItem; - case: "sPlayerAddItem"; - } | { - /** - * @generated from field: NT.ClientPlayerTakeItem c_player_take_item = 19; - */ - value: ClientPlayerTakeItem; - case: "cPlayerTakeItem"; - } | { - /** - * @generated from field: NT.ServerPlayerTakeItem s_player_take_item = 20; - */ - value: ServerPlayerTakeItem; - case: "sPlayerTakeItem"; - } | { - /** - * @generated from field: NT.ClientPlayerPickup c_player_pickup = 21; - */ - value: ClientPlayerPickup; - case: "cPlayerPickup"; - } | { - /** - * @generated from field: NT.ServerPlayerPickup s_player_pickup = 22; - */ - value: ServerPlayerPickup; - case: "sPlayerPickup"; - } | { - /** - * @generated from field: NT.ClientNemesisAbility c_nemesis_ability = 23; - */ - value: ClientNemesisAbility; - case: "cNemesisAbility"; - } | { - /** - * @generated from field: NT.ServerNemesisAbility s_nemesis_ability = 24; - */ - value: ServerNemesisAbility; - case: "sNemesisAbility"; - } | { - /** - * @generated from field: NT.ClientNemesisPickupItem c_nemesis_pickup_item = 25; - */ - value: ClientNemesisPickupItem; - case: "cNemesisPickupItem"; - } | { - /** - * @generated from field: NT.ServerNemesisPickupItem s_nemesis_pickup_item = 26; - */ - value: ServerNemesisPickupItem; - case: "sNemesisPickupItem"; - } | { - /** - * @generated from field: NT.ClientChat c_chat = 27; - */ - value: ClientChat; - case: "cChat"; - } | { - /** - * @generated from field: NT.ServerChat s_chat = 28; - */ - value: ServerChat; - case: "sChat"; - } | { - /** - * @generated from field: NT.ClientPlayerDeath c_player_death = 29; - */ - value: ClientPlayerDeath; - case: "cPlayerDeath"; - } | { - /** - * @generated from field: NT.ServerPlayerDeath s_player_death = 30; - */ - value: ServerPlayerDeath; - case: "sPlayerDeath"; - } | { - /** - * @generated from field: NT.ClientPlayerNewGamePlus c_player_new_game_plus = 31; - */ - value: ClientPlayerNewGamePlus; - case: "cPlayerNewGamePlus"; - } | { - /** - * @generated from field: NT.ServerPlayerNewGamePlus s_player_new_game_plus = 32; - */ - value: ServerPlayerNewGamePlus; - case: "sPlayerNewGamePlus"; - } | { - /** - * @generated from field: NT.ClientPlayerSecretHourglass c_player_secret_hourglass = 33; - */ - value: ClientPlayerSecretHourglass; - case: "cPlayerSecretHourglass"; - } | { - /** - * @generated from field: NT.ServerPlayerSecretHourglass s_player_secret_hourglass = 34; - */ - value: ServerPlayerSecretHourglass; - case: "sPlayerSecretHourglass"; - } | { - /** - * @generated from field: NT.ClientCustomModEvent c_custom_mod_event = 35; - */ - value: ClientCustomModEvent; - case: "cCustomModEvent"; - } | { - /** - * @generated from field: NT.ServerCustomModEvent s_custom_mod_event = 36; - */ - value: ServerCustomModEvent; - case: "sCustomModEvent"; - } | { - /** - * @generated from field: NT.ClientRespawnPenalty c_respawn_penalty = 37; - */ - value: ClientRespawnPenalty; - case: "cRespawnPenalty"; - } | { - /** - * @generated from field: NT.ServerRespawnPenalty s_respawn_penalty = 38; - */ - value: ServerRespawnPenalty; - case: "sRespawnPenalty"; - } | { - /** - * @generated from field: NT.ClientAngerySteve c_angery_steve = 39; - */ - value: ClientAngerySteve; - case: "cAngerySteve"; - } | { - /** - * @generated from field: NT.ServerAngerySteve s_angery_steve = 40; - */ - value: ServerAngerySteve; - case: "sAngerySteve"; - } | { - /** - * @generated from field: NT.ServerStatsUpdate s_stat_update = 42; - */ - value: ServerStatsUpdate; - case: "sStatUpdate"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.GameAction"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "player_move", kind: "message", T: PlayerMove, oneof: "action" }, - { no: 2, name: "player_position", kind: "message", T: PlayerPosition, oneof: "action" }, - { no: 3, name: "c_player_update", kind: "message", T: ClientPlayerUpdate, oneof: "action" }, - { no: 4, name: "s_player_update", kind: "message", T: ServerPlayerUpdate, oneof: "action" }, - { no: 5, name: "c_player_update_inventory", kind: "message", T: ClientPlayerUpdateInventory, oneof: "action" }, - { no: 6, name: "s_player_update_inventory", kind: "message", T: ServerPlayerUpdateInventory, oneof: "action" }, - { no: 7, name: "c_host_item_bank", kind: "message", T: ClientHostItemBank, oneof: "action" }, - { no: 8, name: "s_host_item_bank", kind: "message", T: ServerHostItemBank, oneof: "action" }, - { no: 9, name: "c_host_user_take", kind: "message", T: ClientHostUserTake, oneof: "action" }, - { no: 10, name: "s_host_user_take", kind: "message", T: ServerHostUserTake, oneof: "action" }, - { no: 11, name: "c_host_user_take_gold", kind: "message", T: ClientHostUserTakeGold, oneof: "action" }, - { no: 12, name: "s_host_user_take_gold", kind: "message", T: ServerHostUserTakeGold, oneof: "action" }, - { no: 13, name: "c_player_add_gold", kind: "message", T: ClientPlayerAddGold, oneof: "action" }, - { no: 14, name: "s_player_add_gold", kind: "message", T: ServerPlayerAddGold, oneof: "action" }, - { no: 15, name: "c_player_take_gold", kind: "message", T: ClientPlayerTakeGold, oneof: "action" }, - { no: 16, name: "s_player_take_gold", kind: "message", T: ServerPlayerTakeGold, oneof: "action" }, - { no: 17, name: "c_player_add_item", kind: "message", T: ClientPlayerAddItem, oneof: "action" }, - { no: 18, name: "s_player_add_item", kind: "message", T: ServerPlayerAddItem, oneof: "action" }, - { no: 19, name: "c_player_take_item", kind: "message", T: ClientPlayerTakeItem, oneof: "action" }, - { no: 20, name: "s_player_take_item", kind: "message", T: ServerPlayerTakeItem, oneof: "action" }, - { no: 21, name: "c_player_pickup", kind: "message", T: ClientPlayerPickup, oneof: "action" }, - { no: 22, name: "s_player_pickup", kind: "message", T: ServerPlayerPickup, oneof: "action" }, - { no: 23, name: "c_nemesis_ability", kind: "message", T: ClientNemesisAbility, oneof: "action" }, - { no: 24, name: "s_nemesis_ability", kind: "message", T: ServerNemesisAbility, oneof: "action" }, - { no: 25, name: "c_nemesis_pickup_item", kind: "message", T: ClientNemesisPickupItem, oneof: "action" }, - { no: 26, name: "s_nemesis_pickup_item", kind: "message", T: ServerNemesisPickupItem, oneof: "action" }, - { no: 27, name: "c_chat", kind: "message", T: ClientChat, oneof: "action" }, - { no: 28, name: "s_chat", kind: "message", T: ServerChat, oneof: "action" }, - { no: 29, name: "c_player_death", kind: "message", T: ClientPlayerDeath, oneof: "action" }, - { no: 30, name: "s_player_death", kind: "message", T: ServerPlayerDeath, oneof: "action" }, - { no: 31, name: "c_player_new_game_plus", kind: "message", T: ClientPlayerNewGamePlus, oneof: "action" }, - { no: 32, name: "s_player_new_game_plus", kind: "message", T: ServerPlayerNewGamePlus, oneof: "action" }, - { no: 33, name: "c_player_secret_hourglass", kind: "message", T: ClientPlayerSecretHourglass, oneof: "action" }, - { no: 34, name: "s_player_secret_hourglass", kind: "message", T: ServerPlayerSecretHourglass, oneof: "action" }, - { no: 35, name: "c_custom_mod_event", kind: "message", T: ClientCustomModEvent, oneof: "action" }, - { no: 36, name: "s_custom_mod_event", kind: "message", T: ServerCustomModEvent, oneof: "action" }, - { no: 37, name: "c_respawn_penalty", kind: "message", T: ClientRespawnPenalty, oneof: "action" }, - { no: 38, name: "s_respawn_penalty", kind: "message", T: ServerRespawnPenalty, oneof: "action" }, - { no: 39, name: "c_angery_steve", kind: "message", T: ClientAngerySteve, oneof: "action" }, - { no: 40, name: "s_angery_steve", kind: "message", T: ServerAngerySteve, oneof: "action" }, - { no: 42, name: "s_stat_update", kind: "message", T: ServerStatsUpdate, oneof: "action" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): GameAction { - return new GameAction().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): GameAction { - return new GameAction().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): GameAction { - return new GameAction().fromJsonString(jsonString, options); - } - - static equals(a: GameAction | PlainMessage | undefined, b: GameAction | PlainMessage | undefined): boolean { - return proto3.util.equals(GameAction, a, b); - } -} - -/** - * @generated from message NT.PlayerFrame - */ -export class PlayerFrame extends Message { - /** - * @generated from field: optional float x = 1; - */ - x?: number; - - /** - * @generated from field: optional float y = 2; - */ - y?: number; - - /** - * @generated from field: optional float arm_r = 3; - */ - armR?: number; - - /** - * @generated from field: optional float arm_scale_y = 4; - */ - armScaleY?: number; - - /** - * @generated from field: optional float scale_x = 5; - */ - scaleX?: number; - - /** - * @generated from field: optional int32 anim = 6; - */ - anim?: number; - - /** - * @generated from field: optional int32 held = 7; - */ - held?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.PlayerFrame"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "x", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 2, name: "y", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 3, name: "arm_r", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 4, name: "arm_scale_y", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 5, name: "scale_x", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 6, name: "anim", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - { no: 7, name: "held", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): PlayerFrame { - return new PlayerFrame().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): PlayerFrame { - return new PlayerFrame().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): PlayerFrame { - return new PlayerFrame().fromJsonString(jsonString, options); - } - - static equals(a: PlayerFrame | PlainMessage | undefined, b: PlayerFrame | PlainMessage | undefined): boolean { - return proto3.util.equals(PlayerFrame, a, b); - } -} - -/** - * @generated from message NT.PlayerMove - */ -export class PlayerMove extends Message { - /** - * @generated from field: repeated NT.PlayerFrame frames = 1; - */ - frames: PlayerFrame[] = []; - - /** - * @generated from field: optional string user_id = 15; - */ - userId?: string; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.PlayerMove"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "frames", kind: "message", T: PlayerFrame, repeated: true }, - { no: 15, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): PlayerMove { - return new PlayerMove().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): PlayerMove { - return new PlayerMove().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): PlayerMove { - return new PlayerMove().fromJsonString(jsonString, options); - } - - static equals(a: PlayerMove | PlainMessage | undefined, b: PlayerMove | PlainMessage | undefined): boolean { - return proto3.util.equals(PlayerMove, a, b); - } -} - -/** - * @generated from message NT.PlayerPosition - */ -export class PlayerPosition extends Message { - /** - * @generated from field: NT.PlayerFrame frame = 1; - */ - frame?: PlayerFrame; - - /** - * @generated from field: string user_id = 15; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.PlayerPosition"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "frame", kind: "message", T: PlayerFrame }, - { no: 15, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): PlayerPosition { - return new PlayerPosition().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): PlayerPosition { - return new PlayerPosition().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): PlayerPosition { - return new PlayerPosition().fromJsonString(jsonString, options); - } - - static equals(a: PlayerPosition | PlainMessage | undefined, b: PlayerPosition | PlainMessage | undefined): boolean { - return proto3.util.equals(PlayerPosition, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerUpdate - */ -export class ClientPlayerUpdate extends Message { - /** - * @generated from field: optional float cur_hp = 1; - */ - curHp?: number; - - /** - * @generated from field: optional float max_hp = 2; - */ - maxHp?: number; - - /** - * @generated from field: optional string location = 3; - */ - location?: string; - - /** - * @generated from field: optional bool sampo = 4; - */ - sampo?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerUpdate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "cur_hp", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 2, name: "max_hp", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 3, name: "location", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 4, name: "sampo", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerUpdate { - return new ClientPlayerUpdate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerUpdate { - return new ClientPlayerUpdate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerUpdate { - return new ClientPlayerUpdate().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerUpdate | PlainMessage | undefined, b: ClientPlayerUpdate | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerUpdate, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerUpdate - */ -export class ServerPlayerUpdate extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: optional float cur_hp = 2; - */ - curHp?: number; - - /** - * @generated from field: optional float max_hp = 3; - */ - maxHp?: number; - - /** - * @generated from field: optional string location = 4; - */ - location?: string; - - /** - * @generated from field: optional bool sampo = 5; - */ - sampo?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerUpdate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "cur_hp", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 3, name: "max_hp", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 4, name: "location", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "sampo", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerUpdate { - return new ServerPlayerUpdate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerUpdate { - return new ServerPlayerUpdate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerUpdate { - return new ServerPlayerUpdate().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerUpdate | PlainMessage | undefined, b: ServerPlayerUpdate | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerUpdate, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerUpdateInventory - */ -export class ClientPlayerUpdateInventory extends Message { - /** - * @generated from field: repeated NT.ClientPlayerUpdateInventory.InventoryWand wands = 1; - */ - wands: ClientPlayerUpdateInventory_InventoryWand[] = []; - - /** - * @generated from field: repeated NT.ClientPlayerUpdateInventory.InventoryItem items = 2; - */ - items: ClientPlayerUpdateInventory_InventoryItem[] = []; - - /** - * @generated from field: repeated NT.ClientPlayerUpdateInventory.InventorySpell spells = 3; - */ - spells: ClientPlayerUpdateInventory_InventorySpell[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerUpdateInventory"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "wands", kind: "message", T: ClientPlayerUpdateInventory_InventoryWand, repeated: true }, - { no: 2, name: "items", kind: "message", T: ClientPlayerUpdateInventory_InventoryItem, repeated: true }, - { no: 3, name: "spells", kind: "message", T: ClientPlayerUpdateInventory_InventorySpell, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerUpdateInventory { - return new ClientPlayerUpdateInventory().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerUpdateInventory { - return new ClientPlayerUpdateInventory().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerUpdateInventory { - return new ClientPlayerUpdateInventory().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerUpdateInventory | PlainMessage | undefined, b: ClientPlayerUpdateInventory | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerUpdateInventory, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerUpdateInventory.InventoryWand - */ -export class ClientPlayerUpdateInventory_InventoryWand extends Message { - /** - * @generated from field: uint32 index = 1; - */ - index = 0; - - /** - * @generated from field: NT.Wand wand = 2; - */ - wand?: Wand; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerUpdateInventory.InventoryWand"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "wand", kind: "message", T: Wand }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerUpdateInventory_InventoryWand { - return new ClientPlayerUpdateInventory_InventoryWand().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerUpdateInventory_InventoryWand { - return new ClientPlayerUpdateInventory_InventoryWand().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerUpdateInventory_InventoryWand { - return new ClientPlayerUpdateInventory_InventoryWand().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerUpdateInventory_InventoryWand | PlainMessage | undefined, b: ClientPlayerUpdateInventory_InventoryWand | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerUpdateInventory_InventoryWand, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerUpdateInventory.InventoryItem - */ -export class ClientPlayerUpdateInventory_InventoryItem extends Message { - /** - * @generated from field: uint32 index = 3; - */ - index = 0; - - /** - * @generated from field: NT.Item item = 4; - */ - item?: Item; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerUpdateInventory.InventoryItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 3, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "item", kind: "message", T: Item }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerUpdateInventory_InventoryItem { - return new ClientPlayerUpdateInventory_InventoryItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerUpdateInventory_InventoryItem { - return new ClientPlayerUpdateInventory_InventoryItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerUpdateInventory_InventoryItem { - return new ClientPlayerUpdateInventory_InventoryItem().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerUpdateInventory_InventoryItem | PlainMessage | undefined, b: ClientPlayerUpdateInventory_InventoryItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerUpdateInventory_InventoryItem, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerUpdateInventory.InventorySpell - */ -export class ClientPlayerUpdateInventory_InventorySpell extends Message { - /** - * @generated from field: uint32 index = 1; - */ - index = 0; - - /** - * @generated from field: NT.Spell spell = 2; - */ - spell?: Spell; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerUpdateInventory.InventorySpell"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "spell", kind: "message", T: Spell }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerUpdateInventory_InventorySpell { - return new ClientPlayerUpdateInventory_InventorySpell().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerUpdateInventory_InventorySpell { - return new ClientPlayerUpdateInventory_InventorySpell().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerUpdateInventory_InventorySpell { - return new ClientPlayerUpdateInventory_InventorySpell().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerUpdateInventory_InventorySpell | PlainMessage | undefined, b: ClientPlayerUpdateInventory_InventorySpell | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerUpdateInventory_InventorySpell, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerUpdateInventory - */ -export class ServerPlayerUpdateInventory extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: repeated NT.ServerPlayerUpdateInventory.InventoryWand wands = 2; - */ - wands: ServerPlayerUpdateInventory_InventoryWand[] = []; - - /** - * @generated from field: repeated NT.ServerPlayerUpdateInventory.InventoryItem items = 3; - */ - items: ServerPlayerUpdateInventory_InventoryItem[] = []; - - /** - * @generated from field: repeated NT.ServerPlayerUpdateInventory.InventorySpell spells = 4; - */ - spells: ServerPlayerUpdateInventory_InventorySpell[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerUpdateInventory"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "wands", kind: "message", T: ServerPlayerUpdateInventory_InventoryWand, repeated: true }, - { no: 3, name: "items", kind: "message", T: ServerPlayerUpdateInventory_InventoryItem, repeated: true }, - { no: 4, name: "spells", kind: "message", T: ServerPlayerUpdateInventory_InventorySpell, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerUpdateInventory { - return new ServerPlayerUpdateInventory().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerUpdateInventory { - return new ServerPlayerUpdateInventory().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerUpdateInventory { - return new ServerPlayerUpdateInventory().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerUpdateInventory | PlainMessage | undefined, b: ServerPlayerUpdateInventory | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerUpdateInventory, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerUpdateInventory.InventoryWand - */ -export class ServerPlayerUpdateInventory_InventoryWand extends Message { - /** - * @generated from field: uint32 index = 1; - */ - index = 0; - - /** - * @generated from field: NT.Wand wand = 2; - */ - wand?: Wand; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerUpdateInventory.InventoryWand"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "wand", kind: "message", T: Wand }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerUpdateInventory_InventoryWand { - return new ServerPlayerUpdateInventory_InventoryWand().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerUpdateInventory_InventoryWand { - return new ServerPlayerUpdateInventory_InventoryWand().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerUpdateInventory_InventoryWand { - return new ServerPlayerUpdateInventory_InventoryWand().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerUpdateInventory_InventoryWand | PlainMessage | undefined, b: ServerPlayerUpdateInventory_InventoryWand | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerUpdateInventory_InventoryWand, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerUpdateInventory.InventoryItem - */ -export class ServerPlayerUpdateInventory_InventoryItem extends Message { - /** - * @generated from field: uint32 index = 1; - */ - index = 0; - - /** - * @generated from field: NT.Item item = 2; - */ - item?: Item; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerUpdateInventory.InventoryItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "item", kind: "message", T: Item }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerUpdateInventory_InventoryItem { - return new ServerPlayerUpdateInventory_InventoryItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerUpdateInventory_InventoryItem { - return new ServerPlayerUpdateInventory_InventoryItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerUpdateInventory_InventoryItem { - return new ServerPlayerUpdateInventory_InventoryItem().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerUpdateInventory_InventoryItem | PlainMessage | undefined, b: ServerPlayerUpdateInventory_InventoryItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerUpdateInventory_InventoryItem, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerUpdateInventory.InventorySpell - */ -export class ServerPlayerUpdateInventory_InventorySpell extends Message { - /** - * @generated from field: uint32 index = 1; - */ - index = 0; - - /** - * @generated from field: NT.Spell spell = 2; - */ - spell?: Spell; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerUpdateInventory.InventorySpell"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "spell", kind: "message", T: Spell }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerUpdateInventory_InventorySpell { - return new ServerPlayerUpdateInventory_InventorySpell().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerUpdateInventory_InventorySpell { - return new ServerPlayerUpdateInventory_InventorySpell().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerUpdateInventory_InventorySpell { - return new ServerPlayerUpdateInventory_InventorySpell().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerUpdateInventory_InventorySpell | PlainMessage | undefined, b: ServerPlayerUpdateInventory_InventorySpell | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerUpdateInventory_InventorySpell, a, b); - } -} - -/** - * @generated from message NT.ClientHostItemBank - */ -export class ClientHostItemBank extends Message { - /** - * @generated from field: repeated NT.Wand wands = 1; - */ - wands: Wand[] = []; - - /** - * @generated from field: repeated NT.Spell spells = 2; - */ - spells: Spell[] = []; - - /** - * @generated from field: repeated NT.Item items = 3; - */ - items: Item[] = []; - - /** - * @generated from field: uint32 gold = 4; - */ - gold = 0; - - /** - * @generated from field: repeated NT.EntityItem objects = 5; - */ - objects: EntityItem[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientHostItemBank"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "wands", kind: "message", T: Wand, repeated: true }, - { no: 2, name: "spells", kind: "message", T: Spell, repeated: true }, - { no: 3, name: "items", kind: "message", T: Item, repeated: true }, - { no: 4, name: "gold", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "objects", kind: "message", T: EntityItem, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientHostItemBank { - return new ClientHostItemBank().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientHostItemBank { - return new ClientHostItemBank().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientHostItemBank { - return new ClientHostItemBank().fromJsonString(jsonString, options); - } - - static equals(a: ClientHostItemBank | PlainMessage | undefined, b: ClientHostItemBank | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientHostItemBank, a, b); - } -} - -/** - * @generated from message NT.ServerHostItemBank - */ -export class ServerHostItemBank extends Message { - /** - * @generated from field: repeated NT.Wand wands = 1; - */ - wands: Wand[] = []; - - /** - * @generated from field: repeated NT.Spell spells = 2; - */ - spells: Spell[] = []; - - /** - * @generated from field: repeated NT.Item items = 3; - */ - items: Item[] = []; - - /** - * @generated from field: uint32 gold = 4; - */ - gold = 0; - - /** - * @generated from field: repeated NT.EntityItem objects = 5; - */ - objects: EntityItem[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerHostItemBank"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "wands", kind: "message", T: Wand, repeated: true }, - { no: 2, name: "spells", kind: "message", T: Spell, repeated: true }, - { no: 3, name: "items", kind: "message", T: Item, repeated: true }, - { no: 4, name: "gold", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "objects", kind: "message", T: EntityItem, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerHostItemBank { - return new ServerHostItemBank().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerHostItemBank { - return new ServerHostItemBank().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerHostItemBank { - return new ServerHostItemBank().fromJsonString(jsonString, options); - } - - static equals(a: ServerHostItemBank | PlainMessage | undefined, b: ServerHostItemBank | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerHostItemBank, a, b); - } -} - -/** - * @generated from message NT.ClientHostUserTake - */ -export class ClientHostUserTake extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string id = 2; - */ - id = ""; - - /** - * @generated from field: bool success = 3; - */ - success = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientHostUserTake"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "success", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientHostUserTake { - return new ClientHostUserTake().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientHostUserTake { - return new ClientHostUserTake().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientHostUserTake { - return new ClientHostUserTake().fromJsonString(jsonString, options); - } - - static equals(a: ClientHostUserTake | PlainMessage | undefined, b: ClientHostUserTake | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientHostUserTake, a, b); - } -} - -/** - * @generated from message NT.ServerHostUserTake - */ -export class ServerHostUserTake extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string id = 2; - */ - id = ""; - - /** - * @generated from field: bool success = 3; - */ - success = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerHostUserTake"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "success", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerHostUserTake { - return new ServerHostUserTake().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerHostUserTake { - return new ServerHostUserTake().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerHostUserTake { - return new ServerHostUserTake().fromJsonString(jsonString, options); - } - - static equals(a: ServerHostUserTake | PlainMessage | undefined, b: ServerHostUserTake | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerHostUserTake, a, b); - } -} - -/** - * @generated from message NT.ClientHostUserTakeGold - */ -export class ClientHostUserTakeGold extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - /** - * @generated from field: bool success = 3; - */ - success = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientHostUserTakeGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 3, name: "success", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientHostUserTakeGold { - return new ClientHostUserTakeGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientHostUserTakeGold { - return new ClientHostUserTakeGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientHostUserTakeGold { - return new ClientHostUserTakeGold().fromJsonString(jsonString, options); - } - - static equals(a: ClientHostUserTakeGold | PlainMessage | undefined, b: ClientHostUserTakeGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientHostUserTakeGold, a, b); - } -} - -/** - * @generated from message NT.ServerHostUserTakeGold - */ -export class ServerHostUserTakeGold extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - /** - * @generated from field: bool success = 3; - */ - success = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerHostUserTakeGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 3, name: "success", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerHostUserTakeGold { - return new ServerHostUserTakeGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerHostUserTakeGold { - return new ServerHostUserTakeGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerHostUserTakeGold { - return new ServerHostUserTakeGold().fromJsonString(jsonString, options); - } - - static equals(a: ServerHostUserTakeGold | PlainMessage | undefined, b: ServerHostUserTakeGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerHostUserTakeGold, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddGold - */ -export class ClientPlayerAddGold extends Message { - /** - * @generated from field: uint32 amount = 1; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddGold { - return new ClientPlayerAddGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddGold { - return new ClientPlayerAddGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddGold { - return new ClientPlayerAddGold().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddGold | PlainMessage | undefined, b: ClientPlayerAddGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddGold, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddGold - */ -export class ServerPlayerAddGold extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddGold { - return new ServerPlayerAddGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddGold { - return new ServerPlayerAddGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddGold { - return new ServerPlayerAddGold().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddGold | PlainMessage | undefined, b: ServerPlayerAddGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddGold, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerTakeGold - */ -export class ClientPlayerTakeGold extends Message { - /** - * @generated from field: uint32 amount = 1; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerTakeGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerTakeGold { - return new ClientPlayerTakeGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerTakeGold { - return new ClientPlayerTakeGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerTakeGold { - return new ClientPlayerTakeGold().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerTakeGold | PlainMessage | undefined, b: ClientPlayerTakeGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerTakeGold, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerTakeGold - */ -export class ServerPlayerTakeGold extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerTakeGold"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerTakeGold { - return new ServerPlayerTakeGold().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerTakeGold { - return new ServerPlayerTakeGold().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerTakeGold { - return new ServerPlayerTakeGold().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerTakeGold | PlainMessage | undefined, b: ServerPlayerTakeGold | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerTakeGold, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddItem - */ -export class ClientPlayerAddItem extends Message { - /** - * @generated from oneof NT.ClientPlayerAddItem.item - */ - item: { - /** - * @generated from field: NT.ClientPlayerAddItem.Spells spells = 1; - */ - value: ClientPlayerAddItem_Spells; - case: "spells"; - } | { - /** - * @generated from field: NT.ClientPlayerAddItem.Wands wands = 2; - */ - value: ClientPlayerAddItem_Wands; - case: "wands"; - } | { - /** - * @generated from field: NT.ClientPlayerAddItem.Items flasks = 3; - */ - value: ClientPlayerAddItem_Items; - case: "flasks"; - } | { - /** - * @generated from field: NT.ClientPlayerAddItem.Entities objects = 4; - */ - value: ClientPlayerAddItem_Entities; - case: "objects"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "spells", kind: "message", T: ClientPlayerAddItem_Spells, oneof: "item" }, - { no: 2, name: "wands", kind: "message", T: ClientPlayerAddItem_Wands, oneof: "item" }, - { no: 3, name: "flasks", kind: "message", T: ClientPlayerAddItem_Items, oneof: "item" }, - { no: 4, name: "objects", kind: "message", T: ClientPlayerAddItem_Entities, oneof: "item" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddItem { - return new ClientPlayerAddItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddItem { - return new ClientPlayerAddItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddItem { - return new ClientPlayerAddItem().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddItem | PlainMessage | undefined, b: ClientPlayerAddItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddItem, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddItem.Spells - */ -export class ClientPlayerAddItem_Spells extends Message { - /** - * @generated from field: repeated NT.Spell list = 1; - */ - list: Spell[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddItem.Spells"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "list", kind: "message", T: Spell, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddItem_Spells { - return new ClientPlayerAddItem_Spells().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddItem_Spells { - return new ClientPlayerAddItem_Spells().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddItem_Spells { - return new ClientPlayerAddItem_Spells().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddItem_Spells | PlainMessage | undefined, b: ClientPlayerAddItem_Spells | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddItem_Spells, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddItem.Wands - */ -export class ClientPlayerAddItem_Wands extends Message { - /** - * @generated from field: repeated NT.Wand list = 1; - */ - list: Wand[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddItem.Wands"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "list", kind: "message", T: Wand, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddItem_Wands { - return new ClientPlayerAddItem_Wands().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddItem_Wands { - return new ClientPlayerAddItem_Wands().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddItem_Wands { - return new ClientPlayerAddItem_Wands().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddItem_Wands | PlainMessage | undefined, b: ClientPlayerAddItem_Wands | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddItem_Wands, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddItem.Items - */ -export class ClientPlayerAddItem_Items extends Message { - /** - * @generated from field: repeated NT.Item list = 1; - */ - list: Item[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddItem.Items"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "list", kind: "message", T: Item, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddItem_Items { - return new ClientPlayerAddItem_Items().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddItem_Items { - return new ClientPlayerAddItem_Items().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddItem_Items { - return new ClientPlayerAddItem_Items().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddItem_Items | PlainMessage | undefined, b: ClientPlayerAddItem_Items | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddItem_Items, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerAddItem.Entities - */ -export class ClientPlayerAddItem_Entities extends Message { - /** - * @generated from field: repeated NT.EntityItem list = 1; - */ - list: EntityItem[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerAddItem.Entities"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "list", kind: "message", T: EntityItem, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerAddItem_Entities { - return new ClientPlayerAddItem_Entities().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerAddItem_Entities { - return new ClientPlayerAddItem_Entities().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerAddItem_Entities { - return new ClientPlayerAddItem_Entities().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerAddItem_Entities | PlainMessage | undefined, b: ClientPlayerAddItem_Entities | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerAddItem_Entities, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddItem - */ -export class ServerPlayerAddItem extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from oneof NT.ServerPlayerAddItem.item - */ - item: { - /** - * @generated from field: NT.ServerPlayerAddItem.Spells spells = 2; - */ - value: ServerPlayerAddItem_Spells; - case: "spells"; - } | { - /** - * @generated from field: NT.ServerPlayerAddItem.Wands wands = 3; - */ - value: ServerPlayerAddItem_Wands; - case: "wands"; - } | { - /** - * @generated from field: NT.ServerPlayerAddItem.Items flasks = 4; - */ - value: ServerPlayerAddItem_Items; - case: "flasks"; - } | { - /** - * @generated from field: NT.ServerPlayerAddItem.Entities objects = 5; - */ - value: ServerPlayerAddItem_Entities; - case: "objects"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "spells", kind: "message", T: ServerPlayerAddItem_Spells, oneof: "item" }, - { no: 3, name: "wands", kind: "message", T: ServerPlayerAddItem_Wands, oneof: "item" }, - { no: 4, name: "flasks", kind: "message", T: ServerPlayerAddItem_Items, oneof: "item" }, - { no: 5, name: "objects", kind: "message", T: ServerPlayerAddItem_Entities, oneof: "item" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddItem { - return new ServerPlayerAddItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddItem { - return new ServerPlayerAddItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddItem { - return new ServerPlayerAddItem().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddItem | PlainMessage | undefined, b: ServerPlayerAddItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddItem, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddItem.Spells - */ -export class ServerPlayerAddItem_Spells extends Message { - /** - * @generated from field: repeated NT.Spell list = 1; - */ - list: Spell[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddItem.Spells"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "list", kind: "message", T: Spell, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddItem_Spells { - return new ServerPlayerAddItem_Spells().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddItem_Spells { - return new ServerPlayerAddItem_Spells().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddItem_Spells { - return new ServerPlayerAddItem_Spells().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddItem_Spells | PlainMessage | undefined, b: ServerPlayerAddItem_Spells | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddItem_Spells, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddItem.Wands - */ -export class ServerPlayerAddItem_Wands extends Message { - /** - * @generated from field: repeated NT.Wand list = 2; - */ - list: Wand[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddItem.Wands"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 2, name: "list", kind: "message", T: Wand, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddItem_Wands { - return new ServerPlayerAddItem_Wands().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddItem_Wands { - return new ServerPlayerAddItem_Wands().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddItem_Wands { - return new ServerPlayerAddItem_Wands().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddItem_Wands | PlainMessage | undefined, b: ServerPlayerAddItem_Wands | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddItem_Wands, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddItem.Items - */ -export class ServerPlayerAddItem_Items extends Message { - /** - * @generated from field: repeated NT.Item list = 3; - */ - list: Item[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddItem.Items"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 3, name: "list", kind: "message", T: Item, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddItem_Items { - return new ServerPlayerAddItem_Items().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddItem_Items { - return new ServerPlayerAddItem_Items().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddItem_Items { - return new ServerPlayerAddItem_Items().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddItem_Items | PlainMessage | undefined, b: ServerPlayerAddItem_Items | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddItem_Items, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerAddItem.Entities - */ -export class ServerPlayerAddItem_Entities extends Message { - /** - * @generated from field: repeated NT.EntityItem list = 4; - */ - list: EntityItem[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerAddItem.Entities"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 4, name: "list", kind: "message", T: EntityItem, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerAddItem_Entities { - return new ServerPlayerAddItem_Entities().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerAddItem_Entities { - return new ServerPlayerAddItem_Entities().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerAddItem_Entities { - return new ServerPlayerAddItem_Entities().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerAddItem_Entities | PlainMessage | undefined, b: ServerPlayerAddItem_Entities | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerAddItem_Entities, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerTakeItem - */ -export class ClientPlayerTakeItem extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerTakeItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerTakeItem { - return new ClientPlayerTakeItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerTakeItem { - return new ClientPlayerTakeItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerTakeItem { - return new ClientPlayerTakeItem().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerTakeItem | PlainMessage | undefined, b: ClientPlayerTakeItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerTakeItem, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerTakeItem - */ -export class ServerPlayerTakeItem extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string id = 2; - */ - id = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerTakeItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerTakeItem { - return new ServerPlayerTakeItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerTakeItem { - return new ServerPlayerTakeItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerTakeItem { - return new ServerPlayerTakeItem().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerTakeItem | PlainMessage | undefined, b: ServerPlayerTakeItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerTakeItem, a, b); - } -} - -/** - * @generated from message NT.ClientChat - */ -export class ClientChat extends Message { - /** - * @generated from field: string message = 1; - */ - message = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientChat"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientChat { - return new ClientChat().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientChat { - return new ClientChat().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientChat { - return new ClientChat().fromJsonString(jsonString, options); - } - - static equals(a: ClientChat | PlainMessage | undefined, b: ClientChat | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientChat, a, b); - } -} - -/** - * @generated from message NT.ServerChat - */ -export class ServerChat extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string user_id = 2; - */ - userId = ""; - - /** - * @generated from field: string name = 3; - */ - name = ""; - - /** - * @generated from field: string message = 4; - */ - message = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerChat"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 4, name: "message", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerChat { - return new ServerChat().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerChat { - return new ServerChat().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerChat { - return new ServerChat().fromJsonString(jsonString, options); - } - - static equals(a: ServerChat | PlainMessage | undefined, b: ServerChat | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerChat, a, b); - } -} - -/** - * @generated from message NT.ServerStatsUpdate - */ -export class ServerStatsUpdate extends Message { - /** - * @generated from field: string data = 1; - */ - data = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerStatsUpdate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "data", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerStatsUpdate { - return new ServerStatsUpdate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerStatsUpdate { - return new ServerStatsUpdate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerStatsUpdate { - return new ServerStatsUpdate().fromJsonString(jsonString, options); - } - - static equals(a: ServerStatsUpdate | PlainMessage | undefined, b: ServerStatsUpdate | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerStatsUpdate, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerPickup - */ -export class ClientPlayerPickup extends Message { - /** - * @generated from oneof NT.ClientPlayerPickup.kind - */ - kind: { - /** - * @generated from field: NT.ClientPlayerPickup.HeartPickup heart = 1; - */ - value: ClientPlayerPickup_HeartPickup; - case: "heart"; - } | { - /** - * @generated from field: NT.ClientPlayerPickup.OrbPickup orb = 2; - */ - value: ClientPlayerPickup_OrbPickup; - case: "orb"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "heart", kind: "message", T: ClientPlayerPickup_HeartPickup, oneof: "kind" }, - { no: 2, name: "orb", kind: "message", T: ClientPlayerPickup_OrbPickup, oneof: "kind" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerPickup { - return new ClientPlayerPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerPickup { - return new ClientPlayerPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerPickup { - return new ClientPlayerPickup().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerPickup | PlainMessage | undefined, b: ClientPlayerPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerPickup, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerPickup.HeartPickup - */ -export class ClientPlayerPickup_HeartPickup extends Message { - /** - * @generated from field: bool hp_perk = 1; - */ - hpPerk = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerPickup.HeartPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "hp_perk", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerPickup_HeartPickup { - return new ClientPlayerPickup_HeartPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerPickup_HeartPickup { - return new ClientPlayerPickup_HeartPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerPickup_HeartPickup { - return new ClientPlayerPickup_HeartPickup().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerPickup_HeartPickup | PlainMessage | undefined, b: ClientPlayerPickup_HeartPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerPickup_HeartPickup, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerPickup.OrbPickup - */ -export class ClientPlayerPickup_OrbPickup extends Message { - /** - * @generated from field: uint32 id = 1; - */ - id = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerPickup.OrbPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerPickup_OrbPickup { - return new ClientPlayerPickup_OrbPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerPickup_OrbPickup { - return new ClientPlayerPickup_OrbPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerPickup_OrbPickup { - return new ClientPlayerPickup_OrbPickup().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerPickup_OrbPickup | PlainMessage | undefined, b: ClientPlayerPickup_OrbPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerPickup_OrbPickup, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerPickup - */ -export class ServerPlayerPickup extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from oneof NT.ServerPlayerPickup.kind - */ - kind: { - /** - * @generated from field: NT.ServerPlayerPickup.HeartPickup heart = 2; - */ - value: ServerPlayerPickup_HeartPickup; - case: "heart"; - } | { - /** - * @generated from field: NT.ServerPlayerPickup.OrbPickup orb = 3; - */ - value: ServerPlayerPickup_OrbPickup; - case: "orb"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "heart", kind: "message", T: ServerPlayerPickup_HeartPickup, oneof: "kind" }, - { no: 3, name: "orb", kind: "message", T: ServerPlayerPickup_OrbPickup, oneof: "kind" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerPickup { - return new ServerPlayerPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerPickup { - return new ServerPlayerPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerPickup { - return new ServerPlayerPickup().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerPickup | PlainMessage | undefined, b: ServerPlayerPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerPickup, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerPickup.HeartPickup - */ -export class ServerPlayerPickup_HeartPickup extends Message { - /** - * @generated from field: bool hp_perk = 1; - */ - hpPerk = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerPickup.HeartPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "hp_perk", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerPickup_HeartPickup { - return new ServerPlayerPickup_HeartPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerPickup_HeartPickup { - return new ServerPlayerPickup_HeartPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerPickup_HeartPickup { - return new ServerPlayerPickup_HeartPickup().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerPickup_HeartPickup | PlainMessage | undefined, b: ServerPlayerPickup_HeartPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerPickup_HeartPickup, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerPickup.OrbPickup - */ -export class ServerPlayerPickup_OrbPickup extends Message { - /** - * @generated from field: uint32 id = 1; - */ - id = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerPickup.OrbPickup"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerPickup_OrbPickup { - return new ServerPlayerPickup_OrbPickup().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerPickup_OrbPickup { - return new ServerPlayerPickup_OrbPickup().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerPickup_OrbPickup { - return new ServerPlayerPickup_OrbPickup().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerPickup_OrbPickup | PlainMessage | undefined, b: ServerPlayerPickup_OrbPickup | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerPickup_OrbPickup, a, b); - } -} - -/** - * @generated from message NT.ClientNemesisPickupItem - */ -export class ClientNemesisPickupItem extends Message { - /** - * @generated from field: string game_id = 1; - */ - gameId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientNemesisPickupItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "game_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientNemesisPickupItem { - return new ClientNemesisPickupItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientNemesisPickupItem { - return new ClientNemesisPickupItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientNemesisPickupItem { - return new ClientNemesisPickupItem().fromJsonString(jsonString, options); - } - - static equals(a: ClientNemesisPickupItem | PlainMessage | undefined, b: ClientNemesisPickupItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientNemesisPickupItem, a, b); - } -} - -/** - * @generated from message NT.ServerNemesisPickupItem - */ -export class ServerNemesisPickupItem extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string game_id = 2; - */ - gameId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerNemesisPickupItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "game_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerNemesisPickupItem { - return new ServerNemesisPickupItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerNemesisPickupItem { - return new ServerNemesisPickupItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerNemesisPickupItem { - return new ServerNemesisPickupItem().fromJsonString(jsonString, options); - } - - static equals(a: ServerNemesisPickupItem | PlainMessage | undefined, b: ServerNemesisPickupItem | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerNemesisPickupItem, a, b); - } -} - -/** - * @generated from message NT.ClientNemesisAbility - */ -export class ClientNemesisAbility extends Message { - /** - * @generated from field: string game_id = 1; - */ - gameId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientNemesisAbility"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "game_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientNemesisAbility { - return new ClientNemesisAbility().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientNemesisAbility { - return new ClientNemesisAbility().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientNemesisAbility { - return new ClientNemesisAbility().fromJsonString(jsonString, options); - } - - static equals(a: ClientNemesisAbility | PlainMessage | undefined, b: ClientNemesisAbility | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientNemesisAbility, a, b); - } -} - -/** - * @generated from message NT.ServerNemesisAbility - */ -export class ServerNemesisAbility extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string game_id = 2; - */ - gameId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerNemesisAbility"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "game_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerNemesisAbility { - return new ServerNemesisAbility().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerNemesisAbility { - return new ServerNemesisAbility().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerNemesisAbility { - return new ServerNemesisAbility().fromJsonString(jsonString, options); - } - - static equals(a: ServerNemesisAbility | PlainMessage | undefined, b: ServerNemesisAbility | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerNemesisAbility, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerDeath - */ -export class ClientPlayerDeath extends Message { - /** - * @generated from field: bool is_win = 1; - */ - isWin = false; - - /** - * @generated from field: optional uint32 game_time = 2; - */ - gameTime?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerDeath"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "is_win", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 2, name: "game_time", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerDeath { - return new ClientPlayerDeath().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerDeath { - return new ClientPlayerDeath().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerDeath { - return new ClientPlayerDeath().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerDeath | PlainMessage | undefined, b: ClientPlayerDeath | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerDeath, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerDeath - */ -export class ServerPlayerDeath extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: bool is_win = 2; - */ - isWin = false; - - /** - * @generated from field: optional uint32 game_time = 3; - */ - gameTime?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerDeath"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "is_win", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 3, name: "game_time", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerDeath { - return new ServerPlayerDeath().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerDeath { - return new ServerPlayerDeath().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerDeath { - return new ServerPlayerDeath().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerDeath | PlainMessage | undefined, b: ServerPlayerDeath | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerDeath, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerNewGamePlus - */ -export class ClientPlayerNewGamePlus extends Message { - /** - * @generated from field: uint32 amount = 1; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerNewGamePlus"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerNewGamePlus { - return new ClientPlayerNewGamePlus().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerNewGamePlus { - return new ClientPlayerNewGamePlus().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerNewGamePlus { - return new ClientPlayerNewGamePlus().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerNewGamePlus | PlainMessage | undefined, b: ClientPlayerNewGamePlus | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerNewGamePlus, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerNewGamePlus - */ -export class ServerPlayerNewGamePlus extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerNewGamePlus"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerNewGamePlus { - return new ServerPlayerNewGamePlus().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerNewGamePlus { - return new ServerPlayerNewGamePlus().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerNewGamePlus { - return new ServerPlayerNewGamePlus().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerNewGamePlus | PlainMessage | undefined, b: ServerPlayerNewGamePlus | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerNewGamePlus, a, b); - } -} - -/** - * @generated from message NT.ClientPlayerSecretHourglass - */ -export class ClientPlayerSecretHourglass extends Message { - /** - * @generated from field: string material = 1; - */ - material = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientPlayerSecretHourglass"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "material", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientPlayerSecretHourglass { - return new ClientPlayerSecretHourglass().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientPlayerSecretHourglass { - return new ClientPlayerSecretHourglass().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientPlayerSecretHourglass { - return new ClientPlayerSecretHourglass().fromJsonString(jsonString, options); - } - - static equals(a: ClientPlayerSecretHourglass | PlainMessage | undefined, b: ClientPlayerSecretHourglass | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientPlayerSecretHourglass, a, b); - } -} - -/** - * @generated from message NT.ServerPlayerSecretHourglass - */ -export class ServerPlayerSecretHourglass extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string material = 2; - */ - material = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerPlayerSecretHourglass"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "material", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerPlayerSecretHourglass { - return new ServerPlayerSecretHourglass().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerPlayerSecretHourglass { - return new ServerPlayerSecretHourglass().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerPlayerSecretHourglass { - return new ServerPlayerSecretHourglass().fromJsonString(jsonString, options); - } - - static equals(a: ServerPlayerSecretHourglass | PlainMessage | undefined, b: ServerPlayerSecretHourglass | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerPlayerSecretHourglass, a, b); - } -} - -/** - * @generated from message NT.ClientCustomModEvent - */ -export class ClientCustomModEvent extends Message { - /** - * @generated from field: string payload = 1; - */ - payload = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientCustomModEvent"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "payload", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientCustomModEvent { - return new ClientCustomModEvent().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientCustomModEvent { - return new ClientCustomModEvent().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientCustomModEvent { - return new ClientCustomModEvent().fromJsonString(jsonString, options); - } - - static equals(a: ClientCustomModEvent | PlainMessage | undefined, b: ClientCustomModEvent | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientCustomModEvent, a, b); - } -} - -/** - * @generated from message NT.ServerCustomModEvent - */ -export class ServerCustomModEvent extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string payload = 2; - */ - payload = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerCustomModEvent"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "payload", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerCustomModEvent { - return new ServerCustomModEvent().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerCustomModEvent { - return new ServerCustomModEvent().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerCustomModEvent { - return new ServerCustomModEvent().fromJsonString(jsonString, options); - } - - static equals(a: ServerCustomModEvent | PlainMessage | undefined, b: ServerCustomModEvent | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerCustomModEvent, a, b); - } -} - -/** - * @generated from message NT.ClientRespawnPenalty - */ -export class ClientRespawnPenalty extends Message { - /** - * @generated from field: uint32 deaths = 1; - */ - deaths = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRespawnPenalty"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "deaths", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRespawnPenalty { - return new ClientRespawnPenalty().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRespawnPenalty { - return new ClientRespawnPenalty().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRespawnPenalty { - return new ClientRespawnPenalty().fromJsonString(jsonString, options); - } - - static equals(a: ClientRespawnPenalty | PlainMessage | undefined, b: ClientRespawnPenalty | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRespawnPenalty, a, b); - } -} - -/** - * @generated from message NT.ServerRespawnPenalty - */ -export class ServerRespawnPenalty extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: uint32 deaths = 2; - */ - deaths = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRespawnPenalty"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "deaths", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRespawnPenalty { - return new ServerRespawnPenalty().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRespawnPenalty { - return new ServerRespawnPenalty().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRespawnPenalty { - return new ServerRespawnPenalty().fromJsonString(jsonString, options); - } - - static equals(a: ServerRespawnPenalty | PlainMessage | undefined, b: ServerRespawnPenalty | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRespawnPenalty, a, b); - } -} - -/** - * @generated from message NT.ClientAngerySteve - */ -export class ClientAngerySteve extends Message { - /** - * @generated from field: bool idk = 1; - */ - idk = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientAngerySteve"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "idk", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientAngerySteve { - return new ClientAngerySteve().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientAngerySteve { - return new ClientAngerySteve().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientAngerySteve { - return new ClientAngerySteve().fromJsonString(jsonString, options); - } - - static equals(a: ClientAngerySteve | PlainMessage | undefined, b: ClientAngerySteve | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientAngerySteve, a, b); - } -} - -/** - * @generated from message NT.ServerAngerySteve - */ -export class ServerAngerySteve extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerAngerySteve"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerAngerySteve { - return new ServerAngerySteve().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerAngerySteve { - return new ServerAngerySteve().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerAngerySteve { - return new ServerAngerySteve().fromJsonString(jsonString, options); - } - - static equals(a: ServerAngerySteve | PlainMessage | undefined, b: ServerAngerySteve | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerAngerySteve, a, b); - } -} - -/** - * @generated from message NT.Wand - */ -export class Wand extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: NT.Wand.WandStats stats = 2; - */ - stats?: Wand_WandStats; - - /** - * @generated from field: repeated NT.Spell always_cast = 3; - */ - alwaysCast: Spell[] = []; - - /** - * @generated from field: repeated NT.Spell deck = 4; - */ - deck: Spell[] = []; - - /** - * @generated from field: optional string sent_by = 5; - */ - sentBy?: string; - - /** - * @generated from field: optional string contributed_by = 6; - */ - contributedBy?: string; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Wand"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "stats", kind: "message", T: Wand_WandStats }, - { no: 3, name: "always_cast", kind: "message", T: Spell, repeated: true }, - { no: 4, name: "deck", kind: "message", T: Spell, repeated: true }, - { no: 5, name: "sent_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "contributed_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Wand { - return new Wand().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Wand { - return new Wand().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Wand { - return new Wand().fromJsonString(jsonString, options); - } - - static equals(a: Wand | PlainMessage | undefined, b: Wand | PlainMessage | undefined): boolean { - return proto3.util.equals(Wand, a, b); - } -} - -/** - * @generated from message NT.Wand.WandStats - */ -export class Wand_WandStats extends Message { - /** - * @generated from field: string sprite = 1; - */ - sprite = ""; - - /** - * @generated from field: bool named = 2; - */ - named = false; - - /** - * @generated from field: string ui_name = 3; - */ - uiName = ""; - - /** - * @generated from field: float mana_max = 4; - */ - manaMax = 0; - - /** - * @generated from field: float mana_charge_speed = 5; - */ - manaChargeSpeed = 0; - - /** - * @generated from field: int32 reload_time = 6; - */ - reloadTime = 0; - - /** - * @generated from field: uint32 actions_per_round = 7; - */ - actionsPerRound = 0; - - /** - * @generated from field: uint32 deck_capacity = 8; - */ - deckCapacity = 0; - - /** - * @generated from field: bool shuffle_deck_when_empty = 9; - */ - shuffleDeckWhenEmpty = false; - - /** - * @generated from field: float spread_degrees = 10; - */ - spreadDegrees = 0; - - /** - * @generated from field: float speed_multiplier = 11; - */ - speedMultiplier = 0; - - /** - * @generated from field: int32 fire_rate_wait = 12; - */ - fireRateWait = 0; - - /** - * @generated from field: float tip_x = 13; - */ - tipX = 0; - - /** - * @generated from field: float tip_y = 14; - */ - tipY = 0; - - /** - * @generated from field: float grip_x = 15; - */ - gripX = 0; - - /** - * @generated from field: float grip_y = 16; - */ - gripY = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Wand.WandStats"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "sprite", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "named", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 3, name: "ui_name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 4, name: "mana_max", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 5, name: "mana_charge_speed", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 6, name: "reload_time", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, - { no: 7, name: "actions_per_round", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 8, name: "deck_capacity", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 9, name: "shuffle_deck_when_empty", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 10, name: "spread_degrees", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 11, name: "speed_multiplier", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 12, name: "fire_rate_wait", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, - { no: 13, name: "tip_x", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 14, name: "tip_y", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 15, name: "grip_x", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 16, name: "grip_y", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Wand_WandStats { - return new Wand_WandStats().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Wand_WandStats { - return new Wand_WandStats().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Wand_WandStats { - return new Wand_WandStats().fromJsonString(jsonString, options); - } - - static equals(a: Wand_WandStats | PlainMessage | undefined, b: Wand_WandStats | PlainMessage | undefined): boolean { - return proto3.util.equals(Wand_WandStats, a, b); - } -} - -/** - * @generated from message NT.Spell - */ -export class Spell extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string game_id = 2; - */ - gameId = ""; - - /** - * @generated from field: optional string sent_by = 3; - */ - sentBy?: string; - - /** - * @generated from field: optional string contributed_by = 4; - */ - contributedBy?: string; - - /** - * @generated from field: int32 uses_remaining = 5; - */ - usesRemaining = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Spell"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "game_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "sent_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 4, name: "contributed_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "uses_remaining", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Spell { - return new Spell().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Spell { - return new Spell().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Spell { - return new Spell().fromJsonString(jsonString, options); - } - - static equals(a: Spell | PlainMessage | undefined, b: Spell | PlainMessage | undefined): boolean { - return proto3.util.equals(Spell, a, b); - } -} - -/** - * @generated from message NT.Item - */ -export class Item extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: NT.Item.Color color = 2; - */ - color?: Item_Color; - - /** - * @generated from field: repeated NT.Item.Material content = 3; - */ - content: Item_Material[] = []; - - /** - * @generated from field: optional string sent_by = 4; - */ - sentBy?: string; - - /** - * @generated from field: optional string contributed_by = 5; - */ - contributedBy?: string; - - /** - * @generated from field: bool is_chest = 6 [deprecated = true]; - * @deprecated - */ - isChest = false; - - /** - * @generated from field: string item_type = 7; - */ - itemType = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Item"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "color", kind: "message", T: Item_Color }, - { no: 3, name: "content", kind: "message", T: Item_Material, repeated: true }, - { no: 4, name: "sent_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "contributed_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "is_chest", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 7, name: "item_type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Item { - return new Item().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Item { - return new Item().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Item { - return new Item().fromJsonString(jsonString, options); - } - - static equals(a: Item | PlainMessage | undefined, b: Item | PlainMessage | undefined): boolean { - return proto3.util.equals(Item, a, b); - } -} - -/** - * @generated from message NT.Item.Color - */ -export class Item_Color extends Message { - /** - * @generated from field: float r = 1; - */ - r = 0; - - /** - * @generated from field: float g = 2; - */ - g = 0; - - /** - * @generated from field: float b = 3; - */ - b = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Item.Color"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "r", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 2, name: "g", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 3, name: "b", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Item_Color { - return new Item_Color().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Item_Color { - return new Item_Color().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Item_Color { - return new Item_Color().fromJsonString(jsonString, options); - } - - static equals(a: Item_Color | PlainMessage | undefined, b: Item_Color | PlainMessage | undefined): boolean { - return proto3.util.equals(Item_Color, a, b); - } -} - -/** - * @generated from message NT.Item.Material - */ -export class Item_Material extends Message { - /** - * @generated from field: uint32 id = 1; - */ - id = 0; - - /** - * @generated from field: uint32 amount = 2; - */ - amount = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.Item.Material"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 2, name: "amount", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Item_Material { - return new Item_Material().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Item_Material { - return new Item_Material().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Item_Material { - return new Item_Material().fromJsonString(jsonString, options); - } - - static equals(a: Item_Material | PlainMessage | undefined, b: Item_Material | PlainMessage | undefined): boolean { - return proto3.util.equals(Item_Material, a, b); - } -} - -/** - * @generated from message NT.EntityItem - */ -export class EntityItem extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string path = 2; - */ - path = ""; - - /** - * @generated from field: string sprite = 3; - */ - sprite = ""; - - /** - * @generated from field: optional string sent_by = 4; - */ - sentBy?: string; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.EntityItem"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "sprite", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 4, name: "sent_by", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): EntityItem { - return new EntityItem().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): EntityItem { - return new EntityItem().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): EntityItem { - return new EntityItem().fromJsonString(jsonString, options); - } - - static equals(a: EntityItem | PlainMessage | undefined, b: EntityItem | PlainMessage | undefined): boolean { - return proto3.util.equals(EntityItem, a, b); - } -} - -/** - * @generated from message NT.LobbyAction - */ -export class LobbyAction extends Message { - /** - * @generated from oneof NT.LobbyAction.action - */ - action: { - /** - * @generated from field: NT.ClientRoomCreate c_room_create = 1; - */ - value: ClientRoomCreate; - case: "cRoomCreate"; - } | { - /** - * @generated from field: NT.ServerRoomCreated s_room_created = 2; - */ - value: ServerRoomCreated; - case: "sRoomCreated"; - } | { - /** - * @generated from field: NT.ServerRoomCreateFailed s_room_create_failed = 3; - */ - value: ServerRoomCreateFailed; - case: "sRoomCreateFailed"; - } | { - /** - * @generated from field: NT.ClientRoomUpdate c_room_update = 4; - */ - value: ClientRoomUpdate; - case: "cRoomUpdate"; - } | { - /** - * @generated from field: NT.ServerRoomUpdated s_room_updated = 5; - */ - value: ServerRoomUpdated; - case: "sRoomUpdated"; - } | { - /** - * @generated from field: NT.ServerRoomUpdateFailed s_room_update_failed = 6; - */ - value: ServerRoomUpdateFailed; - case: "sRoomUpdateFailed"; - } | { - /** - * @generated from field: NT.ClientRoomFlagsUpdate c_room_flags_update = 7; - */ - value: ClientRoomFlagsUpdate; - case: "cRoomFlagsUpdate"; - } | { - /** - * @generated from field: NT.ServerRoomFlagsUpdated s_room_flags_updated = 8; - */ - value: ServerRoomFlagsUpdated; - case: "sRoomFlagsUpdated"; - } | { - /** - * @generated from field: NT.ServerRoomFlagsUpdateFailed s_room_flags_update_failed = 9; - */ - value: ServerRoomFlagsUpdateFailed; - case: "sRoomFlagsUpdateFailed"; - } | { - /** - * @generated from field: NT.ClientRoomDelete c_room_delete = 10; - */ - value: ClientRoomDelete; - case: "cRoomDelete"; - } | { - /** - * @generated from field: NT.ServerRoomDeleted s_room_deleted = 11; - */ - value: ServerRoomDeleted; - case: "sRoomDeleted"; - } | { - /** - * @generated from field: NT.ClientJoinRoom c_join_room = 12; - */ - value: ClientJoinRoom; - case: "cJoinRoom"; - } | { - /** - * @generated from field: NT.ServerJoinRoomSuccess s_join_room_success = 13; - */ - value: ServerJoinRoomSuccess; - case: "sJoinRoomSuccess"; - } | { - /** - * @generated from field: NT.ServerJoinRoomFailed s_join_room_failed = 14; - */ - value: ServerJoinRoomFailed; - case: "sJoinRoomFailed"; - } | { - /** - * @generated from field: NT.ServerUserJoinedRoom s_user_joined_room = 15; - */ - value: ServerUserJoinedRoom; - case: "sUserJoinedRoom"; - } | { - /** - * @generated from field: NT.ClientLeaveRoom c_leave_room = 16; - */ - value: ClientLeaveRoom; - case: "cLeaveRoom"; - } | { - /** - * @generated from field: NT.ServerUserLeftRoom s_user_left_room = 17; - */ - value: ServerUserLeftRoom; - case: "sUserLeftRoom"; - } | { - /** - * @generated from field: NT.ClientKickUser c_kick_user = 18; - */ - value: ClientKickUser; - case: "cKickUser"; - } | { - /** - * @generated from field: NT.ServerUserKicked s_user_kicked = 19; - */ - value: ServerUserKicked; - case: "sUserKicked"; - } | { - /** - * @generated from field: NT.ClientBanUser c_ban_user = 20; - */ - value: ClientBanUser; - case: "cBanUser"; - } | { - /** - * @generated from field: NT.ServerUserBanned s_user_banned = 21; - */ - value: ServerUserBanned; - case: "sUserBanned"; - } | { - /** - * @generated from field: NT.ClientReadyState c_ready_state = 22; - */ - value: ClientReadyState; - case: "cReadyState"; - } | { - /** - * @generated from field: NT.ServerUserReadyState s_user_ready_state = 23; - */ - value: ServerUserReadyState; - case: "sUserReadyState"; - } | { - /** - * @generated from field: NT.ClientStartRun c_start_run = 24; - */ - value: ClientStartRun; - case: "cStartRun"; - } | { - /** - * @generated from field: NT.ServerHostStart s_host_start = 25; - */ - value: ServerHostStart; - case: "sHostStart"; - } | { - /** - * @generated from field: NT.ClientRequestRoomList c_request_room_list = 27; - */ - value: ClientRequestRoomList; - case: "cRequestRoomList"; - } | { - /** - * @generated from field: NT.ServerRoomList s_room_list = 28; - */ - value: ServerRoomList; - case: "sRoomList"; - } | { - /** - * @generated from field: NT.ServerDisconnected s_disconnected = 31; - */ - value: ServerDisconnected; - case: "sDisconnected"; - } | { - /** - * @generated from field: NT.ServerRoomAddToList s_room_add_to_list = 32; - */ - value: ServerRoomAddToList; - case: "sRoomAddToList"; - } | { - /** - * @generated from field: NT.ClientRunOver c_run_over = 33; - */ - value: ClientRunOver; - case: "cRunOver"; - } | { case: undefined; value?: undefined } = { case: undefined }; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.LobbyAction"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "c_room_create", kind: "message", T: ClientRoomCreate, oneof: "action" }, - { no: 2, name: "s_room_created", kind: "message", T: ServerRoomCreated, oneof: "action" }, - { no: 3, name: "s_room_create_failed", kind: "message", T: ServerRoomCreateFailed, oneof: "action" }, - { no: 4, name: "c_room_update", kind: "message", T: ClientRoomUpdate, oneof: "action" }, - { no: 5, name: "s_room_updated", kind: "message", T: ServerRoomUpdated, oneof: "action" }, - { no: 6, name: "s_room_update_failed", kind: "message", T: ServerRoomUpdateFailed, oneof: "action" }, - { no: 7, name: "c_room_flags_update", kind: "message", T: ClientRoomFlagsUpdate, oneof: "action" }, - { no: 8, name: "s_room_flags_updated", kind: "message", T: ServerRoomFlagsUpdated, oneof: "action" }, - { no: 9, name: "s_room_flags_update_failed", kind: "message", T: ServerRoomFlagsUpdateFailed, oneof: "action" }, - { no: 10, name: "c_room_delete", kind: "message", T: ClientRoomDelete, oneof: "action" }, - { no: 11, name: "s_room_deleted", kind: "message", T: ServerRoomDeleted, oneof: "action" }, - { no: 12, name: "c_join_room", kind: "message", T: ClientJoinRoom, oneof: "action" }, - { no: 13, name: "s_join_room_success", kind: "message", T: ServerJoinRoomSuccess, oneof: "action" }, - { no: 14, name: "s_join_room_failed", kind: "message", T: ServerJoinRoomFailed, oneof: "action" }, - { no: 15, name: "s_user_joined_room", kind: "message", T: ServerUserJoinedRoom, oneof: "action" }, - { no: 16, name: "c_leave_room", kind: "message", T: ClientLeaveRoom, oneof: "action" }, - { no: 17, name: "s_user_left_room", kind: "message", T: ServerUserLeftRoom, oneof: "action" }, - { no: 18, name: "c_kick_user", kind: "message", T: ClientKickUser, oneof: "action" }, - { no: 19, name: "s_user_kicked", kind: "message", T: ServerUserKicked, oneof: "action" }, - { no: 20, name: "c_ban_user", kind: "message", T: ClientBanUser, oneof: "action" }, - { no: 21, name: "s_user_banned", kind: "message", T: ServerUserBanned, oneof: "action" }, - { no: 22, name: "c_ready_state", kind: "message", T: ClientReadyState, oneof: "action" }, - { no: 23, name: "s_user_ready_state", kind: "message", T: ServerUserReadyState, oneof: "action" }, - { no: 24, name: "c_start_run", kind: "message", T: ClientStartRun, oneof: "action" }, - { no: 25, name: "s_host_start", kind: "message", T: ServerHostStart, oneof: "action" }, - { no: 27, name: "c_request_room_list", kind: "message", T: ClientRequestRoomList, oneof: "action" }, - { no: 28, name: "s_room_list", kind: "message", T: ServerRoomList, oneof: "action" }, - { no: 31, name: "s_disconnected", kind: "message", T: ServerDisconnected, oneof: "action" }, - { no: 32, name: "s_room_add_to_list", kind: "message", T: ServerRoomAddToList, oneof: "action" }, - { no: 33, name: "c_run_over", kind: "message", T: ClientRunOver, oneof: "action" }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): LobbyAction { - return new LobbyAction().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): LobbyAction { - return new LobbyAction().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): LobbyAction { - return new LobbyAction().fromJsonString(jsonString, options); - } - - static equals(a: LobbyAction | PlainMessage | undefined, b: LobbyAction | PlainMessage | undefined): boolean { - return proto3.util.equals(LobbyAction, a, b); - } -} - -/** - * @generated from message NT.ClientRunOver - */ -export class ClientRunOver extends Message { - /** - * @generated from field: optional bool idk = 1; - */ - idk?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRunOver"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "idk", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRunOver { - return new ClientRunOver().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRunOver { - return new ClientRunOver().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRunOver { - return new ClientRunOver().fromJsonString(jsonString, options); - } - - static equals(a: ClientRunOver | PlainMessage | undefined, b: ClientRunOver | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRunOver, a, b); - } -} - -/** - * @generated from message NT.ServerDisconnected - */ -export class ServerDisconnected extends Message { - /** - * @generated from field: string reason = 1; - */ - reason = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerDisconnected"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reason", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerDisconnected { - return new ServerDisconnected().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerDisconnected { - return new ServerDisconnected().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerDisconnected { - return new ServerDisconnected().fromJsonString(jsonString, options); - } - - static equals(a: ServerDisconnected | PlainMessage | undefined, b: ServerDisconnected | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerDisconnected, a, b); - } -} - -/** - * @generated from message NT.ClientRoomDelete - */ -export class ClientRoomDelete extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRoomDelete"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRoomDelete { - return new ClientRoomDelete().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRoomDelete { - return new ClientRoomDelete().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRoomDelete { - return new ClientRoomDelete().fromJsonString(jsonString, options); - } - - static equals(a: ClientRoomDelete | PlainMessage | undefined, b: ClientRoomDelete | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRoomDelete, a, b); - } -} - -/** - * @generated from message NT.ServerRoomDeleted - */ -export class ServerRoomDeleted extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomDeleted"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomDeleted { - return new ServerRoomDeleted().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomDeleted { - return new ServerRoomDeleted().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomDeleted { - return new ServerRoomDeleted().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomDeleted | PlainMessage | undefined, b: ServerRoomDeleted | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomDeleted, a, b); - } -} - -/** - * @generated from message NT.ClientRoomCreate - */ -export class ClientRoomCreate extends Message { - /** - * @generated from field: string name = 1; - */ - name = ""; - - /** - * @generated from field: uint32 gamemode = 2; - */ - gamemode = 0; - - /** - * @generated from field: uint32 max_users = 3; - */ - maxUsers = 0; - - /** - * @generated from field: optional string password = 4; - */ - password?: string; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRoomCreate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 3, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRoomCreate { - return new ClientRoomCreate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRoomCreate { - return new ClientRoomCreate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRoomCreate { - return new ClientRoomCreate().fromJsonString(jsonString, options); - } - - static equals(a: ClientRoomCreate | PlainMessage | undefined, b: ClientRoomCreate | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRoomCreate, a, b); - } -} - -/** - * @generated from message NT.ServerRoomCreated - */ -export class ServerRoomCreated extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: uint32 gamemode = 3; - */ - gamemode = 0; - - /** - * @generated from field: uint32 max_users = 4; - */ - maxUsers = 0; - - /** - * @generated from field: optional string password = 5; - */ - password?: string; - - /** - * @generated from field: bool locked = 6; - */ - locked = false; - - /** - * @generated from field: repeated NT.ServerRoomCreated.User users = 7; - */ - users: ServerRoomCreated_User[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomCreated"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 7, name: "users", kind: "message", T: ServerRoomCreated_User, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomCreated { - return new ServerRoomCreated().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomCreated { - return new ServerRoomCreated().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomCreated { - return new ServerRoomCreated().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomCreated | PlainMessage | undefined, b: ServerRoomCreated | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomCreated, a, b); - } -} - -/** - * @generated from message NT.ServerRoomCreated.User - */ -export class ServerRoomCreated_User extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: bool ready = 3; - */ - ready = false; - - /** - * @generated from field: bool owner = 4; - */ - owner = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomCreated.User"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "ready", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 4, name: "owner", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomCreated_User { - return new ServerRoomCreated_User().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomCreated_User { - return new ServerRoomCreated_User().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomCreated_User { - return new ServerRoomCreated_User().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomCreated_User | PlainMessage | undefined, b: ServerRoomCreated_User | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomCreated_User, a, b); - } -} - -/** - * @generated from message NT.ServerRoomCreateFailed - */ -export class ServerRoomCreateFailed extends Message { - /** - * @generated from field: string reason = 1; - */ - reason = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomCreateFailed"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reason", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomCreateFailed { - return new ServerRoomCreateFailed().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomCreateFailed { - return new ServerRoomCreateFailed().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomCreateFailed { - return new ServerRoomCreateFailed().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomCreateFailed | PlainMessage | undefined, b: ServerRoomCreateFailed | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomCreateFailed, a, b); - } -} - -/** - * @generated from message NT.ClientRoomUpdate - */ -export class ClientRoomUpdate extends Message { - /** - * @generated from field: optional string name = 1; - */ - name?: string; - - /** - * @generated from field: optional uint32 gamemode = 2; - */ - gamemode?: number; - - /** - * @generated from field: optional uint32 max_users = 3; - */ - maxUsers?: number; - - /** - * @generated from field: optional string password = 4; - */ - password?: string; - - /** - * @generated from field: optional bool locked = 5; - */ - locked?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRoomUpdate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 2, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - { no: 3, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - { no: 4, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRoomUpdate { - return new ClientRoomUpdate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRoomUpdate { - return new ClientRoomUpdate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRoomUpdate { - return new ClientRoomUpdate().fromJsonString(jsonString, options); - } - - static equals(a: ClientRoomUpdate | PlainMessage | undefined, b: ClientRoomUpdate | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRoomUpdate, a, b); - } -} - -/** - * @generated from message NT.ServerRoomUpdated - */ -export class ServerRoomUpdated extends Message { - /** - * @generated from field: optional string name = 1; - */ - name?: string; - - /** - * @generated from field: optional uint32 gamemode = 2; - */ - gamemode?: number; - - /** - * @generated from field: optional uint32 max_users = 3; - */ - maxUsers?: number; - - /** - * @generated from field: optional string password = 4; - */ - password?: string; - - /** - * @generated from field: optional bool locked = 5; - */ - locked?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomUpdated"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 2, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - { no: 3, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - { no: 4, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomUpdated { - return new ServerRoomUpdated().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomUpdated { - return new ServerRoomUpdated().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomUpdated { - return new ServerRoomUpdated().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomUpdated | PlainMessage | undefined, b: ServerRoomUpdated | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomUpdated, a, b); - } -} - -/** - * @generated from message NT.ServerRoomUpdateFailed - */ -export class ServerRoomUpdateFailed extends Message { - /** - * @generated from field: string reason = 1; - */ - reason = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomUpdateFailed"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reason", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomUpdateFailed { - return new ServerRoomUpdateFailed().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomUpdateFailed { - return new ServerRoomUpdateFailed().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomUpdateFailed { - return new ServerRoomUpdateFailed().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomUpdateFailed | PlainMessage | undefined, b: ServerRoomUpdateFailed | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomUpdateFailed, a, b); - } -} - -/** - * @generated from message NT.ClientRoomFlagsUpdate - */ -export class ClientRoomFlagsUpdate extends Message { - /** - * @generated from field: repeated NT.ClientRoomFlagsUpdate.GameFlag flags = 1; - */ - flags: ClientRoomFlagsUpdate_GameFlag[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRoomFlagsUpdate"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "flags", kind: "message", T: ClientRoomFlagsUpdate_GameFlag, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRoomFlagsUpdate { - return new ClientRoomFlagsUpdate().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRoomFlagsUpdate { - return new ClientRoomFlagsUpdate().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRoomFlagsUpdate { - return new ClientRoomFlagsUpdate().fromJsonString(jsonString, options); - } - - static equals(a: ClientRoomFlagsUpdate | PlainMessage | undefined, b: ClientRoomFlagsUpdate | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRoomFlagsUpdate, a, b); - } -} - -/** - * @generated from message NT.ClientRoomFlagsUpdate.GameFlag - */ -export class ClientRoomFlagsUpdate_GameFlag extends Message { - /** - * TODO: This seems like a hack, please improve it - * - * @generated from field: string flag = 1; - */ - flag = ""; - - /** - * @generated from field: optional int32 int_val = 2; - */ - intVal?: number; - - /** - * @generated from field: optional string str_val = 3; - */ - strVal?: string; - - /** - * @generated from field: optional float float_val = 4; - */ - floatVal?: number; - - /** - * @generated from field: optional bool bool_val = 5; - */ - boolVal?: boolean; - - /** - * @generated from field: optional uint32 u_int_val = 6; - */ - uIntVal?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRoomFlagsUpdate.GameFlag"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "flag", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "int_val", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - { no: 3, name: "str_val", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 4, name: "float_val", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 5, name: "bool_val", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - { no: 6, name: "u_int_val", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRoomFlagsUpdate_GameFlag { - return new ClientRoomFlagsUpdate_GameFlag().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRoomFlagsUpdate_GameFlag { - return new ClientRoomFlagsUpdate_GameFlag().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRoomFlagsUpdate_GameFlag { - return new ClientRoomFlagsUpdate_GameFlag().fromJsonString(jsonString, options); - } - - static equals(a: ClientRoomFlagsUpdate_GameFlag | PlainMessage | undefined, b: ClientRoomFlagsUpdate_GameFlag | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRoomFlagsUpdate_GameFlag, a, b); - } -} - -/** - * @generated from message NT.ServerRoomFlagsUpdated - */ -export class ServerRoomFlagsUpdated extends Message { - /** - * @generated from field: repeated NT.ServerRoomFlagsUpdated.GameFlag flags = 1; - */ - flags: ServerRoomFlagsUpdated_GameFlag[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomFlagsUpdated"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "flags", kind: "message", T: ServerRoomFlagsUpdated_GameFlag, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomFlagsUpdated { - return new ServerRoomFlagsUpdated().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomFlagsUpdated { - return new ServerRoomFlagsUpdated().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomFlagsUpdated { - return new ServerRoomFlagsUpdated().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomFlagsUpdated | PlainMessage | undefined, b: ServerRoomFlagsUpdated | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomFlagsUpdated, a, b); - } -} - -/** - * @generated from message NT.ServerRoomFlagsUpdated.GameFlag - */ -export class ServerRoomFlagsUpdated_GameFlag extends Message { - /** - * @generated from field: string flag = 1; - */ - flag = ""; - - /** - * @generated from field: optional int32 int_val = 2; - */ - intVal?: number; - - /** - * @generated from field: optional string str_val = 3; - */ - strVal?: string; - - /** - * @generated from field: optional float float_val = 4; - */ - floatVal?: number; - - /** - * @generated from field: optional bool bool_val = 5; - */ - boolVal?: boolean; - - /** - * @generated from field: optional uint32 u_int_val = 6; - */ - uIntVal?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomFlagsUpdated.GameFlag"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "flag", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "int_val", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - { no: 3, name: "str_val", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 4, name: "float_val", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true }, - { no: 5, name: "bool_val", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - { no: 6, name: "u_int_val", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomFlagsUpdated_GameFlag { - return new ServerRoomFlagsUpdated_GameFlag().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomFlagsUpdated_GameFlag { - return new ServerRoomFlagsUpdated_GameFlag().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomFlagsUpdated_GameFlag { - return new ServerRoomFlagsUpdated_GameFlag().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomFlagsUpdated_GameFlag | PlainMessage | undefined, b: ServerRoomFlagsUpdated_GameFlag | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomFlagsUpdated_GameFlag, a, b); - } -} - -/** - * @generated from message NT.ServerRoomFlagsUpdateFailed - */ -export class ServerRoomFlagsUpdateFailed extends Message { - /** - * @generated from field: string reason = 1; - */ - reason = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomFlagsUpdateFailed"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reason", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomFlagsUpdateFailed { - return new ServerRoomFlagsUpdateFailed().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomFlagsUpdateFailed { - return new ServerRoomFlagsUpdateFailed().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomFlagsUpdateFailed { - return new ServerRoomFlagsUpdateFailed().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomFlagsUpdateFailed | PlainMessage | undefined, b: ServerRoomFlagsUpdateFailed | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomFlagsUpdateFailed, a, b); - } -} - -/** - * @generated from message NT.ClientJoinRoom - */ -export class ClientJoinRoom extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: optional string password = 2; - */ - password?: string; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientJoinRoom"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientJoinRoom { - return new ClientJoinRoom().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientJoinRoom { - return new ClientJoinRoom().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientJoinRoom { - return new ClientJoinRoom().fromJsonString(jsonString, options); - } - - static equals(a: ClientJoinRoom | PlainMessage | undefined, b: ClientJoinRoom | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientJoinRoom, a, b); - } -} - -/** - * @generated from message NT.ServerJoinRoomSuccess - */ -export class ServerJoinRoomSuccess extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: uint32 gamemode = 3; - */ - gamemode = 0; - - /** - * @generated from field: uint32 max_users = 4; - */ - maxUsers = 0; - - /** - * @generated from field: optional string password = 5; - */ - password?: string; - - /** - * @generated from field: bool locked = 6; - */ - locked = false; - - /** - * @generated from field: repeated NT.ServerJoinRoomSuccess.User users = 7; - */ - users: ServerJoinRoomSuccess_User[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerJoinRoomSuccess"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 7, name: "users", kind: "message", T: ServerJoinRoomSuccess_User, repeated: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerJoinRoomSuccess { - return new ServerJoinRoomSuccess().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerJoinRoomSuccess { - return new ServerJoinRoomSuccess().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerJoinRoomSuccess { - return new ServerJoinRoomSuccess().fromJsonString(jsonString, options); - } - - static equals(a: ServerJoinRoomSuccess | PlainMessage | undefined, b: ServerJoinRoomSuccess | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerJoinRoomSuccess, a, b); - } -} - -/** - * @generated from message NT.ServerJoinRoomSuccess.User - */ -export class ServerJoinRoomSuccess_User extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: bool ready = 3; - */ - ready = false; - - /** - * @generated from field: bool owner = 4; - */ - owner = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerJoinRoomSuccess.User"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "ready", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 4, name: "owner", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerJoinRoomSuccess_User { - return new ServerJoinRoomSuccess_User().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerJoinRoomSuccess_User { - return new ServerJoinRoomSuccess_User().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerJoinRoomSuccess_User { - return new ServerJoinRoomSuccess_User().fromJsonString(jsonString, options); - } - - static equals(a: ServerJoinRoomSuccess_User | PlainMessage | undefined, b: ServerJoinRoomSuccess_User | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerJoinRoomSuccess_User, a, b); - } -} - -/** - * @generated from message NT.ServerJoinRoomFailed - */ -export class ServerJoinRoomFailed extends Message { - /** - * @generated from field: string reason = 1; - */ - reason = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerJoinRoomFailed"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "reason", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerJoinRoomFailed { - return new ServerJoinRoomFailed().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerJoinRoomFailed { - return new ServerJoinRoomFailed().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerJoinRoomFailed { - return new ServerJoinRoomFailed().fromJsonString(jsonString, options); - } - - static equals(a: ServerJoinRoomFailed | PlainMessage | undefined, b: ServerJoinRoomFailed | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerJoinRoomFailed, a, b); - } -} - -/** - * @generated from message NT.ServerUserJoinedRoom - */ -export class ServerUserJoinedRoom extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerUserJoinedRoom"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerUserJoinedRoom { - return new ServerUserJoinedRoom().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerUserJoinedRoom { - return new ServerUserJoinedRoom().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerUserJoinedRoom { - return new ServerUserJoinedRoom().fromJsonString(jsonString, options); - } - - static equals(a: ServerUserJoinedRoom | PlainMessage | undefined, b: ServerUserJoinedRoom | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerUserJoinedRoom, a, b); - } -} - -/** - * @generated from message NT.ClientLeaveRoom - */ -export class ClientLeaveRoom extends Message { - /** - * should be empty msg - * - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientLeaveRoom"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientLeaveRoom { - return new ClientLeaveRoom().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientLeaveRoom { - return new ClientLeaveRoom().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientLeaveRoom { - return new ClientLeaveRoom().fromJsonString(jsonString, options); - } - - static equals(a: ClientLeaveRoom | PlainMessage | undefined, b: ClientLeaveRoom | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientLeaveRoom, a, b); - } -} - -/** - * @generated from message NT.ServerUserLeftRoom - */ -export class ServerUserLeftRoom extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerUserLeftRoom"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerUserLeftRoom { - return new ServerUserLeftRoom().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerUserLeftRoom { - return new ServerUserLeftRoom().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerUserLeftRoom { - return new ServerUserLeftRoom().fromJsonString(jsonString, options); - } - - static equals(a: ServerUserLeftRoom | PlainMessage | undefined, b: ServerUserLeftRoom | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerUserLeftRoom, a, b); - } -} - -/** - * @generated from message NT.ClientKickUser - */ -export class ClientKickUser extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientKickUser"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientKickUser { - return new ClientKickUser().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientKickUser { - return new ClientKickUser().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientKickUser { - return new ClientKickUser().fromJsonString(jsonString, options); - } - - static equals(a: ClientKickUser | PlainMessage | undefined, b: ClientKickUser | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientKickUser, a, b); - } -} - -/** - * @generated from message NT.ServerUserKicked - */ -export class ServerUserKicked extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerUserKicked"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerUserKicked { - return new ServerUserKicked().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerUserKicked { - return new ServerUserKicked().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerUserKicked { - return new ServerUserKicked().fromJsonString(jsonString, options); - } - - static equals(a: ServerUserKicked | PlainMessage | undefined, b: ServerUserKicked | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerUserKicked, a, b); - } -} - -/** - * @generated from message NT.ClientBanUser - */ -export class ClientBanUser extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientBanUser"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientBanUser { - return new ClientBanUser().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientBanUser { - return new ClientBanUser().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientBanUser { - return new ClientBanUser().fromJsonString(jsonString, options); - } - - static equals(a: ClientBanUser | PlainMessage | undefined, b: ClientBanUser | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientBanUser, a, b); - } -} - -/** - * @generated from message NT.ServerUserBanned - */ -export class ServerUserBanned extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerUserBanned"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerUserBanned { - return new ServerUserBanned().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerUserBanned { - return new ServerUserBanned().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerUserBanned { - return new ServerUserBanned().fromJsonString(jsonString, options); - } - - static equals(a: ServerUserBanned | PlainMessage | undefined, b: ServerUserBanned | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerUserBanned, a, b); - } -} - -/** - * @generated from message NT.ClientReadyState - */ -export class ClientReadyState extends Message { - /** - * @generated from field: bool ready = 1; - */ - ready = false; - - /** - * @generated from field: optional string seed = 2; - */ - seed?: string; - - /** - * @generated from field: repeated string mods = 3; - */ - mods: string[] = []; - - /** - * @generated from field: optional string version = 4; - */ - version?: string; - - /** - * @generated from field: optional bool beta = 5; - */ - beta?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientReadyState"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "ready", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 2, name: "seed", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 3, name: "mods", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, - { no: 4, name: "version", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "beta", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientReadyState { - return new ClientReadyState().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientReadyState { - return new ClientReadyState().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientReadyState { - return new ClientReadyState().fromJsonString(jsonString, options); - } - - static equals(a: ClientReadyState | PlainMessage | undefined, b: ClientReadyState | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientReadyState, a, b); - } -} - -/** - * @generated from message NT.ServerUserReadyState - */ -export class ServerUserReadyState extends Message { - /** - * @generated from field: string user_id = 1; - */ - userId = ""; - - /** - * @generated from field: bool ready = 2; - */ - ready = false; - - /** - * @generated from field: optional string seed = 3; - */ - seed?: string; - - /** - * @generated from field: repeated string mods = 4; - */ - mods: string[] = []; - - /** - * @generated from field: optional string version = 5; - */ - version?: string; - - /** - * @generated from field: optional bool beta = 6; - */ - beta?: boolean; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerUserReadyState"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "ready", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 3, name: "seed", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 4, name: "mods", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, - { no: 5, name: "version", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "beta", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerUserReadyState { - return new ServerUserReadyState().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerUserReadyState { - return new ServerUserReadyState().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerUserReadyState { - return new ServerUserReadyState().fromJsonString(jsonString, options); - } - - static equals(a: ServerUserReadyState | PlainMessage | undefined, b: ServerUserReadyState | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerUserReadyState, a, b); - } -} - -/** - * @generated from message NT.ClientStartRun - */ -export class ClientStartRun extends Message { - /** - * @generated from field: bool forced = 1; - */ - forced = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientStartRun"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "forced", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientStartRun { - return new ClientStartRun().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientStartRun { - return new ClientStartRun().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientStartRun { - return new ClientStartRun().fromJsonString(jsonString, options); - } - - static equals(a: ClientStartRun | PlainMessage | undefined, b: ClientStartRun | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientStartRun, a, b); - } -} - -/** - * @generated from message NT.ServerHostStart - */ -export class ServerHostStart extends Message { - /** - * @generated from field: bool forced = 1; - */ - forced = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerHostStart"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "forced", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerHostStart { - return new ServerHostStart().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerHostStart { - return new ServerHostStart().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerHostStart { - return new ServerHostStart().fromJsonString(jsonString, options); - } - - static equals(a: ServerHostStart | PlainMessage | undefined, b: ServerHostStart | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerHostStart, a, b); - } -} - -/** - * @generated from message NT.ClientRequestRoomList - */ -export class ClientRequestRoomList extends Message { - /** - * @generated from field: uint32 page = 1; - */ - page = 0; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ClientRequestRoomList"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "page", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ClientRequestRoomList { - return new ClientRequestRoomList().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ClientRequestRoomList { - return new ClientRequestRoomList().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ClientRequestRoomList { - return new ClientRequestRoomList().fromJsonString(jsonString, options); - } - - static equals(a: ClientRequestRoomList | PlainMessage | undefined, b: ClientRequestRoomList | PlainMessage | undefined): boolean { - return proto3.util.equals(ClientRequestRoomList, a, b); - } -} - -/** - * @generated from message NT.ServerRoomList - */ -export class ServerRoomList extends Message { - /** - * @generated from field: repeated NT.ServerRoomList.Room rooms = 1; - */ - rooms: ServerRoomList_Room[] = []; - - /** - * @generated from field: optional uint32 pages = 2; - */ - pages?: number; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomList"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "rooms", kind: "message", T: ServerRoomList_Room, repeated: true }, - { no: 2, name: "pages", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomList { - return new ServerRoomList().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomList { - return new ServerRoomList().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomList { - return new ServerRoomList().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomList | PlainMessage | undefined, b: ServerRoomList | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomList, a, b); - } -} - -/** - * @generated from message NT.ServerRoomList.Room - */ -export class ServerRoomList_Room extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: uint32 gamemode = 3; - */ - gamemode = 0; - - /** - * @generated from field: uint32 cur_users = 4; - */ - curUsers = 0; - - /** - * @generated from field: uint32 max_users = 5; - */ - maxUsers = 0; - - /** - * @generated from field: bool protected = 6; - */ - protected = false; - - /** - * @generated from field: string owner = 7; - */ - owner = ""; - - /** - * @generated from field: bool locked = 8; - */ - locked = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomList.Room"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "cur_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 6, name: "protected", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 7, name: "owner", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 8, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomList_Room { - return new ServerRoomList_Room().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomList_Room { - return new ServerRoomList_Room().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomList_Room { - return new ServerRoomList_Room().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomList_Room | PlainMessage | undefined, b: ServerRoomList_Room | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomList_Room, a, b); - } -} - -/** - * @generated from message NT.ServerRoomAddToList - */ -export class ServerRoomAddToList extends Message { - /** - * @generated from field: NT.ServerRoomAddToList.Room room = 1; - */ - room?: ServerRoomAddToList_Room; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomAddToList"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "room", kind: "message", T: ServerRoomAddToList_Room }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomAddToList { - return new ServerRoomAddToList().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomAddToList { - return new ServerRoomAddToList().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomAddToList { - return new ServerRoomAddToList().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomAddToList | PlainMessage | undefined, b: ServerRoomAddToList | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomAddToList, a, b); - } -} - -/** - * @generated from message NT.ServerRoomAddToList.Room - */ -export class ServerRoomAddToList_Room extends Message { - /** - * @generated from field: string id = 1; - */ - id = ""; - - /** - * @generated from field: string name = 2; - */ - name = ""; - - /** - * @generated from field: uint32 gamemode = 3; - */ - gamemode = 0; - - /** - * @generated from field: uint32 cur_users = 4; - */ - curUsers = 0; - - /** - * @generated from field: uint32 max_users = 5; - */ - maxUsers = 0; - - /** - * @generated from field: bool protected = 6; - */ - protected = false; - - /** - * @generated from field: string owner = 7; - */ - owner = ""; - - /** - * @generated from field: bool locked = 8; - */ - locked = false; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "NT.ServerRoomAddToList.Room"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "gamemode", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 4, name: "cur_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 5, name: "max_users", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 6, name: "protected", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 7, name: "owner", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 8, name: "locked", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): ServerRoomAddToList_Room { - return new ServerRoomAddToList_Room().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): ServerRoomAddToList_Room { - return new ServerRoomAddToList_Room().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): ServerRoomAddToList_Room { - return new ServerRoomAddToList_Room().fromJsonString(jsonString, options); - } - - static equals(a: ServerRoomAddToList_Room | PlainMessage | undefined, b: ServerRoomAddToList_Room | PlainMessage | undefined): boolean { - return proto3.util.equals(ServerRoomAddToList_Room, a, b); - } -} - diff --git a/src/pbreflect.ts b/src/pbreflect.ts new file mode 100644 index 0000000..3f18fcb --- /dev/null +++ b/src/pbreflect.ts @@ -0,0 +1,29 @@ +import def from './gen/pbjs_pb.json'; + +const NT = def.nested.NT.nested; + +type FieldList = { [key: string]: { type: string; id: number } }; +type FieldIds = { [K in keyof T]: T[K]['id'] } & unknown; +type MessageIds = { + [K in keyof T]: FieldIds; +} & unknown; + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; + +export const Messages: MessageIds = Object.create(null) as any; +for (const [msgName, defs] of Object.entries(NT) as [keyof typeof NT, (typeof NT)[keyof typeof NT]][]) { + if (!defs.fields) continue; + + const fields: UnionToIntersection> = Object.create(null) as any; + for (const [fieldName, nameid] of Object.entries(defs.fields) as [keyof typeof fields, { id: number }][]) { + fields[fieldName] = nameid.id; + } + Messages[msgName] = fields; +} + +export const gameActions = Object.keys( + def.nested.NT.nested.GameAction.fields, +) as (keyof typeof def.nested.NT.nested.GameAction.fields)[]; +export const lobbyActions = Object.keys( + def.nested.NT.nested.LobbyAction.fields, +) as (keyof typeof def.nested.NT.nested.LobbyAction.fields)[]; diff --git a/src/protohax/fixtures/generate.sh b/src/protohax/fixtures/generate.sh index 1ff2c03..37fe299 100755 --- a/src/protohax/fixtures/generate.sh +++ b/src/protohax/fixtures/generate.sh @@ -2,4 +2,9 @@ HERE="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -(cd "$HERE/../../.." && protoc -I=. --plugin ./node_modules/.bin/protoc-gen-es --es_out . --es_opt target=ts ./src/protohax/fixtures/protohax.proto) \ No newline at end of file +( + cd "$HERE/../../.." && \ + npx pbjs --es6 -w es6 -t static-module ./src/protohax/fixtures/protohax.proto -o ./src/protohax/fixtures/protohax_pb.js && \ + npx pbts -o ./src/protohax/fixtures/protohax_pb.d.ts ./src/protohax/fixtures/protohax_pb.js && \ + npx pbjs -t json ./src/protohax/fixtures/protohax.proto -o ./src/protohax/fixtures/protohax_pb.json +) \ No newline at end of file diff --git a/src/protohax/fixtures/protohax_pb.d.ts b/src/protohax/fixtures/protohax_pb.d.ts new file mode 100644 index 0000000..b4d95e6 --- /dev/null +++ b/src/protohax/fixtures/protohax_pb.d.ts @@ -0,0 +1,164 @@ +import * as $protobuf from "protobufjs"; +import Long = require("long"); +/** Enum enum. */ +export enum Enum { + ENUM_UNSPECIFIED = 0, + ENUM_ONE = 1, + ENUM_TWO = 2 +} + +/** Represents a Message. */ +export class Message implements IMessage { + + /** + * Constructs a new Message. + * @param [properties] Properties to set + */ + constructor(properties?: IMessage); + + /** Message lMessage. */ + public lMessage?: (IMessage|null); + + /** Message singleInt32. */ + public singleInt32: number; + + /** Message singleInt64. */ + public singleInt64: (number|Long); + + /** Message singleUint32. */ + public singleUint32: number; + + /** Message singleUint64. */ + public singleUint64: (number|Long); + + /** Message singleSint32. */ + public singleSint32: number; + + /** Message singleSint64. */ + public singleSint64: (number|Long); + + /** Message singleBool. */ + public singleBool: boolean; + + /** Message singleEnum. */ + public singleEnum: Enum; + + /** Message singleFixed64. */ + public singleFixed64: (number|Long); + + /** Message singleSfixed64. */ + public singleSfixed64: (number|Long); + + /** Message singleDouble. */ + public singleDouble: number; + + /** Message singleString. */ + public singleString: string; + + /** Message singleBytes. */ + public singleBytes: Uint8Array; + + /** Message singleFixed32. */ + public singleFixed32: number; + + /** Message singleSfixed32. */ + public singleSfixed32: number; + + /** Message singleFloat. */ + public singleFloat: number; + + /** Message singleMessage. */ + public singleMessage?: (IMessage|null); + + /** Message repeatedInt32. */ + public repeatedInt32: number[]; + + /** Message repeatedString. */ + public repeatedString: string[]; + + /** Message repeatedBytes. */ + public repeatedBytes: Uint8Array[]; + + /** Message repeatedMessage. */ + public repeatedMessage: IMessage[]; + + /** Message unpackedInt32. */ + public unpackedInt32: number[]; + + /** + * Creates a new Message instance using the specified properties. + * @param [properties] Properties to set + * @returns Message instance + */ + public static create(properties?: IMessage): Message; + + /** + * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. + * @param message Message message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: IMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified Message message, length delimited. Does not implicitly {@link Message.verify|verify} messages. + * @param message Message message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: IMessage, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Message message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): Message; + + /** + * Decodes a Message message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): Message; + + /** + * Verifies a Message message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a Message message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns Message + */ + public static fromObject(object: { [k: string]: any }): Message; + + /** + * Creates a plain object from a Message message. Also converts values to other types if specified. + * @param message Message + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: Message, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Message to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for Message + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; +} diff --git a/src/protohax/fixtures/protohax_pb.js b/src/protohax/fixtures/protohax_pb.js new file mode 100644 index 0000000..ab16be3 --- /dev/null +++ b/src/protohax/fixtures/protohax_pb.js @@ -0,0 +1,952 @@ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +import * as $protobuf from "protobufjs/minimal"; + +// Common aliases +const $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + +// Exported root namespace +const $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + +/** + * Enum enum. + * @exports Enum + * @enum {number} + * @property {number} ENUM_UNSPECIFIED=0 ENUM_UNSPECIFIED value + * @property {number} ENUM_ONE=1 ENUM_ONE value + * @property {number} ENUM_TWO=2 ENUM_TWO value + */ +export const Enum = $root.Enum = (() => { + const valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "ENUM_UNSPECIFIED"] = 0; + values[valuesById[1] = "ENUM_ONE"] = 1; + values[valuesById[2] = "ENUM_TWO"] = 2; + return values; +})(); + +export const Message = $root.Message = (() => { + + /** + * Properties of a Message. + * @exports IMessage + * @interface IMessage + * @property {IMessage|null} [lMessage] Message lMessage + * @property {number|null} [singleInt32] Message singleInt32 + * @property {number|Long|null} [singleInt64] Message singleInt64 + * @property {number|null} [singleUint32] Message singleUint32 + * @property {number|Long|null} [singleUint64] Message singleUint64 + * @property {number|null} [singleSint32] Message singleSint32 + * @property {number|Long|null} [singleSint64] Message singleSint64 + * @property {boolean|null} [singleBool] Message singleBool + * @property {Enum|null} [singleEnum] Message singleEnum + * @property {number|Long|null} [singleFixed64] Message singleFixed64 + * @property {number|Long|null} [singleSfixed64] Message singleSfixed64 + * @property {number|null} [singleDouble] Message singleDouble + * @property {string|null} [singleString] Message singleString + * @property {Uint8Array|null} [singleBytes] Message singleBytes + * @property {number|null} [singleFixed32] Message singleFixed32 + * @property {number|null} [singleSfixed32] Message singleSfixed32 + * @property {number|null} [singleFloat] Message singleFloat + * @property {IMessage|null} [singleMessage] Message singleMessage + * @property {Array.|null} [repeatedInt32] Message repeatedInt32 + * @property {Array.|null} [repeatedString] Message repeatedString + * @property {Array.|null} [repeatedBytes] Message repeatedBytes + * @property {Array.|null} [repeatedMessage] Message repeatedMessage + * @property {Array.|null} [unpackedInt32] Message unpackedInt32 + */ + + /** + * Constructs a new Message. + * @exports Message + * @classdesc Represents a Message. + * @implements IMessage + * @constructor + * @param {IMessage=} [properties] Properties to set + */ + function Message(properties) { + this.repeatedInt32 = []; + this.repeatedString = []; + this.repeatedBytes = []; + this.repeatedMessage = []; + this.unpackedInt32 = []; + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Message lMessage. + * @member {IMessage|null|undefined} lMessage + * @memberof Message + * @instance + */ + Message.prototype.lMessage = null; + + /** + * Message singleInt32. + * @member {number} singleInt32 + * @memberof Message + * @instance + */ + Message.prototype.singleInt32 = 0; + + /** + * Message singleInt64. + * @member {number|Long} singleInt64 + * @memberof Message + * @instance + */ + Message.prototype.singleInt64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Message singleUint32. + * @member {number} singleUint32 + * @memberof Message + * @instance + */ + Message.prototype.singleUint32 = 0; + + /** + * Message singleUint64. + * @member {number|Long} singleUint64 + * @memberof Message + * @instance + */ + Message.prototype.singleUint64 = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Message singleSint32. + * @member {number} singleSint32 + * @memberof Message + * @instance + */ + Message.prototype.singleSint32 = 0; + + /** + * Message singleSint64. + * @member {number|Long} singleSint64 + * @memberof Message + * @instance + */ + Message.prototype.singleSint64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Message singleBool. + * @member {boolean} singleBool + * @memberof Message + * @instance + */ + Message.prototype.singleBool = false; + + /** + * Message singleEnum. + * @member {Enum} singleEnum + * @memberof Message + * @instance + */ + Message.prototype.singleEnum = 0; + + /** + * Message singleFixed64. + * @member {number|Long} singleFixed64 + * @memberof Message + * @instance + */ + Message.prototype.singleFixed64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Message singleSfixed64. + * @member {number|Long} singleSfixed64 + * @memberof Message + * @instance + */ + Message.prototype.singleSfixed64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; + + /** + * Message singleDouble. + * @member {number} singleDouble + * @memberof Message + * @instance + */ + Message.prototype.singleDouble = 0; + + /** + * Message singleString. + * @member {string} singleString + * @memberof Message + * @instance + */ + Message.prototype.singleString = ""; + + /** + * Message singleBytes. + * @member {Uint8Array} singleBytes + * @memberof Message + * @instance + */ + Message.prototype.singleBytes = $util.newBuffer([]); + + /** + * Message singleFixed32. + * @member {number} singleFixed32 + * @memberof Message + * @instance + */ + Message.prototype.singleFixed32 = 0; + + /** + * Message singleSfixed32. + * @member {number} singleSfixed32 + * @memberof Message + * @instance + */ + Message.prototype.singleSfixed32 = 0; + + /** + * Message singleFloat. + * @member {number} singleFloat + * @memberof Message + * @instance + */ + Message.prototype.singleFloat = 0; + + /** + * Message singleMessage. + * @member {IMessage|null|undefined} singleMessage + * @memberof Message + * @instance + */ + Message.prototype.singleMessage = null; + + /** + * Message repeatedInt32. + * @member {Array.} repeatedInt32 + * @memberof Message + * @instance + */ + Message.prototype.repeatedInt32 = $util.emptyArray; + + /** + * Message repeatedString. + * @member {Array.} repeatedString + * @memberof Message + * @instance + */ + Message.prototype.repeatedString = $util.emptyArray; + + /** + * Message repeatedBytes. + * @member {Array.} repeatedBytes + * @memberof Message + * @instance + */ + Message.prototype.repeatedBytes = $util.emptyArray; + + /** + * Message repeatedMessage. + * @member {Array.} repeatedMessage + * @memberof Message + * @instance + */ + Message.prototype.repeatedMessage = $util.emptyArray; + + /** + * Message unpackedInt32. + * @member {Array.} unpackedInt32 + * @memberof Message + * @instance + */ + Message.prototype.unpackedInt32 = $util.emptyArray; + + /** + * Creates a new Message instance using the specified properties. + * @function create + * @memberof Message + * @static + * @param {IMessage=} [properties] Properties to set + * @returns {Message} Message instance + */ + Message.create = function create(properties) { + return new Message(properties); + }; + + /** + * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. + * @function encode + * @memberof Message + * @static + * @param {IMessage} message Message message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Message.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.lMessage != null && Object.hasOwnProperty.call(message, "lMessage")) + $root.Message.encode(message.lMessage, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.singleInt32 != null && Object.hasOwnProperty.call(message, "singleInt32")) + writer.uint32(/* id 11, wireType 0 =*/88).int32(message.singleInt32); + if (message.singleInt64 != null && Object.hasOwnProperty.call(message, "singleInt64")) + writer.uint32(/* id 12, wireType 0 =*/96).int64(message.singleInt64); + if (message.singleUint32 != null && Object.hasOwnProperty.call(message, "singleUint32")) + writer.uint32(/* id 13, wireType 0 =*/104).uint32(message.singleUint32); + if (message.singleUint64 != null && Object.hasOwnProperty.call(message, "singleUint64")) + writer.uint32(/* id 14, wireType 0 =*/112).uint64(message.singleUint64); + if (message.singleSint32 != null && Object.hasOwnProperty.call(message, "singleSint32")) + writer.uint32(/* id 15, wireType 0 =*/120).sint32(message.singleSint32); + if (message.singleSint64 != null && Object.hasOwnProperty.call(message, "singleSint64")) + writer.uint32(/* id 16, wireType 0 =*/128).sint64(message.singleSint64); + if (message.singleBool != null && Object.hasOwnProperty.call(message, "singleBool")) + writer.uint32(/* id 17, wireType 0 =*/136).bool(message.singleBool); + if (message.singleEnum != null && Object.hasOwnProperty.call(message, "singleEnum")) + writer.uint32(/* id 18, wireType 0 =*/144).int32(message.singleEnum); + if (message.singleFixed64 != null && Object.hasOwnProperty.call(message, "singleFixed64")) + writer.uint32(/* id 19, wireType 1 =*/153).fixed64(message.singleFixed64); + if (message.singleSfixed64 != null && Object.hasOwnProperty.call(message, "singleSfixed64")) + writer.uint32(/* id 20, wireType 1 =*/161).sfixed64(message.singleSfixed64); + if (message.singleDouble != null && Object.hasOwnProperty.call(message, "singleDouble")) + writer.uint32(/* id 21, wireType 1 =*/169).double(message.singleDouble); + if (message.singleString != null && Object.hasOwnProperty.call(message, "singleString")) + writer.uint32(/* id 22, wireType 2 =*/178).string(message.singleString); + if (message.singleBytes != null && Object.hasOwnProperty.call(message, "singleBytes")) + writer.uint32(/* id 23, wireType 2 =*/186).bytes(message.singleBytes); + if (message.singleFixed32 != null && Object.hasOwnProperty.call(message, "singleFixed32")) + writer.uint32(/* id 24, wireType 5 =*/197).fixed32(message.singleFixed32); + if (message.singleSfixed32 != null && Object.hasOwnProperty.call(message, "singleSfixed32")) + writer.uint32(/* id 25, wireType 5 =*/205).sfixed32(message.singleSfixed32); + if (message.singleFloat != null && Object.hasOwnProperty.call(message, "singleFloat")) + writer.uint32(/* id 26, wireType 5 =*/213).float(message.singleFloat); + if (message.singleMessage != null && Object.hasOwnProperty.call(message, "singleMessage")) + $root.Message.encode(message.singleMessage, writer.uint32(/* id 27, wireType 2 =*/218).fork()).ldelim(); + if (message.repeatedInt32 != null && message.repeatedInt32.length) { + writer.uint32(/* id 111, wireType 2 =*/890).fork(); + for (let i = 0; i < message.repeatedInt32.length; ++i) + writer.int32(message.repeatedInt32[i]); + writer.ldelim(); + } + if (message.repeatedString != null && message.repeatedString.length) + for (let i = 0; i < message.repeatedString.length; ++i) + writer.uint32(/* id 122, wireType 2 =*/978).string(message.repeatedString[i]); + if (message.repeatedBytes != null && message.repeatedBytes.length) + for (let i = 0; i < message.repeatedBytes.length; ++i) + writer.uint32(/* id 123, wireType 2 =*/986).bytes(message.repeatedBytes[i]); + if (message.repeatedMessage != null && message.repeatedMessage.length) + for (let i = 0; i < message.repeatedMessage.length; ++i) + $root.Message.encode(message.repeatedMessage[i], writer.uint32(/* id 127, wireType 2 =*/1018).fork()).ldelim(); + if (message.unpackedInt32 != null && message.unpackedInt32.length) + for (let i = 0; i < message.unpackedInt32.length; ++i) + writer.uint32(/* id 211, wireType 0 =*/1688).int32(message.unpackedInt32[i]); + return writer; + }; + + /** + * Encodes the specified Message message, length delimited. Does not implicitly {@link Message.verify|verify} messages. + * @function encodeDelimited + * @memberof Message + * @static + * @param {IMessage} message Message message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Message.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a Message message from the specified reader or buffer. + * @function decode + * @memberof Message + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {Message} Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Message.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.Message(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.lMessage = $root.Message.decode(reader, reader.uint32()); + break; + } + case 11: { + message.singleInt32 = reader.int32(); + break; + } + case 12: { + message.singleInt64 = reader.int64(); + break; + } + case 13: { + message.singleUint32 = reader.uint32(); + break; + } + case 14: { + message.singleUint64 = reader.uint64(); + break; + } + case 15: { + message.singleSint32 = reader.sint32(); + break; + } + case 16: { + message.singleSint64 = reader.sint64(); + break; + } + case 17: { + message.singleBool = reader.bool(); + break; + } + case 18: { + message.singleEnum = reader.int32(); + break; + } + case 19: { + message.singleFixed64 = reader.fixed64(); + break; + } + case 20: { + message.singleSfixed64 = reader.sfixed64(); + break; + } + case 21: { + message.singleDouble = reader.double(); + break; + } + case 22: { + message.singleString = reader.string(); + break; + } + case 23: { + message.singleBytes = reader.bytes(); + break; + } + case 24: { + message.singleFixed32 = reader.fixed32(); + break; + } + case 25: { + message.singleSfixed32 = reader.sfixed32(); + break; + } + case 26: { + message.singleFloat = reader.float(); + break; + } + case 27: { + message.singleMessage = $root.Message.decode(reader, reader.uint32()); + break; + } + case 111: { + if (!(message.repeatedInt32 && message.repeatedInt32.length)) + message.repeatedInt32 = []; + if ((tag & 7) === 2) { + let end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.repeatedInt32.push(reader.int32()); + } else + message.repeatedInt32.push(reader.int32()); + break; + } + case 122: { + if (!(message.repeatedString && message.repeatedString.length)) + message.repeatedString = []; + message.repeatedString.push(reader.string()); + break; + } + case 123: { + if (!(message.repeatedBytes && message.repeatedBytes.length)) + message.repeatedBytes = []; + message.repeatedBytes.push(reader.bytes()); + break; + } + case 127: { + if (!(message.repeatedMessage && message.repeatedMessage.length)) + message.repeatedMessage = []; + message.repeatedMessage.push($root.Message.decode(reader, reader.uint32())); + break; + } + case 211: { + if (!(message.unpackedInt32 && message.unpackedInt32.length)) + message.unpackedInt32 = []; + if ((tag & 7) === 2) { + let end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) + message.unpackedInt32.push(reader.int32()); + } else + message.unpackedInt32.push(reader.int32()); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a Message message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof Message + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {Message} Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Message.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a Message message. + * @function verify + * @memberof Message + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + Message.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.lMessage != null && message.hasOwnProperty("lMessage")) { + let error = $root.Message.verify(message.lMessage); + if (error) + return "lMessage." + error; + } + if (message.singleInt32 != null && message.hasOwnProperty("singleInt32")) + if (!$util.isInteger(message.singleInt32)) + return "singleInt32: integer expected"; + if (message.singleInt64 != null && message.hasOwnProperty("singleInt64")) + if (!$util.isInteger(message.singleInt64) && !(message.singleInt64 && $util.isInteger(message.singleInt64.low) && $util.isInteger(message.singleInt64.high))) + return "singleInt64: integer|Long expected"; + if (message.singleUint32 != null && message.hasOwnProperty("singleUint32")) + if (!$util.isInteger(message.singleUint32)) + return "singleUint32: integer expected"; + if (message.singleUint64 != null && message.hasOwnProperty("singleUint64")) + if (!$util.isInteger(message.singleUint64) && !(message.singleUint64 && $util.isInteger(message.singleUint64.low) && $util.isInteger(message.singleUint64.high))) + return "singleUint64: integer|Long expected"; + if (message.singleSint32 != null && message.hasOwnProperty("singleSint32")) + if (!$util.isInteger(message.singleSint32)) + return "singleSint32: integer expected"; + if (message.singleSint64 != null && message.hasOwnProperty("singleSint64")) + if (!$util.isInteger(message.singleSint64) && !(message.singleSint64 && $util.isInteger(message.singleSint64.low) && $util.isInteger(message.singleSint64.high))) + return "singleSint64: integer|Long expected"; + if (message.singleBool != null && message.hasOwnProperty("singleBool")) + if (typeof message.singleBool !== "boolean") + return "singleBool: boolean expected"; + if (message.singleEnum != null && message.hasOwnProperty("singleEnum")) + switch (message.singleEnum) { + default: + return "singleEnum: enum value expected"; + case 0: + case 1: + case 2: + break; + } + if (message.singleFixed64 != null && message.hasOwnProperty("singleFixed64")) + if (!$util.isInteger(message.singleFixed64) && !(message.singleFixed64 && $util.isInteger(message.singleFixed64.low) && $util.isInteger(message.singleFixed64.high))) + return "singleFixed64: integer|Long expected"; + if (message.singleSfixed64 != null && message.hasOwnProperty("singleSfixed64")) + if (!$util.isInteger(message.singleSfixed64) && !(message.singleSfixed64 && $util.isInteger(message.singleSfixed64.low) && $util.isInteger(message.singleSfixed64.high))) + return "singleSfixed64: integer|Long expected"; + if (message.singleDouble != null && message.hasOwnProperty("singleDouble")) + if (typeof message.singleDouble !== "number") + return "singleDouble: number expected"; + if (message.singleString != null && message.hasOwnProperty("singleString")) + if (!$util.isString(message.singleString)) + return "singleString: string expected"; + if (message.singleBytes != null && message.hasOwnProperty("singleBytes")) + if (!(message.singleBytes && typeof message.singleBytes.length === "number" || $util.isString(message.singleBytes))) + return "singleBytes: buffer expected"; + if (message.singleFixed32 != null && message.hasOwnProperty("singleFixed32")) + if (!$util.isInteger(message.singleFixed32)) + return "singleFixed32: integer expected"; + if (message.singleSfixed32 != null && message.hasOwnProperty("singleSfixed32")) + if (!$util.isInteger(message.singleSfixed32)) + return "singleSfixed32: integer expected"; + if (message.singleFloat != null && message.hasOwnProperty("singleFloat")) + if (typeof message.singleFloat !== "number") + return "singleFloat: number expected"; + if (message.singleMessage != null && message.hasOwnProperty("singleMessage")) { + let error = $root.Message.verify(message.singleMessage); + if (error) + return "singleMessage." + error; + } + if (message.repeatedInt32 != null && message.hasOwnProperty("repeatedInt32")) { + if (!Array.isArray(message.repeatedInt32)) + return "repeatedInt32: array expected"; + for (let i = 0; i < message.repeatedInt32.length; ++i) + if (!$util.isInteger(message.repeatedInt32[i])) + return "repeatedInt32: integer[] expected"; + } + if (message.repeatedString != null && message.hasOwnProperty("repeatedString")) { + if (!Array.isArray(message.repeatedString)) + return "repeatedString: array expected"; + for (let i = 0; i < message.repeatedString.length; ++i) + if (!$util.isString(message.repeatedString[i])) + return "repeatedString: string[] expected"; + } + if (message.repeatedBytes != null && message.hasOwnProperty("repeatedBytes")) { + if (!Array.isArray(message.repeatedBytes)) + return "repeatedBytes: array expected"; + for (let i = 0; i < message.repeatedBytes.length; ++i) + if (!(message.repeatedBytes[i] && typeof message.repeatedBytes[i].length === "number" || $util.isString(message.repeatedBytes[i]))) + return "repeatedBytes: buffer[] expected"; + } + if (message.repeatedMessage != null && message.hasOwnProperty("repeatedMessage")) { + if (!Array.isArray(message.repeatedMessage)) + return "repeatedMessage: array expected"; + for (let i = 0; i < message.repeatedMessage.length; ++i) { + let error = $root.Message.verify(message.repeatedMessage[i]); + if (error) + return "repeatedMessage." + error; + } + } + if (message.unpackedInt32 != null && message.hasOwnProperty("unpackedInt32")) { + if (!Array.isArray(message.unpackedInt32)) + return "unpackedInt32: array expected"; + for (let i = 0; i < message.unpackedInt32.length; ++i) + if (!$util.isInteger(message.unpackedInt32[i])) + return "unpackedInt32: integer[] expected"; + } + return null; + }; + + /** + * Creates a Message message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof Message + * @static + * @param {Object.} object Plain object + * @returns {Message} Message + */ + Message.fromObject = function fromObject(object) { + if (object instanceof $root.Message) + return object; + let message = new $root.Message(); + if (object.lMessage != null) { + if (typeof object.lMessage !== "object") + throw TypeError(".Message.lMessage: object expected"); + message.lMessage = $root.Message.fromObject(object.lMessage); + } + if (object.singleInt32 != null) + message.singleInt32 = object.singleInt32 | 0; + if (object.singleInt64 != null) + if ($util.Long) + (message.singleInt64 = $util.Long.fromValue(object.singleInt64)).unsigned = false; + else if (typeof object.singleInt64 === "string") + message.singleInt64 = parseInt(object.singleInt64, 10); + else if (typeof object.singleInt64 === "number") + message.singleInt64 = object.singleInt64; + else if (typeof object.singleInt64 === "object") + message.singleInt64 = new $util.LongBits(object.singleInt64.low >>> 0, object.singleInt64.high >>> 0).toNumber(); + if (object.singleUint32 != null) + message.singleUint32 = object.singleUint32 >>> 0; + if (object.singleUint64 != null) + if ($util.Long) + (message.singleUint64 = $util.Long.fromValue(object.singleUint64)).unsigned = true; + else if (typeof object.singleUint64 === "string") + message.singleUint64 = parseInt(object.singleUint64, 10); + else if (typeof object.singleUint64 === "number") + message.singleUint64 = object.singleUint64; + else if (typeof object.singleUint64 === "object") + message.singleUint64 = new $util.LongBits(object.singleUint64.low >>> 0, object.singleUint64.high >>> 0).toNumber(true); + if (object.singleSint32 != null) + message.singleSint32 = object.singleSint32 | 0; + if (object.singleSint64 != null) + if ($util.Long) + (message.singleSint64 = $util.Long.fromValue(object.singleSint64)).unsigned = false; + else if (typeof object.singleSint64 === "string") + message.singleSint64 = parseInt(object.singleSint64, 10); + else if (typeof object.singleSint64 === "number") + message.singleSint64 = object.singleSint64; + else if (typeof object.singleSint64 === "object") + message.singleSint64 = new $util.LongBits(object.singleSint64.low >>> 0, object.singleSint64.high >>> 0).toNumber(); + if (object.singleBool != null) + message.singleBool = Boolean(object.singleBool); + switch (object.singleEnum) { + default: + if (typeof object.singleEnum === "number") { + message.singleEnum = object.singleEnum; + break; + } + break; + case "ENUM_UNSPECIFIED": + case 0: + message.singleEnum = 0; + break; + case "ENUM_ONE": + case 1: + message.singleEnum = 1; + break; + case "ENUM_TWO": + case 2: + message.singleEnum = 2; + break; + } + if (object.singleFixed64 != null) + if ($util.Long) + (message.singleFixed64 = $util.Long.fromValue(object.singleFixed64)).unsigned = false; + else if (typeof object.singleFixed64 === "string") + message.singleFixed64 = parseInt(object.singleFixed64, 10); + else if (typeof object.singleFixed64 === "number") + message.singleFixed64 = object.singleFixed64; + else if (typeof object.singleFixed64 === "object") + message.singleFixed64 = new $util.LongBits(object.singleFixed64.low >>> 0, object.singleFixed64.high >>> 0).toNumber(); + if (object.singleSfixed64 != null) + if ($util.Long) + (message.singleSfixed64 = $util.Long.fromValue(object.singleSfixed64)).unsigned = false; + else if (typeof object.singleSfixed64 === "string") + message.singleSfixed64 = parseInt(object.singleSfixed64, 10); + else if (typeof object.singleSfixed64 === "number") + message.singleSfixed64 = object.singleSfixed64; + else if (typeof object.singleSfixed64 === "object") + message.singleSfixed64 = new $util.LongBits(object.singleSfixed64.low >>> 0, object.singleSfixed64.high >>> 0).toNumber(); + if (object.singleDouble != null) + message.singleDouble = Number(object.singleDouble); + if (object.singleString != null) + message.singleString = String(object.singleString); + if (object.singleBytes != null) + if (typeof object.singleBytes === "string") + $util.base64.decode(object.singleBytes, message.singleBytes = $util.newBuffer($util.base64.length(object.singleBytes)), 0); + else if (object.singleBytes.length >= 0) + message.singleBytes = object.singleBytes; + if (object.singleFixed32 != null) + message.singleFixed32 = object.singleFixed32 >>> 0; + if (object.singleSfixed32 != null) + message.singleSfixed32 = object.singleSfixed32 | 0; + if (object.singleFloat != null) + message.singleFloat = Number(object.singleFloat); + if (object.singleMessage != null) { + if (typeof object.singleMessage !== "object") + throw TypeError(".Message.singleMessage: object expected"); + message.singleMessage = $root.Message.fromObject(object.singleMessage); + } + if (object.repeatedInt32) { + if (!Array.isArray(object.repeatedInt32)) + throw TypeError(".Message.repeatedInt32: array expected"); + message.repeatedInt32 = []; + for (let i = 0; i < object.repeatedInt32.length; ++i) + message.repeatedInt32[i] = object.repeatedInt32[i] | 0; + } + if (object.repeatedString) { + if (!Array.isArray(object.repeatedString)) + throw TypeError(".Message.repeatedString: array expected"); + message.repeatedString = []; + for (let i = 0; i < object.repeatedString.length; ++i) + message.repeatedString[i] = String(object.repeatedString[i]); + } + if (object.repeatedBytes) { + if (!Array.isArray(object.repeatedBytes)) + throw TypeError(".Message.repeatedBytes: array expected"); + message.repeatedBytes = []; + for (let i = 0; i < object.repeatedBytes.length; ++i) + if (typeof object.repeatedBytes[i] === "string") + $util.base64.decode(object.repeatedBytes[i], message.repeatedBytes[i] = $util.newBuffer($util.base64.length(object.repeatedBytes[i])), 0); + else if (object.repeatedBytes[i].length >= 0) + message.repeatedBytes[i] = object.repeatedBytes[i]; + } + if (object.repeatedMessage) { + if (!Array.isArray(object.repeatedMessage)) + throw TypeError(".Message.repeatedMessage: array expected"); + message.repeatedMessage = []; + for (let i = 0; i < object.repeatedMessage.length; ++i) { + if (typeof object.repeatedMessage[i] !== "object") + throw TypeError(".Message.repeatedMessage: object expected"); + message.repeatedMessage[i] = $root.Message.fromObject(object.repeatedMessage[i]); + } + } + if (object.unpackedInt32) { + if (!Array.isArray(object.unpackedInt32)) + throw TypeError(".Message.unpackedInt32: array expected"); + message.unpackedInt32 = []; + for (let i = 0; i < object.unpackedInt32.length; ++i) + message.unpackedInt32[i] = object.unpackedInt32[i] | 0; + } + return message; + }; + + /** + * Creates a plain object from a Message message. Also converts values to other types if specified. + * @function toObject + * @memberof Message + * @static + * @param {Message} message Message + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + Message.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.arrays || options.defaults) { + object.repeatedInt32 = []; + object.repeatedString = []; + object.repeatedBytes = []; + object.repeatedMessage = []; + object.unpackedInt32 = []; + } + if (options.defaults) { + object.lMessage = null; + object.singleInt32 = 0; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.singleInt64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.singleInt64 = options.longs === String ? "0" : 0; + object.singleUint32 = 0; + if ($util.Long) { + let long = new $util.Long(0, 0, true); + object.singleUint64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.singleUint64 = options.longs === String ? "0" : 0; + object.singleSint32 = 0; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.singleSint64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.singleSint64 = options.longs === String ? "0" : 0; + object.singleBool = false; + object.singleEnum = options.enums === String ? "ENUM_UNSPECIFIED" : 0; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.singleFixed64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.singleFixed64 = options.longs === String ? "0" : 0; + if ($util.Long) { + let long = new $util.Long(0, 0, false); + object.singleSfixed64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.singleSfixed64 = options.longs === String ? "0" : 0; + object.singleDouble = 0; + object.singleString = ""; + if (options.bytes === String) + object.singleBytes = ""; + else { + object.singleBytes = []; + if (options.bytes !== Array) + object.singleBytes = $util.newBuffer(object.singleBytes); + } + object.singleFixed32 = 0; + object.singleSfixed32 = 0; + object.singleFloat = 0; + object.singleMessage = null; + } + if (message.lMessage != null && message.hasOwnProperty("lMessage")) + object.lMessage = $root.Message.toObject(message.lMessage, options); + if (message.singleInt32 != null && message.hasOwnProperty("singleInt32")) + object.singleInt32 = message.singleInt32; + if (message.singleInt64 != null && message.hasOwnProperty("singleInt64")) + if (typeof message.singleInt64 === "number") + object.singleInt64 = options.longs === String ? String(message.singleInt64) : message.singleInt64; + else + object.singleInt64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleInt64) : options.longs === Number ? new $util.LongBits(message.singleInt64.low >>> 0, message.singleInt64.high >>> 0).toNumber() : message.singleInt64; + if (message.singleUint32 != null && message.hasOwnProperty("singleUint32")) + object.singleUint32 = message.singleUint32; + if (message.singleUint64 != null && message.hasOwnProperty("singleUint64")) + if (typeof message.singleUint64 === "number") + object.singleUint64 = options.longs === String ? String(message.singleUint64) : message.singleUint64; + else + object.singleUint64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleUint64) : options.longs === Number ? new $util.LongBits(message.singleUint64.low >>> 0, message.singleUint64.high >>> 0).toNumber(true) : message.singleUint64; + if (message.singleSint32 != null && message.hasOwnProperty("singleSint32")) + object.singleSint32 = message.singleSint32; + if (message.singleSint64 != null && message.hasOwnProperty("singleSint64")) + if (typeof message.singleSint64 === "number") + object.singleSint64 = options.longs === String ? String(message.singleSint64) : message.singleSint64; + else + object.singleSint64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleSint64) : options.longs === Number ? new $util.LongBits(message.singleSint64.low >>> 0, message.singleSint64.high >>> 0).toNumber() : message.singleSint64; + if (message.singleBool != null && message.hasOwnProperty("singleBool")) + object.singleBool = message.singleBool; + if (message.singleEnum != null && message.hasOwnProperty("singleEnum")) + object.singleEnum = options.enums === String ? $root.Enum[message.singleEnum] === undefined ? message.singleEnum : $root.Enum[message.singleEnum] : message.singleEnum; + if (message.singleFixed64 != null && message.hasOwnProperty("singleFixed64")) + if (typeof message.singleFixed64 === "number") + object.singleFixed64 = options.longs === String ? String(message.singleFixed64) : message.singleFixed64; + else + object.singleFixed64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleFixed64) : options.longs === Number ? new $util.LongBits(message.singleFixed64.low >>> 0, message.singleFixed64.high >>> 0).toNumber() : message.singleFixed64; + if (message.singleSfixed64 != null && message.hasOwnProperty("singleSfixed64")) + if (typeof message.singleSfixed64 === "number") + object.singleSfixed64 = options.longs === String ? String(message.singleSfixed64) : message.singleSfixed64; + else + object.singleSfixed64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleSfixed64) : options.longs === Number ? new $util.LongBits(message.singleSfixed64.low >>> 0, message.singleSfixed64.high >>> 0).toNumber() : message.singleSfixed64; + if (message.singleDouble != null && message.hasOwnProperty("singleDouble")) + object.singleDouble = options.json && !isFinite(message.singleDouble) ? String(message.singleDouble) : message.singleDouble; + if (message.singleString != null && message.hasOwnProperty("singleString")) + object.singleString = message.singleString; + if (message.singleBytes != null && message.hasOwnProperty("singleBytes")) + object.singleBytes = options.bytes === String ? $util.base64.encode(message.singleBytes, 0, message.singleBytes.length) : options.bytes === Array ? Array.prototype.slice.call(message.singleBytes) : message.singleBytes; + if (message.singleFixed32 != null && message.hasOwnProperty("singleFixed32")) + object.singleFixed32 = message.singleFixed32; + if (message.singleSfixed32 != null && message.hasOwnProperty("singleSfixed32")) + object.singleSfixed32 = message.singleSfixed32; + if (message.singleFloat != null && message.hasOwnProperty("singleFloat")) + object.singleFloat = options.json && !isFinite(message.singleFloat) ? String(message.singleFloat) : message.singleFloat; + if (message.singleMessage != null && message.hasOwnProperty("singleMessage")) + object.singleMessage = $root.Message.toObject(message.singleMessage, options); + if (message.repeatedInt32 && message.repeatedInt32.length) { + object.repeatedInt32 = []; + for (let j = 0; j < message.repeatedInt32.length; ++j) + object.repeatedInt32[j] = message.repeatedInt32[j]; + } + if (message.repeatedString && message.repeatedString.length) { + object.repeatedString = []; + for (let j = 0; j < message.repeatedString.length; ++j) + object.repeatedString[j] = message.repeatedString[j]; + } + if (message.repeatedBytes && message.repeatedBytes.length) { + object.repeatedBytes = []; + for (let j = 0; j < message.repeatedBytes.length; ++j) + object.repeatedBytes[j] = options.bytes === String ? $util.base64.encode(message.repeatedBytes[j], 0, message.repeatedBytes[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.repeatedBytes[j]) : message.repeatedBytes[j]; + } + if (message.repeatedMessage && message.repeatedMessage.length) { + object.repeatedMessage = []; + for (let j = 0; j < message.repeatedMessage.length; ++j) + object.repeatedMessage[j] = $root.Message.toObject(message.repeatedMessage[j], options); + } + if (message.unpackedInt32 && message.unpackedInt32.length) { + object.unpackedInt32 = []; + for (let j = 0; j < message.unpackedInt32.length; ++j) + object.unpackedInt32[j] = message.unpackedInt32[j]; + } + return object; + }; + + /** + * Converts this Message to JSON. + * @function toJSON + * @memberof Message + * @instance + * @returns {Object.} JSON object + */ + Message.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for Message + * @function getTypeUrl + * @memberof Message + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + Message.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/Message"; + }; + + return Message; +})(); + +export { $root as default }; diff --git a/src/protohax/fixtures/protohax_pb.json b/src/protohax/fixtures/protohax_pb.json new file mode 100644 index 0000000..fbed3aa --- /dev/null +++ b/src/protohax/fixtures/protohax_pb.json @@ -0,0 +1,115 @@ +{ + "nested": { + "Enum": { + "values": { + "ENUM_UNSPECIFIED": 0, + "ENUM_ONE": 1, + "ENUM_TWO": 2 + } + }, + "Message": { + "fields": { + "lMessage": { + "type": "Message", + "id": 1 + }, + "singleInt32": { + "type": "int32", + "id": 11 + }, + "singleInt64": { + "type": "int64", + "id": 12 + }, + "singleUint32": { + "type": "uint32", + "id": 13 + }, + "singleUint64": { + "type": "uint64", + "id": 14 + }, + "singleSint32": { + "type": "sint32", + "id": 15 + }, + "singleSint64": { + "type": "sint64", + "id": 16 + }, + "singleBool": { + "type": "bool", + "id": 17 + }, + "singleEnum": { + "type": "Enum", + "id": 18 + }, + "singleFixed64": { + "type": "fixed64", + "id": 19 + }, + "singleSfixed64": { + "type": "sfixed64", + "id": 20 + }, + "singleDouble": { + "type": "double", + "id": 21 + }, + "singleString": { + "type": "string", + "id": 22 + }, + "singleBytes": { + "type": "bytes", + "id": 23 + }, + "singleFixed32": { + "type": "fixed32", + "id": 24 + }, + "singleSfixed32": { + "type": "sfixed32", + "id": 25 + }, + "singleFloat": { + "type": "float", + "id": 26 + }, + "singleMessage": { + "type": "Message", + "id": 27 + }, + "repeatedInt32": { + "rule": "repeated", + "type": "int32", + "id": 111 + }, + "repeatedString": { + "rule": "repeated", + "type": "string", + "id": 122 + }, + "repeatedBytes": { + "rule": "repeated", + "type": "bytes", + "id": 123 + }, + "repeatedMessage": { + "rule": "repeated", + "type": "Message", + "id": 127 + }, + "unpackedInt32": { + "rule": "repeated", + "type": "int32", + "id": 211, + "options": { + "packed": false + } + } + } + } + } +} \ No newline at end of file diff --git a/src/protohax/fixtures/protohax_pb.ts b/src/protohax/fixtures/protohax_pb.ts deleted file mode 100644 index 4c11699..0000000 --- a/src/protohax/fixtures/protohax_pb.ts +++ /dev/null @@ -1,211 +0,0 @@ -// @generated by protoc-gen-es v1.4.2 with parameter "target=ts" -// @generated from file src/protohax/fixtures/protohax.proto (syntax proto3) -/* eslint-disable */ -// @ts-nocheck - -import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; -import { Message as Message$1, proto3, protoInt64 } from "@bufbuild/protobuf"; - -/** - * @generated from enum Enum - */ -export enum Enum { - /** - * @generated from enum value: ENUM_UNSPECIFIED = 0; - */ - UNSPECIFIED = 0, - - /** - * @generated from enum value: ENUM_ONE = 1; - */ - ONE = 1, - - /** - * @generated from enum value: ENUM_TWO = 2; - */ - TWO = 2, -} -// Retrieve enum metadata with: proto3.getEnumType(Enum) -proto3.util.setEnumType(Enum, "Enum", [ - { no: 0, name: "ENUM_UNSPECIFIED" }, - { no: 1, name: "ENUM_ONE" }, - { no: 2, name: "ENUM_TWO" }, -]); - -/** - * @generated from message Message - */ -export class Message extends Message$1 { - /** - * low (single-byte tag) - * - * @generated from field: Message l_message = 1; - */ - lMessage?: Message; - - /** - * singular - * - * @generated from field: int32 single_int32 = 11; - */ - singleInt32 = 0; - - /** - * @generated from field: int64 single_int64 = 12; - */ - singleInt64 = protoInt64.zero; - - /** - * @generated from field: uint32 single_uint32 = 13; - */ - singleUint32 = 0; - - /** - * @generated from field: uint64 single_uint64 = 14; - */ - singleUint64 = protoInt64.zero; - - /** - * @generated from field: sint32 single_sint32 = 15; - */ - singleSint32 = 0; - - /** - * @generated from field: sint64 single_sint64 = 16; - */ - singleSint64 = protoInt64.zero; - - /** - * @generated from field: bool single_bool = 17; - */ - singleBool = false; - - /** - * @generated from field: Enum single_enum = 18; - */ - singleEnum = Enum.UNSPECIFIED; - - /** - * @generated from field: fixed64 single_fixed64 = 19; - */ - singleFixed64 = protoInt64.zero; - - /** - * @generated from field: sfixed64 single_sfixed64 = 20; - */ - singleSfixed64 = protoInt64.zero; - - /** - * @generated from field: double single_double = 21; - */ - singleDouble = 0; - - /** - * @generated from field: string single_string = 22; - */ - singleString = ""; - - /** - * @generated from field: bytes single_bytes = 23; - */ - singleBytes = new Uint8Array(0); - - /** - * @generated from field: fixed32 single_fixed32 = 24; - */ - singleFixed32 = 0; - - /** - * @generated from field: sfixed32 single_sfixed32 = 25; - */ - singleSfixed32 = 0; - - /** - * @generated from field: float single_float = 26; - */ - singleFloat = 0; - - /** - * @generated from field: Message single_message = 27; - */ - singleMessage?: Message; - - /** - * repeated - * - * @generated from field: repeated int32 repeated_int32 = 111; - */ - repeatedInt32: number[] = []; - - /** - * @generated from field: repeated string repeated_string = 122; - */ - repeatedString: string[] = []; - - /** - * @generated from field: repeated bytes repeated_bytes = 123; - */ - repeatedBytes: Uint8Array[] = []; - - /** - * @generated from field: repeated Message repeated_message = 127; - */ - repeatedMessage: Message[] = []; - - /** - * unpacked repeated - * - * @generated from field: repeated int32 unpacked_int32 = 211 [packed = false]; - */ - unpackedInt32: number[] = []; - - constructor(data?: PartialMessage) { - super(); - proto3.util.initPartial(data, this); - } - - static readonly runtime: typeof proto3 = proto3; - static readonly typeName = "Message"; - static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "l_message", kind: "message", T: Message }, - { no: 11, name: "single_int32", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, - { no: 12, name: "single_int64", kind: "scalar", T: 3 /* ScalarType.INT64 */ }, - { no: 13, name: "single_uint32", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, - { no: 14, name: "single_uint64", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, - { no: 15, name: "single_sint32", kind: "scalar", T: 17 /* ScalarType.SINT32 */ }, - { no: 16, name: "single_sint64", kind: "scalar", T: 18 /* ScalarType.SINT64 */ }, - { no: 17, name: "single_bool", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 18, name: "single_enum", kind: "enum", T: proto3.getEnumType(Enum) }, - { no: 19, name: "single_fixed64", kind: "scalar", T: 6 /* ScalarType.FIXED64 */ }, - { no: 20, name: "single_sfixed64", kind: "scalar", T: 16 /* ScalarType.SFIXED64 */ }, - { no: 21, name: "single_double", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ }, - { no: 22, name: "single_string", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 23, name: "single_bytes", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, - { no: 24, name: "single_fixed32", kind: "scalar", T: 7 /* ScalarType.FIXED32 */ }, - { no: 25, name: "single_sfixed32", kind: "scalar", T: 15 /* ScalarType.SFIXED32 */ }, - { no: 26, name: "single_float", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, - { no: 27, name: "single_message", kind: "message", T: Message }, - { no: 111, name: "repeated_int32", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, - { no: 122, name: "repeated_string", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, - { no: 123, name: "repeated_bytes", kind: "scalar", T: 12 /* ScalarType.BYTES */, repeated: true }, - { no: 127, name: "repeated_message", kind: "message", T: Message, repeated: true }, - { no: 211, name: "unpacked_int32", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true, packed: false }, - ]); - - static fromBinary(bytes: Uint8Array, options?: Partial): Message { - return new Message().fromBinary(bytes, options); - } - - static fromJson(jsonValue: JsonValue, options?: Partial): Message { - return new Message().fromJson(jsonValue, options); - } - - static fromJsonString(jsonString: string, options?: Partial): Message { - return new Message().fromJsonString(jsonString, options); - } - - static equals(a: Message | PlainMessage | undefined, b: Message | PlainMessage | undefined): boolean { - return proto3.util.equals(Message, a, b); - } -} - diff --git a/src/protohax/protohax.test.ts b/src/protohax/protohax.test.ts index 678cda3..ac42d4b 100644 --- a/src/protohax/protohax.test.ts +++ b/src/protohax/protohax.test.ts @@ -1,11 +1,14 @@ import { Enum, Message } from './fixtures/protohax_pb'; import { ProtoHax } from './protohax'; +import Long from 'long'; +import fieldSpec from './fixtures/protohax_pb.json'; describe('ProtoHax', () => { describe('single numeric values', () => { - type proto_scalar = number | bigint | boolean | Enum; + type proto_scalar = number | bigint | boolean | Enum | Long; type messageScalar = { [K in keyof Message as Message[K] extends proto_scalar | proto_scalar[] ? K : never]: K }; type messageScalars = messageScalar[keyof messageScalar] & keyof Message; + type foo = { [K in keyof Message]: 1 } & unknown; type fakeBigint = T extends bigint ? string : T extends bigint[] ? string[] : T; @@ -47,23 +50,23 @@ describe('ProtoHax', () => { scalar('singleSfixed32', -1 ), scalar('singleFloat', asSingle(-1.1)), scalar('singleDouble', -1.1 ), - scalar('singleInt64', '-1' ), - scalar('singleSint64', '-1' ), - scalar('singleSfixed64', '-1' ), + scalar('singleInt64', new Long(-1) ), + scalar('singleSint64', new Long(-1) ), + scalar('singleSfixed64', new Long(-1) ), // zero - scalar('singleInt32', 0 ), - scalar('singleUint32', 0 ), - scalar('singleSint32', 0 ), - scalar('singleFixed32', 0 ), - scalar('singleSfixed32', 0 ), - scalar('singleFloat', 0 ), - scalar('singleDouble', 0 ), - scalar('singleInt64', '0'), - scalar('singleUint64', '0'), - scalar('singleSint64', '0'), - scalar('singleFixed64', '0'), - scalar('singleSfixed64', '0'), + scalar('singleInt32', 0 ), + scalar('singleUint32', 0 ), + scalar('singleSint32', 0 ), + scalar('singleFixed32', 0 ), + scalar('singleSfixed32', 0 ), + scalar('singleFloat', 0 ), + scalar('singleDouble', 0 ), + scalar('singleInt64', new Long(0)), + scalar('singleUint64', new Long(0)), + scalar('singleSint64', new Long(0)), + scalar('singleFixed64', new Long(0)), + scalar('singleSfixed64', new Long(0)), // one scalar('singleInt32', 1 ), @@ -73,38 +76,38 @@ describe('ProtoHax', () => { scalar('singleSfixed32', 1 ), scalar('singleFloat', asSingle(1.1)), scalar('singleDouble', 1.1 ), - scalar('singleInt64', '1' ), - scalar('singleUint64', '1' ), - scalar('singleSint64', '1' ), - scalar('singleFixed64', '1' ), - scalar('singleSfixed64', '1' ), + scalar('singleInt64', new Long(1) ), + scalar('singleUint64', new Long(1) ), + scalar('singleSint64', new Long(1) ), + scalar('singleFixed64', new Long(1) ), + scalar('singleSfixed64', new Long(1) ), // varint edges scalar('singleInt32', 1<<30), - scalar('singleInt64', (0b01100110_10110010_10010111_01011001_10100110_11001001_10111010_00110011n).toString()), + scalar('singleInt64', Long.fromString((0b01100110_10110010_10010111_01011001_10100110_11001001_10111010_00110011n).toString())), // booleans scalar('singleBool', true), scalar('singleBool', false), // enums - scalar('singleEnum', Enum.UNSPECIFIED), - scalar('singleEnum', Enum.ONE), - scalar('singleEnum', Enum.TWO), + scalar('singleEnum', Enum.ENUM_UNSPECIFIED), + scalar('singleEnum', Enum.ENUM_ONE), + scalar('singleEnum', Enum.ENUM_TWO), ]; it.each(tests)('$key $value', ({ key, value }) => { const n = typeof value === 'string' ? BigInt(value) : value; - const pbes_encoded = Buffer.from( - new Message({ + const pbjs_encoded = Buffer.from( + Message.encode({ [key]: n, - }).toBinary(), + }).finish(), ); - const fieldId = Message.fields.findJsonName(key)?.no; + const fieldId = (fieldSpec.nested.Message.fields as any)[key]?.id; expect(fieldId).not.toBeUndefined(); - const phax = new ProtoHax(pbes_encoded); + const phax = new ProtoHax(pbjs_encoded); const readMethod = methodNames[key]; expect(readMethod).not.toBeUndefined(); @@ -116,21 +119,21 @@ describe('ProtoHax', () => { describe('packed repeated', () => { it('reads with .Packed()', () => { const expected = [1, 2, 3]; - const pbes_encoded = Buffer.from(new Message({ repeatedInt32: expected }).toBinary()); + const pbjs_encoded = Buffer.from(Message.encode({ repeatedInt32: expected }).finish()); - const fieldId = Message.fields.findJsonName('repeatedInt32')?.no; + const fieldId = fieldSpec.nested.Message.fields.repeatedInt32.id; expect(fieldId).not.toBeUndefined(); - const phax = new ProtoHax(pbes_encoded); + const phax = new ProtoHax(pbjs_encoded); const actual = phax.with(fieldId!).Packed('Int32'); expect(actual).toEqual(expected); }); it('reads with value readers', () => { const expected = [1, 2, 3]; - const pbes_encoded = Buffer.from(new Message({ repeatedInt32: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ repeatedInt32: expected }).finish()); - const fieldId = Message.fields.findJsonName('repeatedInt32')?.no; + const fieldId = fieldSpec.nested.Message.fields.repeatedInt32.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -148,9 +151,9 @@ describe('ProtoHax', () => { describe('length-delimited', () => { it('reads a string', () => { const expected = 'hi there ☃'; - const pbes_encoded = Buffer.from(new Message({ singleString: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ singleString: expected }).finish()); - const fieldId = Message.fields.findJsonName('singleString')?.no; + const fieldId = fieldSpec.nested.Message.fields.singleString.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -161,9 +164,9 @@ describe('ProtoHax', () => { }); it('reads bytes', () => { const expected = Buffer.from('hi there ☃'); - const pbes_encoded = Buffer.from(new Message({ singleBytes: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ singleBytes: expected }).finish()); - const fieldId = Message.fields.findJsonName('singleBytes')?.no; + const fieldId = fieldSpec.nested.Message.fields.singleBytes.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -174,24 +177,24 @@ describe('ProtoHax', () => { }); it('reads messages', () => { const pbes_encoded = Buffer.from( - new Message({ + Message.encode({ lMessage: { singleBool: true, }, - repeatedMessage: [{ singleEnum: Enum.ONE }, { singleEnum: Enum.TWO }], - }).toBinary(), + repeatedMessage: [{ singleEnum: Enum.ENUM_ONE }, { singleEnum: Enum.ENUM_TWO }], + }).finish(), ); - const lMessage = Message.fields.findJsonName('lMessage')?.no; + const lMessage = fieldSpec.nested.Message.fields.lMessage.id; expect(lMessage).not.toBeUndefined(); - const singleBool = Message.fields.findJsonName('singleBool')?.no; + const singleBool = fieldSpec.nested.Message.fields.singleBool.id; expect(singleBool).not.toBeUndefined(); - const repeatedMessage = Message.fields.findJsonName('repeatedMessage')?.no; + const repeatedMessage = fieldSpec.nested.Message.fields.repeatedMessage.id; expect(repeatedMessage).not.toBeUndefined(); - const singleEnum = Message.fields.findJsonName('singleEnum')?.no; + const singleEnum = fieldSpec.nested.Message.fields.singleEnum.id; expect(singleEnum).not.toBeUndefined(); - const expected = [true, Enum.ONE, Enum.TWO]; + const expected = [true, Enum.ENUM_ONE, Enum.ENUM_TWO]; const actual: [boolean, number, number] = [] as any; actual.push( @@ -211,9 +214,9 @@ describe('ProtoHax', () => { describe('if', () => { it('finds the first value', () => { const expected = 1; - const pbes_encoded = Buffer.from(new Message({ singleInt32: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ singleInt32: expected }).finish()); - const fieldId = Message.fields.findJsonName('singleInt32')?.no; + const fieldId = fieldSpec.nested.Message.fields.singleInt32.id; expect(fieldId).not.toBeUndefined(); let found = false; @@ -224,9 +227,9 @@ describe('ProtoHax', () => { }); it('does nothing on no match', () => { const expected = 1; - const pbes_encoded = Buffer.from(new Message({ singleInt32: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ singleInt32: expected }).finish()); - const fieldId = Message.fields.findJsonName('singleBool')?.no; + const fieldId = fieldSpec.nested.Message.fields.singleBool.id; expect(fieldId).not.toBeUndefined(); let found = false; @@ -240,9 +243,9 @@ describe('ProtoHax', () => { // with arbitrary / unrelated data in the last-read value property. this caused an exception // when the last-read value indicated a group wiretype ("not implemented"). // TODO: this.seek could return a boolean directly indicating whether it terminates an operation - const pbes_encoded = Buffer.from(new Message({ singleString: 'hi there11' }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ singleString: 'hi there11' }).finish()); - const fieldId = Message.fields.findJsonName('singleInt32')?.no; + const fieldId = fieldSpec.nested.Message.fields.singleInt32.id; expect(fieldId).not.toBeUndefined(); let found = false; @@ -255,9 +258,9 @@ describe('ProtoHax', () => { describe('each', () => { it('reads scalars', () => { const expected = [1, 2, 3]; - const pbes_encoded = Buffer.from(new Message({ unpackedInt32: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ unpackedInt32: expected }).finish()); - const fieldId = Message.fields.findJsonName('unpackedInt32')?.no; + const fieldId = fieldSpec.nested.Message.fields.unpackedInt32.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -268,9 +271,9 @@ describe('ProtoHax', () => { }); it('reads strings', () => { const expected = ['a', 'b']; - const pbes_encoded = Buffer.from(new Message({ repeatedString: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ repeatedString: expected }).finish()); - const fieldId = Message.fields.findJsonName('repeatedString')?.no; + const fieldId = fieldSpec.nested.Message.fields.repeatedString.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -282,9 +285,9 @@ describe('ProtoHax', () => { }); it('reads bytes', () => { const expected = ['a', 'b'].map((s) => Buffer.from(s)); - const pbes_encoded = Buffer.from(new Message({ repeatedBytes: expected }).toBinary()); + const pbes_encoded = Buffer.from(Message.encode({ repeatedBytes: expected }).finish()); - const fieldId = Message.fields.findJsonName('repeatedBytes')?.no; + const fieldId = fieldSpec.nested.Message.fields.repeatedBytes.id; expect(fieldId).not.toBeUndefined(); const phax = new ProtoHax(pbes_encoded); @@ -298,8 +301,8 @@ describe('ProtoHax', () => { describe('skip', () => { it('can skip over everything', () => { const pbes_encoded = Buffer.from( - new Message({ - lMessage: new Message({ singleDouble: 1 }), + Message.encode({ + lMessage: Message.encode({ singleDouble: 1 }), singleInt32: 1, singleInt64: 1n, singleUint32: 1, @@ -307,7 +310,7 @@ describe('ProtoHax', () => { singleSint32: 1, singleSint64: 1n, singleBool: true, - singleEnum: Enum.ONE, + singleEnum: Enum.ENUM_ONE, singleFixed64: 1n, singleSfixed64: 1n, singleDouble: 1, @@ -316,13 +319,13 @@ describe('ProtoHax', () => { singleFixed32: 1, singleSfixed32: 1, singleFloat: 1, - singleMessage: new Message({ singleDouble: 1 }), + singleMessage: Message.encode({ singleDouble: 1 }), repeatedInt32: [1], repeatedString: ['hi'], repeatedBytes: [Buffer.from('hi')], - repeatedMessage: [new Message({ singleDouble: 1 })], + repeatedMessage: [Message.encode({ singleDouble: 1 })], unpackedInt32: [1], - }).toBinary(), + }).finish(), ); const phax = new ProtoHax(pbes_encoded); phax.if(1337, (phax) => { diff --git a/src/protoutil.test.ts b/src/protoutil.test.ts index e282b37..b535e76 100644 --- a/src/protoutil.test.ts +++ b/src/protoutil.test.ts @@ -1,6 +1,5 @@ -import { PlainMessage } from '@bufbuild/protobuf'; -import { Envelope } from './gen/messages_pb'; -import { createPlayerPosition, lastPlayerPosition, maybePlayerMove, tagPlayerMove } from './protoutil'; +import { NT } from './gen/pbjs_pb'; +import { maybePlayerMove, tagPlayerMove } from './protoutil'; const asSingle = (v: number) => { const buf = Buffer.alloc(4); @@ -8,68 +7,6 @@ const asSingle = (v: number) => { return buf.readFloatBE(0); }; -describe('lastPlayerPosition', () => { - it('works as expected', () => { - const frame = (x: number, y: number) => ({ x, y, anim: 1, armR: 0.872, armScaleY: 1, held: 1, scaleX: 1 }); - - const encoded = Buffer.from( - new Envelope({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { - frames: [frame(1, 2), frame(3, 4)], - }, - }, - }, - }, - }).toBinary(), - ); - - const playerMovePayload = maybePlayerMove(encoded); - - expect(playerMovePayload).toBeDefined(); - const pos = lastPlayerPosition(playerMovePayload!); - expect(pos).toEqual({ x: 3, y: 4 }); - }); -}); - -describe('createPlayerPosition', () => { - it('works as expected', () => { - const x = 1.3, - y = 5.2; - - const expectedFrame = { - x: asSingle(x), - y: asSingle(y), - }; - - const ppId = Buffer.from('12345'); - const playerPosition = createPlayerPosition(x, y, ppId); - - expect(playerPosition).toBeDefined(); - - const env = Envelope.fromBinary(playerPosition!); - - expect(env).toEqual({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerPosition', - value: { - userId: '12345', - frame: expectedFrame, - }, - }, - }, - }, - } as PlainMessage); - }); -}); - describe('PlayerMove userId tagging', () => { it('works with empty frames', () => { const buf = Buffer.from('0a020a00', 'hex'); @@ -77,100 +14,136 @@ describe('PlayerMove userId tagging', () => { expect(mpm).toBeDefined(); const tagged = tagPlayerMove(mpm!, Buffer.from('foo')); expect(tagged).toBeDefined(); - expect(Envelope.fromBinary(tagged!)).toEqual({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { + expect(NT.Envelope.decode(tagged!).toJSON()).toEqual({ + gameAction: { + sPlayerMoves: { + userFrames: [ + { userId: 'foo', - frames: [], }, - }, + ], }, }, }); }); it('works as expected', () => { - const frame = { x: 1.3, y: -1.3, anim: 1, armR: 0.872, armScaleY: 1, held: 1, scaleX: 1 }; - const mangledFrame = { - ...frame, - x: asSingle(frame.x), - y: asSingle(frame.y), - armR: asSingle(frame.armR), + const frames: NT.ICompactPlayerFrames = { + xInit: 1.3, + xDeltas: [1, -1], + yInit: -1.3, + yDeltas: [1, -1], + animIdx: [1], + animVal: [1], + armR: [1], + armScaleY: 1, + scaleX: 1, + heldIdx: [1], + heldVal: [1], }; - const encoded = Buffer.from( - new Envelope({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { - frames: new Array(15).fill(frame), - }, + const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); + const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); + expect(playerMovePayload).toBeDefined(); + + const pmId = Buffer.from('12345'); + const tagged = tagPlayerMove(playerMovePayload!, pmId); + expect(tagged).toBeDefined(); + + const decoded = NT.Envelope.decode(tagged!); + const expected: NT.IEnvelope = { + gameAction: { + sPlayerMoves: { + userFrames: [ + { + ...frames, + userId: '12345', + xInit: asSingle(frames.xInit!), + yInit: asSingle(frames.yInit!), }, - }, + ], }, - }).toBinary(), - ); - - const playerMovePayload = maybePlayerMove(encoded); + }, + }; + expect(decoded.toJSON()).toEqual(expected); + }); + // protobuf.js fails to merge messages when decoding + it.skip('correctly supports concatenation (protobuf.js)', () => { + const frames: NT.ICompactPlayerFrames = { + xInit: 1.3, + xDeltas: [1, -1], + yInit: -1.3, + yDeltas: [1, -1], + animIdx: [1], + animVal: [1], + armR: [1], + armScaleY: 1, + scaleX: 1, + heldIdx: [1], + heldVal: [1], + }; + const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); + const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); expect(playerMovePayload).toBeDefined(); const pmId = Buffer.from('12345'); - const tagged = tagPlayerMove(playerMovePayload!, pmId); - expect(tagged).toBeDefined(); - const env = Envelope.fromBinary(tagged!); + const encoded2 = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); + const playerMovePayload2 = maybePlayerMove(Buffer.from(encoded2)); + expect(playerMovePayload2).toBeDefined(); + + const pmId2 = Buffer.from('6789'); + const tagged2 = tagPlayerMove(playerMovePayload2!, pmId2); + expect(tagged2).toBeDefined(); + + const concatenated = Buffer.concat([tagged!, tagged2!]); + const decoded = NT.Envelope.decode(concatenated); - expect(env).toEqual({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { + const expected: NT.IEnvelope = { + gameAction: { + sPlayerMoves: { + userFrames: [ + { + ...frames, userId: '12345', - frames: new Array(15).fill(mangledFrame), + xInit: asSingle(frames.xInit!), + yInit: asSingle(frames.yInit!), }, - }, + { + ...frames, + userId: '6789', + xInit: asSingle(frames.xInit!), + yInit: asSingle(frames.yInit!), + }, + ], }, }, - } as PlainMessage); + }; + expect(decoded.toJSON()).toEqual(expected); }); it('refuses client-supplied userId', () => { - const frame = { x: 1.3, y: -1.3, anim: 1, armR: 0.872, armScaleY: 1, held: 1, scaleX: 1 }; - const encoded = Buffer.from( - new Envelope({ - kind: { - case: 'gameAction', - value: { - action: { - case: 'playerMove', - value: { - frames: new Array(15).fill(frame), - userId: 'oh noes', - }, - }, - }, - }, - }).toBinary(), - ); - - const playerMovePayload = maybePlayerMove(encoded); - + const frames: NT.ICompactPlayerFrames = { + xInit: 1.3, + xDeltas: [1, -1], + yInit: -1.3, + yDeltas: [1, -1], + animIdx: [1], + animVal: [1], + armR: [1], + armScaleY: 1, + scaleX: 1, + heldIdx: [1], + heldVal: [1], + userId: 'rejected', + }; + const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); + const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); expect(playerMovePayload).toBeDefined(); const pmId = Buffer.from('12345'); - const tagged = tagPlayerMove(playerMovePayload!, pmId); - expect(tagged).toBeUndefined(); }); }); diff --git a/src/protoutil.ts b/src/protoutil.ts index c60b947..3a9dab7 100644 --- a/src/protoutil.ts +++ b/src/protoutil.ts @@ -1,40 +1,81 @@ import { ProtoHax, Wiretype } from './protohax/protohax'; -import { Envelope, GameAction, LobbyAction, PlayerFrame, PlayerMove, PlayerPosition } from './gen/messages_pb'; - -const gameActionId = Envelope.fields.findJsonName('gameAction')!.no; // assumed <= 16 +import { Messages } from './pbreflect'; + +const gameActionId = Messages.Envelope.gameAction; +const cPlayerMoveId = Messages.GameAction.cPlayerMove; +const sPlayerMovesId = Messages.GameAction.sPlayerMoves; +const cpfPlayerId = Messages.CompactPlayerFrames.userId; +const userFramesId = Messages.ServerPlayerMoves.userFrames; + +export const maybePlayerMove = (envelope: Buffer) => + new ProtoHax(envelope).with(gameActionId).with(cPlayerMoveId).Bytes(); + +const sizeofVarint32 = (val: number): number => { + if (val <= 0x7f) return 1; + if (val <= 0x3fff) return 2; + if (val <= 0x1fffff) return 3; + if (val <= 0xfffffff) return 4; + if (val <= 0xffffffff) return 5; + throw new RangeError('Invalid value (too many bits)'); +}; +const writeVarint32 = (buf: Buffer, val: number, pos: number): number => { + if (val <= 0x7f) { + buf[pos++] = val; + return 1; + } -const playerPositionId = GameAction.fields.findJsonName('playerPosition')!.no; // assumed <= 16 -const ppFrameId = PlayerPosition.fields.findJsonName('frame')!.no; // assumed <= 16 -const ppUserId = PlayerPosition.fields.findJsonName('userId')!.no; // assumed <= 16 + if (val <= 0x3fff) { + buf[pos++] = (val & 0x7f) | 0x80; + buf[pos++] = (val >>> 7) & 0x7f; + return 2; + } -const playerMoveId = GameAction.fields.findJsonName('playerMove')!.no; // assumed <= 16 -const pmUserId = PlayerMove.fields.findJsonName('userId')!.no; // assumed <= 16 + if (val <= 0x1fffff) { + buf[pos++] = (val & 0x7f) | 0x80; + buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; + buf[pos++] = (val >>> 14) & 0x7f; + return 3; + } -const playerFrameXid = PlayerFrame.fields.findJsonName('x')!.no; // assumed <= 16 -const playerFrameYid = PlayerFrame.fields.findJsonName('y')!.no; // assumed <= 16 + if (val <= 0xfffffff) { + buf[pos++] = (val & 0x7f) | 0x80; + buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; + buf[pos++] = ((val >>> 14) & 0x7f) | 0x80; + buf[pos++] = (val >>> 21) & 0x7f; + return 4; + } -export const maybePlayerMove = (envelope: Buffer) => { - let playerMovePayload: Buffer | undefined = undefined as any; - new ProtoHax(envelope as Buffer).with(gameActionId).if(playerMoveId, (phax) => { - playerMovePayload = phax.Bytes(); - }); - return playerMovePayload; + if (val <= 0xffffffff) { + buf[pos++] = (val & 0x7f) | 0x80; + buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; + buf[pos++] = ((val >>> 14) & 0x7f) | 0x80; + buf[pos++] = ((val >>> 21) & 0x7f) | 0x80; + buf[pos++] = (val >>> 28) & 0x0f; + return 5; + } + throw new RangeError('Invalid value (too many bits)'); }; -export const tagPlayerMove = (rawPlayerMove: Buffer, pmId: Buffer): Buffer | undefined => { - const embeddedUserId = new ProtoHax(rawPlayerMove).with(ppUserId).Bytes(); +export const tagPlayerMove = (cpf: Buffer, pmId: Buffer): Buffer | undefined => { + // reject c2s CompactPlayerFrames with userId specified + const embeddedUserId = new ProtoHax(cpf).with(cpfPlayerId).Bytes(); if (embeddedUserId.length > 0) return; // prettier-ignore - const playerMovePayloadSize = ( - (1 + 1) // string tag + length + const userFramesPayloadSize = ( + (1 + 1) // userId string tag + length + pmId.length // userId (string) payload - + rawPlayerMove.length // frames[] payload - repeated Frame + + cpf.length // CompactPlayerFrames message (from client) ); - const playerMoveHeaderSize = playerMovePayloadSize > 127 ? 3 : 2; - const gameActionPayloadSize = playerMovePayloadSize + playerMoveHeaderSize; - const gameActionHeaderSize = gameActionPayloadSize > 127 ? 3 : 2; - const msgLength = gameActionHeaderSize + playerMoveHeaderSize + playerMovePayloadSize; + const userFramesHeaderSize = sizeofVarint32(userFramesPayloadSize) + 1; + + const spmPayloadSize = userFramesPayloadSize + userFramesHeaderSize; + const spmHeaderSize = sizeofVarint32(spmPayloadSize) + 1; + + const gameActionPayloadSize = spmPayloadSize + spmHeaderSize; + const gameActionHeaderSize = sizeofVarint32(gameActionPayloadSize) + 1; + + const msgLength = gameActionHeaderSize + spmHeaderSize + userFramesHeaderSize + userFramesPayloadSize; const buf = Buffer.alloc(msgLength); @@ -42,93 +83,24 @@ export const tagPlayerMove = (rawPlayerMove: Buffer, pmId: Buffer): Buffer | und // write GameAction tag+length buf[pos++] = (gameActionId << 3) | Wiretype.LEN; - if (gameActionHeaderSize === 2) { - buf[pos++] = gameActionPayloadSize; - } else { - buf[pos++] = (gameActionPayloadSize & 0x7f) | 0x80; - buf[pos++] = (gameActionPayloadSize >>> 7) & 0x7f; - } + pos += writeVarint32(buf, gameActionPayloadSize, pos); - // write PlayerMove tag+length - buf[pos++] = (playerMoveId << 3) | Wiretype.LEN; - if (playerMoveHeaderSize === 2) { - buf[pos++] = playerMovePayloadSize; - } else { - buf[pos++] = (playerMovePayloadSize & 0x7f) | 0x80; - buf[pos++] = (playerMovePayloadSize >>> 7) & 0x7f; - } + // write ServerPlayerMoves tag+length + buf[pos++] = (sPlayerMovesId << 3) | Wiretype.LEN; + pos += writeVarint32(buf, spmPayloadSize, pos); + + // write CompactPlayerFrames tag+length + buf[pos++] = (userFramesId << 3) | Wiretype.LEN; + pos += writeVarint32(buf, userFramesPayloadSize, pos); // write userId - buf[pos++] = (pmUserId << 3) | Wiretype.LEN; + buf[pos++] = (cpfPlayerId << 3) | Wiretype.LEN; buf[pos++] = pmId.length; pmId.copy(buf, pos, 0); pos += pmId.length; - // write frames[] - rawPlayerMove.copy(buf, pos); - - return buf; -}; - -export const lastPlayerPosition = (rawPlayerMove: Buffer) => { - let x: number = 0; - let y: number = 0; - - // extract the last frame message - new ProtoHax(rawPlayerMove).each(ppFrameId, (phax) => { - phax.if(playerFrameXid, (p) => { - x = p.Float(); - }); - phax.if(playerFrameYid, (p) => { - y = p.Float(); - }); - }); - - return { x, y }; -}; - -export const createPlayerPosition = (x: number, y: number, ppId: Buffer): Buffer | undefined => { - // prettier-ignore - const msgLength = ( - (1 + 1) // GameAction: tag + length - + (1 + 1) // PlayerPosition: tag + length - + (1 + 1) // PlayerPosition.userId: tag + length - + ppId.length // userId payload - + (1 + 1) // PlayerPosition.frame: tag + length - + (1 + 4) // frame.x: tag + value - + (1 + 4) // frame.y: tag + value - ); - const buf = Buffer.alloc(msgLength); - - let pos = 0; - - // write GameAction tag+length - buf[pos++] = (gameActionId << 3) | Wiretype.LEN; - buf[pos++] = msgLength - pos; - - // write PlayerPosition tag+length - buf[pos++] = (playerPositionId << 3) | Wiretype.LEN; - buf[pos++] = msgLength - pos; - - // write userId - buf[pos++] = (ppUserId << 3) | Wiretype.LEN; - buf[pos++] = ppId.length; - ppId.copy(buf, pos, 0); - pos += ppId.length; - - // write Frame tag+length - buf[pos++] = (ppFrameId << 3) | Wiretype.LEN; - buf[pos++] = msgLength - pos; - - // write x - buf[pos++] = (playerFrameXid << 3) | Wiretype.I32; - buf.writeFloatLE(x, pos); - pos += 4; - - // write y - buf[pos++] = (playerFrameYid << 3) | Wiretype.I32; - buf.writeFloatLE(y, pos); - pos += 4; + // write the client-sent compactframes data + cpf.copy(buf, pos); return buf; }; diff --git a/src/state/lobby.ts b/src/state/lobby.ts index b4fcff5..2f1b055 100644 --- a/src/state/lobby.ts +++ b/src/state/lobby.ts @@ -1,10 +1,10 @@ -import * as NT from '../gen/messages_pb'; -import { Handlers, LobbyActions } from '../types'; +import { NT } from '../gen/pbjs_pb'; import { ClientAuthWebSocket } from '../ws_handlers'; import { Publishers, M } from '../util'; +import { LobbyActionHandlers } from '../types'; import { IUser, UserState } from './user'; -import { RoomState } from './room'; +import { RoomState, RoomStateUpdateOpts } from './room'; export const SYSTEM_USER: IUser = { id: '-1', name: '[SYSTEM]' }; export const ANNOUNCEMENT: IUser = { id: '-2', name: '[ANNOUNCEMENT]' }; @@ -19,7 +19,7 @@ type createRoomParams = Simplify[0], 'r * Represents the state of an NT lobby. Currently there is exactly one lobby per * running instance. */ -export class LobbyState implements Handlers { +export class LobbyState implements LobbyActionHandlers { private readonly publishers: Publishers; readonly broadcast: ReturnType; @@ -213,12 +213,14 @@ export class LobbyState implements Handlers { //// message handlers //// cRoomCreate(payload: NT.ClientRoomCreate, user: UserState) { + const { password, ...opts } = payload; const room = this.createRoom({ lobby: this, owner: user, opts: { locked: false, - ...payload, + password: password ?? undefined, + ...opts, }, publishers: this.publishers, devMode: this.devMode, @@ -238,7 +240,12 @@ export class LobbyState implements Handlers { } cRoomUpdate(payload: NT.ClientRoomUpdate, user: UserState) { - const reason = user.room()?.update(user, payload); + // TODO: HAX: this is a workaround for protobuf.js generating weird oneof-shaped things with + // string|undefined|null values when using proto3 optionals instead of plain optional properties. + // connecting these to TypeBox is painful, so we're just yoloing it. we can improve by dropping + // the use of proto3 optional or making the runtypes more thoroughly align to the specifics + // of the proto generation library we are using + const reason = user.room()?.update(user, payload as RoomStateUpdateOpts); if (reason) user.send(M.sRoomUpdateFailed({ reason })); } diff --git a/src/state/room.ts b/src/state/room.ts index 3778204..c56f40d 100644 --- a/src/state/room.ts +++ b/src/state/room.ts @@ -1,4 +1,3 @@ -import * as NT from '../gen/messages_pb'; import { CreateBigRoomOpts, CreateRoomOpts, @@ -8,7 +7,7 @@ import { } from '../runtypes/room_opts'; import { Publishers, M, createChat } from '../util'; import { tagPlayerMove } from '../protoutil'; -import { GameActions, Handlers } from '../types'; +import { GameActionHandlers } from '../types'; import { statsUrl } from '../env_vars'; import { IUser, UserState } from './user'; @@ -18,6 +17,7 @@ import { StatsEvent, StatsRecorder } from './stats_recorder'; import { v4 as uuidv4 } from 'uuid'; import Debug from 'debug'; +import { NT } from '../gen/pbjs_pb'; const debug = Debug('nt:room'); let id = 0; @@ -44,14 +44,14 @@ export type RoomUpdate = { users: RoomUserUpdate[]; }; -type RoomStateCreateOpts = CreateRoomOpts | CreateBigRoomOpts; -type RoomStateUpdateOpts = UpdateRoomOpts | UpdateBigRoomOpts; +export type RoomStateCreateOpts = CreateRoomOpts | CreateBigRoomOpts; +export type RoomStateUpdateOpts = UpdateRoomOpts | UpdateBigRoomOpts; /** * Represents the state of a room in an NT lobby */ -export class RoomState implements Handlers { - private static readonly emptyFlags = M.sRoomFlagsUpdated().toBinary(); +export class RoomState implements GameActionHandlers<'cPlayerMove'> { + private static readonly emptyFlags = M.sRoomFlagsUpdated({}, true); private readonly lobby: LobbyState; private readonly broadcast: ReturnType; @@ -255,7 +255,7 @@ export class RoomState implements Handlers { } if (_opts.name !== undefined) this.name = _opts.name; - if (_opts.password !== undefined) this.password = _opts.password; + if (_opts.password != undefined) this.password = _opts.password; if (_opts.gamemode !== undefined) this.gamemode = _opts.gamemode; if (_opts.maxUsers !== undefined) this.maxUsers = _opts.maxUsers; if (_opts.locked !== undefined) this.locked = _opts.locked; @@ -277,7 +277,7 @@ export class RoomState implements Handlers { debug(this.id, 'updating flags', payload.flags.map(this.DEBUG_ONLY_flagValue)); - const flags = M.sRoomFlagsUpdated(payload).toBinary(); + const flags = M.sRoomFlagsUpdated(payload, true); // store the last-known flags as an already-encoded buffer, since we'll be // replaying it to people who join @@ -393,7 +393,7 @@ export class RoomState implements Handlers { * @param user UserState instance of the joining user * @param password Supplied password, if any, that the user gave when attempting to join */ - join(user: UserState, password?: string): void { + join(user: UserState, password?: string | null): void { let reason: string | null = null; let joinMessage = 'joining'; const room = user.room(); @@ -560,10 +560,10 @@ export class RoomState implements Handlers { //// message handlers //// - playerMoveRaw(payload: Buffer, user: UserState) { + cPlayerMove(cpf: Buffer, user: UserState) { if (!this.inProgress) return; - const bigUpdate = tagPlayerMove(payload, user.playerIdBuf); + const bigUpdate = tagPlayerMove(cpf, user.playerIdBuf); if (!bigUpdate) return; user.broadcast(this.topic, new Uint8Array(bigUpdate)); @@ -628,13 +628,16 @@ export class RoomState implements Handlers { // { ignoreSelf: true } user.broadcast(this.topic, M.sPlayerPickup({ userId: user.id, ...payload })); - switch (payload.kind.case) { + switch (payload.kind) { case 'heart': this.stats?.increment(user, StatsEvent.HeartPickup); break; case 'orb': this.stats?.increment(user, StatsEvent.OrbPickup); break; + default: + debug('unknown oneof case in cPlayerPickup: ', payload.kind); + break; } } cNemesisAbility(payload: NT.ClientNemesisAbility, user: UserState) { @@ -717,7 +720,7 @@ export class RoomState implements Handlers { private static readonly sym_unknown: unique symbol = Symbol('unknown'); /*istanbul ignore next*/ - private DEBUG_ONLY_flagValue = (flag: NT.ClientRoomFlagsUpdate_GameFlag) => { + private DEBUG_ONLY_flagValue = (flag: NT.ClientRoomFlagsUpdate.IGameFlag) => { // TODO: the NT app weirdly/incorrectly uses _optionality_ of flag message fields to signal // whether they are enabled or disabled. improving this requires a change to the app itself. // since we are unable to determine the expected type of a flag, we must currently rely on diff --git a/src/state/user.ts b/src/state/user.ts index a09f2ad..006fe10 100644 --- a/src/state/user.ts +++ b/src/state/user.ts @@ -1,5 +1,5 @@ -import * as NT from '../gen/messages_pb'; import { M } from '../util'; +import { NT } from '../gen/pbjs_pb'; import { RoomState } from './room'; import { LobbyState } from './lobby'; @@ -111,7 +111,7 @@ export class UserState implements IUser { * @param message The Envelope, or encoded Envelope, to send */ send(message: Uint8Array | NT.Envelope) { - this.socket?.send(message instanceof Uint8Array ? message : message.toBinary(), true, false); + this.socket?.send(message instanceof Uint8Array ? message : NT.Envelope.encode(message).finish(), true, false); } /** @@ -121,7 +121,12 @@ export class UserState implements IUser { * @param message The Envelope, or encoded Envelope, to send */ broadcast(topic: string, message: Uint8Array | NT.Envelope) { - this.socket?.publish(topic, message instanceof Uint8Array ? message : message.toBinary(), true, false); + this.socket?.publish( + topic, + message instanceof Uint8Array ? message : NT.Envelope.encode(message).finish(), + true, + false, + ); } /** diff --git a/src/types.ts b/src/types.ts index 793ede7..b7b3ca7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,26 +1,59 @@ -import type { Message } from '@bufbuild/protobuf'; - -import * as NT from './gen/messages_pb'; +import { Message } from 'protobufjs'; +import { NT } from './gen/pbjs_pb'; import type { UserState } from './state/user'; -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; -type Simplify = { [K in keyof U]: U[K] } & unknown; -type MessageType> = T extends Message ? P : never; +export interface ActionCreator { + (data: Exclude, encoded: true): Uint8Array; + (data: Exclude, encoded: false): NT.Envelope; + (data: Exclude): NT.Envelope; +} + +export type GameActionCreators = { + [K in keyof NT.IGameAction]-?: ActionCreator; +}; +export type LobbyActionCreators = { + [K in keyof NT.ILobbyAction]-?: ActionCreator; +}; + +type Simplify = { [K in keyof T]: T[K] } & unknown; -export type Handler> = (payload: MessageType, user: UserState) => void; +export type MessageInstance = Message & T; -export type Handlers }> = Simplify< - UnionToIntersection< - U extends { case: infer C extends `c${string}`; value: infer M extends Message } - ? { [K in C]: Handler } - : never - > +export type DecodedHandlers = { + [K in T]: (payload: any, user: UserState) => void; +}; +export type RawHandlers = { + [K in T]: (payload: Buffer, user: UserState) => void; +}; + +type GAH = keyof NT.IGameAction & `c${string}`; +export type GameActionHandlers = Simplify< + DecodedHandlers> & RawHandlers >; -export type LobbyActions = Exclude; -export type LobbyActionNames = LobbyActions['case'] & `c${string}`; -export type GameActions = Exclude; -export type GameActionNames = GameActions['case'] & `c${string}`; +type LAH = keyof NT.ILobbyAction & `c${string}`; +export type LobbyActionHandlers = Simplify< + DecodedHandlers> & RawHandlers +>; + +// type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; +// type Simplify = { [K in keyof U]: U[K] } & unknown; +// type MessageType> = T extends Message ? P : never; + +// export type Handler> = (payload: MessageType, user: UserState) => void; + +// export type Handlers }> = Simplify< +// UnionToIntersection< +// U extends { case: infer C extends `c${string}`; value: infer M extends Message } +// ? { [K in C]: Handler } +// : never +// > +// >; + +// export type LobbyActions = Exclude; +// export type LobbyActionNames = LobbyActions['case'] & `c${string}`; +// export type GameActions = Exclude; +// export type GameActionNames = GameActions['case'] & `c${string}`; -export const isClientLobbyAction = (v: unknown): v is LobbyActionNames => typeof v === 'string' && v.startsWith('c'); -export const isClientGameAction = (v: unknown): v is GameActionNames => typeof v === 'string' && v.startsWith('c'); +// export const isClientLobbyAction = (v: unknown): v is LobbyActionNames => typeof v === 'string' && v.startsWith('c'); +// export const isClientGameAction = (v: unknown): v is GameActionNames => typeof v === 'string' && v.startsWith('c'); diff --git a/src/util.ts b/src/util.ts index 6eafeea..220deb2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,69 +1,38 @@ +import { createHmac, randomBytes } from 'node:crypto'; import { TemplatedApp, WebSocket } from 'uWebSockets.js'; -import { AnyMessage, PlainMessage } from '@bufbuild/protobuf'; import { v4 as uuidv4 } from 'uuid'; import { IUser } from './state/user'; -import { Envelope, GameAction, LobbyAction } from './gen/messages_pb'; - -import { GameActions, LobbyActions } from './types'; -import { createHmac, randomBytes } from 'node:crypto'; +import { NT } from './gen/pbjs_pb'; +import { gameActions, lobbyActions } from './pbreflect'; +import { ActionCreator, GameActionCreators, LobbyActionCreators } from './types'; import Debug from 'debug'; const debug = Debug('nt:util'); -type LobbyActionCreator = { - [K in LobbyActions['case']]: (LobbyActions & { case: K })['value']; -}; -type GameActionCreator = { - [K in GameActions['case']]: (GameActions & { case: K })['value']; -}; - -type Creators> = { - [K in keyof T]: (data?: PlainMessage) => Envelope; -} & unknown; - /** * Factory functions for each action type. Each function - * accepts an action payload and returns an `Envelope` instance + * accepts an action payload and returns an `NT.Envelope` instance * * @example * ```ts * M.cChat({ message: 'hi there' }) * ``` */ -export const M: Creators = {} as any; +export const M: GameActionCreators & LobbyActionCreators = {} as any; -for (const f of GameAction.fields.list()) { - if (f.kind !== 'message' || f.oneof?.name !== 'action') continue; - (M as any)[f.jsonName] = (data: PlainMessage | undefined) => - new Envelope({ - kind: { - case: 'gameAction', - value: { - action: { - case: f.jsonName as any, - value: new f.T(data) as any, - }, - }, - }, - }); +for (const key of gameActions) { + M[key] = ((data, encoded) => + encoded + ? NT.Envelope.encode({ gameAction: { [key]: data } }).finish() + : NT.Envelope.fromObject({ gameAction: { [key]: data } })) as ActionCreator; } - -for (const f of LobbyAction.fields.list()) { - if (f.kind !== 'message' || f.oneof?.name !== 'action') continue; - (M as any)[f.jsonName] = (data: PlainMessage | undefined) => - new Envelope({ - kind: { - case: 'lobbyAction', - value: { - action: { - case: f.jsonName as any, - value: new f.T(data) as any, - }, - }, - }, - }); +for (const key of lobbyActions) { + M[key] = ((data, encoded) => + encoded + ? NT.Envelope.encode({ lobbyAction: { [key]: data } }).finish() + : NT.Envelope.fromObject({ lobbyAction: { [key]: data } })) as ActionCreator; } type HasPublish = Pick, 'publish'>; @@ -72,12 +41,12 @@ type HasPublish = Pick, 'publish'>; * Returns a set of factory functions for publishing to specific topics on the uWS app */ export const BindPublishers = (app: TemplatedApp, createChatId: () => string = uuidv4) => { - const publish = (topic: string, message: Uint8Array | Envelope, target: HasPublish = app) => { - target.publish(topic, message instanceof Uint8Array ? message : message.toBinary(), true, false); + const publish = (topic: string, message: Uint8Array | NT.Envelope, target: HasPublish = app) => { + target.publish(topic, message instanceof Uint8Array ? message : NT.Envelope.encode(message).finish(), true, false); }; return { - broadcast: (topic: string) => (message: Uint8Array | Envelope, socket?: HasPublish) => + broadcast: (topic: string) => (message: Uint8Array | NT.Envelope, socket?: HasPublish) => publish(topic, message, socket), chat: (topic: string) => (user: IUser, message: string, socket?: HasPublish) => publish( diff --git a/src/ws_handlers.ts b/src/ws_handlers.ts index beb0f60..7d10cd6 100644 --- a/src/ws_handlers.ts +++ b/src/ws_handlers.ts @@ -7,11 +7,10 @@ import { UserState } from './state/user'; import { RoomState } from './state/room'; import { maybePlayerMove } from './protoutil'; - -import * as NT from './gen/messages_pb'; +import { shortHash } from './util'; +import { NT } from './gen/pbjs_pb'; import type Debug from 'debug'; -import { shortHash } from './util'; let conn_id = 0; @@ -104,6 +103,26 @@ export const createMessageHandler = ({ users.set(ws, user); }); + const handleLobbyAction = (lobby: LobbyState, action: NT.LobbyAction, user: UserState) => { + const method = action.action as Exclude & `c${string}`; + if (!method || !method.startsWith('c')) return; + + if (action.action && typeof lobby[method] === 'function') { + const payload: NT.LobbyAction[typeof method] = action[method]; + if (!payload) return; + lobby[method](payload as any, user); + } + }; + const handleGameAction = (room: RoomState, action: NT.GameAction, user: UserState) => { + const method = action.action as Exclude & `c${string}`; + if (!method || !method.startsWith('c')) return; + + if (action.action && typeof room[method] === 'function') { + const payload: NT.GameAction[typeof method] = action[method]; + if (!payload) return; + room[method](payload as any, user); + } + }; const handleMessage = trycatch( 'handleMessage', (ws: ClientAuthWebSocket, message: ArrayBuffer, isBinary: boolean) => { @@ -115,42 +134,27 @@ export const createMessageHandler = ({ // optimized message handling for player move updates const buf = Buffer.from(message); - const playerMovePayload = maybePlayerMove(buf); - - if (playerMovePayload) { - user.room()?.playerMoveRaw(playerMovePayload, user); + // returns the nested CompactPlayerFrames message when present + const cpf = maybePlayerMove(buf); + if (cpf.length > 0) { + user.room()?.cPlayerMove(cpf, user); return; } // fall back to proper decode/encode for everything else - const msg = NT.Envelope.fromBinary(buf); - - // debug(user.name, msg.kind.case, msg.kind.value?.action.case); + const msg = NT.Envelope.decode(buf); - const { case: actionType, value: actionPayload } = msg.kind; - if (!actionType || !actionPayload) return; // empty "kind" - - const { case: action, value: payload } = actionPayload.action; - if (!action || !payload) return; // empty "action" - - let target: LobbyState | RoomState | null = null; - switch (actionType) { + switch (msg.kind) { case 'lobbyAction': - target = lobby; + if (msg.lobbyAction) handleLobbyAction(lobby, msg.lobbyAction as NT.LobbyAction, user); break; case 'gameAction': - target = user.room(); + const userRoom = user.room(); + if (userRoom === null) return; + if (msg.gameAction) handleGameAction(userRoom, msg.gameAction as NT.GameAction, user); break; - } - - try { - if (target && action) { - const method = (target as any)[action]; - if (typeof method === 'function') method.call(target, payload, user); - } - } catch (e) { - console.error('Caught error from handler', actionType, action, e); - // debug('Caught error from handler', actionType, action, e); + default: + return; } }, ); diff --git a/tsconfig.json b/tsconfig.json index c261869..32354df 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,9 +13,9 @@ "resolveJsonModule": true, "isolatedModules": true, "incremental": true, - + "allowJs": true, "outDir": "dist" }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": ["src/**/*.ts", "src/**/*.js"], + "exclude": ["node_modules", "coverage"] } From e773358812bb178ac99c65425b7fce0496babd1b Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sun, 3 Dec 2023 17:09:59 +0000 Subject: [PATCH 2/5] Extract proto generation and handling to a separate module --- Dockerfile | 9 +- babel.config.js | 3 - package-lock.json | 544 +--------- package.json | 5 +- proto/messages.proto | 721 ------------- restart.sh | 9 +- src/compact_move.test.ts | 135 --- ...ompact_move.ts => compact_move_analyze.ts} | 121 +-- src/conformance/lobby.test.ts | 6 +- src/pbreflect.ts | 29 - src/protohax/fixtures/generate.sh | 10 - src/protohax/fixtures/protohax.proto | 68 -- src/protohax/fixtures/protohax_pb.d.ts | 164 --- src/protohax/fixtures/protohax_pb.js | 952 ------------------ src/protohax/fixtures/protohax_pb.json | 115 --- src/protohax/protohax.test.ts | 347 ------- src/protohax/protohax.ts | 380 ------- src/protoutil.test.ts | 149 --- src/protoutil.ts | 106 -- src/state/lobby.ts | 4 +- src/state/room.ts | 5 +- src/state/user.ts | 3 +- src/types.ts | 2 +- src/util.ts | 29 +- src/ws_handlers.ts | 3 +- tsconfig.json | 7 +- 26 files changed, 51 insertions(+), 3875 deletions(-) delete mode 100644 babel.config.js delete mode 100644 proto/messages.proto delete mode 100644 src/compact_move.test.ts rename src/{compact_move.ts => compact_move_analyze.ts} (66%) delete mode 100644 src/pbreflect.ts delete mode 100755 src/protohax/fixtures/generate.sh delete mode 100644 src/protohax/fixtures/protohax.proto delete mode 100644 src/protohax/fixtures/protohax_pb.d.ts delete mode 100644 src/protohax/fixtures/protohax_pb.js delete mode 100644 src/protohax/fixtures/protohax_pb.json delete mode 100644 src/protohax/protohax.test.ts delete mode 100644 src/protohax/protohax.ts delete mode 100644 src/protoutil.test.ts delete mode 100644 src/protoutil.ts diff --git a/Dockerfile b/Dockerfile index 5205d02..b0a2779 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,16 +3,9 @@ FROM node:21-slim AS build WORKDIR /src COPY package.json package-lock.json ./ RUN npm install -COPY proto/ ./proto/ -COPY tsconfig.json babel.config.js ./ COPY src/ ./src/ -RUN mkdir -p ./src/gen && \ - npx pbjs --es6 -w es6 -t static-module proto/messages.proto > ./src/gen/pbjs_pb.js && \ - npx pbts ./src/gen/pbjs_pb.js > ./src/gen/pbjs_pb.d.ts && \ - npx pbjs -t json proto/messages.proto > ./src/gen/pbjs_pb.json - +COPY tsconfig.json jest.config.js ./ # run tests -COPY jest.config.js . RUN npm test # build diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 18b6e17..0000000 --- a/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - plugins: ['@babel/plugin-transform-modules-commonjs'], -}; diff --git a/package-lock.json b/package-lock.json index 64baf0f..8c71286 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,11 @@ "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", + "nt-message": "github:noita-together/nt-message#v1.0.0", "uuid": "^9.0.1", "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0" }, "devDependencies": { - "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@types/debug": "^4.1.10", "@types/jest": "^29.5.7", "@types/jsonwebtoken": "^9.0.3", @@ -24,8 +24,6 @@ "jest": "^29.7.0", "nodemon": "^3.0.1", "prettier": "^3.0.3", - "protobufjs": "^7.2.5", - "protobufjs-cli": "^1.1.2", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "ts-node-dev": "^2.0.0", @@ -614,23 +612,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -1437,18 +1418,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jsdoc/salty": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.6.tgz", - "integrity": "sha512-aA+awb5yoml8TQ3CzI5Ue7sM3VMRC4l1zJJW4fgZ8OCL1wshJZhNzaf0PL85DSnOUw6QuFgeHGD/eq/xwwAF2g==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v12.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1487,32 +1456,27 @@ "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dev": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -1521,32 +1485,27 @@ "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@sinclair/typebox": { "version": "0.31.17", @@ -1697,28 +1656,6 @@ "@types/node": "*" } }, - "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true - }, "node_modules/@types/ms": { "version": "0.7.32", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.32.tgz", @@ -1729,7 +1666,6 @@ "version": "20.8.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz", "integrity": "sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==", - "dev": true, "dependencies": { "undici-types": "~5.25.1" } @@ -1791,15 +1727,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -2022,12 +1949,6 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2176,18 +2097,6 @@ } ] }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2422,12 +2331,6 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2517,15 +2420,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2590,66 +2484,6 @@ "node": ">=8" } }, - "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2663,24 +2497,6 @@ "node": ">=4" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2751,12 +2567,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -3824,56 +3634,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -3944,15 +3704,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -3971,19 +3722,6 @@ "node": ">=6" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -3999,15 +3737,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -4029,12 +3758,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4085,8 +3808,7 @@ "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", - "dev": true + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/lru-cache": { "version": "6.0.0", @@ -4129,56 +3851,6 @@ "tmpl": "1.0.5" } }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", - "dev": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4356,6 +4028,15 @@ "node": ">=8" } }, + "node_modules/nt-message": { + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/noita-together/nt-message.git#d68d7d879f2d9272f2e4af636747ce9c927c8461", + "license": "MIT", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.2.5" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4389,23 +4070,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4585,15 +4249,6 @@ } } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/prettier": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", @@ -4652,7 +4307,6 @@ "version": "7.2.5", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", - "dev": true, "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -4672,74 +4326,6 @@ "node": ">=12.0.0" } }, - "node_modules/protobufjs-cli": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.2.tgz", - "integrity": "sha512-8ivXWxT39gZN4mm4ArQyJrRgnIwZqffBWoLDsE21TmMcKI3XwJMV4lEF2WU02C4JAtgYYc2SfJIltelD8to35g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "escodegen": "^1.13.0", - "espree": "^9.0.0", - "estraverse": "^5.1.0", - "glob": "^8.0.0", - "jsdoc": "^4.0.0", - "minimist": "^1.2.0", - "semver": "^7.1.2", - "tmp": "^0.2.1", - "uglify-js": "^3.7.7" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "protobufjs": "^7.0.0" - } - }, - "node_modules/protobufjs-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/protobufjs-cli/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/protobufjs-cli/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -4818,15 +4404,6 @@ "node": ">=0.10.0" } }, - "node_modules/requizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", - "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -5226,33 +4803,6 @@ "node": ">=0.8" } }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp/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/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5505,18 +5055,6 @@ "node": ">= 8" } }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5551,41 +5089,16 @@ "node": ">=14.17" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, "node_modules/undici-types": { "version": "5.25.3", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", - "dev": true + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" }, "node_modules/update-browserslist-db": { "version": "1.0.13", @@ -5705,15 +5218,6 @@ "node": ">= 8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5750,12 +5254,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index ebcf74b..ef20d48 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,11 @@ "description": "Noita together lobby and game server", "main": "index.js", "scripts": { - "genproto": "mkdir -p ./src/gen && pbjs --es6 -w es6 -t static-module proto/messages.proto -o ./src/gen/pbjs_pb.js && pbts -o ./src/gen/pbjs_pb.d.ts ./src/gen/pbjs_pb.js && pbjs -t json proto/messages.proto -o ./src/gen/pbjs_pb.json", "test": "tsc --noEmit && npx jest" }, "author": "Kris Reeves", "license": "ISC", "devDependencies": { - "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@types/debug": "^4.1.10", "@types/jest": "^29.5.7", "@types/jsonwebtoken": "^9.0.3", @@ -18,8 +16,6 @@ "jest": "^29.7.0", "nodemon": "^3.0.1", "prettier": "^3.0.3", - "protobufjs": "^7.2.5", - "protobufjs-cli": "^1.1.2", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "ts-node-dev": "^2.0.0", @@ -30,6 +26,7 @@ "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", + "nt-message": "github:noita-together/nt-message#v1.0.0", "uuid": "^9.0.1", "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0" } diff --git a/proto/messages.proto b/proto/messages.proto deleted file mode 100644 index 3fa5e7b..0000000 --- a/proto/messages.proto +++ /dev/null @@ -1,721 +0,0 @@ -syntax = "proto3"; -package NT; - -message Envelope { - oneof kind { - GameAction game_action = 1; - LobbyAction lobby_action = 50; - } -} - -message GameAction { - oneof action { - // The c prefix stands for "client", which refers to this application - // The s prefix stands for "server", which refers to the online Noita game server - - CompactPlayerFrames c_player_move = 1; - ServerPlayerMoves s_player_moves = 2; - - ClientPlayerUpdate c_player_update = 3; - ServerPlayerUpdate s_player_update = 4; - - ClientPlayerUpdateInventory c_player_update_inventory = 5; - ServerPlayerUpdateInventory s_player_update_inventory = 6; - - ClientHostItemBank c_host_item_bank = 7; - ServerHostItemBank s_host_item_bank = 8; - - ClientHostUserTake c_host_user_take = 9; - ServerHostUserTake s_host_user_take = 10; - - ClientHostUserTakeGold c_host_user_take_gold = 11; - ServerHostUserTakeGold s_host_user_take_gold = 12; - - ClientPlayerAddGold c_player_add_gold = 13; - ServerPlayerAddGold s_player_add_gold = 14; - - ClientPlayerTakeGold c_player_take_gold = 15; - ServerPlayerTakeGold s_player_take_gold = 16; - - ClientPlayerAddItem c_player_add_item = 17; - ServerPlayerAddItem s_player_add_item = 18; - - ClientPlayerTakeItem c_player_take_item = 19; - ServerPlayerTakeItem s_player_take_item = 20; - - ClientPlayerPickup c_player_pickup = 21; - ServerPlayerPickup s_player_pickup = 22; - - ClientNemesisAbility c_nemesis_ability = 23; - ServerNemesisAbility s_nemesis_ability = 24; - - ClientNemesisPickupItem c_nemesis_pickup_item = 25; - ServerNemesisPickupItem s_nemesis_pickup_item = 26; - - ClientChat c_chat = 27; - ServerChat s_chat = 28; - - ClientPlayerDeath c_player_death = 29; - ServerPlayerDeath s_player_death = 30; - - ClientPlayerNewGamePlus c_player_new_game_plus = 31; - ServerPlayerNewGamePlus s_player_new_game_plus = 32; - - ClientPlayerSecretHourglass c_player_secret_hourglass = 33; - ServerPlayerSecretHourglass s_player_secret_hourglass = 34; - - ClientCustomModEvent c_custom_mod_event = 35; - ServerCustomModEvent s_custom_mod_event = 36; - - ClientRespawnPenalty c_respawn_penalty = 37; - ServerRespawnPenalty s_respawn_penalty = 38; - - ClientAngerySteve c_angery_steve = 39; - ServerAngerySteve s_angery_steve = 40; - - ServerStatsUpdate s_stat_update = 42; - } -} - -message PlayerFrame { - optional float x = 1; - optional float y = 2; - optional float arm_r = 3; - optional float arm_scale_y = 4; - optional float scale_x = 5; - optional int32 anim = 6; - optional int32 held = 7; -} -message OldClientPlayerMove { - repeated PlayerFrame frames = 1; -} - -message OldServerPlayerMove { - string user_id = 1; - repeated PlayerFrame frames = 2; -} - -message CompactPlayerFrames { - float x_init = 1; - float y_init = 2; - repeated sint32 x_deltas = 3; - repeated sint32 y_deltas = 4; - repeated int32 arm_r = 5; - int32 armScaleY = 6; - int32 scaleX = 7; - repeated int32 anim_idx = 8; - repeated int32 anim_val = 9; - repeated int32 held_idx = 10; - repeated int32 held_val = 11; - - string user_id = 15; -} -message ServerPlayerMoves { - repeated CompactPlayerFrames user_frames = 1; -} - -message ClientPlayerUpdate { - optional float cur_hp = 1; - optional float max_hp = 2; - optional string location = 3; - optional bool sampo = 4; -} - -message ServerPlayerUpdate { - string user_id = 1; - optional float cur_hp = 2; - optional float max_hp = 3; - optional string location = 4; - optional bool sampo = 5; -} - -message ClientPlayerUpdateInventory { - repeated InventoryWand wands = 1; - repeated InventoryItem items = 2; - repeated InventorySpell spells = 3; - - message InventoryWand { - uint32 index = 1; - Wand wand = 2; - } - - message InventoryItem { - uint32 index = 3; - Item item = 4; - } - - message InventorySpell { - uint32 index = 1; - Spell spell = 2; - } -} - -message ServerPlayerUpdateInventory { - string user_id = 1; - repeated InventoryWand wands = 2; - repeated InventoryItem items = 3; - repeated InventorySpell spells = 4; - - message InventoryWand { - uint32 index = 1; - Wand wand = 2; - } - - message InventoryItem { - uint32 index = 1; - Item item = 2; - } - - message InventorySpell { - uint32 index = 1; - Spell spell = 2; - } -} - -message ClientHostItemBank { - repeated Wand wands = 1; - repeated Spell spells = 2; - repeated Item items = 3; - uint32 gold = 4; - repeated EntityItem objects = 5; -} - -message ServerHostItemBank { - repeated Wand wands = 1; - repeated Spell spells = 2; - repeated Item items = 3; - uint32 gold = 4; - repeated EntityItem objects = 5; -} - -message ClientHostUserTake { - string user_id = 1; - string id = 2; - bool success = 3; -} - -message ServerHostUserTake { - string user_id = 1; - string id = 2; - bool success = 3; -} - -message ClientHostUserTakeGold { - string user_id = 1; - uint32 amount = 2; - bool success = 3; -} - -message ServerHostUserTakeGold { - string user_id = 1; - uint32 amount = 2; - bool success = 3; -} - -message ClientPlayerAddGold { - uint32 amount = 1; -} - -message ServerPlayerAddGold { - string user_id = 1; - uint32 amount = 2; -} - -message ClientPlayerTakeGold { - uint32 amount = 1; -} - -message ServerPlayerTakeGold { - string user_id = 1; - uint32 amount = 2; -} - -message ClientPlayerAddItem { - oneof item { - Spells spells = 1; - Wands wands = 2; - Items flasks = 3; - Entities objects = 4; - } - - message Spells { - repeated Spell list = 1; - } - message Wands { - repeated Wand list = 1; - } - message Items { - repeated Item list = 1; - } - message Entities { - repeated EntityItem list = 1; - } -} - -message ServerPlayerAddItem { - string user_id = 1; - oneof item { - Spells spells = 2; - Wands wands = 3; - Items flasks = 4; - Entities objects = 5; - } - - message Spells { - repeated Spell list = 1; - } - message Wands { - repeated Wand list = 2; - } - message Items { - repeated Item list = 3; - } - message Entities { - repeated EntityItem list = 4; - } -} - -message ClientPlayerTakeItem { - string id = 1; -} - -message ServerPlayerTakeItem { - string user_id = 1; - string id = 2; -} - -message ClientChat { - string message = 1; -} - -message ServerChat { - string id = 1; - string user_id = 2; - string name = 3; - string message = 4; -} - -message ServerStatsUpdate{ - string data = 1; -} - - -message ClientPlayerPickup { - oneof kind { - HeartPickup heart = 1; - OrbPickup orb = 2; - } - - message HeartPickup { - bool hp_perk = 1; - } - - message OrbPickup { - uint32 id = 1; - } -} - -message ServerPlayerPickup { - string user_id = 1; - oneof kind { - HeartPickup heart = 2; - OrbPickup orb = 3; - } - - message HeartPickup { - bool hp_perk = 1; - } - - message OrbPickup { - uint32 id = 1; - } -} - -message ClientNemesisPickupItem { - string game_id = 1; -} - -message ServerNemesisPickupItem { - string user_id = 1; - string game_id = 2; -} - -message ClientNemesisAbility { - string game_id = 1; -} - -message ServerNemesisAbility { - string user_id = 1; - string game_id = 2; -} - -message ClientPlayerDeath { - bool is_win = 1; - optional uint32 game_time = 2; -} - -message ServerPlayerDeath { - string user_id = 1; - bool is_win = 2; - optional uint32 game_time = 3; -} - -message ClientPlayerNewGamePlus { - uint32 amount = 1; -} - -message ServerPlayerNewGamePlus { - string user_id = 1; - uint32 amount = 2; -} - -message ClientPlayerSecretHourglass { - string material = 1; -} - -message ServerPlayerSecretHourglass { - string user_id = 1; - string material = 2; -} - -message ClientCustomModEvent { - string payload = 1; -} - -message ServerCustomModEvent { - string user_id = 1; - string payload = 2; -} - -message ClientRespawnPenalty { - uint32 deaths = 1; -} - -message ServerRespawnPenalty { - string user_id = 1; - uint32 deaths = 2; -} - -message ClientAngerySteve { - bool idk = 1; -} - -message ServerAngerySteve { - string user_id = 1; -} - -message Wand { - string id = 1; - WandStats stats = 2; - repeated Spell always_cast = 3; - repeated Spell deck = 4; - optional string sent_by = 5; - optional string contributed_by = 6; - - message WandStats { - string sprite = 1; - bool named = 2; - string ui_name = 3; - float mana_max = 4; - float mana_charge_speed = 5; - int32 reload_time = 6; - uint32 actions_per_round = 7; - uint32 deck_capacity = 8; - bool shuffle_deck_when_empty = 9; - float spread_degrees = 10; - float speed_multiplier = 11; - int32 fire_rate_wait = 12; - float tip_x = 13; - float tip_y = 14; - float grip_x = 15; - float grip_y = 16; - } -} - -message Spell { - string id = 1; - string game_id = 2; - optional string sent_by = 3; - optional string contributed_by = 4; - int32 uses_remaining = 5; -} - -message Item { - string id = 1; - Color color = 2; - repeated Material content = 3; - optional string sent_by = 4; - optional string contributed_by = 5; - bool is_chest = 6 [deprecated=true]; - string item_type = 7; - - message Color { - float r = 1; - float g = 2; - float b = 3; - } - message Material { - uint32 id = 1; - uint32 amount = 2; - } -} - -message EntityItem { - string id = 1; - string path = 2; - string sprite = 3; - optional string sent_by = 4; -} - -message LobbyAction { - oneof action { - ClientRoomCreate c_room_create = 1; - ServerRoomCreated s_room_created = 2; - ServerRoomCreateFailed s_room_create_failed = 3; - - ClientRoomUpdate c_room_update = 4; - ServerRoomUpdated s_room_updated = 5; - ServerRoomUpdateFailed s_room_update_failed = 6; - - ClientRoomFlagsUpdate c_room_flags_update = 7; - ServerRoomFlagsUpdated s_room_flags_updated = 8; - ServerRoomFlagsUpdateFailed s_room_flags_update_failed = 9; - - ClientRoomDelete c_room_delete = 10; - ServerRoomDeleted s_room_deleted = 11; - - ClientJoinRoom c_join_room = 12; - ServerJoinRoomSuccess s_join_room_success = 13; - ServerJoinRoomFailed s_join_room_failed = 14; - ServerUserJoinedRoom s_user_joined_room = 15; - - ClientLeaveRoom c_leave_room = 16; - ServerUserLeftRoom s_user_left_room = 17; - - ClientKickUser c_kick_user = 18; - ServerUserKicked s_user_kicked = 19; - - ClientBanUser c_ban_user = 20; - ServerUserBanned s_user_banned = 21; - - ClientReadyState c_ready_state = 22; - ServerUserReadyState s_user_ready_state = 23; - - ClientStartRun c_start_run = 24; - ServerHostStart s_host_start = 25; - - ClientRequestRoomList c_request_room_list = 27; - ServerRoomList s_room_list = 28; - - ServerDisconnected s_disconnected = 31; - ServerRoomAddToList s_room_add_to_list = 32; - - ClientRunOver c_run_over = 33; - } -} - -message ClientRunOver { - optional bool idk = 1; -} - -message ServerDisconnected { - string reason = 1; -} - -message ClientRoomDelete { - string id = 1; -} - -message ServerRoomDeleted { - string id = 1; -} - -message ClientRoomCreate { - string name = 1; - uint32 gamemode = 2; - uint32 max_users = 3; - optional string password = 4; -} - -message ServerRoomCreated { - string id = 1; - string name = 2; - uint32 gamemode = 3; - uint32 max_users = 4; - optional string password = 5; - bool locked = 6; - repeated User users = 7; - - message User { - string user_id = 1; - string name = 2; - bool ready = 3; - bool owner = 4; - } -} - -message ServerRoomCreateFailed { - string reason = 1; -} - -message ClientRoomUpdate { - optional string name = 1; - optional uint32 gamemode = 2; - optional uint32 max_users = 3; - optional string password = 4; - optional bool locked = 5; -} - -message ServerRoomUpdated { - optional string name = 1; - optional uint32 gamemode = 2; - optional uint32 max_users = 3; - optional string password = 4; - optional bool locked = 5; -} - -message ServerRoomUpdateFailed{ - string reason = 1; -} - -message ClientRoomFlagsUpdate { - repeated GameFlag flags = 1; - message GameFlag { - // TODO: This seems like a hack, please improve it - string flag = 1; - optional int32 int_val = 2; - optional string str_val = 3; - optional float float_val = 4; - optional bool bool_val = 5; - optional uint32 u_int_val = 6; - } -} - -message ServerRoomFlagsUpdated { - repeated GameFlag flags = 1; - message GameFlag { - string flag = 1; - optional int32 int_val = 2; - optional string str_val = 3; - optional float float_val = 4; - optional bool bool_val = 5; - optional uint32 u_int_val = 6; - } -} - -message ServerRoomFlagsUpdateFailed { - string reason = 1; -} - -message ClientJoinRoom { - string id = 1; - optional string password = 2; -} - -message ServerJoinRoomSuccess { - string id = 1; - string name = 2; - uint32 gamemode = 3; - uint32 max_users = 4; - optional string password = 5; - bool locked = 6; - repeated User users = 7; - - message User { - string user_id = 1; - string name = 2; - bool ready = 3; - bool owner = 4; - } -} - -message ServerJoinRoomFailed { - string reason = 1; -} - -message ServerUserJoinedRoom { - string user_id = 1; - string name = 2; -} - -message ClientLeaveRoom { - string user_id = 1; //should be empty msg -} - -message ServerUserLeftRoom { - string user_id = 1; -} - -message ClientKickUser { - string user_id = 1; -} - -message ServerUserKicked { - string user_id = 1; -} - -message ClientBanUser { - string user_id = 1; -} - -message ServerUserBanned { - string user_id = 1; -} - -message ClientReadyState { - bool ready = 1; - optional string seed = 2; - repeated string mods = 3; - optional string version = 4; - optional bool beta = 5; -} - -message ServerUserReadyState { - string user_id = 1; - bool ready = 2; - optional string seed = 3; - repeated string mods = 4; - optional string version = 5; - optional bool beta = 6; -} - -message ClientStartRun { - bool forced = 1; -} - -message ServerHostStart { - bool forced = 1; -} - -message ClientRequestRoomList { - uint32 page = 1; -} - -message ServerRoomList { - repeated Room rooms = 1; - optional uint32 pages = 2; - message Room { - string id = 1; - string name = 2; - uint32 gamemode = 3; - uint32 cur_users = 4; - uint32 max_users = 5; - bool protected = 6; - string owner = 7; - bool locked = 8; - } -} - -message ServerRoomAddToList { - Room room = 1; - message Room { - string id = 1; - string name = 2; - uint32 gamemode = 3; - uint32 cur_users = 4; - uint32 max_users = 5; - bool protected = 6; - string owner = 7; - bool locked = 8; - } -} \ No newline at end of file diff --git a/restart.sh b/restart.sh index a626472..9e4f376 100755 --- a/restart.sh +++ b/restart.sh @@ -9,7 +9,8 @@ source "$HERE/.env" CONTAINER_NAME="lobby-server" IMAGE_NAME="lobby-server" TLS_SERVER_NAME="lobby.noitatogether.com" -ANCHOR_IP="$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address)" +# ANCHOR_IP="$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address)" +ANCHOR_IP="127.0.0.1" docker stop "$CONTAINER_NAME" || true docker rm "$CONTAINER_NAME" || true @@ -18,9 +19,9 @@ docker run -d --name "$CONTAINER_NAME" \ --restart "unless-stopped" \ --network "nt" \ --network-alias "$TLS_SERVER_NAME" \ - -v "/etc/letsencrypt/archive/$TLS_SERVER_NAME:/etc/letsencrypt/archive/$TLS_SERVER_NAME" \ - -v "/etc/letsencrypt/live/$TLS_SERVER_NAME:/etc/letsencrypt/live/$TLS_SERVER_NAME" \ - -p "$ANCHOR_IP:443:443" \ + -v "$HERE/tls:/etc/letsencrypt/archive/$TLS_SERVER_NAME" \ + -v "$HERE/tls:/etc/letsencrypt/live/$TLS_SERVER_NAME" \ + -p "$ANCHOR_IP:4443:443" \ -e "DEBUG=*" \ -e "JWT_SECRET=$SECRET_JWT_ACCESS" \ -e "JWT_REFRESH=$SECRET_JWT_REFRESH" \ diff --git a/src/compact_move.test.ts b/src/compact_move.test.ts deleted file mode 100644 index 4e0ff50..0000000 --- a/src/compact_move.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { - createArmrCoder, - createDeltaCoder, - decodeBitfield, - decodeStable, - encodeBitfield, - encodeStable, -} from './compact_move'; -import { NT } from './gen/pbjs_pb'; - -describe('compact move encoding', () => { - describe('armR encoding', () => { - const tests: { targetBytes: number; maxError: number; value: number }[] = [ - { targetBytes: 1, maxError: (Math.PI * 2 + 1) / 2 ** 7 }, - { targetBytes: 2, maxError: (Math.PI * 2 + 1) / 2 ** 14 }, - { targetBytes: 3, maxError: (Math.PI * 2 + 1) / 2 ** 21 }, - ].flatMap((obj) => - [ - -Math.PI, - -1, - 0, - 1, - Math.PI, - // fake fuzz testing. should complain enough if something is amiss - Math.random() * (Math.PI * 2) - Math.PI, - Math.random() * (Math.PI * 2) - Math.PI, - Math.random() * (Math.PI * 2) - Math.PI, - Math.random() * (Math.PI * 2) - Math.PI, - ].map((value) => ({ ...obj, value })), - ); - - it.each(tests)('bytes<=$targetBytes maxError<=$maxError value=$value', ({ targetBytes, maxError, value }) => { - const { encodeArmR, decodeArmR } = createArmrCoder(targetBytes); - const encoded = encodeArmR(value); - const roundtrip = decodeArmR(encoded); - expect(Math.abs(roundtrip - value)).toBeLessThanOrEqual(maxError); - const serialized = NT.CompactPlayerFrames.encode({ armR: [encoded] }).finish(); - const serializedSize = serialized.length - 2; // [tag][len][varint] - expect(serializedSize).toBeLessThanOrEqual(targetBytes); - }); - }); - describe('delta encoding', () => { - const tests: { fractionalDigits: number; maxError: number; value: number }[] = [ - { fractionalDigits: 1, maxError: 0.1 / 2 }, - { fractionalDigits: 2, maxError: 0.01 / 2 }, - { fractionalDigits: 3, maxError: 0.001 / 2 }, - ].flatMap((obj) => - [ - 5, - 5.49, - 5.51, - 1005, - 1005.49, - 1005.51, - // fake fuzz testing. should complain enough if something is amiss - 1 + Math.random(), - 1 + Math.random(), - 1 + Math.random(), - 1 + Math.random(), - ].map((value) => ({ ...obj, value })), - ); - - it.each(tests)( - 'fractionalDigits=$fractionalDigits maxError<=$maxError value=$value', - ({ fractionalDigits, maxError, value }) => { - const { encodeDelta, decodeDelta } = createDeltaCoder(fractionalDigits); - - const seq = [1, value]; - let i = 0; - - const { init, deltas } = encodeDelta(seq.length, () => seq[i++])!; - - const res: number[] = new Array(2); - decodeDelta(init, deltas, (i, v) => { - res[i] = v; - }); - - expect(res[0]).toEqual(seq[0]); - expect(Math.abs(seq[1] - res[1])).toBeLessThanOrEqual(maxError); - }, - ); - it('avoids cumulative error', () => { - const vs = new Array(30).fill(1).map((v, idx) => idx * 1.05); - const { encodeDelta, decodeDelta } = createDeltaCoder(1); - const { init, deltas } = encodeDelta(30, (i) => vs[i]); - const res: number[] = new Array(30); - decodeDelta(init, deltas, (i, v) => { - res[i] = v; - }); - expect(Math.abs(res[29] - vs[29])).toBeLessThanOrEqual(0.051); - }); - }); - describe('bitfield encoding', () => { - const tests: { name: string; vs: number[] }[] = [ - { name: 'empty', vs: [] }, - { name: 'asymmetrical', vs: [-1, 1, 1, -1, -1] }, - { name: 'max length', vs: new Array(32).fill(1) }, - ]; - it.each(tests)('$name', ({ vs }) => { - const encoded = encodeBitfield(vs.length, (i) => vs[i]); - const decoded = new Array(vs.length); - decodeBitfield(vs.length, encoded, (i, v) => { - decoded[i] = v; - }); - expect(decoded).toStrictEqual(vs); - }); - it('throws on invalid values', () => { - expect(() => encodeBitfield(1, () => 2)).toThrow('Invalid'); - }); - it('throws on too-long arrays', () => { - expect(() => encodeBitfield(33, () => 1)).toThrow('Cannot encode'); - }); - }); - describe('stable value encoding', () => { - const tests: { name: string; vs: number[] }[] = [ - { name: 'empty', vs: [] }, - { name: '0->1 midway', vs: [0, 0, 1, 1] }, - { name: '0->1->0', vs: [0, 1, 0] }, - { name: 'all 1', vs: [1, 1, 1] }, - ]; - it.each(tests)('$name', ({ vs }) => { - const { idxs, vals } = encodeStable(vs.length, (i) => vs[i]); - const decoded = new Array(vs.length); - decodeStable(vs.length, idxs, vals, (i, v) => { - decoded[i] = v; - }); - expect(decoded).toStrictEqual(vs); - }); - it('throws on mismatched inputs', () => { - expect(() => { - decodeStable(1, [], [1], () => {}); - }).toThrow('Invalid'); - }); - }); -}); diff --git a/src/compact_move.ts b/src/compact_move_analyze.ts similarity index 66% rename from src/compact_move.ts rename to src/compact_move_analyze.ts index a3eec3a..6790e67 100644 --- a/src/compact_move.ts +++ b/src/compact_move_analyze.ts @@ -2,7 +2,15 @@ import PATH from 'node:path'; import { once } from 'node:events'; import { createReadStream } from 'node:fs'; import { createInterface } from 'node:readline'; -import { NT } from './gen/pbjs_pb'; +import { + NT, + createArmrCoder, + createDeltaCoder, + decodeBitfield, + decodeStable, + encodeBitfield, + encodeStable, +} from 'nt-message'; const infile = PATH.resolve(__dirname, '../ndjson'); @@ -49,117 +57,6 @@ let lastStats = Date.now() + 5000; const DELTA_FACTOR = 2 ** 6; const ROT_FACTOR = 2 ** 7; -/** - * Create an encoder-decoder pair for lossy-encoding radian - * values (`armR`) to integers that can be compactly encoded - * as varints. - * @param targetBytes The size, in bytes, of encoded values - * when serialized as a varint - */ -export const createArmrCoder = (targetBytes: number) => { - const factor = 2 ** (7 * targetBytes); - const pi2 = Math.PI * 2 + 1; - - return { - /** - * Lossily encode `v`, a value in radians between -PI and PI, - * as an unsigned integer to fit within `targetBytes` of - * serialized protobuf output. - * @see {createArmrCoder} - */ - encodeArmR: (v: number) => (((v + Math.PI) * factor) / pi2) | 0, - /** - * Decode a lossily-encoded value `v` to a value in radians - * between -PI and PI. - * @see {createArmrCoder} - */ - decodeArmR: (v: number) => (v * pi2) / factor - Math.PI, - }; -}; - -export const createDeltaCoder = (fractionalDigits: number) => { - const factor = 10 ** fractionalDigits; - return { - encodeDelta: (len: number, get: (i: number) => number): { init: number; deltas: number[] } => { - if (len === 0) return { init: 0, deltas: [] }; - - const init = get(0); - const deltas: number[] = []; - - if (typeof init !== 'number') throw new Error('Invalid value'); - - let last = init; - for (let i = 1; i < len; i++) { - const val = get(i); - if (typeof val !== 'number') throw new Error('Invalid value'); - - const d = Math.round((val - last) * factor); - deltas.push(d); - last += d / factor; // ameliorate rounding errors - } - return { init, deltas }; - }, - decodeDelta: (init: number, deltas: number[], set: (i: number, v: number) => void): void => { - let cum = init; - set(0, cum); - for (let i = 0; i < deltas.length; i++) { - cum += deltas[i] / factor; - set(i + 1, cum); - } - }, - }; -}; - -export const encodeBitfield = (len: number, next: (i: number) => number): number => { - if (len > 32) throw new Error('Cannot encode more than 32 values in a bitfield'); - let res = 0; - for (let i = 0; i < len; i++) { - const val = next(i); - // values must be -1 or 1 - if (val !== -1 && val !== 1) throw new Error('Invalid value: ' + val); - res |= ((val + 1) >>> 1) << i; - // javascript bitwise operations operate on 32-bit signed integers - } - return res >>> 0; // convert to unsigned -}; -export const decodeBitfield = (len: number, val: number, set: (i: number, val: number) => void): void => { - if (len > 32) throw new Error('Cannot encode more than 32 values in a bitfield'); - for (let i = 0; i < len; i++) { - set(i, ((val & 1) << 1) - 1); - val >>>= 1; - } -}; - -export const encodeStable = (len: number, get: (i: number) => number): { idxs: number[]; vals: number[] } => { - let last = 0; - const idxs: number[] = []; - const vals: number[] = []; - for (let i = 0; i < len; i++) { - const val = get(i); - if (val === last) continue; - idxs.push(i); - vals.push(val); - last = val; - } - return { idxs, vals }; -}; -export const decodeStable = ( - len: number, - idxs: number[], - vals: number[], - set: (i: number, val: number) => void, -): void => { - if (idxs.length !== vals.length) throw new Error('Invalid data: arrays must be same length'); - let cur = 0; - for (let i = 0, pos = 0; i < len; i++) { - if (idxs[pos] === i) { - cur = vals[pos]; - pos++; - } - set(i, cur); - } -}; - const { encodeArmR, decodeArmR } = createArmrCoder(1); const { encodeDelta, decodeDelta } = createDeltaCoder(1); diff --git a/src/conformance/lobby.test.ts b/src/conformance/lobby.test.ts index 930991f..e7675e8 100644 --- a/src/conformance/lobby.test.ts +++ b/src/conformance/lobby.test.ts @@ -1,10 +1,10 @@ -import { NT } from '../gen/pbjs_pb'; +import { RecognizedString } from 'uWebSockets.js'; +import { M, NT } from 'nt-message'; import { createJwtFns } from '../jwt'; import { AuthProvider, ClientAuth } from '../runtypes/client_auth'; import { LobbyState, SYSTEM_USER } from '../state/lobby'; -import { BindPublishers, M } from '../util'; +import { BindPublishers } from '../util'; import { ClientAuthWebSocket, TaggedClientAuth, createMessageHandler } from '../ws_handlers'; -import { RecognizedString } from 'uWebSockets.js'; type sentMessage = { topic: string | null; diff --git a/src/pbreflect.ts b/src/pbreflect.ts deleted file mode 100644 index 3f18fcb..0000000 --- a/src/pbreflect.ts +++ /dev/null @@ -1,29 +0,0 @@ -import def from './gen/pbjs_pb.json'; - -const NT = def.nested.NT.nested; - -type FieldList = { [key: string]: { type: string; id: number } }; -type FieldIds = { [K in keyof T]: T[K]['id'] } & unknown; -type MessageIds = { - [K in keyof T]: FieldIds; -} & unknown; - -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; - -export const Messages: MessageIds = Object.create(null) as any; -for (const [msgName, defs] of Object.entries(NT) as [keyof typeof NT, (typeof NT)[keyof typeof NT]][]) { - if (!defs.fields) continue; - - const fields: UnionToIntersection> = Object.create(null) as any; - for (const [fieldName, nameid] of Object.entries(defs.fields) as [keyof typeof fields, { id: number }][]) { - fields[fieldName] = nameid.id; - } - Messages[msgName] = fields; -} - -export const gameActions = Object.keys( - def.nested.NT.nested.GameAction.fields, -) as (keyof typeof def.nested.NT.nested.GameAction.fields)[]; -export const lobbyActions = Object.keys( - def.nested.NT.nested.LobbyAction.fields, -) as (keyof typeof def.nested.NT.nested.LobbyAction.fields)[]; diff --git a/src/protohax/fixtures/generate.sh b/src/protohax/fixtures/generate.sh deleted file mode 100755 index 37fe299..0000000 --- a/src/protohax/fixtures/generate.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -HERE="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -( - cd "$HERE/../../.." && \ - npx pbjs --es6 -w es6 -t static-module ./src/protohax/fixtures/protohax.proto -o ./src/protohax/fixtures/protohax_pb.js && \ - npx pbts -o ./src/protohax/fixtures/protohax_pb.d.ts ./src/protohax/fixtures/protohax_pb.js && \ - npx pbjs -t json ./src/protohax/fixtures/protohax.proto -o ./src/protohax/fixtures/protohax_pb.json -) \ No newline at end of file diff --git a/src/protohax/fixtures/protohax.proto b/src/protohax/fixtures/protohax.proto deleted file mode 100644 index 22ec6cf..0000000 --- a/src/protohax/fixtures/protohax.proto +++ /dev/null @@ -1,68 +0,0 @@ -syntax = "proto3"; - -// message := (tag value)* -// -// tag := (field << 3) bit-or wire_type; -// encoded as uint32 varint -// value := varint for wire_type == VARINT, -// i32 for wire_type == I32, -// i64 for wire_type == I64, -// len-prefix for wire_type == LEN, -// for wire_type == SGROUP or EGROUP -// -// varint := int32 | int64 | uint32 | uint64 | bool | enum | sint32 | sint64; -// encoded as varints (sintN are ZigZag-encoded first) -// i32 := sfixed32 | fixed32 | float; -// encoded as 4-byte little-endian; -// memcpy of the equivalent C types (u?int32_t, float) -// i64 := sfixed64 | fixed64 | double; -// encoded as 8-byte little-endian; -// memcpy of the equivalent C types (u?int64_t, double) -// -// len-prefix := size (message | string | bytes | packed); -// size encoded as int32 varint -// string := valid UTF-8 string (e.g. ASCII); -// max 2GB of bytes -// bytes := any sequence of 8-bit bytes; -// max 2GB of bytes -// packed := varint* | i32* | i64*, -// consecutive values of the type specified in `.proto` - -enum Enum { - ENUM_UNSPECIFIED = 0; - ENUM_ONE = 1; - ENUM_TWO = 2; -} - -message Message { - // low (single-byte tag) - Message l_message = 1; - - // singular - int32 single_int32 = 11; - int64 single_int64 = 12; - uint32 single_uint32 = 13; - uint64 single_uint64 = 14; - sint32 single_sint32 = 15; - sint64 single_sint64 = 16; - bool single_bool = 17; - Enum single_enum = 18; - fixed64 single_fixed64 = 19; - sfixed64 single_sfixed64 = 20; - double single_double = 21; - string single_string = 22; - bytes single_bytes = 23; - fixed32 single_fixed32 = 24; - sfixed32 single_sfixed32 = 25; - float single_float = 26; - Message single_message = 27; - - // repeated - repeated int32 repeated_int32 = 111; - repeated string repeated_string = 122; - repeated bytes repeated_bytes = 123; - repeated Message repeated_message = 127; - - // unpacked repeated - repeated int32 unpacked_int32 = 211 [packed=false]; -} \ No newline at end of file diff --git a/src/protohax/fixtures/protohax_pb.d.ts b/src/protohax/fixtures/protohax_pb.d.ts deleted file mode 100644 index b4d95e6..0000000 --- a/src/protohax/fixtures/protohax_pb.d.ts +++ /dev/null @@ -1,164 +0,0 @@ -import * as $protobuf from "protobufjs"; -import Long = require("long"); -/** Enum enum. */ -export enum Enum { - ENUM_UNSPECIFIED = 0, - ENUM_ONE = 1, - ENUM_TWO = 2 -} - -/** Represents a Message. */ -export class Message implements IMessage { - - /** - * Constructs a new Message. - * @param [properties] Properties to set - */ - constructor(properties?: IMessage); - - /** Message lMessage. */ - public lMessage?: (IMessage|null); - - /** Message singleInt32. */ - public singleInt32: number; - - /** Message singleInt64. */ - public singleInt64: (number|Long); - - /** Message singleUint32. */ - public singleUint32: number; - - /** Message singleUint64. */ - public singleUint64: (number|Long); - - /** Message singleSint32. */ - public singleSint32: number; - - /** Message singleSint64. */ - public singleSint64: (number|Long); - - /** Message singleBool. */ - public singleBool: boolean; - - /** Message singleEnum. */ - public singleEnum: Enum; - - /** Message singleFixed64. */ - public singleFixed64: (number|Long); - - /** Message singleSfixed64. */ - public singleSfixed64: (number|Long); - - /** Message singleDouble. */ - public singleDouble: number; - - /** Message singleString. */ - public singleString: string; - - /** Message singleBytes. */ - public singleBytes: Uint8Array; - - /** Message singleFixed32. */ - public singleFixed32: number; - - /** Message singleSfixed32. */ - public singleSfixed32: number; - - /** Message singleFloat. */ - public singleFloat: number; - - /** Message singleMessage. */ - public singleMessage?: (IMessage|null); - - /** Message repeatedInt32. */ - public repeatedInt32: number[]; - - /** Message repeatedString. */ - public repeatedString: string[]; - - /** Message repeatedBytes. */ - public repeatedBytes: Uint8Array[]; - - /** Message repeatedMessage. */ - public repeatedMessage: IMessage[]; - - /** Message unpackedInt32. */ - public unpackedInt32: number[]; - - /** - * Creates a new Message instance using the specified properties. - * @param [properties] Properties to set - * @returns Message instance - */ - public static create(properties?: IMessage): Message; - - /** - * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. - * @param message Message message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encode(message: IMessage, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Encodes the specified Message message, length delimited. Does not implicitly {@link Message.verify|verify} messages. - * @param message Message message or plain object to encode - * @param [writer] Writer to encode to - * @returns Writer - */ - public static encodeDelimited(message: IMessage, writer?: $protobuf.Writer): $protobuf.Writer; - - /** - * Decodes a Message message from the specified reader or buffer. - * @param reader Reader or buffer to decode from - * @param [length] Message length if known beforehand - * @returns Message - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): Message; - - /** - * Decodes a Message message from the specified reader or buffer, length delimited. - * @param reader Reader or buffer to decode from - * @returns Message - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): Message; - - /** - * Verifies a Message message. - * @param message Plain object to verify - * @returns `null` if valid, otherwise the reason why it is not - */ - public static verify(message: { [k: string]: any }): (string|null); - - /** - * Creates a Message message from a plain object. Also converts values to their respective internal types. - * @param object Plain object - * @returns Message - */ - public static fromObject(object: { [k: string]: any }): Message; - - /** - * Creates a plain object from a Message message. Also converts values to other types if specified. - * @param message Message - * @param [options] Conversion options - * @returns Plain object - */ - public static toObject(message: Message, options?: $protobuf.IConversionOptions): { [k: string]: any }; - - /** - * Converts this Message to JSON. - * @returns JSON object - */ - public toJSON(): { [k: string]: any }; - - /** - * Gets the default type url for Message - * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns The default type url - */ - public static getTypeUrl(typeUrlPrefix?: string): string; -} diff --git a/src/protohax/fixtures/protohax_pb.js b/src/protohax/fixtures/protohax_pb.js deleted file mode 100644 index ab16be3..0000000 --- a/src/protohax/fixtures/protohax_pb.js +++ /dev/null @@ -1,952 +0,0 @@ -/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ -import * as $protobuf from "protobufjs/minimal"; - -// Common aliases -const $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; - -// Exported root namespace -const $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); - -/** - * Enum enum. - * @exports Enum - * @enum {number} - * @property {number} ENUM_UNSPECIFIED=0 ENUM_UNSPECIFIED value - * @property {number} ENUM_ONE=1 ENUM_ONE value - * @property {number} ENUM_TWO=2 ENUM_TWO value - */ -export const Enum = $root.Enum = (() => { - const valuesById = {}, values = Object.create(valuesById); - values[valuesById[0] = "ENUM_UNSPECIFIED"] = 0; - values[valuesById[1] = "ENUM_ONE"] = 1; - values[valuesById[2] = "ENUM_TWO"] = 2; - return values; -})(); - -export const Message = $root.Message = (() => { - - /** - * Properties of a Message. - * @exports IMessage - * @interface IMessage - * @property {IMessage|null} [lMessage] Message lMessage - * @property {number|null} [singleInt32] Message singleInt32 - * @property {number|Long|null} [singleInt64] Message singleInt64 - * @property {number|null} [singleUint32] Message singleUint32 - * @property {number|Long|null} [singleUint64] Message singleUint64 - * @property {number|null} [singleSint32] Message singleSint32 - * @property {number|Long|null} [singleSint64] Message singleSint64 - * @property {boolean|null} [singleBool] Message singleBool - * @property {Enum|null} [singleEnum] Message singleEnum - * @property {number|Long|null} [singleFixed64] Message singleFixed64 - * @property {number|Long|null} [singleSfixed64] Message singleSfixed64 - * @property {number|null} [singleDouble] Message singleDouble - * @property {string|null} [singleString] Message singleString - * @property {Uint8Array|null} [singleBytes] Message singleBytes - * @property {number|null} [singleFixed32] Message singleFixed32 - * @property {number|null} [singleSfixed32] Message singleSfixed32 - * @property {number|null} [singleFloat] Message singleFloat - * @property {IMessage|null} [singleMessage] Message singleMessage - * @property {Array.|null} [repeatedInt32] Message repeatedInt32 - * @property {Array.|null} [repeatedString] Message repeatedString - * @property {Array.|null} [repeatedBytes] Message repeatedBytes - * @property {Array.|null} [repeatedMessage] Message repeatedMessage - * @property {Array.|null} [unpackedInt32] Message unpackedInt32 - */ - - /** - * Constructs a new Message. - * @exports Message - * @classdesc Represents a Message. - * @implements IMessage - * @constructor - * @param {IMessage=} [properties] Properties to set - */ - function Message(properties) { - this.repeatedInt32 = []; - this.repeatedString = []; - this.repeatedBytes = []; - this.repeatedMessage = []; - this.unpackedInt32 = []; - if (properties) - for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * Message lMessage. - * @member {IMessage|null|undefined} lMessage - * @memberof Message - * @instance - */ - Message.prototype.lMessage = null; - - /** - * Message singleInt32. - * @member {number} singleInt32 - * @memberof Message - * @instance - */ - Message.prototype.singleInt32 = 0; - - /** - * Message singleInt64. - * @member {number|Long} singleInt64 - * @memberof Message - * @instance - */ - Message.prototype.singleInt64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * Message singleUint32. - * @member {number} singleUint32 - * @memberof Message - * @instance - */ - Message.prototype.singleUint32 = 0; - - /** - * Message singleUint64. - * @member {number|Long} singleUint64 - * @memberof Message - * @instance - */ - Message.prototype.singleUint64 = $util.Long ? $util.Long.fromBits(0,0,true) : 0; - - /** - * Message singleSint32. - * @member {number} singleSint32 - * @memberof Message - * @instance - */ - Message.prototype.singleSint32 = 0; - - /** - * Message singleSint64. - * @member {number|Long} singleSint64 - * @memberof Message - * @instance - */ - Message.prototype.singleSint64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * Message singleBool. - * @member {boolean} singleBool - * @memberof Message - * @instance - */ - Message.prototype.singleBool = false; - - /** - * Message singleEnum. - * @member {Enum} singleEnum - * @memberof Message - * @instance - */ - Message.prototype.singleEnum = 0; - - /** - * Message singleFixed64. - * @member {number|Long} singleFixed64 - * @memberof Message - * @instance - */ - Message.prototype.singleFixed64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * Message singleSfixed64. - * @member {number|Long} singleSfixed64 - * @memberof Message - * @instance - */ - Message.prototype.singleSfixed64 = $util.Long ? $util.Long.fromBits(0,0,false) : 0; - - /** - * Message singleDouble. - * @member {number} singleDouble - * @memberof Message - * @instance - */ - Message.prototype.singleDouble = 0; - - /** - * Message singleString. - * @member {string} singleString - * @memberof Message - * @instance - */ - Message.prototype.singleString = ""; - - /** - * Message singleBytes. - * @member {Uint8Array} singleBytes - * @memberof Message - * @instance - */ - Message.prototype.singleBytes = $util.newBuffer([]); - - /** - * Message singleFixed32. - * @member {number} singleFixed32 - * @memberof Message - * @instance - */ - Message.prototype.singleFixed32 = 0; - - /** - * Message singleSfixed32. - * @member {number} singleSfixed32 - * @memberof Message - * @instance - */ - Message.prototype.singleSfixed32 = 0; - - /** - * Message singleFloat. - * @member {number} singleFloat - * @memberof Message - * @instance - */ - Message.prototype.singleFloat = 0; - - /** - * Message singleMessage. - * @member {IMessage|null|undefined} singleMessage - * @memberof Message - * @instance - */ - Message.prototype.singleMessage = null; - - /** - * Message repeatedInt32. - * @member {Array.} repeatedInt32 - * @memberof Message - * @instance - */ - Message.prototype.repeatedInt32 = $util.emptyArray; - - /** - * Message repeatedString. - * @member {Array.} repeatedString - * @memberof Message - * @instance - */ - Message.prototype.repeatedString = $util.emptyArray; - - /** - * Message repeatedBytes. - * @member {Array.} repeatedBytes - * @memberof Message - * @instance - */ - Message.prototype.repeatedBytes = $util.emptyArray; - - /** - * Message repeatedMessage. - * @member {Array.} repeatedMessage - * @memberof Message - * @instance - */ - Message.prototype.repeatedMessage = $util.emptyArray; - - /** - * Message unpackedInt32. - * @member {Array.} unpackedInt32 - * @memberof Message - * @instance - */ - Message.prototype.unpackedInt32 = $util.emptyArray; - - /** - * Creates a new Message instance using the specified properties. - * @function create - * @memberof Message - * @static - * @param {IMessage=} [properties] Properties to set - * @returns {Message} Message instance - */ - Message.create = function create(properties) { - return new Message(properties); - }; - - /** - * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. - * @function encode - * @memberof Message - * @static - * @param {IMessage} message Message message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Message.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.lMessage != null && Object.hasOwnProperty.call(message, "lMessage")) - $root.Message.encode(message.lMessage, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - if (message.singleInt32 != null && Object.hasOwnProperty.call(message, "singleInt32")) - writer.uint32(/* id 11, wireType 0 =*/88).int32(message.singleInt32); - if (message.singleInt64 != null && Object.hasOwnProperty.call(message, "singleInt64")) - writer.uint32(/* id 12, wireType 0 =*/96).int64(message.singleInt64); - if (message.singleUint32 != null && Object.hasOwnProperty.call(message, "singleUint32")) - writer.uint32(/* id 13, wireType 0 =*/104).uint32(message.singleUint32); - if (message.singleUint64 != null && Object.hasOwnProperty.call(message, "singleUint64")) - writer.uint32(/* id 14, wireType 0 =*/112).uint64(message.singleUint64); - if (message.singleSint32 != null && Object.hasOwnProperty.call(message, "singleSint32")) - writer.uint32(/* id 15, wireType 0 =*/120).sint32(message.singleSint32); - if (message.singleSint64 != null && Object.hasOwnProperty.call(message, "singleSint64")) - writer.uint32(/* id 16, wireType 0 =*/128).sint64(message.singleSint64); - if (message.singleBool != null && Object.hasOwnProperty.call(message, "singleBool")) - writer.uint32(/* id 17, wireType 0 =*/136).bool(message.singleBool); - if (message.singleEnum != null && Object.hasOwnProperty.call(message, "singleEnum")) - writer.uint32(/* id 18, wireType 0 =*/144).int32(message.singleEnum); - if (message.singleFixed64 != null && Object.hasOwnProperty.call(message, "singleFixed64")) - writer.uint32(/* id 19, wireType 1 =*/153).fixed64(message.singleFixed64); - if (message.singleSfixed64 != null && Object.hasOwnProperty.call(message, "singleSfixed64")) - writer.uint32(/* id 20, wireType 1 =*/161).sfixed64(message.singleSfixed64); - if (message.singleDouble != null && Object.hasOwnProperty.call(message, "singleDouble")) - writer.uint32(/* id 21, wireType 1 =*/169).double(message.singleDouble); - if (message.singleString != null && Object.hasOwnProperty.call(message, "singleString")) - writer.uint32(/* id 22, wireType 2 =*/178).string(message.singleString); - if (message.singleBytes != null && Object.hasOwnProperty.call(message, "singleBytes")) - writer.uint32(/* id 23, wireType 2 =*/186).bytes(message.singleBytes); - if (message.singleFixed32 != null && Object.hasOwnProperty.call(message, "singleFixed32")) - writer.uint32(/* id 24, wireType 5 =*/197).fixed32(message.singleFixed32); - if (message.singleSfixed32 != null && Object.hasOwnProperty.call(message, "singleSfixed32")) - writer.uint32(/* id 25, wireType 5 =*/205).sfixed32(message.singleSfixed32); - if (message.singleFloat != null && Object.hasOwnProperty.call(message, "singleFloat")) - writer.uint32(/* id 26, wireType 5 =*/213).float(message.singleFloat); - if (message.singleMessage != null && Object.hasOwnProperty.call(message, "singleMessage")) - $root.Message.encode(message.singleMessage, writer.uint32(/* id 27, wireType 2 =*/218).fork()).ldelim(); - if (message.repeatedInt32 != null && message.repeatedInt32.length) { - writer.uint32(/* id 111, wireType 2 =*/890).fork(); - for (let i = 0; i < message.repeatedInt32.length; ++i) - writer.int32(message.repeatedInt32[i]); - writer.ldelim(); - } - if (message.repeatedString != null && message.repeatedString.length) - for (let i = 0; i < message.repeatedString.length; ++i) - writer.uint32(/* id 122, wireType 2 =*/978).string(message.repeatedString[i]); - if (message.repeatedBytes != null && message.repeatedBytes.length) - for (let i = 0; i < message.repeatedBytes.length; ++i) - writer.uint32(/* id 123, wireType 2 =*/986).bytes(message.repeatedBytes[i]); - if (message.repeatedMessage != null && message.repeatedMessage.length) - for (let i = 0; i < message.repeatedMessage.length; ++i) - $root.Message.encode(message.repeatedMessage[i], writer.uint32(/* id 127, wireType 2 =*/1018).fork()).ldelim(); - if (message.unpackedInt32 != null && message.unpackedInt32.length) - for (let i = 0; i < message.unpackedInt32.length; ++i) - writer.uint32(/* id 211, wireType 0 =*/1688).int32(message.unpackedInt32[i]); - return writer; - }; - - /** - * Encodes the specified Message message, length delimited. Does not implicitly {@link Message.verify|verify} messages. - * @function encodeDelimited - * @memberof Message - * @static - * @param {IMessage} message Message message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - Message.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a Message message from the specified reader or buffer. - * @function decode - * @memberof Message - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {Message} Message - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Message.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - let end = length === undefined ? reader.len : reader.pos + length, message = new $root.Message(); - while (reader.pos < end) { - let tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - message.lMessage = $root.Message.decode(reader, reader.uint32()); - break; - } - case 11: { - message.singleInt32 = reader.int32(); - break; - } - case 12: { - message.singleInt64 = reader.int64(); - break; - } - case 13: { - message.singleUint32 = reader.uint32(); - break; - } - case 14: { - message.singleUint64 = reader.uint64(); - break; - } - case 15: { - message.singleSint32 = reader.sint32(); - break; - } - case 16: { - message.singleSint64 = reader.sint64(); - break; - } - case 17: { - message.singleBool = reader.bool(); - break; - } - case 18: { - message.singleEnum = reader.int32(); - break; - } - case 19: { - message.singleFixed64 = reader.fixed64(); - break; - } - case 20: { - message.singleSfixed64 = reader.sfixed64(); - break; - } - case 21: { - message.singleDouble = reader.double(); - break; - } - case 22: { - message.singleString = reader.string(); - break; - } - case 23: { - message.singleBytes = reader.bytes(); - break; - } - case 24: { - message.singleFixed32 = reader.fixed32(); - break; - } - case 25: { - message.singleSfixed32 = reader.sfixed32(); - break; - } - case 26: { - message.singleFloat = reader.float(); - break; - } - case 27: { - message.singleMessage = $root.Message.decode(reader, reader.uint32()); - break; - } - case 111: { - if (!(message.repeatedInt32 && message.repeatedInt32.length)) - message.repeatedInt32 = []; - if ((tag & 7) === 2) { - let end2 = reader.uint32() + reader.pos; - while (reader.pos < end2) - message.repeatedInt32.push(reader.int32()); - } else - message.repeatedInt32.push(reader.int32()); - break; - } - case 122: { - if (!(message.repeatedString && message.repeatedString.length)) - message.repeatedString = []; - message.repeatedString.push(reader.string()); - break; - } - case 123: { - if (!(message.repeatedBytes && message.repeatedBytes.length)) - message.repeatedBytes = []; - message.repeatedBytes.push(reader.bytes()); - break; - } - case 127: { - if (!(message.repeatedMessage && message.repeatedMessage.length)) - message.repeatedMessage = []; - message.repeatedMessage.push($root.Message.decode(reader, reader.uint32())); - break; - } - case 211: { - if (!(message.unpackedInt32 && message.unpackedInt32.length)) - message.unpackedInt32 = []; - if ((tag & 7) === 2) { - let end2 = reader.uint32() + reader.pos; - while (reader.pos < end2) - message.unpackedInt32.push(reader.int32()); - } else - message.unpackedInt32.push(reader.int32()); - break; - } - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a Message message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof Message - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {Message} Message - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - Message.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a Message message. - * @function verify - * @memberof Message - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - Message.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.lMessage != null && message.hasOwnProperty("lMessage")) { - let error = $root.Message.verify(message.lMessage); - if (error) - return "lMessage." + error; - } - if (message.singleInt32 != null && message.hasOwnProperty("singleInt32")) - if (!$util.isInteger(message.singleInt32)) - return "singleInt32: integer expected"; - if (message.singleInt64 != null && message.hasOwnProperty("singleInt64")) - if (!$util.isInteger(message.singleInt64) && !(message.singleInt64 && $util.isInteger(message.singleInt64.low) && $util.isInteger(message.singleInt64.high))) - return "singleInt64: integer|Long expected"; - if (message.singleUint32 != null && message.hasOwnProperty("singleUint32")) - if (!$util.isInteger(message.singleUint32)) - return "singleUint32: integer expected"; - if (message.singleUint64 != null && message.hasOwnProperty("singleUint64")) - if (!$util.isInteger(message.singleUint64) && !(message.singleUint64 && $util.isInteger(message.singleUint64.low) && $util.isInteger(message.singleUint64.high))) - return "singleUint64: integer|Long expected"; - if (message.singleSint32 != null && message.hasOwnProperty("singleSint32")) - if (!$util.isInteger(message.singleSint32)) - return "singleSint32: integer expected"; - if (message.singleSint64 != null && message.hasOwnProperty("singleSint64")) - if (!$util.isInteger(message.singleSint64) && !(message.singleSint64 && $util.isInteger(message.singleSint64.low) && $util.isInteger(message.singleSint64.high))) - return "singleSint64: integer|Long expected"; - if (message.singleBool != null && message.hasOwnProperty("singleBool")) - if (typeof message.singleBool !== "boolean") - return "singleBool: boolean expected"; - if (message.singleEnum != null && message.hasOwnProperty("singleEnum")) - switch (message.singleEnum) { - default: - return "singleEnum: enum value expected"; - case 0: - case 1: - case 2: - break; - } - if (message.singleFixed64 != null && message.hasOwnProperty("singleFixed64")) - if (!$util.isInteger(message.singleFixed64) && !(message.singleFixed64 && $util.isInteger(message.singleFixed64.low) && $util.isInteger(message.singleFixed64.high))) - return "singleFixed64: integer|Long expected"; - if (message.singleSfixed64 != null && message.hasOwnProperty("singleSfixed64")) - if (!$util.isInteger(message.singleSfixed64) && !(message.singleSfixed64 && $util.isInteger(message.singleSfixed64.low) && $util.isInteger(message.singleSfixed64.high))) - return "singleSfixed64: integer|Long expected"; - if (message.singleDouble != null && message.hasOwnProperty("singleDouble")) - if (typeof message.singleDouble !== "number") - return "singleDouble: number expected"; - if (message.singleString != null && message.hasOwnProperty("singleString")) - if (!$util.isString(message.singleString)) - return "singleString: string expected"; - if (message.singleBytes != null && message.hasOwnProperty("singleBytes")) - if (!(message.singleBytes && typeof message.singleBytes.length === "number" || $util.isString(message.singleBytes))) - return "singleBytes: buffer expected"; - if (message.singleFixed32 != null && message.hasOwnProperty("singleFixed32")) - if (!$util.isInteger(message.singleFixed32)) - return "singleFixed32: integer expected"; - if (message.singleSfixed32 != null && message.hasOwnProperty("singleSfixed32")) - if (!$util.isInteger(message.singleSfixed32)) - return "singleSfixed32: integer expected"; - if (message.singleFloat != null && message.hasOwnProperty("singleFloat")) - if (typeof message.singleFloat !== "number") - return "singleFloat: number expected"; - if (message.singleMessage != null && message.hasOwnProperty("singleMessage")) { - let error = $root.Message.verify(message.singleMessage); - if (error) - return "singleMessage." + error; - } - if (message.repeatedInt32 != null && message.hasOwnProperty("repeatedInt32")) { - if (!Array.isArray(message.repeatedInt32)) - return "repeatedInt32: array expected"; - for (let i = 0; i < message.repeatedInt32.length; ++i) - if (!$util.isInteger(message.repeatedInt32[i])) - return "repeatedInt32: integer[] expected"; - } - if (message.repeatedString != null && message.hasOwnProperty("repeatedString")) { - if (!Array.isArray(message.repeatedString)) - return "repeatedString: array expected"; - for (let i = 0; i < message.repeatedString.length; ++i) - if (!$util.isString(message.repeatedString[i])) - return "repeatedString: string[] expected"; - } - if (message.repeatedBytes != null && message.hasOwnProperty("repeatedBytes")) { - if (!Array.isArray(message.repeatedBytes)) - return "repeatedBytes: array expected"; - for (let i = 0; i < message.repeatedBytes.length; ++i) - if (!(message.repeatedBytes[i] && typeof message.repeatedBytes[i].length === "number" || $util.isString(message.repeatedBytes[i]))) - return "repeatedBytes: buffer[] expected"; - } - if (message.repeatedMessage != null && message.hasOwnProperty("repeatedMessage")) { - if (!Array.isArray(message.repeatedMessage)) - return "repeatedMessage: array expected"; - for (let i = 0; i < message.repeatedMessage.length; ++i) { - let error = $root.Message.verify(message.repeatedMessage[i]); - if (error) - return "repeatedMessage." + error; - } - } - if (message.unpackedInt32 != null && message.hasOwnProperty("unpackedInt32")) { - if (!Array.isArray(message.unpackedInt32)) - return "unpackedInt32: array expected"; - for (let i = 0; i < message.unpackedInt32.length; ++i) - if (!$util.isInteger(message.unpackedInt32[i])) - return "unpackedInt32: integer[] expected"; - } - return null; - }; - - /** - * Creates a Message message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof Message - * @static - * @param {Object.} object Plain object - * @returns {Message} Message - */ - Message.fromObject = function fromObject(object) { - if (object instanceof $root.Message) - return object; - let message = new $root.Message(); - if (object.lMessage != null) { - if (typeof object.lMessage !== "object") - throw TypeError(".Message.lMessage: object expected"); - message.lMessage = $root.Message.fromObject(object.lMessage); - } - if (object.singleInt32 != null) - message.singleInt32 = object.singleInt32 | 0; - if (object.singleInt64 != null) - if ($util.Long) - (message.singleInt64 = $util.Long.fromValue(object.singleInt64)).unsigned = false; - else if (typeof object.singleInt64 === "string") - message.singleInt64 = parseInt(object.singleInt64, 10); - else if (typeof object.singleInt64 === "number") - message.singleInt64 = object.singleInt64; - else if (typeof object.singleInt64 === "object") - message.singleInt64 = new $util.LongBits(object.singleInt64.low >>> 0, object.singleInt64.high >>> 0).toNumber(); - if (object.singleUint32 != null) - message.singleUint32 = object.singleUint32 >>> 0; - if (object.singleUint64 != null) - if ($util.Long) - (message.singleUint64 = $util.Long.fromValue(object.singleUint64)).unsigned = true; - else if (typeof object.singleUint64 === "string") - message.singleUint64 = parseInt(object.singleUint64, 10); - else if (typeof object.singleUint64 === "number") - message.singleUint64 = object.singleUint64; - else if (typeof object.singleUint64 === "object") - message.singleUint64 = new $util.LongBits(object.singleUint64.low >>> 0, object.singleUint64.high >>> 0).toNumber(true); - if (object.singleSint32 != null) - message.singleSint32 = object.singleSint32 | 0; - if (object.singleSint64 != null) - if ($util.Long) - (message.singleSint64 = $util.Long.fromValue(object.singleSint64)).unsigned = false; - else if (typeof object.singleSint64 === "string") - message.singleSint64 = parseInt(object.singleSint64, 10); - else if (typeof object.singleSint64 === "number") - message.singleSint64 = object.singleSint64; - else if (typeof object.singleSint64 === "object") - message.singleSint64 = new $util.LongBits(object.singleSint64.low >>> 0, object.singleSint64.high >>> 0).toNumber(); - if (object.singleBool != null) - message.singleBool = Boolean(object.singleBool); - switch (object.singleEnum) { - default: - if (typeof object.singleEnum === "number") { - message.singleEnum = object.singleEnum; - break; - } - break; - case "ENUM_UNSPECIFIED": - case 0: - message.singleEnum = 0; - break; - case "ENUM_ONE": - case 1: - message.singleEnum = 1; - break; - case "ENUM_TWO": - case 2: - message.singleEnum = 2; - break; - } - if (object.singleFixed64 != null) - if ($util.Long) - (message.singleFixed64 = $util.Long.fromValue(object.singleFixed64)).unsigned = false; - else if (typeof object.singleFixed64 === "string") - message.singleFixed64 = parseInt(object.singleFixed64, 10); - else if (typeof object.singleFixed64 === "number") - message.singleFixed64 = object.singleFixed64; - else if (typeof object.singleFixed64 === "object") - message.singleFixed64 = new $util.LongBits(object.singleFixed64.low >>> 0, object.singleFixed64.high >>> 0).toNumber(); - if (object.singleSfixed64 != null) - if ($util.Long) - (message.singleSfixed64 = $util.Long.fromValue(object.singleSfixed64)).unsigned = false; - else if (typeof object.singleSfixed64 === "string") - message.singleSfixed64 = parseInt(object.singleSfixed64, 10); - else if (typeof object.singleSfixed64 === "number") - message.singleSfixed64 = object.singleSfixed64; - else if (typeof object.singleSfixed64 === "object") - message.singleSfixed64 = new $util.LongBits(object.singleSfixed64.low >>> 0, object.singleSfixed64.high >>> 0).toNumber(); - if (object.singleDouble != null) - message.singleDouble = Number(object.singleDouble); - if (object.singleString != null) - message.singleString = String(object.singleString); - if (object.singleBytes != null) - if (typeof object.singleBytes === "string") - $util.base64.decode(object.singleBytes, message.singleBytes = $util.newBuffer($util.base64.length(object.singleBytes)), 0); - else if (object.singleBytes.length >= 0) - message.singleBytes = object.singleBytes; - if (object.singleFixed32 != null) - message.singleFixed32 = object.singleFixed32 >>> 0; - if (object.singleSfixed32 != null) - message.singleSfixed32 = object.singleSfixed32 | 0; - if (object.singleFloat != null) - message.singleFloat = Number(object.singleFloat); - if (object.singleMessage != null) { - if (typeof object.singleMessage !== "object") - throw TypeError(".Message.singleMessage: object expected"); - message.singleMessage = $root.Message.fromObject(object.singleMessage); - } - if (object.repeatedInt32) { - if (!Array.isArray(object.repeatedInt32)) - throw TypeError(".Message.repeatedInt32: array expected"); - message.repeatedInt32 = []; - for (let i = 0; i < object.repeatedInt32.length; ++i) - message.repeatedInt32[i] = object.repeatedInt32[i] | 0; - } - if (object.repeatedString) { - if (!Array.isArray(object.repeatedString)) - throw TypeError(".Message.repeatedString: array expected"); - message.repeatedString = []; - for (let i = 0; i < object.repeatedString.length; ++i) - message.repeatedString[i] = String(object.repeatedString[i]); - } - if (object.repeatedBytes) { - if (!Array.isArray(object.repeatedBytes)) - throw TypeError(".Message.repeatedBytes: array expected"); - message.repeatedBytes = []; - for (let i = 0; i < object.repeatedBytes.length; ++i) - if (typeof object.repeatedBytes[i] === "string") - $util.base64.decode(object.repeatedBytes[i], message.repeatedBytes[i] = $util.newBuffer($util.base64.length(object.repeatedBytes[i])), 0); - else if (object.repeatedBytes[i].length >= 0) - message.repeatedBytes[i] = object.repeatedBytes[i]; - } - if (object.repeatedMessage) { - if (!Array.isArray(object.repeatedMessage)) - throw TypeError(".Message.repeatedMessage: array expected"); - message.repeatedMessage = []; - for (let i = 0; i < object.repeatedMessage.length; ++i) { - if (typeof object.repeatedMessage[i] !== "object") - throw TypeError(".Message.repeatedMessage: object expected"); - message.repeatedMessage[i] = $root.Message.fromObject(object.repeatedMessage[i]); - } - } - if (object.unpackedInt32) { - if (!Array.isArray(object.unpackedInt32)) - throw TypeError(".Message.unpackedInt32: array expected"); - message.unpackedInt32 = []; - for (let i = 0; i < object.unpackedInt32.length; ++i) - message.unpackedInt32[i] = object.unpackedInt32[i] | 0; - } - return message; - }; - - /** - * Creates a plain object from a Message message. Also converts values to other types if specified. - * @function toObject - * @memberof Message - * @static - * @param {Message} message Message - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - Message.toObject = function toObject(message, options) { - if (!options) - options = {}; - let object = {}; - if (options.arrays || options.defaults) { - object.repeatedInt32 = []; - object.repeatedString = []; - object.repeatedBytes = []; - object.repeatedMessage = []; - object.unpackedInt32 = []; - } - if (options.defaults) { - object.lMessage = null; - object.singleInt32 = 0; - if ($util.Long) { - let long = new $util.Long(0, 0, false); - object.singleInt64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.singleInt64 = options.longs === String ? "0" : 0; - object.singleUint32 = 0; - if ($util.Long) { - let long = new $util.Long(0, 0, true); - object.singleUint64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.singleUint64 = options.longs === String ? "0" : 0; - object.singleSint32 = 0; - if ($util.Long) { - let long = new $util.Long(0, 0, false); - object.singleSint64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.singleSint64 = options.longs === String ? "0" : 0; - object.singleBool = false; - object.singleEnum = options.enums === String ? "ENUM_UNSPECIFIED" : 0; - if ($util.Long) { - let long = new $util.Long(0, 0, false); - object.singleFixed64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.singleFixed64 = options.longs === String ? "0" : 0; - if ($util.Long) { - let long = new $util.Long(0, 0, false); - object.singleSfixed64 = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; - } else - object.singleSfixed64 = options.longs === String ? "0" : 0; - object.singleDouble = 0; - object.singleString = ""; - if (options.bytes === String) - object.singleBytes = ""; - else { - object.singleBytes = []; - if (options.bytes !== Array) - object.singleBytes = $util.newBuffer(object.singleBytes); - } - object.singleFixed32 = 0; - object.singleSfixed32 = 0; - object.singleFloat = 0; - object.singleMessage = null; - } - if (message.lMessage != null && message.hasOwnProperty("lMessage")) - object.lMessage = $root.Message.toObject(message.lMessage, options); - if (message.singleInt32 != null && message.hasOwnProperty("singleInt32")) - object.singleInt32 = message.singleInt32; - if (message.singleInt64 != null && message.hasOwnProperty("singleInt64")) - if (typeof message.singleInt64 === "number") - object.singleInt64 = options.longs === String ? String(message.singleInt64) : message.singleInt64; - else - object.singleInt64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleInt64) : options.longs === Number ? new $util.LongBits(message.singleInt64.low >>> 0, message.singleInt64.high >>> 0).toNumber() : message.singleInt64; - if (message.singleUint32 != null && message.hasOwnProperty("singleUint32")) - object.singleUint32 = message.singleUint32; - if (message.singleUint64 != null && message.hasOwnProperty("singleUint64")) - if (typeof message.singleUint64 === "number") - object.singleUint64 = options.longs === String ? String(message.singleUint64) : message.singleUint64; - else - object.singleUint64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleUint64) : options.longs === Number ? new $util.LongBits(message.singleUint64.low >>> 0, message.singleUint64.high >>> 0).toNumber(true) : message.singleUint64; - if (message.singleSint32 != null && message.hasOwnProperty("singleSint32")) - object.singleSint32 = message.singleSint32; - if (message.singleSint64 != null && message.hasOwnProperty("singleSint64")) - if (typeof message.singleSint64 === "number") - object.singleSint64 = options.longs === String ? String(message.singleSint64) : message.singleSint64; - else - object.singleSint64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleSint64) : options.longs === Number ? new $util.LongBits(message.singleSint64.low >>> 0, message.singleSint64.high >>> 0).toNumber() : message.singleSint64; - if (message.singleBool != null && message.hasOwnProperty("singleBool")) - object.singleBool = message.singleBool; - if (message.singleEnum != null && message.hasOwnProperty("singleEnum")) - object.singleEnum = options.enums === String ? $root.Enum[message.singleEnum] === undefined ? message.singleEnum : $root.Enum[message.singleEnum] : message.singleEnum; - if (message.singleFixed64 != null && message.hasOwnProperty("singleFixed64")) - if (typeof message.singleFixed64 === "number") - object.singleFixed64 = options.longs === String ? String(message.singleFixed64) : message.singleFixed64; - else - object.singleFixed64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleFixed64) : options.longs === Number ? new $util.LongBits(message.singleFixed64.low >>> 0, message.singleFixed64.high >>> 0).toNumber() : message.singleFixed64; - if (message.singleSfixed64 != null && message.hasOwnProperty("singleSfixed64")) - if (typeof message.singleSfixed64 === "number") - object.singleSfixed64 = options.longs === String ? String(message.singleSfixed64) : message.singleSfixed64; - else - object.singleSfixed64 = options.longs === String ? $util.Long.prototype.toString.call(message.singleSfixed64) : options.longs === Number ? new $util.LongBits(message.singleSfixed64.low >>> 0, message.singleSfixed64.high >>> 0).toNumber() : message.singleSfixed64; - if (message.singleDouble != null && message.hasOwnProperty("singleDouble")) - object.singleDouble = options.json && !isFinite(message.singleDouble) ? String(message.singleDouble) : message.singleDouble; - if (message.singleString != null && message.hasOwnProperty("singleString")) - object.singleString = message.singleString; - if (message.singleBytes != null && message.hasOwnProperty("singleBytes")) - object.singleBytes = options.bytes === String ? $util.base64.encode(message.singleBytes, 0, message.singleBytes.length) : options.bytes === Array ? Array.prototype.slice.call(message.singleBytes) : message.singleBytes; - if (message.singleFixed32 != null && message.hasOwnProperty("singleFixed32")) - object.singleFixed32 = message.singleFixed32; - if (message.singleSfixed32 != null && message.hasOwnProperty("singleSfixed32")) - object.singleSfixed32 = message.singleSfixed32; - if (message.singleFloat != null && message.hasOwnProperty("singleFloat")) - object.singleFloat = options.json && !isFinite(message.singleFloat) ? String(message.singleFloat) : message.singleFloat; - if (message.singleMessage != null && message.hasOwnProperty("singleMessage")) - object.singleMessage = $root.Message.toObject(message.singleMessage, options); - if (message.repeatedInt32 && message.repeatedInt32.length) { - object.repeatedInt32 = []; - for (let j = 0; j < message.repeatedInt32.length; ++j) - object.repeatedInt32[j] = message.repeatedInt32[j]; - } - if (message.repeatedString && message.repeatedString.length) { - object.repeatedString = []; - for (let j = 0; j < message.repeatedString.length; ++j) - object.repeatedString[j] = message.repeatedString[j]; - } - if (message.repeatedBytes && message.repeatedBytes.length) { - object.repeatedBytes = []; - for (let j = 0; j < message.repeatedBytes.length; ++j) - object.repeatedBytes[j] = options.bytes === String ? $util.base64.encode(message.repeatedBytes[j], 0, message.repeatedBytes[j].length) : options.bytes === Array ? Array.prototype.slice.call(message.repeatedBytes[j]) : message.repeatedBytes[j]; - } - if (message.repeatedMessage && message.repeatedMessage.length) { - object.repeatedMessage = []; - for (let j = 0; j < message.repeatedMessage.length; ++j) - object.repeatedMessage[j] = $root.Message.toObject(message.repeatedMessage[j], options); - } - if (message.unpackedInt32 && message.unpackedInt32.length) { - object.unpackedInt32 = []; - for (let j = 0; j < message.unpackedInt32.length; ++j) - object.unpackedInt32[j] = message.unpackedInt32[j]; - } - return object; - }; - - /** - * Converts this Message to JSON. - * @function toJSON - * @memberof Message - * @instance - * @returns {Object.} JSON object - */ - Message.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - /** - * Gets the default type url for Message - * @function getTypeUrl - * @memberof Message - * @static - * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") - * @returns {string} The default type url - */ - Message.getTypeUrl = function getTypeUrl(typeUrlPrefix) { - if (typeUrlPrefix === undefined) { - typeUrlPrefix = "type.googleapis.com"; - } - return typeUrlPrefix + "/Message"; - }; - - return Message; -})(); - -export { $root as default }; diff --git a/src/protohax/fixtures/protohax_pb.json b/src/protohax/fixtures/protohax_pb.json deleted file mode 100644 index fbed3aa..0000000 --- a/src/protohax/fixtures/protohax_pb.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "nested": { - "Enum": { - "values": { - "ENUM_UNSPECIFIED": 0, - "ENUM_ONE": 1, - "ENUM_TWO": 2 - } - }, - "Message": { - "fields": { - "lMessage": { - "type": "Message", - "id": 1 - }, - "singleInt32": { - "type": "int32", - "id": 11 - }, - "singleInt64": { - "type": "int64", - "id": 12 - }, - "singleUint32": { - "type": "uint32", - "id": 13 - }, - "singleUint64": { - "type": "uint64", - "id": 14 - }, - "singleSint32": { - "type": "sint32", - "id": 15 - }, - "singleSint64": { - "type": "sint64", - "id": 16 - }, - "singleBool": { - "type": "bool", - "id": 17 - }, - "singleEnum": { - "type": "Enum", - "id": 18 - }, - "singleFixed64": { - "type": "fixed64", - "id": 19 - }, - "singleSfixed64": { - "type": "sfixed64", - "id": 20 - }, - "singleDouble": { - "type": "double", - "id": 21 - }, - "singleString": { - "type": "string", - "id": 22 - }, - "singleBytes": { - "type": "bytes", - "id": 23 - }, - "singleFixed32": { - "type": "fixed32", - "id": 24 - }, - "singleSfixed32": { - "type": "sfixed32", - "id": 25 - }, - "singleFloat": { - "type": "float", - "id": 26 - }, - "singleMessage": { - "type": "Message", - "id": 27 - }, - "repeatedInt32": { - "rule": "repeated", - "type": "int32", - "id": 111 - }, - "repeatedString": { - "rule": "repeated", - "type": "string", - "id": 122 - }, - "repeatedBytes": { - "rule": "repeated", - "type": "bytes", - "id": 123 - }, - "repeatedMessage": { - "rule": "repeated", - "type": "Message", - "id": 127 - }, - "unpackedInt32": { - "rule": "repeated", - "type": "int32", - "id": 211, - "options": { - "packed": false - } - } - } - } - } -} \ No newline at end of file diff --git a/src/protohax/protohax.test.ts b/src/protohax/protohax.test.ts deleted file mode 100644 index ac42d4b..0000000 --- a/src/protohax/protohax.test.ts +++ /dev/null @@ -1,347 +0,0 @@ -import { Enum, Message } from './fixtures/protohax_pb'; -import { ProtoHax } from './protohax'; -import Long from 'long'; -import fieldSpec from './fixtures/protohax_pb.json'; - -describe('ProtoHax', () => { - describe('single numeric values', () => { - type proto_scalar = number | bigint | boolean | Enum | Long; - type messageScalar = { [K in keyof Message as Message[K] extends proto_scalar | proto_scalar[] ? K : never]: K }; - type messageScalars = messageScalar[keyof messageScalar] & keyof Message; - type foo = { [K in keyof Message]: 1 } & unknown; - - type fakeBigint = T extends bigint ? string : T extends bigint[] ? string[] : T; - - const scalar = (key: T, value: fakeBigint) => ({ key, value }); - - type phaxReadMethod = { - [K in keyof ProtoHax as ProtoHax[K] extends () => proto_scalar | proto_scalar[] ? K : never]: K; - }; - type phaxReadMethods = phaxReadMethod[keyof phaxReadMethod] & keyof ProtoHax; - - const asSingle = (v: number) => { - const buf = Buffer.alloc(4); - buf.writeFloatBE(v, 0); - return buf.readFloatBE(0); - }; - - const methodNames: Record = { - singleInt32: 'Int32', - singleUint32: 'Uint32', - singleSint32: 'Sint32', - singleFixed32: 'Fixed32', - singleSfixed32: 'Sfixed32', - singleFloat: 'Float', - singleDouble: 'Double', - singleInt64: 'Int64', - singleUint64: 'Uint64', - singleSint64: 'Sint64', - singleFixed64: 'Fixed64', - singleSfixed64: 'Sfixed64', - singleBool: 'Bool', - singleEnum: 'Enum', - }; - - // prettier-ignore - const tests: {key: keyof Message, value: any}[] = [ - // small negatives - scalar('singleInt32', -1 ), // TODO: deal with reading negative varints when we expect 32 bits (but the encoder wrote 64 bits) - scalar('singleSint32', -1 ), - scalar('singleSfixed32', -1 ), - scalar('singleFloat', asSingle(-1.1)), - scalar('singleDouble', -1.1 ), - scalar('singleInt64', new Long(-1) ), - scalar('singleSint64', new Long(-1) ), - scalar('singleSfixed64', new Long(-1) ), - - // zero - scalar('singleInt32', 0 ), - scalar('singleUint32', 0 ), - scalar('singleSint32', 0 ), - scalar('singleFixed32', 0 ), - scalar('singleSfixed32', 0 ), - scalar('singleFloat', 0 ), - scalar('singleDouble', 0 ), - scalar('singleInt64', new Long(0)), - scalar('singleUint64', new Long(0)), - scalar('singleSint64', new Long(0)), - scalar('singleFixed64', new Long(0)), - scalar('singleSfixed64', new Long(0)), - - // one - scalar('singleInt32', 1 ), - scalar('singleUint32', 1 ), - scalar('singleSint32', 1 ), - scalar('singleFixed32', 1 ), - scalar('singleSfixed32', 1 ), - scalar('singleFloat', asSingle(1.1)), - scalar('singleDouble', 1.1 ), - scalar('singleInt64', new Long(1) ), - scalar('singleUint64', new Long(1) ), - scalar('singleSint64', new Long(1) ), - scalar('singleFixed64', new Long(1) ), - scalar('singleSfixed64', new Long(1) ), - - // varint edges - scalar('singleInt32', 1<<30), - scalar('singleInt64', Long.fromString((0b01100110_10110010_10010111_01011001_10100110_11001001_10111010_00110011n).toString())), - - // booleans - scalar('singleBool', true), - scalar('singleBool', false), - - // enums - scalar('singleEnum', Enum.ENUM_UNSPECIFIED), - scalar('singleEnum', Enum.ENUM_ONE), - scalar('singleEnum', Enum.ENUM_TWO), - ]; - - it.each(tests)('$key $value', ({ key, value }) => { - const n = typeof value === 'string' ? BigInt(value) : value; - const pbjs_encoded = Buffer.from( - Message.encode({ - [key]: n, - }).finish(), - ); - - const fieldId = (fieldSpec.nested.Message.fields as any)[key]?.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbjs_encoded); - const readMethod = methodNames[key]; - expect(readMethod).not.toBeUndefined(); - - const res = phax.with(fieldId!)[readMethod!](); - expect(res.toString()).toEqual(n.toString()); - }); - }); - - describe('packed repeated', () => { - it('reads with .Packed()', () => { - const expected = [1, 2, 3]; - const pbjs_encoded = Buffer.from(Message.encode({ repeatedInt32: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.repeatedInt32.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbjs_encoded); - - const actual = phax.with(fieldId!).Packed('Int32'); - expect(actual).toEqual(expected); - }); - it('reads with value readers', () => { - const expected = [1, 2, 3]; - const pbes_encoded = Buffer.from(Message.encode({ repeatedInt32: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.repeatedInt32.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - let actual: number[] = []; - phax.if(fieldId!, (phax) => { - while (!phax.atEnd()) { - actual.push(phax.Int32()); - } - }); - expect(actual).toEqual(expected); - }); - }); - - describe('length-delimited', () => { - it('reads a string', () => { - const expected = 'hi there ☃'; - const pbes_encoded = Buffer.from(Message.encode({ singleString: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.singleString.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - const actual = phax.with(fieldId!).String(); - - expect(expected).toEqual(actual); - }); - it('reads bytes', () => { - const expected = Buffer.from('hi there ☃'); - const pbes_encoded = Buffer.from(Message.encode({ singleBytes: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.singleBytes.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - const actual = phax.with(fieldId!).Bytes(); - - expect(expected).toEqual(actual); - }); - it('reads messages', () => { - const pbes_encoded = Buffer.from( - Message.encode({ - lMessage: { - singleBool: true, - }, - repeatedMessage: [{ singleEnum: Enum.ENUM_ONE }, { singleEnum: Enum.ENUM_TWO }], - }).finish(), - ); - - const lMessage = fieldSpec.nested.Message.fields.lMessage.id; - expect(lMessage).not.toBeUndefined(); - const singleBool = fieldSpec.nested.Message.fields.singleBool.id; - expect(singleBool).not.toBeUndefined(); - const repeatedMessage = fieldSpec.nested.Message.fields.repeatedMessage.id; - expect(repeatedMessage).not.toBeUndefined(); - const singleEnum = fieldSpec.nested.Message.fields.singleEnum.id; - expect(singleEnum).not.toBeUndefined(); - - const expected = [true, Enum.ENUM_ONE, Enum.ENUM_TWO]; - const actual: [boolean, number, number] = [] as any; - - actual.push( - new ProtoHax(pbes_encoded) // - .with(lMessage!) - .with(singleBool!) - .Bool(), - ); - - new ProtoHax(pbes_encoded) // - .each(repeatedMessage!, (phax) => actual.push(phax.with(singleEnum!).Enum())); - - expect(actual).toEqual(expected); - }); - }); - - describe('if', () => { - it('finds the first value', () => { - const expected = 1; - const pbes_encoded = Buffer.from(Message.encode({ singleInt32: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.singleInt32.id; - expect(fieldId).not.toBeUndefined(); - - let found = false; - const actual = new ProtoHax(pbes_encoded).if(fieldId!, () => { - found = true; - }); - expect(found).toEqual(true); - }); - it('does nothing on no match', () => { - const expected = 1; - const pbes_encoded = Buffer.from(Message.encode({ singleInt32: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.singleBool.id; - expect(fieldId).not.toBeUndefined(); - - let found = false; - const actual = new ProtoHax(pbes_encoded).if(fieldId!, () => { - found = true; - }); - expect(found).toEqual(false); - }); - it('bugfix: incorrect usage of last', () => { - // explanation: `if` did not check this.ok after calling seek, so it attempted to call skip - // with arbitrary / unrelated data in the last-read value property. this caused an exception - // when the last-read value indicated a group wiretype ("not implemented"). - // TODO: this.seek could return a boolean directly indicating whether it terminates an operation - const pbes_encoded = Buffer.from(Message.encode({ singleString: 'hi there11' }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.singleInt32.id; - expect(fieldId).not.toBeUndefined(); - - let found = false; - new ProtoHax(pbes_encoded).if(fieldId!, () => { - found = true; - }); - expect(found).toEqual(false); - }); - }); - describe('each', () => { - it('reads scalars', () => { - const expected = [1, 2, 3]; - const pbes_encoded = Buffer.from(Message.encode({ unpackedInt32: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.unpackedInt32.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - let actual: number[] = []; - phax.each(fieldId!, (phax) => actual.push(phax.Int32())); - expect(actual).toEqual(expected); - }); - it('reads strings', () => { - const expected = ['a', 'b']; - const pbes_encoded = Buffer.from(Message.encode({ repeatedString: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.repeatedString.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - const actual: string[] = []; - phax.each(fieldId!, (phax) => actual.push(phax.String())); - - expect(expected).toEqual(actual); - }); - it('reads bytes', () => { - const expected = ['a', 'b'].map((s) => Buffer.from(s)); - const pbes_encoded = Buffer.from(Message.encode({ repeatedBytes: expected }).finish()); - - const fieldId = fieldSpec.nested.Message.fields.repeatedBytes.id; - expect(fieldId).not.toBeUndefined(); - - const phax = new ProtoHax(pbes_encoded); - - const actual: Buffer[] = []; - phax.each(fieldId!, (phax) => actual.push(phax.Bytes())); - - expect(expected).toEqual(actual); - }); - }); - describe('skip', () => { - it('can skip over everything', () => { - const pbes_encoded = Buffer.from( - Message.encode({ - lMessage: Message.encode({ singleDouble: 1 }), - singleInt32: 1, - singleInt64: 1n, - singleUint32: 1, - singleUint64: 1n, - singleSint32: 1, - singleSint64: 1n, - singleBool: true, - singleEnum: Enum.ENUM_ONE, - singleFixed64: 1n, - singleSfixed64: 1n, - singleDouble: 1, - singleString: 'hi', - singleBytes: Buffer.from('hi'), - singleFixed32: 1, - singleSfixed32: 1, - singleFloat: 1, - singleMessage: Message.encode({ singleDouble: 1 }), - repeatedInt32: [1], - repeatedString: ['hi'], - repeatedBytes: [Buffer.from('hi')], - repeatedMessage: [Message.encode({ singleDouble: 1 })], - unpackedInt32: [1], - }).finish(), - ); - const phax = new ProtoHax(pbes_encoded); - phax.if(1337, (phax) => { - throw new Error('should not be called'); - }); - }); - }); - - describe('empty values', () => { - it('returns defaults', () => { - const empty = Buffer.of(); - expect(new ProtoHax(empty).String()).toEqual(''); - expect(new ProtoHax(empty).Bytes()).toEqual(empty); - expect(new ProtoHax(empty).Int32()).toEqual(0); - expect(new ProtoHax(empty).Enum()).toEqual(0); - expect(new ProtoHax(empty).Bool()).toEqual(false); - }); - }); -}); diff --git a/src/protohax/protohax.ts b/src/protohax/protohax.ts deleted file mode 100644 index 4f5c5f9..0000000 --- a/src/protohax/protohax.ts +++ /dev/null @@ -1,380 +0,0 @@ -export const enum Wiretype { - VARINT = 0, - I64 = 1, - LEN = 2, - SGROUP = 3, - EGROUP = 4, - I32 = 5, -} - -export const enum DigResult { - SUCCESS = 0, - NOT_FOUND = 1, - ERROR = 2, -} - -export const EMPTY_BUFFER = Buffer.of(); - -type Packable = keyof ProtoHax & - ( - | 'Int32' - | 'Int64' - | 'Uint32' - | 'Uint64' - | 'Bool' - | 'Enum' - | 'Sint32' - | 'Sint64' - | 'Sfixed32' - | 'Fixed32' - | 'Float' - | 'Sfixed64' - | 'Fixed64' - | 'Double' - ); -type Unpacked = ProtoHax[T] extends () => infer P ? P : never; - -/** - * Read selected values from a serialized protocol buffer message. Used to optimize - * the processing time of PlayerMove messages. - */ -export class ProtoHax { - private pos: number = 0; - private last: number = 0; - private end: number; - private ok: boolean; - - constructor(private buf: Buffer) { - this.end = buf.length; - this.ok = this.pos < this.end; - } - - atEnd() { - return this.pos >= this.end; - } - - private varint(): void { - if (!this.ok) return; - - this.last = 0; - - // read up to 4 bytes of a varint (bitwise-safe value up to 28 bits of payload) - for (var b = 0, shift = 0; shift < 28; shift += 7) { - b = this.buf[this.pos++]; - this.last |= (b & 0x7f) << shift; - - if ((b & 0x80) === 0) return; // we hit the end of the varint - } - - // if we still have bytes to read, we failed - this.ok = (b & 0x80) === 0; - } - - private skipVarint() { - if (!this.ok) return; - - // varints can be up to 10 bytes, representing up to a 64-bit unsigned int - for (var i = 0; i < 10; i++) { - if ((this.buf[this.pos++] & 0x80) === 0) return; - } - - // we read 10 bytes all with an MSB of 1, we weren't at a valid varint - this.ok = false; - } - - // skip the specified number of bytes - private skipBytes(bytes: number) { - this.pos += bytes; - } - - private skipGroup(sgroup: number) { - var until = sgroup ^ (Wiretype.EGROUP ^ Wiretype.SGROUP); - do { - this.skip(); // skip the current tag's payload - this.varint(); // read the next tag - } while (this.ok && this.last !== until); - } - - // skip over a payload. the tag should be in `this.last` - private skip() { - if (!this.ok) return; - - // prettier-ignore - switch (this.last & 0x07) { - // VARINT: int32, int64, uint32, uint64, sint32, sint64, bool, enum - case Wiretype.VARINT: this.skipVarint(); break; - // I64: fixed64, sfixed64, double - case Wiretype.I64: this.skipBytes(8); break; - // LEN: string, bytes, embedded messages, packed repeated fields - case Wiretype.LEN: this.varint(); this.skipBytes(this.last); break; - // SGROUP: group start (deprecated) - case Wiretype.SGROUP: this.skipGroup(this.last); break; - // EGROUP: group end (deprecated) - case Wiretype.EGROUP: break; - // I32: fixed32, sfixed32, float - case Wiretype.I32: this.skipBytes(4); break; - default: throw new Error('Invalid wire type: '+(this.last&0x07)); - } - - this.ok = this.pos < this.buf.length; - } - - private readVarint32(): number { - this.varint(); - - // if varint succeeded, the value was read in <= 4 bytes and we can just - // return and call it a day - if (this.ok) return this.last >>> 0; - - // we've read 4 out of a possible 10 bytes so far. the worst case is -1, which will be - // 9* 0xff followed by 0x01. There are four remaining bits that might have meaning to - // us, and the rest can be ignored since we're only reading a 32 bit number. - // - // even though the wiretype of this varint knows it's a 32 bit number, it still records - // all 64 bits. it's unclear whether that is sane behavior, but because the data is - // recorded as little-endian, it has the effect that very large negative values stored - // as int32 will be smaller in their varint encoding. see: - // https://github.com/protocolbuffers/protobuf-javascript/blob/8730ba5e0f5153c5889c356193d93778c6300932/binary/encoder.js#L145-L172 - // - // either way, we have to deal with the data we could potentially receive. - - // read the 5th byte - var b = this.buf[this.pos++]; - this.ok = (b & 0x80) === 0; - - // store the last 4 bits of the 5th input byte in the top 4 bits of the value - // ____aaaa aaabbbbb bbcccccc cddddddd - // 0___eeee - // eeee____ ________ ________ ________ - this.last |= (b & 0x0f) << 28; - - // consume up to 5 more bytes of varint and discard them - for (var i = 0; !this.ok && i < 5; i++) { - b = this.buf[this.pos++]; - this.ok = (b & 0x80) === 0; - } - - if (!this.ok) throw new Error('VARINT read failed'); - - // return as unsigned - return this.last >>> 0; - } - - private readVarint64(): bigint { - if (!this.ok) return 0n; - this.varint(); - - var big = BigInt(this.last); - if (this.ok) return big; - - // it's a big one, read the rest. this could probably be - // done more efficiently by working with in 32 bit space - // as regular js numbers. however, that's a pain and i'm - // just looking for something that clearly works for now - for (var b = 0, shift = 28n; shift < 70n; shift += 7n) { - b = this.buf[this.pos++]; - big |= (BigInt(b) & 0x7fn) << shift; - - if ((b & 0x80) === 0) break; // we hit the end of the varint - } - - this.ok = (b & 0x80) === 0; - if (!this.ok) throw new Error('VARINT64 read failed'); - - // we can technically construct >64bit values; we rely on - // the calling functions to interpret and truncate the data - return big & 0xffffffffffffffffn; - } - - // varint := int32 | int64 | uint32 | uint64 | bool | enum | sint32 | sint64; - // encoded as varints (sintN are ZigZag-encoded first) - Int32(): number { - if (!this.ok) return 0; - return this.readVarint32() | 0; - } - Int64(): bigint { - if (!this.ok) return 0n; - return BigInt.asIntN(64, this.readVarint64()); - } - Uint32(): number { - if (!this.ok) return 0; - return this.readVarint32() >>> 0; - } - Uint64(): bigint { - if (!this.ok) return 0n; - return BigInt.asUintN(64, this.readVarint64()); - } - Bool(): boolean { - if (!this.ok) return false; - var val = this.readVarint32(); - switch (val) { - case 0: - return false; - case 1: - return true; - default: - throw new Error('Invalid boolean value'); - } - } - Enum(): number { - if (!this.ok) return 0; - var val = this.readVarint32(); - return val; - } - Sint32(): number { - if (!this.ok) return 0; - var zze = this.readVarint32(); - return (zze >>> 1) ^ -(zze & 1); - } - Sint64(): bigint { - if (!this.ok) return 0n; - var zze = this.readVarint64(); - return (zze >> 1n) ^ -(zze & 1n); - } - - // i32 := sfixed32 | fixed32 | float; - // encoded as 4-byte little-endian; - // memcpy of the equivalent C types (u?int32_t, float) - Sfixed32(): number { - if (!this.ok || this.pos > this.end - 4) return 0; - var val = this.buf.readInt32LE(this.pos); - this.pos += 4; - return val; - } - Fixed32(): number { - if (!this.ok || this.pos > this.end - 4) return 0; - var val = this.buf.readUint32LE(this.pos); - this.pos += 4; - return val; - } - Float(): number { - if (!this.ok || this.pos > this.end - 4) return 0; - var val = this.buf.readFloatLE(this.pos); - this.pos += 4; - return val; - } - - // i64 := sfixed64 | fixed64 | double; - // encoded as 8-byte little-endian; - // memcpy of the equivalent C types (u?int64_t, double) - Sfixed64(): bigint { - if (!this.ok || this.pos > this.end - 8) return 0n; - var val = this.buf.readBigInt64LE(this.pos); - this.pos += 8; - return val; - } - Fixed64(): bigint { - if (!this.ok || this.pos > this.end - 8) return 0n; - var val = this.buf.readBigUint64LE(this.pos); - this.pos += 8; - return val; - } - Double(): number { - if (!this.ok || this.pos > this.end - 8) return 0; - var val = this.buf.readDoubleLE(this.pos); - this.pos += 8; - return val; - } - - // len-prefix := size (message | string | bytes | packed); - // size encoded as int32 varint - Bytes(): Buffer { - if (!this.ok) return EMPTY_BUFFER; - return this.buf.subarray(this.pos, this.end); - } - - String(): string { - if (!this.ok) return ''; - return this.buf.toString('utf-8', this.pos, this.end); - } - - // Only repeated fields of primitive numeric types can be declared "packed". - // These are types that would normally use the VARINT, I32, or I64 wire types. - Packed(type: T): Unpacked[] { - var arr: Unpacked[] = []; - while (this.ok && this.pos < this.end) { - arr.push(this[type]() as Unpacked); - } - if (!this.ok) throw new Error('packed read failed'); - return arr; - } - - private seek(fieldId: number) { - if (!this.ok) return; - this.varint(); - while (this.last >>> 3 !== fieldId) { - this.skip(); - if (!this.ok) break; - this.varint(); - } - } - - private size(): number { - switch ((this.last & 0x07) as Wiretype) { - case Wiretype.VARINT: - return 0; - case Wiretype.I64: - return 8; - case Wiretype.I32: - return 4; - case Wiretype.LEN: - return this.readVarint32(); - // can't know the size of groups without reading them, and - // we don't really care. - case Wiretype.SGROUP: - case Wiretype.EGROUP: - throw new Error('not implemented'); - } - } - - /** - * Seek to the next instance of fieldId, which must be a LEN wiretype, - * and rescope this instance to its payload - */ - with(fieldId: number): ProtoHax { - this.seek(fieldId); - if (!this.ok) return this; - var size = this.size(); - this.end = size ? this.pos + size : this.end; - return this; - } - - /** - * Find the next instance of the specified fieldId, and call the callback - * with a new ProtoHax instance if found. - */ - if(fieldId: number, cb: (phax: ProtoHax) => void): ProtoHax { - if (!this.ok) return this; - this.seek(fieldId); - if (!this.ok) return this; - var size = this.size(); - var val; - if (size > 0) { - val = this.buf.subarray(this.pos, this.pos + size); - // move the pointer forward by the size of the payload - this.pos += size; - } else { - val = this.buf.subarray(this.pos); - // we're assuming here that size=0 is a varint, and everything - // else (that doesn't throw an error) has a size that's known - // up-front. therefore, in order to move our position pointer - // forward, all we have to do here is skip a varint - this.skipVarint(); - } - if (this.ok) cb(new ProtoHax(val)); - this.ok = this.pos < this.end; - return this; - } - - /** - * Find all instances of the specified fieldId and call the callback - * with a new ProtoHax instance for each. - */ - each(fieldId: number, cb: (phax: ProtoHax) => void): ProtoHax { - while (this.ok) { - this.if(fieldId, cb); - // this.skip(); - } - return this; - } -} diff --git a/src/protoutil.test.ts b/src/protoutil.test.ts deleted file mode 100644 index b535e76..0000000 --- a/src/protoutil.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { NT } from './gen/pbjs_pb'; -import { maybePlayerMove, tagPlayerMove } from './protoutil'; - -const asSingle = (v: number) => { - const buf = Buffer.alloc(4); - buf.writeFloatBE(v, 0); - return buf.readFloatBE(0); -}; - -describe('PlayerMove userId tagging', () => { - it('works with empty frames', () => { - const buf = Buffer.from('0a020a00', 'hex'); - const mpm = maybePlayerMove(buf); - expect(mpm).toBeDefined(); - const tagged = tagPlayerMove(mpm!, Buffer.from('foo')); - expect(tagged).toBeDefined(); - expect(NT.Envelope.decode(tagged!).toJSON()).toEqual({ - gameAction: { - sPlayerMoves: { - userFrames: [ - { - userId: 'foo', - }, - ], - }, - }, - }); - }); - it('works as expected', () => { - const frames: NT.ICompactPlayerFrames = { - xInit: 1.3, - xDeltas: [1, -1], - yInit: -1.3, - yDeltas: [1, -1], - animIdx: [1], - animVal: [1], - armR: [1], - armScaleY: 1, - scaleX: 1, - heldIdx: [1], - heldVal: [1], - }; - - const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); - const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); - expect(playerMovePayload).toBeDefined(); - - const pmId = Buffer.from('12345'); - const tagged = tagPlayerMove(playerMovePayload!, pmId); - expect(tagged).toBeDefined(); - - const decoded = NT.Envelope.decode(tagged!); - const expected: NT.IEnvelope = { - gameAction: { - sPlayerMoves: { - userFrames: [ - { - ...frames, - userId: '12345', - xInit: asSingle(frames.xInit!), - yInit: asSingle(frames.yInit!), - }, - ], - }, - }, - }; - expect(decoded.toJSON()).toEqual(expected); - }); - // protobuf.js fails to merge messages when decoding - it.skip('correctly supports concatenation (protobuf.js)', () => { - const frames: NT.ICompactPlayerFrames = { - xInit: 1.3, - xDeltas: [1, -1], - yInit: -1.3, - yDeltas: [1, -1], - animIdx: [1], - animVal: [1], - armR: [1], - armScaleY: 1, - scaleX: 1, - heldIdx: [1], - heldVal: [1], - }; - - const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); - const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); - expect(playerMovePayload).toBeDefined(); - - const pmId = Buffer.from('12345'); - const tagged = tagPlayerMove(playerMovePayload!, pmId); - expect(tagged).toBeDefined(); - - const encoded2 = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); - const playerMovePayload2 = maybePlayerMove(Buffer.from(encoded2)); - expect(playerMovePayload2).toBeDefined(); - - const pmId2 = Buffer.from('6789'); - const tagged2 = tagPlayerMove(playerMovePayload2!, pmId2); - expect(tagged2).toBeDefined(); - - const concatenated = Buffer.concat([tagged!, tagged2!]); - const decoded = NT.Envelope.decode(concatenated); - - const expected: NT.IEnvelope = { - gameAction: { - sPlayerMoves: { - userFrames: [ - { - ...frames, - userId: '12345', - xInit: asSingle(frames.xInit!), - yInit: asSingle(frames.yInit!), - }, - { - ...frames, - userId: '6789', - xInit: asSingle(frames.xInit!), - yInit: asSingle(frames.yInit!), - }, - ], - }, - }, - }; - expect(decoded.toJSON()).toEqual(expected); - }); - it('refuses client-supplied userId', () => { - const frames: NT.ICompactPlayerFrames = { - xInit: 1.3, - xDeltas: [1, -1], - yInit: -1.3, - yDeltas: [1, -1], - animIdx: [1], - animVal: [1], - armR: [1], - armScaleY: 1, - scaleX: 1, - heldIdx: [1], - heldVal: [1], - userId: 'rejected', - }; - const encoded = NT.Envelope.encode({ gameAction: { cPlayerMove: frames } }).finish(); - const playerMovePayload = maybePlayerMove(Buffer.from(encoded)); - expect(playerMovePayload).toBeDefined(); - - const pmId = Buffer.from('12345'); - const tagged = tagPlayerMove(playerMovePayload!, pmId); - expect(tagged).toBeUndefined(); - }); -}); diff --git a/src/protoutil.ts b/src/protoutil.ts deleted file mode 100644 index 3a9dab7..0000000 --- a/src/protoutil.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { ProtoHax, Wiretype } from './protohax/protohax'; -import { Messages } from './pbreflect'; - -const gameActionId = Messages.Envelope.gameAction; -const cPlayerMoveId = Messages.GameAction.cPlayerMove; -const sPlayerMovesId = Messages.GameAction.sPlayerMoves; -const cpfPlayerId = Messages.CompactPlayerFrames.userId; -const userFramesId = Messages.ServerPlayerMoves.userFrames; - -export const maybePlayerMove = (envelope: Buffer) => - new ProtoHax(envelope).with(gameActionId).with(cPlayerMoveId).Bytes(); - -const sizeofVarint32 = (val: number): number => { - if (val <= 0x7f) return 1; - if (val <= 0x3fff) return 2; - if (val <= 0x1fffff) return 3; - if (val <= 0xfffffff) return 4; - if (val <= 0xffffffff) return 5; - throw new RangeError('Invalid value (too many bits)'); -}; -const writeVarint32 = (buf: Buffer, val: number, pos: number): number => { - if (val <= 0x7f) { - buf[pos++] = val; - return 1; - } - - if (val <= 0x3fff) { - buf[pos++] = (val & 0x7f) | 0x80; - buf[pos++] = (val >>> 7) & 0x7f; - return 2; - } - - if (val <= 0x1fffff) { - buf[pos++] = (val & 0x7f) | 0x80; - buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; - buf[pos++] = (val >>> 14) & 0x7f; - return 3; - } - - if (val <= 0xfffffff) { - buf[pos++] = (val & 0x7f) | 0x80; - buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; - buf[pos++] = ((val >>> 14) & 0x7f) | 0x80; - buf[pos++] = (val >>> 21) & 0x7f; - return 4; - } - - if (val <= 0xffffffff) { - buf[pos++] = (val & 0x7f) | 0x80; - buf[pos++] = ((val >>> 7) & 0x7f) | 0x80; - buf[pos++] = ((val >>> 14) & 0x7f) | 0x80; - buf[pos++] = ((val >>> 21) & 0x7f) | 0x80; - buf[pos++] = (val >>> 28) & 0x0f; - return 5; - } - throw new RangeError('Invalid value (too many bits)'); -}; - -export const tagPlayerMove = (cpf: Buffer, pmId: Buffer): Buffer | undefined => { - // reject c2s CompactPlayerFrames with userId specified - const embeddedUserId = new ProtoHax(cpf).with(cpfPlayerId).Bytes(); - if (embeddedUserId.length > 0) return; - - // prettier-ignore - const userFramesPayloadSize = ( - (1 + 1) // userId string tag + length - + pmId.length // userId (string) payload - + cpf.length // CompactPlayerFrames message (from client) - ); - const userFramesHeaderSize = sizeofVarint32(userFramesPayloadSize) + 1; - - const spmPayloadSize = userFramesPayloadSize + userFramesHeaderSize; - const spmHeaderSize = sizeofVarint32(spmPayloadSize) + 1; - - const gameActionPayloadSize = spmPayloadSize + spmHeaderSize; - const gameActionHeaderSize = sizeofVarint32(gameActionPayloadSize) + 1; - - const msgLength = gameActionHeaderSize + spmHeaderSize + userFramesHeaderSize + userFramesPayloadSize; - - const buf = Buffer.alloc(msgLength); - - let pos = 0; - - // write GameAction tag+length - buf[pos++] = (gameActionId << 3) | Wiretype.LEN; - pos += writeVarint32(buf, gameActionPayloadSize, pos); - - // write ServerPlayerMoves tag+length - buf[pos++] = (sPlayerMovesId << 3) | Wiretype.LEN; - pos += writeVarint32(buf, spmPayloadSize, pos); - - // write CompactPlayerFrames tag+length - buf[pos++] = (userFramesId << 3) | Wiretype.LEN; - pos += writeVarint32(buf, userFramesPayloadSize, pos); - - // write userId - buf[pos++] = (cpfPlayerId << 3) | Wiretype.LEN; - buf[pos++] = pmId.length; - pmId.copy(buf, pos, 0); - pos += pmId.length; - - // write the client-sent compactframes data - cpf.copy(buf, pos); - - return buf; -}; diff --git a/src/state/lobby.ts b/src/state/lobby.ts index 2f1b055..046afcb 100644 --- a/src/state/lobby.ts +++ b/src/state/lobby.ts @@ -1,6 +1,6 @@ -import { NT } from '../gen/pbjs_pb'; +import { M, NT } from 'nt-message'; import { ClientAuthWebSocket } from '../ws_handlers'; -import { Publishers, M } from '../util'; +import { Publishers } from '../util'; import { LobbyActionHandlers } from '../types'; import { IUser, UserState } from './user'; diff --git a/src/state/room.ts b/src/state/room.ts index c56f40d..5b80265 100644 --- a/src/state/room.ts +++ b/src/state/room.ts @@ -5,8 +5,8 @@ import { UpdateRoomOpts, validateRoomOpts, } from '../runtypes/room_opts'; -import { Publishers, M, createChat } from '../util'; -import { tagPlayerMove } from '../protoutil'; +import { Publishers, createChat } from '../util'; +import { M, NT, tagPlayerMove } from 'nt-message'; import { GameActionHandlers } from '../types'; import { statsUrl } from '../env_vars'; @@ -17,7 +17,6 @@ import { StatsEvent, StatsRecorder } from './stats_recorder'; import { v4 as uuidv4 } from 'uuid'; import Debug from 'debug'; -import { NT } from '../gen/pbjs_pb'; const debug = Debug('nt:room'); let id = 0; diff --git a/src/state/user.ts b/src/state/user.ts index 006fe10..17392e4 100644 --- a/src/state/user.ts +++ b/src/state/user.ts @@ -1,5 +1,4 @@ -import { M } from '../util'; -import { NT } from '../gen/pbjs_pb'; +import { M, NT } from 'nt-message'; import { RoomState } from './room'; import { LobbyState } from './lobby'; diff --git a/src/types.ts b/src/types.ts index b7b3ca7..eb84ce7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { Message } from 'protobufjs'; -import { NT } from './gen/pbjs_pb'; +import { NT } from 'nt-message'; import type { UserState } from './state/user'; export interface ActionCreator { diff --git a/src/util.ts b/src/util.ts index 220deb2..8cffe47 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,40 +1,13 @@ import { createHmac, randomBytes } from 'node:crypto'; import { TemplatedApp, WebSocket } from 'uWebSockets.js'; import { v4 as uuidv4 } from 'uuid'; +import { M, NT } from 'nt-message'; import { IUser } from './state/user'; -import { NT } from './gen/pbjs_pb'; -import { gameActions, lobbyActions } from './pbreflect'; -import { ActionCreator, GameActionCreators, LobbyActionCreators } from './types'; - import Debug from 'debug'; const debug = Debug('nt:util'); -/** - * Factory functions for each action type. Each function - * accepts an action payload and returns an `NT.Envelope` instance - * - * @example - * ```ts - * M.cChat({ message: 'hi there' }) - * ``` - */ -export const M: GameActionCreators & LobbyActionCreators = {} as any; - -for (const key of gameActions) { - M[key] = ((data, encoded) => - encoded - ? NT.Envelope.encode({ gameAction: { [key]: data } }).finish() - : NT.Envelope.fromObject({ gameAction: { [key]: data } })) as ActionCreator; -} -for (const key of lobbyActions) { - M[key] = ((data, encoded) => - encoded - ? NT.Envelope.encode({ lobbyAction: { [key]: data } }).finish() - : NT.Envelope.fromObject({ lobbyAction: { [key]: data } })) as ActionCreator; -} - type HasPublish = Pick, 'publish'>; /** diff --git a/src/ws_handlers.ts b/src/ws_handlers.ts index 7d10cd6..2c18c89 100644 --- a/src/ws_handlers.ts +++ b/src/ws_handlers.ts @@ -6,9 +6,8 @@ import { LobbyState } from './state/lobby'; import { UserState } from './state/user'; import { RoomState } from './state/room'; -import { maybePlayerMove } from './protoutil'; +import { NT, maybePlayerMove } from 'nt-message'; import { shortHash } from './util'; -import { NT } from './gen/pbjs_pb'; import type Debug from 'debug'; diff --git a/tsconfig.json b/tsconfig.json index 32354df..269d0b3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,16 +5,15 @@ "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, - "noEmit": false, + "noEmit": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, "module": "CommonJS", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "incremental": true, - "allowJs": true, - "outDir": "dist" + "incremental": false, + "allowJs": false, }, "include": ["src/**/*.ts", "src/**/*.js"], "exclude": ["node_modules", "coverage"] From a81d2a6a08b478579a45345fac0cb38c70344617 Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sun, 17 Dec 2023 18:28:00 +0000 Subject: [PATCH 3/5] Use NPM dependency instead of git dependency --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- src/compact_move_analyze.ts | 2 +- src/conformance/lobby.test.ts | 2 +- src/state/lobby.ts | 2 +- src/state/room.ts | 2 +- src/state/user.ts | 2 +- src/types.ts | 2 +- src/util.ts | 2 +- src/ws_handlers.ts | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c71286..b80042e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,10 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@noita-together/nt-message": "^0.0.1", "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", - "nt-message": "github:noita-together/nt-message#v1.0.0", "uuid": "^9.0.1", "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0" }, @@ -1453,6 +1453,15 @@ "node": ">= 8" } }, + "node_modules/@noita-together/nt-message": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@noita-together/nt-message/-/nt-message-0.0.1.tgz", + "integrity": "sha512-CzDtkQAsWqSospGL7dM/aZyBfnDwb+z5zRNMx9tRPPNlooPwgc3obkEkMPeprzkOb7hE3vIgydmFEcjeEcNRMg==", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.2.5" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -4028,15 +4037,6 @@ "node": ">=8" } }, - "node_modules/nt-message": { - "version": "1.0.0", - "resolved": "git+ssh://git@github.com/noita-together/nt-message.git#d68d7d879f2d9272f2e4af636747ce9c927c8461", - "license": "MIT", - "dependencies": { - "long": "^5.2.3", - "protobufjs": "^7.2.5" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index ef20d48..8dae54a 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,10 @@ "typescript": "^5.2.2" }, "dependencies": { + "@noita-together/nt-message": "^0.0.1", "@sinclair/typebox": "^0.31.17", "debug": "^4.3.4", "jsonwebtoken": "^9.0.2", - "nt-message": "github:noita-together/nt-message#v1.0.0", "uuid": "^9.0.1", "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.34.0" } diff --git a/src/compact_move_analyze.ts b/src/compact_move_analyze.ts index 6790e67..d34c603 100644 --- a/src/compact_move_analyze.ts +++ b/src/compact_move_analyze.ts @@ -10,7 +10,7 @@ import { decodeStable, encodeBitfield, encodeStable, -} from 'nt-message'; +} from '@noita-together/nt-message'; const infile = PATH.resolve(__dirname, '../ndjson'); diff --git a/src/conformance/lobby.test.ts b/src/conformance/lobby.test.ts index e7675e8..afc1d6f 100644 --- a/src/conformance/lobby.test.ts +++ b/src/conformance/lobby.test.ts @@ -1,5 +1,5 @@ import { RecognizedString } from 'uWebSockets.js'; -import { M, NT } from 'nt-message'; +import { M, NT } from '@noita-together/nt-message'; import { createJwtFns } from '../jwt'; import { AuthProvider, ClientAuth } from '../runtypes/client_auth'; import { LobbyState, SYSTEM_USER } from '../state/lobby'; diff --git a/src/state/lobby.ts b/src/state/lobby.ts index 046afcb..0250c11 100644 --- a/src/state/lobby.ts +++ b/src/state/lobby.ts @@ -1,4 +1,4 @@ -import { M, NT } from 'nt-message'; +import { M, NT } from '@noita-together/nt-message'; import { ClientAuthWebSocket } from '../ws_handlers'; import { Publishers } from '../util'; import { LobbyActionHandlers } from '../types'; diff --git a/src/state/room.ts b/src/state/room.ts index 5b80265..e929fbb 100644 --- a/src/state/room.ts +++ b/src/state/room.ts @@ -6,7 +6,7 @@ import { validateRoomOpts, } from '../runtypes/room_opts'; import { Publishers, createChat } from '../util'; -import { M, NT, tagPlayerMove } from 'nt-message'; +import { M, NT, tagPlayerMove } from '@noita-together/nt-message'; import { GameActionHandlers } from '../types'; import { statsUrl } from '../env_vars'; diff --git a/src/state/user.ts b/src/state/user.ts index 17392e4..c0bb3f2 100644 --- a/src/state/user.ts +++ b/src/state/user.ts @@ -1,4 +1,4 @@ -import { M, NT } from 'nt-message'; +import { M, NT } from '@noita-together/nt-message'; import { RoomState } from './room'; import { LobbyState } from './lobby'; diff --git a/src/types.ts b/src/types.ts index eb84ce7..9ca874a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { Message } from 'protobufjs'; -import { NT } from 'nt-message'; +import { NT } from '@noita-together/nt-message'; import type { UserState } from './state/user'; export interface ActionCreator { diff --git a/src/util.ts b/src/util.ts index 8cffe47..6230d9e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,7 +1,7 @@ import { createHmac, randomBytes } from 'node:crypto'; import { TemplatedApp, WebSocket } from 'uWebSockets.js'; import { v4 as uuidv4 } from 'uuid'; -import { M, NT } from 'nt-message'; +import { M, NT } from '@noita-together/nt-message'; import { IUser } from './state/user'; diff --git a/src/ws_handlers.ts b/src/ws_handlers.ts index 2c18c89..fc7a3e5 100644 --- a/src/ws_handlers.ts +++ b/src/ws_handlers.ts @@ -6,7 +6,7 @@ import { LobbyState } from './state/lobby'; import { UserState } from './state/user'; import { RoomState } from './state/room'; -import { NT, maybePlayerMove } from 'nt-message'; +import { NT, maybePlayerMove } from '@noita-together/nt-message'; import { shortHash } from './util'; import type Debug from 'debug'; From bb82133c984e15a91b5278cc2ce698d84859984d Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sun, 17 Dec 2023 22:06:04 +0000 Subject: [PATCH 4/5] Temporarily remove disconnected users from rooms --- src/conformance/lobby.test.ts | 176 ++++++++++++++++++++++++++++++---- src/state/room.ts | 42 +++++--- src/state/user.ts | 6 +- 3 files changed, 190 insertions(+), 34 deletions(-) diff --git a/src/conformance/lobby.test.ts b/src/conformance/lobby.test.ts index afc1d6f..28311aa 100644 --- a/src/conformance/lobby.test.ts +++ b/src/conformance/lobby.test.ts @@ -253,6 +253,7 @@ describe('lobby conformance tests', () => { handleMessage(user2, M.cJoinRoom({ id: roomId }, true), true); expectLobbyAction('sUserJoinedRoom'); + expectGameAction('sChat'); expectLobbyAction('sJoinRoomSuccess'); expectLobbyAction('sRoomFlagsUpdated'); expectLobbyAction('sUserReadyState'); @@ -264,17 +265,9 @@ describe('lobby conformance tests', () => { expect(sentMessages).toEqual([]); }); - it('allows disconnected users to rejoin locked rooms. owner retains ownership', () => { - const { - testSocket, - handleOpen, - handleMessage, - handleClose, - sentMessages, - lobby, - expectGameAction, - expectLobbyAction, - } = createTestEnv(false); + it('allows disconnected users to rejoin locked, in-progress rooms. owner retains ownership', () => { + const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(false); const owner = testSocket('ownerid', 'owner'); const player = testSocket('playerid', 'player'); @@ -288,12 +281,13 @@ describe('lobby conformance tests', () => { expect(sRoomCreated.id).toMatch(uuidRE); const roomId = sRoomCreated.id!; - const sRoomAddToList = expectLobbyAction('sRoomAddToList'); + expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); handleMessage(player, M.cJoinRoom({ id: roomId }, true), true); expectLobbyAction('sUserJoinedRoom'); + expectGameAction('sChat'); expectLobbyAction('sJoinRoomSuccess'); expectLobbyAction('sRoomFlagsUpdated'); expectLobbyAction('sUserReadyState'); @@ -303,18 +297,73 @@ describe('lobby conformance tests', () => { expectLobbyAction('sRoomUpdated'); expect(sentMessages).toEqual([]); + handleMessage(owner, M.cStartRun({}, true), true); + expectLobbyAction('sHostStart'); + handleClose(player, 1006, Buffer.from('test')); + expectLobbyAction('sUserLeftRoom'); + const chat1 = expectGameAction('sChat'); + expect(chat1.message).toMatch(/disconnected/); // player can rejoin const player2 = testSocket('playerid', 'player'); handleOpen(player2); handleMessage(player2, M.cJoinRoom({ id: roomId }, true), true); + expectLobbyAction('sUserJoinedRoom'); + const chat2 = expectGameAction('sChat'); + expect(chat2.message).toMatch(/rejoined/); expectLobbyAction('sJoinRoomSuccess'); expectLobbyAction('sRoomFlagsUpdated'); expectLobbyAction('sUserReadyState'); expect(sentMessages).toEqual([]); }); + + it('disallows disconnected users from rejoining locked, NOT in-progress rooms', () => { + const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = + createTestEnv(false); + const owner = testSocket('ownerid', 'owner'); + const player = testSocket('playerid', 'player'); + + handleOpen(owner); + handleOpen(player); + + // create a room - should have a uuid for its id + handleMessage(owner, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); + + const sRoomCreated = expectLobbyAction('sRoomCreated'); + expect(sRoomCreated.id).toMatch(uuidRE); + const roomId = sRoomCreated.id!; + + expectLobbyAction('sRoomAddToList'); + expect(sentMessages).toEqual([]); + + handleMessage(player, M.cJoinRoom({ id: roomId }, true), true); + + expectLobbyAction('sUserJoinedRoom'); + expectGameAction('sChat'); + expectLobbyAction('sJoinRoomSuccess'); + expectLobbyAction('sRoomFlagsUpdated'); + expectLobbyAction('sUserReadyState'); + expect(sentMessages).toEqual([]); + + handleMessage(owner, M.cRoomUpdate({ locked: true }, true), true); + expectLobbyAction('sRoomUpdated'); + expect(sentMessages).toEqual([]); + + handleClose(player, 1006, Buffer.from('test')); + expectLobbyAction('sUserLeftRoom'); + const chat1 = expectGameAction('sChat'); + expect(chat1.message).toMatch(/disconnected/); + + // player can rejoin + const player2 = testSocket('playerid', 'player'); + handleOpen(player2); + handleMessage(player2, M.cJoinRoom({ id: roomId }, true), true); + + expectLobbyAction('sJoinRoomFailed'); + expect(sentMessages).toEqual([]); + }); }); describe('dev mode', () => { @@ -531,6 +580,16 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat1', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, users.user2, @@ -644,6 +703,16 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + sm( + '/room/room1', + user2, + M.sChat({ + id: 'chat1', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, user2, @@ -1176,7 +1245,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat2', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: 'user2 has been banned from this room.', @@ -1249,7 +1318,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat2', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: 'user2 has been kicked from this room.', @@ -1263,6 +1332,17 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat3', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, users.user2, @@ -1340,7 +1420,7 @@ describe('lobby conformance tests', () => { // name: 'cJoinRoom - success (twice)', tests.push({ - name: 'cRoomUpdate - success (twice)', + name: 'cJoinRoom - success (twice)', clientMessages: (users) => [ ...u1create_u2join_no_password.clientMessages(users), [ @@ -1352,6 +1432,24 @@ describe('lobby conformance tests', () => { ], serverMessages: (users) => [ ...u1create_u2join_no_password.serverMessages(users), + sm( + '/room/1', + users.user2, + M.sUserJoinedRoom({ + userId: '2', + name: 'user2', + }), + ), + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat2', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 rejoined the room.', + }), + ), sm( null, users.user2, @@ -1470,6 +1568,16 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat1', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, users.user2, @@ -1584,7 +1692,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat2', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: 'user2 has left.', @@ -1598,6 +1706,16 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat3', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, users.user2, @@ -1657,7 +1775,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat2', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: 'user2 has left.', @@ -2140,6 +2258,16 @@ describe('lobby conformance tests', () => { name: 'user2', }), ), + sm( + '/room/room1', + users.user2, + M.sChat({ + id: 'chat1', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'user2 joined the room.', + }), + ), sm( null, users.user2, @@ -2247,6 +2375,16 @@ describe('lobby conformance tests', () => { name: 'myndzi', }), ), + sm( + '/room/room1', + users.myndzi, + M.sChat({ + id: 'chat2', + userId: SYSTEM_USER.id, + name: SYSTEM_USER.name, + message: 'myndzi joined the room.', + }), + ), sm( null, users.myndzi, @@ -2429,7 +2567,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat3', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: `Stats for run can be found at https://noitatogether.com/api/stats/room1/stats1/html`, @@ -2488,7 +2626,7 @@ describe('lobby conformance tests', () => { '/room/room1', null, M.sChat({ - id: 'chat1', + id: 'chat3', userId: SYSTEM_USER.id, name: SYSTEM_USER.name, message: `Stats for run can be found at https://noitatogether.com/api/stats/room1/stats1/html`, diff --git a/src/state/room.ts b/src/state/room.ts index e929fbb..7f2f905 100644 --- a/src/state/room.ts +++ b/src/state/room.ts @@ -394,7 +394,7 @@ export class RoomState implements GameActionHandlers<'cPlayerMove'> { */ join(user: UserState, password?: string | null): void { let reason: string | null = null; - let joinMessage = 'joining'; + let joinMessage = 'joined the room.'; const room = user.room(); if (!room) { @@ -408,7 +408,7 @@ export class RoomState implements GameActionHandlers<'cPlayerMove'> { // room. delete the old room if user is the owner, otherwise just leave room.delete(user) || room.part(user); } else { - joinMessage = 'rejoining'; + joinMessage = 'rejoined the room.'; // user (probably) got disconnected while in a room, and is rejoining it. allow them in // the NT app currently provides no way to join a room without leaving the // current room, but could in the future. if so, this assumption changes @@ -422,18 +422,17 @@ export class RoomState implements GameActionHandlers<'cPlayerMove'> { debug(this.id, joinMessage, user.id, user.name); this.users.add(user); - // don't re-broadcast reconnect rejoins - if (room !== this) { - // broadcast the join to everyone except the user that joined - // that user will receive a different confirmation in `user.joined` - user.broadcast( - this.topic, - M.sUserJoinedRoom({ - userId: user.id, - name: user.name, - }), - ); - } + // broadcast the join to everyone except the user that joined + // that user will receive a different confirmation in `user.joined` + user.broadcast( + this.topic, + M.sUserJoinedRoom({ + userId: user.id, + name: user.name, + }), + ); + this.broadcast(this.chat(SYSTEM_USER, `${user.name} ${joinMessage}`)); + user.joined(this); // this.playerPositions.updatePlayers(this.users); @@ -502,6 +501,21 @@ export class RoomState implements GameActionHandlers<'cPlayerMove'> { this.removeUser(null, actor, M.sUserLeftRoom, 'has left.'); } + disconnected(actor: UserState) { + if (this.inProgress) { + // if user disconnected while game is in progress, leave them in the list of + // users so that they can reconnect. + + // send a "user left room" message to the lobby to update the NT app's UI + this.broadcast(M.sUserLeftRoom({ userId: actor.id })); + // send a message explaining what happened + this.broadcast(this.chat(SYSTEM_USER, `${actor.name} disconnected.`)); + } else { + // if user disconnected while game is NOT in progress, just remove them + this.removeUser(null, actor, M.sUserLeftRoom, 'disconnected.'); + } + } + /** * Remove a user from this room. They may rejoin. * diff --git a/src/state/user.ts b/src/state/user.ts index c0bb3f2..9be44f4 100644 --- a/src/state/user.ts +++ b/src/state/user.ts @@ -182,11 +182,15 @@ export class UserState implements IUser { debug(this.id, this.name, 'disconnected'); this.socket = null; - if (this.currentRoom !== null && this.currentRoom.owner === this) { + if (this.currentRoom === null) return; + + if (this.currentRoom.owner === this) { // currently, the room host holds the state for the game. if they get disconnected, // that state is lost and the game is unrecoverable. make this more explicit by // directly destroying the room if the host gets disconnected. this.currentRoom.destroy(); + } else { + this.currentRoom.disconnected(this); } } From ec2503d33a24a3e1f9b9b254ccd70dffab8fe82f Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sun, 17 Dec 2023 22:36:02 +0000 Subject: [PATCH 5/5] Clean up "ghost" users from lobby state When a user disconnects from an in-progress room, their state object is kept around. If they never reconnect, they weren't being removed from the LobbyState instance when the room is eventually destroyed. --- src/conformance/lobby.test.ts | 42 +++++++++++++++++++++++++++++------ src/state/lobby.ts | 5 +++++ src/state/room.ts | 3 ++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/conformance/lobby.test.ts b/src/conformance/lobby.test.ts index 28311aa..fdb95b1 100644 --- a/src/conformance/lobby.test.ts +++ b/src/conformance/lobby.test.ts @@ -208,14 +208,23 @@ describe('lobby conformance tests', () => { describe('disconnect handling', () => { it('cleans up a room when all active users have disconnected', () => { - const { testSocket, handleOpen, handleMessage, handleClose, sentMessages, expectGameAction, expectLobbyAction } = - createTestEnv(false); - const user = testSocket('id', 'name'); + const { + testSocket, + handleOpen, + handleMessage, + handleClose, + sentMessages, + lobby, + expectGameAction, + expectLobbyAction, + } = createTestEnv(false); + const host = testSocket('id', 'host'); + const player = testSocket('id2', 'player'); - handleOpen(user); + handleOpen(host); // create a room - should have a uuid for its id - handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); + handleMessage(host, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "host's room" }, true), true); const sRoomCreated = expectLobbyAction('sRoomCreated'); expect(sRoomCreated.id).toMatch(uuidRE); @@ -223,12 +232,31 @@ describe('lobby conformance tests', () => { expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); - handleClose(user, 1006, Buffer.from('test')); + handleOpen(player); + handleMessage(player, M.cJoinRoom({ id: sRoomCreated.id }, true), true); + + expectLobbyAction('sUserJoinedRoom'); + expectGameAction('sChat'); + expectLobbyAction('sJoinRoomSuccess'); + expectLobbyAction('sRoomFlagsUpdated'); + expectLobbyAction('sUserReadyState'); + + // start the game so that the UserState is retained in the RoomState + handleMessage(host, M.cStartRun({}, true), true); + expectLobbyAction('sHostStart'); + + handleClose(player, 1006, Buffer.from('test')); + expectLobbyAction('sUserLeftRoom'); // synthetic "leave" message for disconnected user + expectGameAction('sChat'); // chat message saying user disconnected + + handleClose(host, 1006, Buffer.from('test')); const sRoomDeleted = expectLobbyAction('sRoomDeleted'); expect(sRoomDeleted.id).toMatch(uuidRE); expect(sentMessages).toEqual([]); + // ensure the lobby cleans up references to ghost users who were still present in the room + expect(lobby.getInfo().users).toEqual(0); }); it('leaves a room active when at least one connected user is present', () => { @@ -238,7 +266,6 @@ describe('lobby conformance tests', () => { const user2 = testSocket('id2', 'name2'); handleOpen(user); - handleOpen(user2); // create a room - should have a uuid for its id handleMessage(user, M.cRoomCreate({ gamemode: 0, maxUsers: 5, name: "name's room" }, true), true); @@ -250,6 +277,7 @@ describe('lobby conformance tests', () => { expectLobbyAction('sRoomAddToList'); expect(sentMessages).toEqual([]); + handleOpen(user2); handleMessage(user2, M.cJoinRoom({ id: roomId }, true), true); expectLobbyAction('sUserJoinedRoom'); diff --git a/src/state/lobby.ts b/src/state/lobby.ts index 0250c11..233d938 100644 --- a/src/state/lobby.ts +++ b/src/state/lobby.ts @@ -179,6 +179,11 @@ export class LobbyState implements LobbyActionHandlers { * @param room RoomState instance of the room that was destroyed */ roomDestroyed(room: RoomState) { + for (const user of room.getUsers()) { + if (!user.isConnected()) { + this.users.delete(user.id); + } + } this.rooms.delete(room.id); } diff --git a/src/state/room.ts b/src/state/room.ts index 7f2f905..d2b94f7 100644 --- a/src/state/room.ts +++ b/src/state/room.ts @@ -565,9 +565,10 @@ export class RoomState implements GameActionHandlers<'cPlayerMove'> { // to be receiving the message. user.parted(this); } - this.users.clear(); this.lobby.roomDestroyed(this); + this.users.clear(); + debug(this.id, 'destroyed'); }