From e300ba5e63530ee63d31c1ad58012edc24969671 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Thu, 28 Dec 2023 16:22:54 -0700 Subject: [PATCH 01/56] group binding modules by folder name --- client_wasm/bindings.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index 0de0b57..cb3b016 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -5,7 +5,7 @@ #include "conway/structure_properties.hh" #include "utility/vector2.hh" -EMSCRIPTEN_BINDINGS(Vector2) { +EMSCRIPTEN_BINDINGS(Utility) { emscripten::class_("Vector2") .constructor() .function("x", &Vector2::x) @@ -14,7 +14,7 @@ EMSCRIPTEN_BINDINGS(Vector2) { .function("set_y", &Vector2::set_y); } -EMSCRIPTEN_BINDINGS(LifeGrid) { +EMSCRIPTEN_BINDINGS(Conway) { emscripten::class_("LifeGrid") .constructor() .function("dimensions", &LifeGrid::dimensions) @@ -22,9 +22,7 @@ EMSCRIPTEN_BINDINGS(LifeGrid) { .function("Load", &LifeGrid::Load) .function("Compare", &LifeGrid::Compare) .function("Tick", &LifeGrid::Tick); -} -EMSCRIPTEN_BINDINGS(StructureProperties) { emscripten::class_("StructureProperties") .constructor &>() @@ -34,9 +32,7 @@ EMSCRIPTEN_BINDINGS(StructureProperties) { .function("income", &StructureProperties::income) .function("build_area", &StructureProperties::build_area) .function("checks", &StructureProperties::checks); -} -EMSCRIPTEN_BINDINGS(Structure) { emscripten::class_("Structure") .constructor() .function("CheckIntegrity", &Structure::CheckIntegrity) From 6dffec912d21837d64fbf1b2f478cd02ba8b938d Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Thu, 28 Dec 2023 17:05:06 -0700 Subject: [PATCH 02/56] setup vite for client --- .eslintrc.cjs | 21 + .prettierignore | 9 + client/index.html | 13 + client/public/room-interaction.js | 54 +- client/public/{index.html => test.html} | 0 client/public/vite.config.ts | 11 + client/src/App.tsx | 9 + client/src/index.css | 3 + client/src/main.tsx | 10 + package.json | 28 +- pnpm-lock.yaml | 2111 ++++++++++++++++++++++- prettier.config.mjs | 15 + tsconfig.json | 24 + 13 files changed, 2259 insertions(+), 49 deletions(-) create mode 100644 .eslintrc.cjs create mode 100644 .prettierignore create mode 100644 client/index.html rename client/public/{index.html => test.html} (100%) create mode 100644 client/public/vite.config.ts create mode 100644 client/src/App.tsx create mode 100644 client/src/index.css create mode 100644 client/src/main.tsx create mode 100644 prettier.config.mjs create mode 100644 tsconfig.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..c698826 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { + browser: true, + es2020: true, + }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["build", "bazel-*", "dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..20b3060 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +bazel-* +build +dist +node_modules + +**/*.html +**/*.md + +pnpm-lock.yaml diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..a42cd2b --- /dev/null +++ b/client/index.html @@ -0,0 +1,13 @@ + + + + + + + client test + + +
+ + + \ No newline at end of file diff --git a/client/public/room-interaction.js b/client/public/room-interaction.js index f98a458..e061fc9 100644 --- a/client/public/room-interaction.js +++ b/client/public/room-interaction.js @@ -2,76 +2,76 @@ const server_url = "ws://localhost:3000"; // js for fast debugging? (at least won't need to set up jsx tooling) function CreateButton(label, callback) { - const el = document.createElement('button'); + const el = document.createElement("button"); el.innerText = label; - el.addEventListener('click', callback); + el.addEventListener("click", callback); return el; } class ClientInstance { constructor() { this.ws = null; - const body = document.createElement('div'); + const body = document.createElement("div"); const info = { - 'logs': document.createElement('div'), - 'room': document.createElement('div'), + logs: document.createElement("div"), + room: document.createElement("div"), }; this.element = { - 'info': document.createElement('div'), - 'connect': CreateButton("Connect", () => this.Connect()), - 'disconnect': CreateButton("Disconnect", () => this.Disconnect()), - 'payload': document.createElement('input'), - 'send': CreateButton("Send", () => { + info: document.createElement("div"), + connect: CreateButton("Connect", () => this.Connect()), + disconnect: CreateButton("Disconnect", () => this.Disconnect()), + payload: document.createElement("input"), + send: CreateButton("Send", () => { this.Send(this.element.payload.value); this.element.payload.value = ""; }), }; this.element.info.append(...Object.values(info)); - this.element.info.classList.add('info'); + this.element.info.classList.add("info"); body.append(...Object.values(this.element)); this.element = Object.assign(this.element, info); this.element.body = body; - console.log(this.element) + console.log(this.element); this.logs = []; - for(let i = 0; i < 10; i ++) this.PushLog(-1, ''); + for (let i = 0; i < 10; i++) this.PushLog(-1, ""); } Connect() { - if(this.ws) this.Disconnect(); + if (this.ws) this.Disconnect(); this.ws = new WebSocket(server_url); this.ws.onopen = (event) => { - this.PushLog(event.timeStamp, 'socket opened'); - } + this.PushLog(event.timeStamp, "socket opened"); + }; this.ws.onclose = (event) => { - this.PushLog(event.timeStamp, 'socket closed'); - } + this.PushLog(event.timeStamp, "socket closed"); + }; this.ws.onerror = (event) => { console.warn(event); - this.PushLog(event.timeStamp, 'socket error'); - } + this.PushLog(event.timeStamp, "socket error"); + }; this.ws.onmessage = (event) => { - console.log('data', event.data); + console.log("data", event.data); this.PushLog(event.timeStamp, `<- ${event.data}`); - } + }; } Disconnect() { this.ws.close(); this.ws = null; } PushLog(time, log) { - this.logs.push(`[${(~~time).toString().padStart(6, '_')}] ${log}`); - this.element.logs.innerText = this.logs.slice(-10).join('\n'); + this.logs.push(`[${(~~time).toString().padStart(6, "_")}] ${log}`); + this.element.logs.innerText = this.logs.slice(-10).join("\n"); } Send(data) { - if(!this.ws) throw 'socket is not open; cannot send'; + if (!this.ws) throw "socket is not open; cannot send"; this.ws.send(data); - this.PushLog(new Event('').timeStamp, `-> ${data}`) + this.PushLog(new Event("").timeStamp, `-> ${data}`); } } const c = new ClientInstance(); document.body.append(c.element.body); -const c2 = [...new Array(2)].map(x => { +const c2 = [...new Array(2)].map((x) => { const client = new ClientInstance(); document.body.append(client.element.body); return client; diff --git a/client/public/index.html b/client/public/test.html similarity index 100% rename from client/public/index.html rename to client/public/test.html diff --git a/client/public/vite.config.ts b/client/public/vite.config.ts new file mode 100644 index 0000000..4874ac4 --- /dev/null +++ b/client/public/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + root: "client", + build: { + outDir: "../dist", + }, +}); diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..23a3344 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,9 @@ +function App() { + return ( + <> +
Hello world
+ + ); +} + +export default App; diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..8db9b2a --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,3 @@ +:root { + font-family: "Courier New", Courier, monospace; +} diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..f25366e --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/package.json b/package.json index ba3608e..2b4127d 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,16 @@ "test": "tests" }, "scripts": { - "dev": "npx http-server client/public", - "build": "npm run build:pbjs && npm run build:bazel", + "dev": "vite dev ./client", + "dev:test": "npx http-server client/public", + "build": "npm run build:pbjs && npm run build:bazel && npm run build:client", "build:pbjs": "npx pbjs -t static-module -w es6 -o ./build/bundle_proto.js ./**/*.proto && npx pbts -o ./build/bundle_proto.d.ts ./build/bundle_proto.js", "build:bazel": "npx bazel build //:conway", + "build:client": "tsc && vite build", + "format": "npx prettier client client_wasm --write", + "format:check": "npx prettier client client_wasm --check", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -26,7 +32,23 @@ "homepage": "https://github.com/ucrcyber/conway-rts#readme", "devDependencies": { "@bazel/bazelisk": "^1.18.0", + "@types/react": "^18.2.46", + "@types/react-dom": "^18.2.18", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.56.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "prettier": "^3.1.1", "protobufjs": "^7.2.5", - "protobufjs-cli": "^1.1.2" + "protobufjs-cli": "^1.1.2", + "typescript": "^5.3.3", + "vite": "^5.0.10", + "vitest": "^1.1.0" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11414a8..5acc1fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,19 +4,193 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + devDependencies: '@bazel/bazelisk': specifier: ^1.18.0 version: 1.18.0 + '@types/react': + specifier: ^18.2.46 + version: 18.2.46 + '@types/react-dom': + specifier: ^18.2.18 + version: 18.2.18 + '@typescript-eslint/eslint-plugin': + specifier: ^6.16.0 + version: 6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.16.0 + version: 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.2.1(vite@5.0.10) + eslint: + specifier: ^8.56.0 + version: 8.56.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.56.0) + eslint-plugin-react-refresh: + specifier: ^0.4.5 + version: 0.4.5(eslint@8.56.0) + prettier: + specifier: ^3.1.1 + version: 3.1.1 protobufjs: specifier: ^7.2.5 version: 7.2.5 protobufjs-cli: specifier: ^1.1.2 version: 1.1.2(protobufjs@7.2.5) + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vite: + specifier: ^5.0.10 + version: 5.0.10 + vitest: + specifier: ^1.1.0 + version: 1.1.0 packages: + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.6: + resolution: {integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helpers': 7.23.6 + '@babel/parser': 7.23.6 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.6 + '@babel/types': 7.23.6 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} @@ -27,6 +201,31 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.6: + resolution: {integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.6 + '@babel/types': 7.23.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + /@babel/parser@7.23.6: resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} engines: {node: '>=6.0.0'} @@ -35,6 +234,53 @@ packages: '@babel/types': 7.23.6 dev: true + /@babel/plugin-transform-react-jsx-self@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-source@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + dev: true + + /@babel/traverse@7.23.6: + resolution: {integrity: sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.23.6: resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} engines: {node: '>=6.9.0'} @@ -49,6 +295,307 @@ packages: hasBin: true dev: true + /@esbuild/aix-ppc64@0.19.10: + resolution: {integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.10: + resolution: {integrity: sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.19.10: + resolution: {integrity: sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.19.10: + resolution: {integrity: sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.19.10: + resolution: {integrity: sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.19.10: + resolution: {integrity: sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.19.10: + resolution: {integrity: sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.19.10: + resolution: {integrity: sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.19.10: + resolution: {integrity: sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.19.10: + resolution: {integrity: sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.19.10: + resolution: {integrity: sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.19.10: + resolution: {integrity: sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.19.10: + resolution: {integrity: sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.19.10: + resolution: {integrity: sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.19.10: + resolution: {integrity: sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.10: + resolution: {integrity: sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.19.10: + resolution: {integrity: sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.19.10: + resolution: {integrity: sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.19.10: + resolution: {integrity: sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.19.10: + resolution: {integrity: sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.19.10: + resolution: {integrity: sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.19.10: + resolution: {integrity: sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.19.10: + resolution: {integrity: sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.56.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.56.0: + resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@jsdoc/salty@0.2.7: resolution: {integrity: sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==} engines: {node: '>=v12.0.0'} @@ -56,6 +603,27 @@ packages: lodash: 4.17.21 dev: true + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.16.0 + dev: true + /@protobufjs/aspromise@1.1.2: resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} dev: true @@ -83,41 +651,398 @@ packages: resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} dev: true - /@protobufjs/inquire@1.1.0: - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: true + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: true + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: true + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: true + + /@rollup/rollup-android-arm-eabi@4.9.1: + resolution: {integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.9.1: + resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.9.1: + resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.9.1: + resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.9.1: + resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.9.1: + resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.9.1: + resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.9.1: + resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.9.1: + resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.9.1: + resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.9.1: + resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.9.1: + resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.9.1: + resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + dev: true + + /@types/babel__traverse@7.20.4: + resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/linkify-it@3.0.5: + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + dev: true + + /@types/markdown-it@12.2.3: + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + dev: true + + /@types/mdurl@1.0.5: + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + dev: true + + /@types/node@20.10.5: + resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + dev: true + + /@types/react-dom@18.2.18: + resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} + dependencies: + '@types/react': 18.2.46 + dev: true + + /@types/react@18.2.46: + resolution: {integrity: sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + dev: true + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + dev: true + + /@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + dev: true + + /@typescript-eslint/eslint-plugin@6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/type-utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.3.0 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + eslint: 8.56.0 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.16.0: + resolution: {integrity: sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 + dev: true + + /@typescript-eslint/type-utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.16.0: + resolution: {integrity: sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.16.0(typescript@5.3.3): + resolution: {integrity: sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + eslint: 8.56.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript dev: true - /@protobufjs/path@1.1.2: - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + /@typescript-eslint/visitor-keys@6.16.0: + resolution: {integrity: sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.16.0 + eslint-visitor-keys: 3.4.3 dev: true - /@protobufjs/pool@1.1.0: - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@protobufjs/utf8@1.1.0: - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + /@vitejs/plugin-react@4.2.1(vite@5.0.10): + resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.6) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.0 + vite: 5.0.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@1.1.0: + resolution: {integrity: sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w==} + dependencies: + '@vitest/spy': 1.1.0 + '@vitest/utils': 1.1.0 + chai: 4.3.10 dev: true - /@types/linkify-it@3.0.5: - resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + /@vitest/runner@1.1.0: + resolution: {integrity: sha512-zdNLJ00pm5z/uhbWF6aeIJCGMSyTyWImy3Fcp9piRGvueERFlQFbUwCpzVce79OLm2UHk9iwaMSOaU9jVHgNVw==} + dependencies: + '@vitest/utils': 1.1.0 + p-limit: 5.0.0 + pathe: 1.1.1 dev: true - /@types/markdown-it@12.2.3: - resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + /@vitest/snapshot@1.1.0: + resolution: {integrity: sha512-5O/wyZg09V5qmNmAlUgCBqflvn2ylgsWJRRuPrnHEfDNT6tQpQ8O1isNGgo+VxofISHqz961SG3iVvt3SPK/QQ==} dependencies: - '@types/linkify-it': 3.0.5 - '@types/mdurl': 1.0.5 + magic-string: 0.30.5 + pathe: 1.1.1 + pretty-format: 29.7.0 dev: true - /@types/mdurl@1.0.5: - resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + /@vitest/spy@1.1.0: + resolution: {integrity: sha512-sNOVSU/GE+7+P76qYo+VXdXhXffzWZcYIPQfmkiRxaNCSPiLANvQx5Mx6ZURJ/ndtEkUJEpvKLXqAYTKEY+lTg==} + dependencies: + tinyspy: 2.2.0 dev: true - /@types/node@20.10.5: - resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} + /@vitest/utils@1.1.0: + resolution: {integrity: sha512-z+s510fKmYz4Y41XhNs3vcuFTFhcij2YF7F8VQfMEYAAUfqQh0Zfg7+w9xdgFGhPf3tX3TicAe+8BDITk6ampQ==} dependencies: - undici-types: 5.26.5 + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 dev: true /acorn-jsx@5.3.2(acorn@8.11.2): @@ -128,12 +1053,38 @@ packages: acorn: 8.11.2 dev: true + /acorn-walk@8.3.1: + resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@8.11.2: resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} engines: {node: '>=0.4.0'} hasBin: true dev: true + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -141,10 +1092,24 @@ packages: color-convert: 2.0.1 dev: true + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -166,6 +1131,38 @@ packages: balanced-match: 1.0.2 dev: true + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001572 + electron-to-chromium: 1.4.616 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /caniuse-lite@1.0.30001572: + resolution: {integrity: sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==} + dev: true + /catharsis@0.9.0: resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} engines: {node: '>= 10'} @@ -173,6 +1170,28 @@ packages: lodash: 4.17.21 dev: true + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -181,6 +1200,18 @@ packages: supports-color: 7.2.0 dev: true + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -188,6 +1219,10 @@ packages: color-name: 1.1.4 dev: true + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true @@ -196,19 +1231,124 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /electron-to-chromium@1.4.616: + resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} + dev: true + /entities@2.1.0: resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} dev: true + /esbuild@0.19.10: + resolution: {integrity: sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.10 + '@esbuild/android-arm': 0.19.10 + '@esbuild/android-arm64': 0.19.10 + '@esbuild/android-x64': 0.19.10 + '@esbuild/darwin-arm64': 0.19.10 + '@esbuild/darwin-x64': 0.19.10 + '@esbuild/freebsd-arm64': 0.19.10 + '@esbuild/freebsd-x64': 0.19.10 + '@esbuild/linux-arm': 0.19.10 + '@esbuild/linux-arm64': 0.19.10 + '@esbuild/linux-ia32': 0.19.10 + '@esbuild/linux-loong64': 0.19.10 + '@esbuild/linux-mips64el': 0.19.10 + '@esbuild/linux-ppc64': 0.19.10 + '@esbuild/linux-riscv64': 0.19.10 + '@esbuild/linux-s390x': 0.19.10 + '@esbuild/linux-x64': 0.19.10 + '@esbuild/netbsd-x64': 0.19.10 + '@esbuild/openbsd-x64': 0.19.10 + '@esbuild/sunos-x64': 0.19.10 + '@esbuild/win32-arm64': 0.19.10 + '@esbuild/win32-ia32': 0.19.10 + '@esbuild/win32-x64': 0.19.10 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} dev: true + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + /escodegen@1.14.3: resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} engines: {node: '>=4.0'} @@ -222,11 +1362,83 @@ packages: source-map: 0.6.1 dev: true + /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-plugin-react-refresh@0.4.5(eslint@8.56.0): + resolution: {integrity: sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /eslint@8.56.0: + resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.56.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -242,6 +1454,20 @@ packages: hasBin: true dev: true + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + /estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} @@ -257,14 +1483,125 @@ packages: engines: {node: '>=0.10.0'} dev: true + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fastq@1.16.0: + resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -287,15 +1624,71 @@ packages: once: 1.4.0 dev: true + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -307,6 +1700,47 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + /js2xmlparser@4.0.2: resolution: {integrity: sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==} dependencies: @@ -335,6 +1769,40 @@ packages: underscore: 1.13.6 dev: true + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + /klaw@3.0.0: resolution: {integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==} dependencies: @@ -349,12 +1817,39 @@ packages: type-check: 0.3.2 dev: true + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + /linkify-it@3.0.3: resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} dependencies: uc.micro: 1.0.6 dev: true + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.4.2 + pkg-types: 1.0.3 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true @@ -363,6 +1858,25 @@ packages: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} dev: true + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -370,6 +1884,13 @@ packages: yallist: 4.0.0 dev: true + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} peerDependencies: @@ -401,6 +1922,28 @@ packages: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: true + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -414,6 +1957,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true @@ -424,12 +1974,53 @@ packages: hasBin: true dev: true + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.11.2 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.2 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true + + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /optionator@0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -442,16 +2033,130 @@ packages: word-wrap: 1.2.5 dev: true + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} dev: true + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /postcss@8.4.32: + resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} dev: true + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /protobufjs-cli@1.1.2(protobufjs@7.2.5): resolution: {integrity: sha512-8ivXWxT39gZN4mm4ArQyJrRgnIwZqffBWoLDsE21TmMcKI3XwJMV4lEF2WU02C4JAtgYYc2SfJIltelD8to35g==} engines: {node: '>=12.0.0'} @@ -491,12 +2196,57 @@ packages: long: 5.2.3 dev: true + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + /requizzle@0.2.4: resolution: {integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==} dependencies: lodash: 4.17.21 dev: true + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -504,6 +2254,44 @@ packages: glob: 7.2.3 dev: true + /rollup@4.9.1: + resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.9.1 + '@rollup/rollup-android-arm64': 4.9.1 + '@rollup/rollup-darwin-arm64': 4.9.1 + '@rollup/rollup-darwin-x64': 4.9.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.9.1 + '@rollup/rollup-linux-arm64-gnu': 4.9.1 + '@rollup/rollup-linux-arm64-musl': 4.9.1 + '@rollup/rollup-linux-riscv64-gnu': 4.9.1 + '@rollup/rollup-linux-x64-gnu': 4.9.1 + '@rollup/rollup-linux-x64-musl': 4.9.1 + '@rollup/rollup-win32-arm64-msvc': 4.9.1 + '@rollup/rollup-win32-ia32-msvc': 4.9.1 + '@rollup/rollup-win32-x64-msvc': 4.9.1 + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -512,6 +2300,37 @@ packages: lru-cache: 6.0.0 dev: true + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -519,11 +2338,44 @@ packages: dev: true optional: true + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.2 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -531,6 +2383,24 @@ packages: has-flag: 4.0.0 dev: true + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.8.1: + resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + /tmp@0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} @@ -543,6 +2413,22 @@ packages: engines: {node: '>=4'} dev: true + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.0.3(typescript@5.3.3): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.3.3 + dev: true + /type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -550,10 +2436,37 @@ packages: prelude-ls: 1.1.2 dev: true + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + /uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} dev: true + /ufo@1.3.2: + resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -568,6 +2481,152 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true + /update-browserslist-db@1.0.13(browserslist@4.22.2): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.2 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /vite-node@1.1.0: + resolution: {integrity: sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 5.0.10 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@5.0.10: + resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.19.10 + postcss: 8.4.32 + rollup: 4.9.1 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.1.0: + resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@vitest/expect': 1.1.0 + '@vitest/runner': 1.1.0 + '@vitest/snapshot': 1.1.0 + '@vitest/spy': 1.1.0 + '@vitest/utils': 1.1.0 + acorn-walk: 8.3.1 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.5 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.8.1 + vite: 5.0.10 + vite-node: 1.1.0 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -581,6 +2640,20 @@ packages: resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} dev: true + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 0000000..5979aa0 --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,15 @@ +// prettier.config.js, .prettierrc.js, prettier.config.mjs, or .prettierrc.mjs + +/** @type {import("prettier").Config} */ +const config = { + tabWidth: 2, + useTabs: false, + semi: true, + singleQuote: false, + jsxSingleQuote: true, + trailingComma: "all", + arrowParens: "always", + proseWrap: "preserve", +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..eb4018f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler Mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["client/src", "client/vite.config.ts"] +} From cc972ad35f4f822f9660bba24988be5fa1d948cd Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 21:14:53 -0700 Subject: [PATCH 03/56] bind vector2 zero-parameter constructor --- client_wasm/bindings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index cb3b016..ff3bff1 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -7,6 +7,7 @@ EMSCRIPTEN_BINDINGS(Utility) { emscripten::class_("Vector2") + .constructor<>() .constructor() .function("x", &Vector2::x) .function("y", &Vector2::y) From 33980c966748eeb8b50a1b8c1e8ed9288d127699 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 22:33:24 -0700 Subject: [PATCH 04/56] improve cell-level accessor/mutators for client usage --- client_wasm/bindings.cpp | 8 +++++++- conway/life_grid.cc | 12 ++++++++++++ conway/life_grid.hh | 15 ++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index ff3bff1..1fab425 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -16,10 +16,16 @@ EMSCRIPTEN_BINDINGS(Utility) { } EMSCRIPTEN_BINDINGS(Conway) { + emscripten::enum_("LifeState") + .value("DEAD", LifeState::DEAD) + .value("ALIVE", LifeState::ALIVE); + emscripten::class_("LifeGrid") .constructor() .function("dimensions", &LifeGrid::dimensions) - .function("GetCell", &LifeGrid::GetCell) + .function("GetCell", &LifeGrid::IsCellAlive) + .function("SetCell", &LifeGrid::SetCell) + .function("ResetCell", &LifeGrid::ResetCell) .function("Load", &LifeGrid::Load) .function("Compare", &LifeGrid::Compare) .function("Tick", &LifeGrid::Tick); diff --git a/conway/life_grid.cc b/conway/life_grid.cc index 2f83013..9956d6b 100644 --- a/conway/life_grid.cc +++ b/conway/life_grid.cc @@ -46,6 +46,18 @@ LifeState LifeGrid::GetCell(const Vector2& coordinate) const { return grid_[coordinate.y()][coordinate.x()] ? LifeState::ALIVE : LifeState::DEAD; } +bool LifeGrid::IsCellAlive(const Vector2& coordinate) const { + return grid_[coordinate.y()][coordinate.x()]; +} + +void LifeGrid::SetCell(const Vector2& coordinate) { + grid_[coordinate.y()][coordinate.x()] = true; +} + +void LifeGrid::ResetCell(const Vector2& coordinate) { + grid_[coordinate.y()][coordinate.x()] = false; +} + bool LifeGrid::Load(const LifeGrid& life_grid, const Vector2& offset) { const Vector2 bottom_right = offset + life_grid.dimensions_; if(bottom_right.x() > dimensions_.x() || bottom_right.y() > dimensions_.y()) return false; diff --git a/conway/life_grid.hh b/conway/life_grid.hh index 3d93d27..fc0f874 100644 --- a/conway/life_grid.hh +++ b/conway/life_grid.hh @@ -35,9 +35,22 @@ class LifeGrid { /// @brief query the status of a single cell /// @param position which cell - /// @return whether the cell is alive or dead + /// @return cell state LifeState GetCell(const Vector2& position) const; + /// @brief query the status of a single cell + /// @param position which cell + /// @return whether the cell is alive or dead + bool IsCellAlive(const Vector2& position) const; + + /// @brief sets the specified cell as alive (set) + /// @param position which cell + void SetCell(const Vector2& position); + + /// @brief sets the specified cell as dead (reset) + /// @param position which cell + void ResetCell(const Vector2& position); + /// @brief loads a payload LifeGrid onto the LifeGrid; /// used for loading templates when players build something /// @param payload what to load From f8784488fd764a95542be0214e90cf21f4a953e0 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 22:34:32 -0700 Subject: [PATCH 05/56] modularize wasm so vite can load the default module --- client_wasm/BUILD.bazel | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index 13f9281..d6603d9 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -9,11 +9,13 @@ DEFAULT_EMSCRIPTEN_LINKOPTS = [ # "-flto", # Specify lto (has to be set on for compiler as well) "--bind", # Compiles the source code using the Embind bindings to connect C/C++ and JavaScript "--closure 1", # Run the closure compiler + # "--embind-emit-tsd interface.d.ts", # emit typescript types file + # "-s EXPORTED_FUNCTIONS=_free,_malloc", "-s MALLOC=emmalloc", # Switch to using the much smaller implementation - "-s ALLOW_MEMORY_GROWTH=0", # Our example doesn't need memory growth + "-s ALLOW_MEMORY_GROWTH=1", "-s USE_PTHREADS=0", # Disable pthreads "-s ASSERTIONS=0", # Turn off assertions - # "-s EXPORT_ES6=1", # Export as es6 module, used for rollup + "-s EXPORT_ES6=1", # Export as es6 module, used for rollup "-s MODULARIZE=1", # Allows us to manually invoke the initializatio of wasm "-s EXPORT_NAME=createModule", # Not used, but good to specify # "-s USE_ES6_IMPORT_META=1", # needed for somethingggg --- Disable loading from import meta since we use rollup From 1928e84192920fddd4f5041d3262e296ee966f97 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 22:35:56 -0700 Subject: [PATCH 06/56] setup vite configs --- .../vite.config.ts => vite.config.mts} | 6 +++++ package.json | 2 +- tsconfig.json | 8 +++++- vite.config.mts | 25 +++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) rename client/{public/vite.config.ts => vite.config.mts} (67%) create mode 100644 vite.config.mts diff --git a/client/public/vite.config.ts b/client/vite.config.mts similarity index 67% rename from client/public/vite.config.ts rename to client/vite.config.mts index 4874ac4..232833f 100644 --- a/client/public/vite.config.ts +++ b/client/vite.config.mts @@ -1,5 +1,8 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import { ALIASES } from "../vite.config.mts"; + +console.log(ALIASES); // https://vitejs.dev/config/ export default defineConfig({ @@ -8,4 +11,7 @@ export default defineConfig({ build: { outDir: "../dist", }, + resolve: { + alias: ALIASES, + }, }); diff --git a/package.json b/package.json index 2b4127d..1196505 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "format:check": "npx prettier client client_wasm --check", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npx vitest" }, "repository": { "type": "git", diff --git a/tsconfig.json b/tsconfig.json index eb4018f..dfb3f92 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,12 @@ "module": "ESNext", "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "@/lib-wasm": ["bazel-bin/client_wasm/conway_lib_wasm/conway_lib_wasm_embind"], + "@/lib": ["client_wasm/conway"] + }, + /* Bundler Mode */ "moduleResolution": "Bundler", "allowImportingTsExtensions": true, @@ -20,5 +26,5 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["client/src", "client/vite.config.ts"] + "include": ["client/src", "client/vite.config.ts", "client_wasm"] } diff --git a/vite.config.mts b/vite.config.mts new file mode 100644 index 0000000..c26387a --- /dev/null +++ b/vite.config.mts @@ -0,0 +1,25 @@ +/// +import { configDefaults, defineConfig } from "vitest/config"; +import { resolve } from "path"; + +export const ALIASES = [ + { + find: "@/lib-wasm", + replacement: resolve(__dirname, "bazel-bin/client_wasm/conway_lib_wasm/conway_lib_wasm_embind"), + }, + { + find: "@/lib", + replacement: resolve(__dirname, "client_wasm/conway"), + }, +]; + +// https://vitejs.dev/config/ +export default defineConfig({ + resolve: { + alias: ALIASES, + }, + test: { + root: '.', + exclude: [...configDefaults.exclude, 'external', 'bazel-*/**/*'], + }, +}); From d8d8130f7145ee977eefdddea67477abfc46d9d8 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 22:38:39 -0700 Subject: [PATCH 07/56] add basic tests for emscripten compiled library --- client_wasm/LifeGrid.spec.ts | 13 +++++++++++++ client_wasm/Vector2.spec.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 client_wasm/LifeGrid.spec.ts create mode 100644 client_wasm/Vector2.spec.ts diff --git a/client_wasm/LifeGrid.spec.ts b/client_wasm/LifeGrid.spec.ts new file mode 100644 index 0000000..b5dd980 --- /dev/null +++ b/client_wasm/LifeGrid.spec.ts @@ -0,0 +1,13 @@ +/** + * Test emscripten compiled //utility:vector2 + */ + +import { expect, test } from "vitest"; +import Conway from '@/lib'; + +test("constructor", () => { + const lifeGrid = new Conway.LifeGrid(new Conway.Vector2(10, 15)); + expect(lifeGrid.dimensions().x()).toBe(10); + expect(lifeGrid.dimensions().y()).toBe(15); + lifeGrid.Tick(); +}); diff --git a/client_wasm/Vector2.spec.ts b/client_wasm/Vector2.spec.ts new file mode 100644 index 0000000..7849db3 --- /dev/null +++ b/client_wasm/Vector2.spec.ts @@ -0,0 +1,30 @@ +/** + * Test emscripten compiled //utility:vector2 + */ + +import { expect, test } from "vitest"; +import Conway from '@/lib'; + +test("default constructor", () => { + const u = new Conway.Vector2(); + expect(u.x()).toBe(0); + expect(u.y()).toBe(0); +}); + +test("parameterized constructor", () => { + const u = new Conway.Vector2(-727, 1234); + expect(u.x()).toBe(-727); + expect(u.y()).toBe(1234); +}); + +test("accessor & mutators", () => { + const u = new Conway.Vector2(); + expect(u.x()).toBe(0); + expect(u.y()).toBe(0); + u.set_x(1234); + expect(u.x()).toBe(1234); + expect(u.y()).toBe(0); + u.set_y(-9876); + expect(u.x()).toBe(1234); + expect(u.y()).toBe(-9876); +}); From cf649de0c7823a2d6eda9a431a1bccb1ca858b3c Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 22:39:50 -0700 Subject: [PATCH 08/56] add basic typing file for conway library --embind-emit-tsd doesn't work too well --- client_wasm/conway.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 client_wasm/conway.ts diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts new file mode 100644 index 0000000..e8f487d --- /dev/null +++ b/client_wasm/conway.ts @@ -0,0 +1,40 @@ +/** + * Typescript bindings for the emscripten outputs + */ + +import ConwayWasm from "@/lib-wasm"; + +interface Type extends Function { // eslint-disable-line @typescript-eslint/ban-types + new (...args: any[]): T; // eslint-disable-line @typescript-eslint/no-explicit-any +} + +interface Vector2 { + x: () => number; + y: () => number; + set_x: (x: number) => void; + set_y: (y: number) => void; +} + +enum LifeState { + DEAD, + ALIVE, +} + +interface LifeGrid { + dimensions: () => Vector2; + GetCell: (position: Vector2) => boolean; + SetCell: (position: Vector2) => void; + ResetCell: (position: Vector2) => void; + Load: (payload: LifeGrid, position: Vector2) => void; + Compare: (payload: LifeGrid, position: Vector2) => number; + Tick: () => void; +} + +interface ConwayLib { + Vector2: Type; + LifeState: typeof LifeState; + LifeGrid: Type; +} + +const Conway = (await ConwayWasm()) as ConwayLib; +export default Conway; From c9d974d73ee1fa5202721a3b48c9737b5b902998 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 23:02:14 -0700 Subject: [PATCH 09/56] set up a basic interactive life grid though react doesn't seem to be right for this purpose --- client/src/App.tsx | 3 ++ client/src/InteractiveLifeGrid.tsx | 66 ++++++++++++++++++++++++++++++ client/src/index.css | 22 ++++++++++ client_wasm/LifeGrid.spec.ts | 2 +- client_wasm/Vector2.spec.ts | 2 +- client_wasm/conway.ts | 3 +- 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 client/src/InteractiveLifeGrid.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 23a3344..0a21a11 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,7 +1,10 @@ +import InteractiveLifeGrid from "./InteractiveLifeGrid"; + function App() { return ( <>
Hello world
+ ); } diff --git a/client/src/InteractiveLifeGrid.tsx b/client/src/InteractiveLifeGrid.tsx new file mode 100644 index 0000000..68d555d --- /dev/null +++ b/client/src/InteractiveLifeGrid.tsx @@ -0,0 +1,66 @@ +import Conway from "@/lib"; +import { useCallback, useEffect, useState } from "react"; + +type InteractiveLifeGridProps = { + width: number; + height: number; +}; + +function InteractiveLifeGrid({ width, height }: InteractiveLifeGridProps) { + const [, updateState] = useState({}); + const [isPlaying, setPlaying] = useState(false); + const forceUpdate = useCallback(() => updateState({}), []); + const [grid] = useState( + new Conway.LifeGrid(new Conway.Vector2(width, height)), + ); + + useEffect(() => { + const interval = setInterval(() => { + if (isPlaying) { + grid.Tick(); + forceUpdate(); + } + }, 50); + return () => clearInterval(interval); + }, [isPlaying]); + + return ( + <> +
+ {[...new Array(grid.dimensions().y())].map((_, i) => ( +
+ {[...new Array(grid.dimensions().x())].map((_, j) => ( +
{ + const pos = new Conway.Vector2(j, i); + if (!grid.GetCell(pos)) grid.SetCell(pos); + else grid.ResetCell(pos); + forceUpdate(); + }} + >
+ ))} +
+ ))} +
+ + + + ); +} + +export default InteractiveLifeGrid; diff --git a/client/src/index.css b/client/src/index.css index 8db9b2a..41c20f1 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,3 +1,25 @@ :root { font-family: "Courier New", Courier, monospace; } + +.lifegrid { + display: flex; + flex-direction: column; + background: gray; + margin: auto; + width: fit-content; +} +.lifegrid-row { + display: flex; + flex-direction: row; +} +.lifegrid-row > * { + width: 10px; + height: 10px; +} +.lifegrid-alive { + background: white; +} +.lifegrid-dead { + background: black; +} diff --git a/client_wasm/LifeGrid.spec.ts b/client_wasm/LifeGrid.spec.ts index b5dd980..ebb0572 100644 --- a/client_wasm/LifeGrid.spec.ts +++ b/client_wasm/LifeGrid.spec.ts @@ -3,7 +3,7 @@ */ import { expect, test } from "vitest"; -import Conway from '@/lib'; +import Conway from "@/lib"; test("constructor", () => { const lifeGrid = new Conway.LifeGrid(new Conway.Vector2(10, 15)); diff --git a/client_wasm/Vector2.spec.ts b/client_wasm/Vector2.spec.ts index 7849db3..fbc371b 100644 --- a/client_wasm/Vector2.spec.ts +++ b/client_wasm/Vector2.spec.ts @@ -3,7 +3,7 @@ */ import { expect, test } from "vitest"; -import Conway from '@/lib'; +import Conway from "@/lib"; test("default constructor", () => { const u = new Conway.Vector2(); diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index e8f487d..148b0bb 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -4,7 +4,8 @@ import ConwayWasm from "@/lib-wasm"; -interface Type extends Function { // eslint-disable-line @typescript-eslint/ban-types +interface Type extends Function { + // eslint-disable-line @typescript-eslint/ban-types new (...args: any[]): T; // eslint-disable-line @typescript-eslint/no-explicit-any } From 33d0d4fe9521574e56a56ef4227bec9992b42b84 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 29 Dec 2023 23:26:44 -0700 Subject: [PATCH 10/56] implement socket connection logger in tsx --- client/src/App.tsx | 18 +++++++++++++++++- client/src/SocketLogger.tsx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 client/src/SocketLogger.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 0a21a11..cd3e118 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,10 +1,26 @@ +import { useEffect, useState } from "react"; import InteractiveLifeGrid from "./InteractiveLifeGrid"; +import SocketLogger from "./SocketLogger"; + +const server_url = "ws://localhost:3000"; function App() { + const [socket, setSocket] = useState(null); + + useEffect(() => { + console.log("Creating websocket"); + const s = new WebSocket(server_url); + s.onopen = ({ timeStamp }) => console.log(timeStamp, "opened"); + s.onclose = ({ timeStamp }) => console.log(timeStamp, "closed"); + s.addEventListener("message", ({data}) => console.log(data)); + setSocket(s); + return () => s?.close(); + }, []); + return ( <>
Hello world
- + {socket && } ); } diff --git a/client/src/SocketLogger.tsx b/client/src/SocketLogger.tsx new file mode 100644 index 0000000..39fbe7f --- /dev/null +++ b/client/src/SocketLogger.tsx @@ -0,0 +1,30 @@ +import { useEffect, useState } from "react"; + +type SocketLoggerProps = { + socket: WebSocket; +}; + +function SocketLogger({ socket }: SocketLoggerProps) { + const [logs, setLogs] = useState([]); + + useEffect(() => { + const processEvent = (event: MessageEvent) => { + setLogs(logs => [...logs, event.data as string]); + }; + socket.addEventListener("message", processEvent); + return () => socket.removeEventListener("message", processEvent); + }, []); + + return ( + <> + +
+ {logs.map((log, index) => ( +
{log}
+ ))} +
+ + ); +} + +export default SocketLogger; From ca48e22ea97c3fbef643c86958dced7023f6a862 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:02:25 -0700 Subject: [PATCH 11/56] move structure and structure_properties protos into //conway --- {proto => conway}/structure.proto | 0 {proto => conway}/structure_properties.proto | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {proto => conway}/structure.proto (100%) rename {proto => conway}/structure_properties.proto (100%) diff --git a/proto/structure.proto b/conway/structure.proto similarity index 100% rename from proto/structure.proto rename to conway/structure.proto diff --git a/proto/structure_properties.proto b/conway/structure_properties.proto similarity index 100% rename from proto/structure_properties.proto rename to conway/structure_properties.proto From 1ddda23694af7d5674380f532e2662bd8f1e231b Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:03:38 -0700 Subject: [PATCH 12/56] update vector2 visibility to be accessible from //conway and //conway_rts --- utility/BUILD.bazel | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utility/BUILD.bazel b/utility/BUILD.bazel index 755fe6d..e5cc218 100644 --- a/utility/BUILD.bazel +++ b/utility/BUILD.bazel @@ -35,7 +35,10 @@ cc_test( proto_library( name = "vector2_proto", srcs = ["vector2.proto"], - visibility = [], # set later + visibility = [ + "//conway:__pkg__", + "//conway_rts:__pkg__", + ], ) cc_proto_library( From 768f20e80f76cbdc673cceefc6de4653edbd5de1 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:04:51 -0700 Subject: [PATCH 13/56] setup build rules for //conway protos --- conway/BUILD.bazel | 38 +++++++++++++++++++++++++++++++ conway/life_grid.proto | 2 +- conway/structure.proto | 2 +- conway/structure_properties.proto | 4 ++-- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/conway/BUILD.bazel b/conway/BUILD.bazel index d6226d5..5a945de 100644 --- a/conway/BUILD.bazel +++ b/conway/BUILD.bazel @@ -22,6 +22,17 @@ cc_library( ], ) +proto_library( + name = "life_grid_proto", + srcs = ["life_grid.proto"], + deps = ["//utility:vector2_proto"], +) + +cc_proto_library( + name = "life_grid_cc_proto", + deps = [":life_grid_proto"], +) + cc_test( name = "life_grid_test", size = "small", @@ -48,6 +59,18 @@ cc_library( ], ) +proto_library( + name = "structure_proto", + srcs = ["structure.proto"], + visibility = ["//conway_rts:__pkg__"], + deps = ["//utility:vector2_proto"], +) + +cc_proto_library( + name = "structure_cc_proto", + deps = [":structure_proto"], +) + cc_test( name = "structure_test", size = "small", @@ -73,6 +96,21 @@ cc_library( ], ) +proto_library( + name = "structure_properties_proto", + srcs = ["structure_properties.proto"], + visibility = ["//conway_rts:__pkg__"], + deps = [ + ":life_grid_proto", + "//utility:vector2_proto", + ], +) + +cc_proto_library( + name = "structure_properties_cc_proto", + deps = [":structure_properties_proto"], +) + cc_test( name = "structure_properties_test", size = "small", diff --git a/conway/life_grid.proto b/conway/life_grid.proto index a47298c..66b8626 100644 --- a/conway/life_grid.proto +++ b/conway/life_grid.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package conway; -import "vector2.proto"; +import "utility/vector2.proto"; // encoded as a flattened 2d array, a[i][j] = flattened[i*cols + j] message LifeGrid { diff --git a/conway/structure.proto b/conway/structure.proto index 37eb3fb..71de177 100644 --- a/conway/structure.proto +++ b/conway/structure.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package conway; -import "vector2.proto"; +import "utility/vector2.proto"; message Structure { // which structure this maps to in a StructureLookup diff --git a/conway/structure_properties.proto b/conway/structure_properties.proto index 110ae5f..00cc2a4 100644 --- a/conway/structure_properties.proto +++ b/conway/structure_properties.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package conway; -import "life_grid.proto"; -import "vector2.proto"; +import "conway/life_grid.proto"; +import "utility/vector2.proto"; message StructureProperties { conway.LifeGrid grid = 1; From 9ef8beb2484a286557b62f3329c17efa27d0788f Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:09:22 -0700 Subject: [PATCH 14/56] move structure_lookup.proto into //conway_rts used for structure table initialization so structures can be referenced by id in events --- {proto => conway_rts}/structure_lookup.proto | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {proto => conway_rts}/structure_lookup.proto (100%) diff --git a/proto/structure_lookup.proto b/conway_rts/structure_lookup.proto similarity index 100% rename from proto/structure_lookup.proto rename to conway_rts/structure_lookup.proto From f2cc7f7a643afd8c11ba8dd277c71ceb56a5d240 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:33:05 -0700 Subject: [PATCH 15/56] setup build rules for //conway_rts protos --- conway_rts/BUILD.bazel | 101 ++++++++++++++++++++++++++++++ conway_rts/event.proto | 4 +- conway_rts/event_base.proto | 6 +- conway_rts/event_room.proto | 4 +- conway_rts/room.proto | 16 +++-- conway_rts/structure_lookup.proto | 2 +- conway_rts/team.proto | 4 +- 7 files changed, 123 insertions(+), 14 deletions(-) diff --git a/conway_rts/BUILD.bazel b/conway_rts/BUILD.bazel index 2fd7b14..3b17929 100644 --- a/conway_rts/BUILD.bazel +++ b/conway_rts/BUILD.bazel @@ -12,6 +12,7 @@ cc_library( name = "client", srcs = ["client.cc"], hdrs = ["client.hh"], + visibility = ["//server:__pkg__"], deps = [ ":array_buffer", ":event", @@ -19,6 +20,16 @@ cc_library( ], ) +proto_library( + name = "client_proto", + srcs = ["client.proto"], +) + +cc_proto_library( + name = "client_cc_proto", + deps = [":client_proto"], +) + cc_test( name = "client_test", size = "small", @@ -36,6 +47,20 @@ cc_library( deps = [":array_buffer"], ) +proto_library( + name = "event_proto", + srcs = ["event.proto"], + deps = [ + ":event_base_proto", + ":event_room_proto", + ], +) + +cc_proto_library( + name = "event_cc_proto", + deps = [":event_proto"], +) + cc_test( name = "event_test", size = "small", @@ -56,6 +81,7 @@ cc_library( name = "room", srcs = ["room.cc"], hdrs = ["room.hh"], + visibility = ["//server:__pkg__"], deps = [ ":client", ":event", @@ -65,6 +91,22 @@ cc_library( ], ) +proto_library( + name = "room_proto", + srcs = ["room.proto"], + visibility = ["//server:__pkg__"], + deps = [ + ":client_proto", + ":team_proto", + "//utility:vector2_proto", + ], +) + +cc_proto_library( + name = "room_cc_proto", + deps = [":room_proto"], +) + cc_test( name = "room_test", size = "small", @@ -80,6 +122,7 @@ cc_library( srcs = ["team.cc"], hdrs = ["team.hh"], copts = CC_OPTS, + visibility = ["//server:__pkg__"], deps = [ ":client", ":event", @@ -88,6 +131,22 @@ cc_library( ], ) +proto_library( + name = "team_proto", + srcs = ["team.proto"], + visibility = ["//server:__pkg__"], + deps = [ + ":client_proto", + "//conway:structure_proto", + ], +) + +cc_proto_library( + name = "team_cc_proto", + visibility = ["//server:__pkg__"], + deps = [":team_proto"], +) + cc_test( name = "team_test", size = "small", @@ -97,3 +156,45 @@ cc_test( "@googletest//:gtest_main", ], ) + +proto_library( + name = "event_base_proto", + srcs = ["event_base.proto"], + deps = [ + ":structure_lookup_proto", + ":team_proto", + "//utility:vector2_proto", + ], +) + +cc_proto_library( + name = "event_base_cc_proto", + visibility = ["//server:__pkg__"], + deps = [":event_base_proto"], +) + +proto_library( + name = "event_room_proto", + srcs = ["event_room.proto"], + deps = [ + ":client_proto", + ":room_proto", + ], +) + +cc_proto_library( + name = "event_room_cc_proto", + visibility = ["//server:__pkg__"], + deps = [":event_room_proto"], +) + +proto_library( + name = "structure_lookup_proto", + srcs = ["structure_lookup.proto"], + deps = ["//conway:structure_properties_proto"], +) + +cc_proto_library( + name = "structure_lookup_cc_proto", + deps = [":structure_lookup_proto"], +) diff --git a/conway_rts/event.proto b/conway_rts/event.proto index 958c6d4..47fd221 100644 --- a/conway_rts/event.proto +++ b/conway_rts/event.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package conway; -import "event_base.proto"; -import "event_room.proto"; +import "conway_rts/event_base.proto"; +import "conway_rts/event_room.proto"; // wrapper class for every type of event message Event { diff --git a/conway_rts/event_base.proto b/conway_rts/event_base.proto index 0f49735..9cdc40c 100644 --- a/conway_rts/event_base.proto +++ b/conway_rts/event_base.proto @@ -2,9 +2,9 @@ syntax = "proto3"; package conway.event.base; -import "structure_lookup.proto"; -import "team.proto"; -import "vector2.proto"; +import "conway_rts/structure_lookup.proto"; +import "conway_rts/team.proto"; +import "utility/vector2.proto"; message Build { int32 id = 1; diff --git a/conway_rts/event_room.proto b/conway_rts/event_room.proto index ca22af1..045b1ce 100644 --- a/conway_rts/event_room.proto +++ b/conway_rts/event_room.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package conway.event.room; -import "client.proto"; -import "room.proto"; +import "conway_rts/client.proto"; +import "conway_rts/room.proto"; message Create { conway.Room room = 1; diff --git a/conway_rts/room.proto b/conway_rts/room.proto index 923b653..888d9be 100644 --- a/conway_rts/room.proto +++ b/conway_rts/room.proto @@ -2,14 +2,22 @@ syntax = "proto3"; package conway; -import "client.proto"; -import "team.proto"; -import "vector2.proto"; +import "conway_rts/client.proto"; +import "conway_rts/team.proto"; +import "utility/vector2.proto"; +/// full room details to initialize a room message Room { int32 id = 1; string name = 2; conway.Vector2 dimensions = 3; repeated conway.Client clients = 4; repeated conway.Team teams = 5; -} \ No newline at end of file +} + +/// simplified room details for listing +message RoomListing { + int32 id = 1; + string name = 2; + int32 client_count = 3; +} diff --git a/conway_rts/structure_lookup.proto b/conway_rts/structure_lookup.proto index 2b4b66a..d519387 100644 --- a/conway_rts/structure_lookup.proto +++ b/conway_rts/structure_lookup.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package conway; -import "structure_properties.proto"; +import "conway/structure_properties.proto"; message StructureLookup { repeated conway.StructureProperties table = 1; diff --git a/conway_rts/team.proto b/conway_rts/team.proto index aa6f903..11b8a48 100644 --- a/conway_rts/team.proto +++ b/conway_rts/team.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package conway; -import "client.proto"; -import "structure.proto"; +import "conway_rts/client.proto"; +import "conway/structure.proto"; message Team { int32 id = 1; From 1e535b934fc8162c5b2cae73b82d1288707b6ee0 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 12:54:48 -0700 Subject: [PATCH 16/56] delete /external/*/CMakeLists.txt --- external/CMakeLists.txt | 11 ----------- external/googletest/CMakeLists.txt | 23 ----------------------- external/libuv/CMakeLists.txt | 19 ------------------- external/uSockets/CMakeLists.txt | 15 --------------- external/uWebSockets/CMakeLists.txt | 13 ------------- external/zlib/CMakeLists.txt | 19 ------------------- 6 files changed, 100 deletions(-) delete mode 100644 external/CMakeLists.txt delete mode 100644 external/googletest/CMakeLists.txt delete mode 100644 external/libuv/CMakeLists.txt delete mode 100644 external/uSockets/CMakeLists.txt delete mode 100644 external/uWebSockets/CMakeLists.txt delete mode 100644 external/zlib/CMakeLists.txt diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt deleted file mode 100644 index 9a99cd7..0000000 --- a/external/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# LITERAL MAGIC by JACK :pray: :pray: -# https://stackoverflow.com/a/75848101/21507383 - -add_subdirectory(googletest) -add_subdirectory(libuv) -add_subdirectory(zlib) -add_subdirectory(uSockets) -add_subdirectory(uWebSockets) - -include_directories(${external_include_dirs}) -set(external_include_dirs ${external_include_dirs} PARENT_SCOPE) \ No newline at end of file diff --git a/external/googletest/CMakeLists.txt b/external/googletest/CMakeLists.txt deleted file mode 100644 index 7374eea..0000000 --- a/external/googletest/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.24) - -set(CMAKE_C_STANDARD 11) - -include(FetchContent) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.13.x - GIT_SHALLOW ON - GIT_SUBMODULES "" -) - -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) - -set(external_include_dirs - ${external_include_dirs} - ${googletest_SOURCE_DIR} - ${googletest_BINARY_DIR} -) -include_directories(${external_include_dirs}) -set(external_include_dirs ${external_include_dirs} PARENT_SCOPE) \ No newline at end of file diff --git a/external/libuv/CMakeLists.txt b/external/libuv/CMakeLists.txt deleted file mode 100644 index 130a369..0000000 --- a/external/libuv/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -set(CMAKE_C_STANDARD 11) -include(FetchContent) -FetchContent_Declare( - libuv_content - GIT_REPOSITORY https://github.com/libuv/libuv - GIT_TAG v1.x - GIT_SHALLOW ON - GIT_SUBMODULES "" -) -FetchContent_MakeAvailable(libuv_content) -target_link_libraries(uv uv_a) - -set(external_include_dirs - ${external_include_dirs} - ${libuv_content_SOURCE_DIR}/include -) -include_directories(${external_include_dirs}) -set(external_include_dirs ${external_include_dirs} PARENT_SCOPE) \ No newline at end of file diff --git a/external/uSockets/CMakeLists.txt b/external/uSockets/CMakeLists.txt deleted file mode 100644 index 87ebab4..0000000 --- a/external/uSockets/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -set(CMAKE_C_STANDARD 11) -include(FetchContent) -FetchContent_Declare( - uSockets_content - GIT_REPOSITORY https://github.com/uNetworking/uSockets - GIT_TAG v0.8.5 - GIT_SHALLOW ON - GIT_SUBMODULES "" -) -FetchContent_MakeAvailable(uSockets_content) -file(GLOB_RECURSE SOURCES ${usockets_content_SOURCE_DIR}/src/*.c) -add_library(uSockets ${SOURCES}) -target_include_directories(uSockets PUBLIC ${usockets_content_SOURCE_DIR}/src ${external_include_dirs}) -target_compile_definitions(uSockets PRIVATE LIBUS_NO_SSL) diff --git a/external/uWebSockets/CMakeLists.txt b/external/uWebSockets/CMakeLists.txt deleted file mode 100644 index 9c99648..0000000 --- a/external/uWebSockets/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -include(FetchContent) -FetchContent_Declare( - uWebSockets_content - GIT_REPOSITORY https://github.com/uNetworking/uWebSockets - GIT_TAG v20.37.0 - GIT_SHALLOW ON - GIT_SUBMODULES "" -) -FetchContent_MakeAvailable(uWebSockets_content) -add_library(uWebSockets INTERFACE) -target_include_directories(uWebSockets INTERFACE ${uwebsockets_content_SOURCE_DIR}/src/ ${CMAKE_CURRENT_BINARY_DIR}/${ZLIB_LIBRARY}) -target_link_libraries(uWebSockets INTERFACE uSockets uv zlibstatic) diff --git a/external/zlib/CMakeLists.txt b/external/zlib/CMakeLists.txt deleted file mode 100644 index 4bbd52f..0000000 --- a/external/zlib/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -set(CMAKE_C_STANDARD 11) -include(FetchContent) -FetchContent_Declare( - zlib_content - GIT_REPOSITORY https://github.com/madler/zlib - GIT_TAG v1.3 - GIT_SHALLOW ON - GIT_SUBMODULES "" -) -FetchContent_MakeAvailable(zlib_content) - -set(external_include_dirs - ${external_include_dirs} - ${zlib_content_SOURCE_DIR} # some weird renaming business happens here with zconf.h -> zconf.h.included - ${zlib_content_BINARY_DIR} # zconf.h is untouched here though -) -include_directories(${external_include_dirs}) -set(external_include_dirs ${external_include_dirs} PARENT_SCOPE) \ No newline at end of file From 84303a9368c4216f4726de54e4320592ed321ef6 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 13:02:03 -0700 Subject: [PATCH 17/56] move //external to //externals //external is a reserved bazel folder oops --- WORKSPACE | 12 ++++++------ externals/BUILD.bazel | 6 ++++++ {external => externals}/BUILD.boringssl.bazel | 0 {external => externals}/BUILD.libuv.bazel | 0 {external => externals}/BUILD.lsquic.bazel | 0 {external => externals}/BUILD.usockets.bazel | 0 {external => externals}/BUILD.uwebsockets.bazel | 0 {external => externals}/BUILD.zlib.bazel | 0 8 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 externals/BUILD.bazel rename {external => externals}/BUILD.boringssl.bazel (100%) rename {external => externals}/BUILD.libuv.bazel (100%) rename {external => externals}/BUILD.lsquic.bazel (100%) rename {external => externals}/BUILD.usockets.bazel (100%) rename {external => externals}/BUILD.uwebsockets.bazel (100%) rename {external => externals}/BUILD.zlib.bazel (100%) diff --git a/WORKSPACE b/WORKSPACE index 7cc995d..bf52c2f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -67,7 +67,7 @@ register_emscripten_toolchains() ############### http_archive( name = "uwebsockets", - build_file = "BUILD.uwebsockets.bazel", + build_file = "@//externals:BUILD.uwebsockets.bazel", sha256 = "6794e7895eb8cc182024a0ae482a581eaa82f55f7cca53ae88b30738449f3cb9", strip_prefix = "uWebSockets-20.51.0", url = "https://github.com/uNetworking/uWebSockets/archive/refs/tags/v20.51.0.tar.gz", @@ -75,21 +75,21 @@ http_archive( git_repository( name = "usockets", - build_file = "BUILD.usockets.bazel", + build_file = "@//externals:BUILD.usockets.bazel", commit = "8cd4cb66eb061b2594ca114b9ea1ead64613ad4b", # v0.8.6 remote = "git@github.com:uNetworking/uSockets.git", ) git_repository( name = "boringssl", - build_file = "BUILD.boringssl.bazel", + build_file = "@//externals:BUILD.boringssl.bazel", commit = "1ccef4908ce04adc6d246262846f3cd8a111fa44", # specific version used with usockets v0.8.6 remote = "git@github.com:google/boringssl.git", ) git_repository( name = "lsquic", - build_file = "BUILD.lsquic.bazel", + build_file = "@//externals:BUILD.lsquic.bazel", commit = "3bbf683f25ab84826951350c57ae226c88c54422", # v3.2.0 recursive_init_submodules = True, remote = "git@github.com:litespeedtech/lsquic.git", @@ -97,7 +97,7 @@ git_repository( http_archive( name = "libuv", - build_file = "BUILD.libuv.bazel", + build_file = "@//externals:BUILD.libuv.bazel", sha256 = "d50af7e6d72526db137e66fad812421c8a1cae09d146b0ec2bb9a22c5f23ba93", strip_prefix = "libuv-1.47.0", url = "https://github.com/libuv/libuv/archive/refs/tags/v1.47.0.tar.gz", @@ -105,7 +105,7 @@ http_archive( http_archive( name = "zlib", - build_file = "BUILD.zlib.bazel", + build_file = "@//externals:BUILD.zlib.bazel", sha256 = "ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e", strip_prefix = "zlib-1.3", url = "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz", diff --git a/externals/BUILD.bazel b/externals/BUILD.bazel new file mode 100644 index 0000000..6b7a1f6 --- /dev/null +++ b/externals/BUILD.bazel @@ -0,0 +1,6 @@ +""" +This folder includes BUILD.bazel files for external libraries. + +This file exists to mark the //externals folder as a package so its contents +are accessible from WORKSPACE to be used for http_archive build_file's. +""" diff --git a/external/BUILD.boringssl.bazel b/externals/BUILD.boringssl.bazel similarity index 100% rename from external/BUILD.boringssl.bazel rename to externals/BUILD.boringssl.bazel diff --git a/external/BUILD.libuv.bazel b/externals/BUILD.libuv.bazel similarity index 100% rename from external/BUILD.libuv.bazel rename to externals/BUILD.libuv.bazel diff --git a/external/BUILD.lsquic.bazel b/externals/BUILD.lsquic.bazel similarity index 100% rename from external/BUILD.lsquic.bazel rename to externals/BUILD.lsquic.bazel diff --git a/external/BUILD.usockets.bazel b/externals/BUILD.usockets.bazel similarity index 100% rename from external/BUILD.usockets.bazel rename to externals/BUILD.usockets.bazel diff --git a/external/BUILD.uwebsockets.bazel b/externals/BUILD.uwebsockets.bazel similarity index 100% rename from external/BUILD.uwebsockets.bazel rename to externals/BUILD.uwebsockets.bazel diff --git a/external/BUILD.zlib.bazel b/externals/BUILD.zlib.bazel similarity index 100% rename from external/BUILD.zlib.bazel rename to externals/BUILD.zlib.bazel From 4660a529f49c5025d968349ce007b8a6f3e08cb9 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 14:11:58 -0700 Subject: [PATCH 18/56] setup autocomplete from bazel generated files --- BUILD.bazel | 7 +++++++ MODULE.bazel | 9 +++++++++ README.md | 13 ++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 2a92cd3..9ae45a0 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,12 @@ # load("//tools/protobufjs:rules.bzl", "protobufjs_library") +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands", + # targets = ["//server"], +) + filegroup( name = "conway", srcs = [ diff --git a/MODULE.bazel b/MODULE.bazel index 8181221..85bcf38 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,3 +5,12 @@ There's emsdk in WORKSPACE but it's not added to the Bazel Central Registry yet """ bazel_dep(name = "googletest", version = "1.14.0") + +# Hedron's Compile Commands Extractor for Bazel +# https://github.com/hedronvision/bazel-compile-commands-extractor +bazel_dep(name = "hedron_compile_commands", dev_dependency = True) +git_override( + module_name = "hedron_compile_commands", + remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", + commit = "9d438afd4febf622aa05b9a267c09439717e58b9", +) diff --git a/README.md b/README.md index 8311ccc..70e5093 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,14 @@ conway rts cyber project (but still being designed) ## Running Install [bazelisk](https://github.com/bazelbuild/bazelisk) ``` -bazelisk # installs bazel with specific version -bazel query ... # list targets -bazel test ... --config=cl # run all tests (windows, cl.exe) -bazel test ... --config=gcc # run all tests (linux, gcc) -balze build ... # build all targets (config pattern still applies) +bazelisk # installs bazel with specific version +bazel query ... # list targets +bazel test ... # run all tests +bazel build ... # build all targets (config pattern still applies) +bazel run //:refresh_compile_commands # initialize intellisense files +bazel run //server # builds and starts the game server +npm run test # runs vitest (clientside tests) +npm run dev # runs the client ``` # Rough UML Diagram From 1dc886124e7ccc18ffc07f7a2d6694d088fbeef4 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 17:05:54 -0700 Subject: [PATCH 19/56] setup basic server-client communication (only register name and query rooms) --- client/src/App.tsx | 5 ++- client/src/Socket.ts | 32 +++++++++++++++ client/src/SocketLogger.tsx | 18 +++++---- client/src/SocketUtils.tsx | 37 +++++++++++++++++ client/vite.config.mts | 2 - conway_rts/BUILD.bazel | 1 + conway_rts/event_room.proto | 3 ++ server/BUILD.bazel | 31 +++++++++++++- server/client_socket.cc | 15 +++++++ server/client_socket.hh | 27 +++++++++++++ server/main.cpp | 81 ++++++++++++++++++++++++++++++++----- server/socket.proto | 31 ++++++++++++++ tsconfig.json | 1 + vite.config.mts | 4 ++ 14 files changed, 267 insertions(+), 21 deletions(-) create mode 100644 client/src/Socket.ts create mode 100644 client/src/SocketUtils.tsx create mode 100644 server/client_socket.cc create mode 100644 server/client_socket.hh create mode 100644 server/socket.proto diff --git a/client/src/App.tsx b/client/src/App.tsx index cd3e118..21116cd 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import InteractiveLifeGrid from "./InteractiveLifeGrid"; import SocketLogger from "./SocketLogger"; +import SocketUtils from "./SocketUtils"; const server_url = "ws://localhost:3000"; @@ -12,7 +12,7 @@ function App() { const s = new WebSocket(server_url); s.onopen = ({ timeStamp }) => console.log(timeStamp, "opened"); s.onclose = ({ timeStamp }) => console.log(timeStamp, "closed"); - s.addEventListener("message", ({data}) => console.log(data)); + s.addEventListener("message", ({ data }) => console.log(data)); setSocket(s); return () => s?.close(); }, []); @@ -20,6 +20,7 @@ function App() { return ( <>
Hello world
+ {socket && } {socket && } ); diff --git a/client/src/Socket.ts b/client/src/Socket.ts new file mode 100644 index 0000000..b2a68c0 --- /dev/null +++ b/client/src/Socket.ts @@ -0,0 +1,32 @@ +/** + * Methods for interacting with the server. + */ + +import * as Proto from "@/proto"; + +export function register(socket: WebSocket, name: string) { + const event = Proto.server.Event.create(); + event.register = { name }; + socket.send(Proto.server.Event.encode(event).finish()); +} + +export function requestRoomList(socket: WebSocket) { + const event = Proto.server.Event.create(); + event.roomList = {}; + socket.send(Proto.server.Event.encode(event).finish()); +} + +export function createRoom(socket: WebSocket) { + const event = Proto.server.Event.create(); + event.action = {}; + event.action.roomCreate = {}; + socket.send(Proto.server.Event.encode(event).finish()); +} + +export function joinRoom(socket: WebSocket, roomId: number) { + const event = Proto.server.Event.create(); + event.action = {}; + event.action.roomJoin = {}; + event.action.roomJoin.roomId = roomId; + socket.send(Proto.server.Event.encode(event).finish()); +} diff --git a/client/src/SocketLogger.tsx b/client/src/SocketLogger.tsx index 39fbe7f..3c0bc33 100644 --- a/client/src/SocketLogger.tsx +++ b/client/src/SocketLogger.tsx @@ -1,15 +1,19 @@ import { useEffect, useState } from "react"; +import { UseSocket } from "./SocketUtils"; -type SocketLoggerProps = { - socket: WebSocket; -}; - -function SocketLogger({ socket }: SocketLoggerProps) { +function SocketLogger({ socket }: UseSocket) { const [logs, setLogs] = useState([]); useEffect(() => { - const processEvent = (event: MessageEvent) => { - setLogs(logs => [...logs, event.data as string]); + const processEvent = async (event: MessageEvent) => { + console.log(event.data); + if (event.data instanceof Blob) { + const arrayBuffer = await event.data.arrayBuffer(); + const view = new Uint8Array(arrayBuffer); + setLogs((logs) => [...logs, view.join(", ")]); + } else { + setLogs((logs) => [...logs, event.data as string]); + } }; socket.addEventListener("message", processEvent); return () => socket.removeEventListener("message", processEvent); diff --git a/client/src/SocketUtils.tsx b/client/src/SocketUtils.tsx new file mode 100644 index 0000000..89b8c8e --- /dev/null +++ b/client/src/SocketUtils.tsx @@ -0,0 +1,37 @@ +/** + * Debug component for manually sending payloads to server. + */ + +import { createRoom, register, requestRoomList } from "./Socket"; + +export type UseSocket = { + socket: WebSocket; +}; + +function SocketUtils({ socket }: UseSocket) { + const utils = [ + { + label: "register", + callback: () => register(socket, "TEST"), + }, + { + label: "list rooms", + callback: () => requestRoomList(socket), + }, + { + label: "create room", + callback: () => createRoom(socket), + }, + ]; + return ( +
+ {utils.map(({ label, callback }, index) => ( + + ))} +
+ ); +} + +export default SocketUtils; diff --git a/client/vite.config.mts b/client/vite.config.mts index 232833f..009745d 100644 --- a/client/vite.config.mts +++ b/client/vite.config.mts @@ -2,8 +2,6 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { ALIASES } from "../vite.config.mts"; -console.log(ALIASES); - // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], diff --git a/conway_rts/BUILD.bazel b/conway_rts/BUILD.bazel index 3b17929..edc2478 100644 --- a/conway_rts/BUILD.bazel +++ b/conway_rts/BUILD.bazel @@ -50,6 +50,7 @@ cc_library( proto_library( name = "event_proto", srcs = ["event.proto"], + visibility = ["//server:__pkg__"], deps = [ ":event_base_proto", ":event_room_proto", diff --git a/conway_rts/event_room.proto b/conway_rts/event_room.proto index 045b1ce..2d727bd 100644 --- a/conway_rts/event_room.proto +++ b/conway_rts/event_room.proto @@ -10,7 +10,10 @@ message Create { } message Join { + /// which room to join int32 room_id = 1; + + /// client that is joining the room (set by server) conway.Client client = 2; } diff --git a/server/BUILD.bazel b/server/BUILD.bazel index a81c7b5..0a2fec1 100644 --- a/server/BUILD.bazel +++ b/server/BUILD.bazel @@ -4,5 +4,34 @@ cc_binary( name = "server", srcs = ["main.cpp"], copts = CC_OPTS, - deps = ["@uwebsockets"], + deps = [ + ":client_socket", + ":socket_cc_proto", + "@uwebsockets", + ], +) + +cc_library( + name = "client_socket", + srcs = ["client_socket.cc"], + hdrs = ["client_socket.hh"], + deps = [ + "//conway_rts:client", + "//conway_rts:room", + "//conway_rts:team", + ], +) + +proto_library( + name = "socket_proto", + srcs = ["socket.proto"], + deps = [ + "//conway_rts:event_proto", + "//conway_rts:room_proto", + ], +) + +cc_proto_library( + name = "socket_cc_proto", + deps = [":socket_proto"], ) diff --git a/server/client_socket.cc b/server/client_socket.cc new file mode 100644 index 0000000..4f8e427 --- /dev/null +++ b/server/client_socket.cc @@ -0,0 +1,15 @@ +#include "server/client_socket.hh" + +#include + +int ClientSocket::instance_count = 1; + +void ClientSocket::Initialize(const std::string &name) { + if (name.empty()) throw std::invalid_argument("name should not be an empty string"); + client = Client(ClientSocket::instance_count, name); + ++ClientSocket::instance_count; +} + +bool ClientSocket::IsInitialized() const { + return !client.name().empty(); +} diff --git a/server/client_socket.hh b/server/client_socket.hh new file mode 100644 index 0000000..a27f2dd --- /dev/null +++ b/server/client_socket.hh @@ -0,0 +1,27 @@ +#ifndef SERVER_CLIENTSOCKET_HH_ +#define SERVER_CLIENTSOCKET_HH_ + +#include + +#include "conway_rts/client.hh" +#include "conway_rts/room.hh" +#include "conway_rts/team.hh" + +/// @brief ws->getUserData returns one of these, used in the uwebsocket server +struct ClientSocket { + int counter = 0; // counts how many messages a client sent (for testing) + Client client; // client object, use to transform inputs into events + Team *team = nullptr; // team this client belongs to; push events into here + Room *room = nullptr; // room this client is in; used to notify client about the room + + /// used to determine next id (counter) + static int instance_count; + + /// initializes the client with a name and unique id (name should be non empty) + void Initialize(const std::string &name); + + /// returns whether this was initialized or not (non empty name) + bool IsInitialized() const; +}; + +#endif // SERVER_CLIENTSOCKET_HH_ diff --git a/server/main.cpp b/server/main.cpp index ec9bae3..d24bb73 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,19 +1,25 @@ #include "App.h" +#include + +#include "conway_rts/room.hh" +#include "server/client_socket.hh" +#include "server/socket.pb.h" +#include "utility/vector2.hh" + /* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */ constexpr int PORT = 3000; constexpr bool using_SSL = true; -/* ws->getUserData returns one of these */ -struct PerSocketData { - int counter = 0; // counts how many messages a client sent (for testing) -}; - -// typedef uWS::WebSocket WebSocket; -// this type is really buggy with vscode intellisense +typedef ClientSocket PerSocketData; +typedef uWS::WebSocket WebSocket; +// Note: make sure you're using C++20 for VScode intellisense int main() { + std::map rooms; + rooms[-1] = Room("kitchen", Vector2(100, 100)); + uWS::SSLApp({ .key_file_name = "misc/key.pem", .cert_file_name = "misc/cert.pem", @@ -35,12 +41,69 @@ int main() { PerSocketData& user_data = *(ws->getUserData()); std::cout << "open" << user_data.counter << std::endl; }, - .message = [](auto *ws, std::string_view message, uWS::OpCode opCode) { + .message = [&](WebSocket *ws, std::string_view message, uWS::OpCode opCode) { PerSocketData& user_data = *(ws->getUserData()); ++(user_data.counter); // echo message back - ws->send(message, opCode, message.length() > 16 * 1024); + // ws->send(message, opCode, message.length() > 16 * 1024); + + server::Event event; + if (event.ParseFromArray(&*message.begin(), message.size())) { + std::cout << "[rx] event\n"; + switch (event.event_case()) { + case server::Event::EventCase::EVENT_NOT_SET: + std::cout << "unset event??"; + break; + case server::Event::EventCase::kRegister: { + if (event.has_register_() && !user_data.IsInitialized()) { + user_data.Initialize(event.register_().name()); + event.mutable_register_()->set_acknowledged(true); + std::string ack; + event.SerializeToString(&ack); + ws->send(ack); + } + break; + } + case server::Event::EventCase::kRoomList: { + event.mutable_room_list()->clear_rooms(); + for (const auto& [room_key, room] : rooms) { + conway::RoomListing &room_listing = *event.mutable_room_list()->add_rooms(); + room_listing.set_id(room_key); + room_listing.set_client_count(room.clients().size()); + room_listing.set_name(room.name()); + } + std::string ack; + event.SerializeToString(&ack); + ws->send(ack); + break; + } + case server::Event::EventCase::kAction: { + switch (event.action().event_case()) { + case conway::Event::EventCase::EVENT_NOT_SET: break; + case conway::Event::EventCase::kRoomCreate: { + break; + } + case conway::Event::EventCase::kRoomJoin: { + break; + } + case conway::Event::EventCase::kRoomLeave: { + break; + } + case conway::Event::EventCase::kBaseInitialize: break; + case conway::Event::EventCase::kBaseBuild: break; + default: + std::cout << "invalid action case??"; + } + break; + } + default: + std::cout << "event undefined??"; + } + std::cout << std::endl; + } else { + std::cout << "[rx] (unknown) " << message << std::endl; + } }, .drain = [](auto */*ws*/) { /* Check ws->getBufferedAmount() here */ diff --git a/server/socket.proto b/server/socket.proto new file mode 100644 index 0000000..81f24f6 --- /dev/null +++ b/server/socket.proto @@ -0,0 +1,31 @@ +// Server-client communication outside of the game (room coordination) +syntax = "proto3"; + +package server; + +import "conway_rts/room.proto"; +import "conway_rts/event.proto"; + +/// Server-client data packet +message Event { + oneof event { + Register register = 1; + RoomList room_list = 2; + conway.Event action = 3; + } +} + +/// Event to register client name +message Register { + /// name client wants to register + string name = 1; + + /// whether the server accepted client name registration + bool acknowledged = 2; +} + +/// Event to request the room list +message RoomList { + /// rooms that can be joined + repeated conway.RoomListing rooms = 1; +} diff --git a/tsconfig.json b/tsconfig.json index dfb3f92..970382e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "baseUrl": ".", "paths": { + "@/proto": ["build/bundle_proto"], "@/lib-wasm": ["bazel-bin/client_wasm/conway_lib_wasm/conway_lib_wasm_embind"], "@/lib": ["client_wasm/conway"] }, diff --git a/vite.config.mts b/vite.config.mts index c26387a..b365aed 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -11,6 +11,10 @@ export const ALIASES = [ find: "@/lib", replacement: resolve(__dirname, "client_wasm/conway"), }, + { + find: "@/proto", + replacement: resolve(__dirname, "build/bundle_proto"), + } ]; // https://vitejs.dev/config/ From fa9592351976542e37be6c7aff704f5e0087efa1 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sat, 30 Dec 2023 19:11:22 -0700 Subject: [PATCH 20/56] display blob bytes as hex --- client/src/SocketLogger.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/SocketLogger.tsx b/client/src/SocketLogger.tsx index 3c0bc33..ab9a742 100644 --- a/client/src/SocketLogger.tsx +++ b/client/src/SocketLogger.tsx @@ -10,7 +10,10 @@ function SocketLogger({ socket }: UseSocket) { if (event.data instanceof Blob) { const arrayBuffer = await event.data.arrayBuffer(); const view = new Uint8Array(arrayBuffer); - setLogs((logs) => [...logs, view.join(", ")]); + const log = [...view] + .map((x: number) => x.toString(16).padStart(2, "0")) + .join(" "); + setLogs((logs) => [...logs, log]); } else { setLogs((logs) => [...logs, event.data as string]); } From 47add26d622af6ed5aa10eb520317e62a0f898b5 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Mon, 1 Jan 2024 15:28:58 -0700 Subject: [PATCH 21/56] implement adding clients into room --- conway_rts/room.cc | 118 ++++++++++++++++++++++++++++++---------- conway_rts/room.hh | 98 ++++++++++++++++++++------------- conway_rts/room_test.cc | 24 +++++++- conway_rts/team.hh | 1 - 4 files changed, 172 insertions(+), 69 deletions(-) diff --git a/conway_rts/room.cc b/conway_rts/room.cc index e0111be..21e6a08 100644 --- a/conway_rts/room.cc +++ b/conway_rts/room.cc @@ -6,15 +6,11 @@ // other includes -Room::Room(): Room::Room("a room") -{} +Room::Room() : Room::Room("a room") {} -Room::Room(const std::string& name): - Room::Room(name, Vector2(50, 50)) -{} +Room::Room(const std::string &name) : Room::Room(name, Vector2(50, 50)) {} -Room::Room(const std::string& name, const Vector2& dimensions): name_(name) -{ +Room::Room(const std::string &name, const Vector2 &dimensions) : name_(name) { grid_ = LifeGrid(dimensions); } @@ -31,7 +27,7 @@ Room::Room(const std::string& name, const Vector2& dimensions): name_(name) // bool Room::operator==(const Room& other) const { // if(this == &other) return true; - + // return true; // } @@ -39,32 +35,96 @@ Room::Room(const std::string& name, const Vector2& dimensions): name_(name) // return !(*this == other); // } -void Room::Initialize() { - grid_ = LifeGrid(grid().dimensions()); - +void Room::Initialize() { grid_ = LifeGrid(grid().dimensions()); } + +void Room::SetName(const std::string &new_name) { name_ = new_name; } + +bool Room::AddClient(const Client &client) { + if (clients_.count(client.id())) { + return false; + } + clients_[client.id()] = client; + return true; } -void Room::SetName(const std::string& new_name) { - name_ = new_name; +int Room::AddTeam(const std::vector &team_members) { + ++team_instance_count; + Team new_team(team_instance_count); + if (teams_.count(new_team.id())) { + throw std::runtime_error("team id exists"); + } + + const int new_team_id = new_team.id(); + teams_[new_team.id()] = std::move(new_team); + + // Add players using Room::AddToTeam which automatically + // removes players who are already in a team. + for (const Client &team_member : team_members) { + AddToTeam(new_team_id, team_member); + } + return new_team_id; +} + +bool Room::AddToTeam(int team_id, const Client &client) { + if (teams_.count(team_id) == 0) { + return false; + } + const bool added_successfully = teams_[team_id].AddMember(client); + if (added_successfully) { + AddClient(client); + /// TODO: Remove from other teams. + } + return added_successfully; } -void Room::LoadStructures(const std::vector& new_structures) { +bool Room::RemoveFromTeam(int team_id, const Client &client) { + if (teams_.count(team_id) == 0) { + return false; + } + throw std::exception("not implemented, depends on Team::RemoveMember but " + "this doesn't exist yet"); + // return teams_[team_id].RemoveMember(client); +} + +bool Room::RemoveClient(const Client &client) { + if (!clients_.count(client.id())) { + return false; + } + clients_.erase(client.id()); + return true; +} + +Team *Room::GetTeam(int team_id) { + if (teams_.count(team_id)) { + return &teams_.at(team_id); + } + return nullptr; +} + +void Room::LoadStructures( + const std::vector &new_structures) { structure_lookup_ = new_structures; } -void Room::Tick(EventQueue& next_queue) { +void Room::Tick(EventQueue &next_queue) { // this processes events that have been filtered by Team::Tick while (!event_queue().empty() && event_queue().front().time() <= time()) { const Event event = std::move(event_queue_.front()); event_queue_.pop_front(); - - if(event.data().size() != 4) throw std::logic_error("invalid event data"); + if (event.data().size() != 4) { + throw std::logic_error("invalid event data"); + } + const int building_id = event.data()[3]; - if(building_id < 0 || building_id >= event.data().size()) throw std::logic_error("invalid event building_id"); - - const StructureProperties& props = structure_lookup()[building_id]; + if (building_id < 0 || building_id >= event.data().size()) { + throw std::logic_error("invalid event building_id"); + } + + const StructureProperties &props = structure_lookup()[building_id]; const Vector2 position(event.data()[1], event.data()[2]); - const int refund = props.grid().dimensions().x() * props.grid().dimensions().y() - grid().Compare(props.grid(), position); + const int refund = + props.grid().dimensions().x() * props.grid().dimensions().y() - + grid().Compare(props.grid(), position); grid_.Load(props.grid(), position); next_queue.push_back(event); @@ -91,25 +151,27 @@ void Room::Tick(EventQueue& next_queue) { // 1 member-1 // 2 member-2 // ``` -std::ostream& operator<<(std::ostream& out, const Room& rhs) { +std::ostream &operator<<(std::ostream &out, const Room &rhs) { // TODO: make no trailing whitespace - out << rhs.name() << "\n" << rhs.grid() << "\n" - << rhs.teams().size() << " " << rhs.clients().size() << "\n"; - for(const auto& team : rhs.teams()) out << team << "\n"; + out << rhs.name() << "\n" + << rhs.grid() << "\n" + << rhs.teams().size() << " " << rhs.clients().size() << "\n"; + for (const std::pair &team_entry : rhs.teams()) + out << team_entry.second << "\n"; return out; } -std::istream& operator>>(std::istream& in, Room& rhs) { +std::istream &operator>>(std::istream &in, Room &rhs) { throw "not implemented"; return in; } -bool Room::SerializeToOstream(std::ostream& out) const { +bool Room::SerializeToOstream(std::ostream &out) const { out << *this; return true; } -bool Room::ParseFromIstream(std::istream& in) { +bool Room::ParseFromIstream(std::istream &in) { in >> *this; return true; } \ No newline at end of file diff --git a/conway_rts/room.hh b/conway_rts/room.hh index 9aafdf6..7504d53 100644 --- a/conway_rts/room.hh +++ b/conway_rts/room.hh @@ -5,71 +5,93 @@ #include #include -#include "conway_rts/client.hh" -#include "conway_rts/team.hh" #include "conway/structure_properties.hh" -#include "conway_rts/event.hh" +#include "conway_rts/client.hh" #include "conway_rts/event_queue.hh" +#include "conway_rts/team.hh" /// @brief game instance class Room { - public: +public: Room(); - Room(const std::string& name); - Room(const std::string& name, const Vector2& dimensions); + Room(const std::string &name); + Room(const std::string &name, const Vector2 &dimensions); /// @brief resets the grid and team statuses void Initialize(); /// @brief update room name - /// @param new_name - void SetName(const std::string& new_name); + /// @param new_name + void SetName(const std::string &new_name); + + /// Adds a client to the room so that they are tracked, if not + /// in a team, they are implicitly a spectator. + /// Returns true if successful. + bool AddClient(const Client &client); + + /// Add a set of clients (team members) as a team to the room. + /// (outside *-> player) + /// If a player is already in the room or in a team, they will + /// be removed from that status. + /// Returns the team_id. + int AddTeam(const std::vector &team_members); + + /// Adds a client to a team specified by team id. (spectator -> player) + /// If a player isn't in the room, they'll be added to the room. + /// If a player is already in a team, they'll be removed from that team. + /// Returns true if successful + bool AddToTeam(int team_id, const Client &client); + + /// Removes a client from a team specified by team id. (player -> spectator) + /// Returns true if successful + bool RemoveFromTeam(int team_id, const Client &client); + + /// Removes the client from the room; removes from the team if they are + /// playing. (spectator -> outside) Returns true if successful + bool RemoveClient(const Client &client); + + /// Returns a pointer to a team (or nullptr if it doesn't exist) + /// Used to modify the team input queue. + /// TODO: pass EventQueue around instead of the entire team object? + Team *GetTeam(int team_id); /// @brief updates structure_lookup vector - /// @param new_structures - void LoadStructures(const std::vector& new_structures); + /// @param new_structures + void LoadStructures(const std::vector &new_structures); - /// @brief advances one tick (increments `_time`), pushes accepted events into `event_queue` to be broadcasted - void Tick(EventQueue& next_queue); + /// @brief advances one tick (increments `_time`), pushes accepted events into + /// `event_queue` to be broadcasted + void Tick(EventQueue &next_queue); - friend std::ostream& operator<<(std::ostream& out, const Room& rhs); - friend std::istream& operator>>(std::istream& in, Room& rhs); - bool SerializeToOstream(std::ostream& out) const; - bool ParseFromIstream(std::istream& in); + friend std::ostream &operator<<(std::ostream &out, const Room &rhs); + friend std::istream &operator>>(std::istream &in, Room &rhs); + bool SerializeToOstream(std::ostream &out) const; + bool ParseFromIstream(std::istream &in); // accessor/mutators - const std::string& name() const { - return name_; - } - const std::vector& structure_lookup() const { + const std::string &name() const { return name_; } + const std::vector &structure_lookup() const { return structure_lookup_; } - int time() const { - return time_; - } - const std::vector& clients() const { - return clients_; - } - const std::vector& teams() const { - return teams_; - } - const LifeGrid& grid() const { - return grid_; - } - const EventQueue& event_queue() const { - return event_queue_; - } + int time() const { return time_; } + const std::map &clients() const { return clients_; } + const std::map &teams() const { return teams_; } + const LifeGrid &grid() const { return grid_; } + const EventQueue &event_queue() const { return event_queue_; } - private: +private: std::string name_; // TODO: room size limits std::vector structure_lookup_; int time_ = 0; - std::vector clients_; - std::vector teams_; + std::map clients_; + std::map teams_; LifeGrid grid_; EventQueue event_queue_; + + /// used to assign a unique team id + int team_instance_count = 0; }; #endif // CONWAY_INCLUDE_ROOM_HPP \ No newline at end of file diff --git a/conway_rts/room_test.cc b/conway_rts/room_test.cc index a001cc3..4a22a1d 100644 --- a/conway_rts/room_test.cc +++ b/conway_rts/room_test.cc @@ -8,14 +8,34 @@ TEST(Room, initialization) { Room room("kitchen"); EXPECT_EQ(room.name(), "kitchen"); + EXPECT_TRUE(room.clients().empty()) + << "Room client list should initially be empty."; + EXPECT_TRUE(room.teams().empty()) + << "Room team list should initially be empty."; // std::cout << room; } +TEST(Room, room_join) { + Room room; + Client client1(1, "c1"); + Client client2(2, "c2"); + Client client3(3, "c3"); + Client client4(4, "c4"); + + room.AddClient(client1); + EXPECT_EQ(room.clients().count(1), 1) << "client 1 should be added"; + const int team_id_client1 = room.AddTeam({client1}); + const Team *team_client1_ptr = room.GetTeam(team_id_client1); + EXPECT_NE(team_client1_ptr, nullptr) << "a team should be created for client1"; + + room.AddTeam({client2, client3}); + EXPECT_EQ(room.clients().count(2), 1) << "client 2 should be added after adding team {2,3}"; + EXPECT_EQ(room.clients().count(3), 1) << "client 3 should be added after adding team {2,3}"; +} + TEST(Room, updating_grid_from_events) { EventQueue events; Client client(1, "c1"); Team team(9999, 1000); team.AddMember(client); - - } \ No newline at end of file diff --git a/conway_rts/team.hh b/conway_rts/team.hh index 6793f73..e88cee3 100644 --- a/conway_rts/team.hh +++ b/conway_rts/team.hh @@ -2,7 +2,6 @@ #define CONWAY_INCLUDE_TEAM_HPP #include -#include #include #include From 3ab8937175108da7816e28b458c9e49eb9e426a5 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Mon, 1 Jan 2024 17:13:10 -0700 Subject: [PATCH 22/56] implement Vector2::CopyToProtobuf() method this is useful due to nested protobuf messages --- utility/BUILD.bazel | 1 + utility/vector2.cc | 8 ++++++-- utility/vector2.hh | 4 ++++ utility/vector2_test.cc | 9 +++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/utility/BUILD.bazel b/utility/BUILD.bazel index e5cc218..c55f298 100644 --- a/utility/BUILD.bazel +++ b/utility/BUILD.bazel @@ -19,6 +19,7 @@ cc_library( "//client_wasm:__pkg__", "//conway:__pkg__", ], + deps = [":vector2_cc_proto"], ) cc_test( diff --git a/utility/vector2.cc b/utility/vector2.cc index feafe4c..a00219b 100644 --- a/utility/vector2.cc +++ b/utility/vector2.cc @@ -1,7 +1,5 @@ #include "vector2.hh" -#include - Vector2::Vector2() : Vector2(0, 0) {} Vector2::Vector2(int x, int y) : x_(x), y_(y) {} @@ -52,6 +50,12 @@ bool Vector2::operator!=(const Vector2& rhs) const { return !(*this == rhs); } +conway::Vector2& Vector2::CopyToProtobuf(conway::Vector2& pb) const { + pb.set_x(x()); + pb.set_y(y()); + return pb; +} + // ### Format // ``` // ([x_] [y_]) diff --git a/utility/vector2.hh b/utility/vector2.hh index ed65b6d..65bfa49 100644 --- a/utility/vector2.hh +++ b/utility/vector2.hh @@ -4,6 +4,8 @@ #include #include +#include "utility/vector2.pb.h" + /// 2D vector for positional coordinates (immutable) class Vector2 { public: @@ -32,6 +34,8 @@ class Vector2 { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + conway::Vector2& CopyToProtobuf(conway::Vector2& pb) const; + // structured bindings -- https://devblogs.microsoft.com/oldnewthing/20201015-00/?p=104369 template std::tuple_element_t get() const { diff --git a/utility/vector2_test.cc b/utility/vector2_test.cc index 7e2569b..5528c12 100644 --- a/utility/vector2_test.cc +++ b/utility/vector2_test.cc @@ -97,4 +97,13 @@ TEST(Vector2, cc_proto) { EXPECT_EQ(protobuf.y(), decoded.y()) << "protobuf vector2 y should match source after being encoded and decoded"; free(buffer); +} + +TEST(Vector2, copy_into_proto) { + conway::Vector2 protobuf; + Vector2 vector(1234, 5678); + vector.CopyToProtobuf(protobuf); + + EXPECT_EQ(protobuf.x(), vector.x()) << "x value should've been copied to protobuf"; + EXPECT_EQ(protobuf.y(), vector.y()) << "y value should've been copied to protobuf"; } \ No newline at end of file From 268602f15be8159422acb73a50a6f0792e94a717 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Mon, 1 Jan 2024 21:06:26 -0700 Subject: [PATCH 23/56] bump to bazel 7.0.0 fixes zlib pre-1.3 K&R-related code errors, would make azel build ... fail when it tried to build the zlib component of @bazel-tools --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index f9da12e..4122521 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.3.2 \ No newline at end of file +7.0.0 \ No newline at end of file From 613e21f718e2d68b0ada72ddc91f481d3aba6a43 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Mon, 1 Jan 2024 21:07:37 -0700 Subject: [PATCH 24/56] migrate zlib into MODULE.bazel apparently this is in the module registry which is cool --- MODULE.bazel | 1 + WORKSPACE | 8 -------- externals/BUILD.zlib.bazel | 13 ------------- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 externals/BUILD.zlib.bazel diff --git a/MODULE.bazel b/MODULE.bazel index 85bcf38..a811ec7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,6 +5,7 @@ There's emsdk in WORKSPACE but it's not added to the Bazel Central Registry yet """ bazel_dep(name = "googletest", version = "1.14.0") +bazel_dep(name = "zlib", version = "1.3") # Hedron's Compile Commands Extractor for Bazel # https://github.com/hedronvision/bazel-compile-commands-extractor diff --git a/WORKSPACE b/WORKSPACE index bf52c2f..dfd57be 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -102,11 +102,3 @@ http_archive( strip_prefix = "libuv-1.47.0", url = "https://github.com/libuv/libuv/archive/refs/tags/v1.47.0.tar.gz", ) - -http_archive( - name = "zlib", - build_file = "@//externals:BUILD.zlib.bazel", - sha256 = "ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e", - strip_prefix = "zlib-1.3", - url = "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz", -) diff --git a/externals/BUILD.zlib.bazel b/externals/BUILD.zlib.bazel deleted file mode 100644 index cb0f03f..0000000 --- a/externals/BUILD.zlib.bazel +++ /dev/null @@ -1,13 +0,0 @@ -CC_OPTS = select({ - "@bazel_tools//src/conditions:windows": ["/std:c++17"], - "//conditions:default": ["--std=c++17"], -}) - -cc_library( - name = "zlib", - srcs = glob(["*.c"]), - hdrs = glob(["*.h"]), - copts = CC_OPTS, - includes = ["."], - visibility = ["//visibility:public"], -) From 10e01892fef5a64ec7fe7555e2d01c843ef46df7 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 15:21:10 -0700 Subject: [PATCH 25/56] implement ::CopyToProtobuf() methods for //conway/... targets --- conway/BUILD.bazel | 3 +++ conway/life_grid.cc | 13 ++++++++++++- conway/life_grid.hh | 3 +++ conway/life_grid_test.cc | 20 ++++++++++++++++++++ conway/structure.cc | 7 ++++++- conway/structure.hh | 6 +++++- conway/structure_properties.cc | 17 ++++++++++++++++- conway/structure_properties.hh | 4 +++- 8 files changed, 68 insertions(+), 5 deletions(-) diff --git a/conway/BUILD.bazel b/conway/BUILD.bazel index 5a945de..79d8b00 100644 --- a/conway/BUILD.bazel +++ b/conway/BUILD.bazel @@ -17,6 +17,7 @@ cc_library( "//conway_rts:__pkg__", ], deps = [ + ":life_grid_cc_proto", ":life_state", "//utility:vector2", ], @@ -55,6 +56,7 @@ cc_library( deps = [ ":life_grid", ":structure_properties", + ":structure_cc_proto", "//utility:vector2", ], ) @@ -92,6 +94,7 @@ cc_library( deps = [ ":life_grid", ":life_state", + ":structure_properties_cc_proto", "//utility:vector2", ], ) diff --git a/conway/life_grid.cc b/conway/life_grid.cc index 9956d6b..c732092 100644 --- a/conway/life_grid.cc +++ b/conway/life_grid.cc @@ -165,4 +165,15 @@ bool LifeGrid::SerializeToOstream(std::ostream& out) const { bool LifeGrid::ParseFromIstream(std::istream& in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::LifeGrid& LifeGrid::CopyToProtobuf(conway::LifeGrid& pb) const { + dimensions().CopyToProtobuf(*pb.mutable_dimensions()); + pb.clear_flat_grid(); + for (int i = 0; i < dimensions().y(); ++i) { + for (int j = 0; j < dimensions().x(); ++j) { + pb.add_flat_grid(IsCellAlive(Vector2(j, i))); + } + } + return pb; +} diff --git a/conway/life_grid.hh b/conway/life_grid.hh index fc0f874..a1266db 100644 --- a/conway/life_grid.hh +++ b/conway/life_grid.hh @@ -4,6 +4,7 @@ #include #include "conway/life_state.hh" +#include "conway/life_grid.pb.h" #include "utility/vector2.hh" // TODO: write iterators (they should exist?) @@ -73,6 +74,8 @@ class LifeGrid { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + conway::LifeGrid& CopyToProtobuf(conway::LifeGrid& pb) const; + // accessor/mutators Vector2 dimensions() const { return dimensions_; diff --git a/conway/life_grid_test.cc b/conway/life_grid_test.cc index 9ef0d44..7fecde0 100644 --- a/conway/life_grid_test.cc +++ b/conway/life_grid_test.cc @@ -103,4 +103,24 @@ TEST(LifeGrid, nextGeneration){ grid.Tick(); offset = offset + Vector2(1, 1); } +} + +TEST(LifeGrid, copy_into_proto) { + conway::LifeGrid pb; + LifeGrid glider; + + // .#. + // ..# + // ### + // ... + std::istringstream in("4 3 .#. ..# ### ..."); + in >> glider; + + glider.CopyToProtobuf(pb); + EXPECT_EQ(pb.dimensions().x(), 3); + EXPECT_EQ(pb.dimensions().y(), 4); + const std::vector expected { 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0 }; + for (int i = 0; i < 12; ++i) { + EXPECT_EQ(pb.flat_grid().at(i), expected[i]) << "checking i=" << i; + } } \ No newline at end of file diff --git a/conway/structure.cc b/conway/structure.cc index 3f051a5..d6d3601 100644 --- a/conway/structure.cc +++ b/conway/structure.cc @@ -97,4 +97,9 @@ bool Structure::SerializeToOstream(std::ostream& out) const { bool Structure::ParseFromIstream(std::istream& in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::Structure& Structure::CopyToProtobuf(conway::Structure &pb) const { + position().CopyToProtobuf(*pb.mutable_position()); + return pb; +} diff --git a/conway/structure.hh b/conway/structure.hh index 26f97dc..2622ec2 100644 --- a/conway/structure.hh +++ b/conway/structure.hh @@ -1,11 +1,11 @@ #ifndef CONWAY_INCLUDE_STRUCTURE_HPP #define CONWAY_INCLUDE_STRUCTURE_HPP -#include #include #include #include "conway/structure_properties.hh" +#include "conway/structure.pb.h" #include "utility/vector2.hh" /// @brief used for sorting, but when you still want to modify non-sort properties @@ -40,6 +40,10 @@ class Structure { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + // Note: only copies position! Internal structure doesn't know about what its structure_id is. + conway::Structure& CopyToProtobuf(conway::Structure &pb) const; + + // accessors/mutators bool active() const { return active_; } diff --git a/conway/structure_properties.cc b/conway/structure_properties.cc index bf8ca4c..d6e538c 100644 --- a/conway/structure_properties.cc +++ b/conway/structure_properties.cc @@ -1,5 +1,6 @@ #include "conway/structure_properties.hh" +#include #include StructureProperties::StructureProperties(): @@ -150,4 +151,18 @@ bool StructureProperties::SerializeToOstream(std::ostream& out) const { bool StructureProperties::ParseFromIstream(std::istream& in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::StructureProperties& StructureProperties::CopyToProtobuf(conway::StructureProperties &pb) const { + pb.set_activation_cost(activation_cost()); + pb.set_build_area(build_area()); + pb.set_income(income()); + pb.set_name(name()); + grid().CopyToProtobuf(*pb.mutable_grid()); + pb.clear_checks(); + for (const Vector2 &check : checks()) { + conway::Vector2 *check_pb = pb.add_checks(); + check.CopyToProtobuf(*check_pb); + } + return pb; +} diff --git a/conway/structure_properties.hh b/conway/structure_properties.hh index 8c36f67..c71cda0 100644 --- a/conway/structure_properties.hh +++ b/conway/structure_properties.hh @@ -1,11 +1,11 @@ #ifndef CONWAY_INCLUDE_STRUCTUREPROPERTIES_HPP #define CONWAY_INCLUDE_STRUCTUREPROPERTIES_HPP -#include #include #include #include "conway/life_grid.hh" +#include "conway/structure_properties.pb.h" #include "utility/vector2.hh" class StructureProperties { @@ -41,6 +41,8 @@ class StructureProperties { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + conway::StructureProperties& CopyToProtobuf(conway::StructureProperties &pb) const; + // accessors/mutators const std::string& name() const { return name_; From ef9449732da52e18330df46450700722be65678e Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 15:25:37 -0700 Subject: [PATCH 26/56] update //conway_rts:client dependencies --- conway_rts/BUILD.bazel | 2 +- conway_rts/array_buffer.hh | 2 ++ conway_rts/client.hh | 5 ++--- utility/BUILD.bazel | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/conway_rts/BUILD.bazel b/conway_rts/BUILD.bazel index edc2478..eae4e41 100644 --- a/conway_rts/BUILD.bazel +++ b/conway_rts/BUILD.bazel @@ -16,7 +16,7 @@ cc_library( deps = [ ":array_buffer", ":event", - "//conway:structure", + "//utility:vector2", ], ) diff --git a/conway_rts/array_buffer.hh b/conway_rts/array_buffer.hh index d9a2fd0..41316d3 100644 --- a/conway_rts/array_buffer.hh +++ b/conway_rts/array_buffer.hh @@ -1,6 +1,8 @@ #ifndef CONWAY_INCLUDE_ARRAYBUFFER_HPP #define CONWAY_INCLUDE_ARRAYBUFFER_HPP +#include + /// TODO: turn this into std::vector or maybe char[] to store data /// when there are network issues in the future typedef std::vector ArrayBuffer; diff --git a/conway_rts/client.hh b/conway_rts/client.hh index fd98a5f..42755ea 100644 --- a/conway_rts/client.hh +++ b/conway_rts/client.hh @@ -1,12 +1,11 @@ #ifndef CONWAY_INCLUDE_CLIENT_HPP #define CONWAY_INCLUDE_CLIENT_HPP -#include #include -#include "conway/structure.hh" -#include "conway_rts/array_buffer.hh" +// #include "conway_rts/client.pb.h" #include "conway_rts/event.hh" +#include "utility/vector2.hh" /// @brief a single client and the actions they want to do class Client { diff --git a/utility/BUILD.bazel b/utility/BUILD.bazel index c55f298..502eae8 100644 --- a/utility/BUILD.bazel +++ b/utility/BUILD.bazel @@ -18,6 +18,7 @@ cc_library( visibility = [ "//client_wasm:__pkg__", "//conway:__pkg__", + "//conway_rts:__pkg__", ], deps = [":vector2_cc_proto"], ) From ad8275b33f2c4ad2bb627ca0df596dde15cb2339 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 15:51:36 -0700 Subject: [PATCH 27/56] implement partial ::CopyToProtobuf() method support for //conway_rts/... targets --- conway_rts/BUILD.bazel | 3 +++ conway_rts/client.cc | 8 +++++++- conway_rts/client.hh | 5 ++++- conway_rts/client_test.cc | 12 +++++++++--- conway_rts/room.cc | 26 +++++++++++++++++++++++++- conway_rts/room.hh | 4 ++++ conway_rts/team.cc | 19 ++++++++++++++++++- conway_rts/team.hh | 3 +++ 8 files changed, 73 insertions(+), 7 deletions(-) diff --git a/conway_rts/BUILD.bazel b/conway_rts/BUILD.bazel index eae4e41..855dc21 100644 --- a/conway_rts/BUILD.bazel +++ b/conway_rts/BUILD.bazel @@ -15,6 +15,7 @@ cc_library( visibility = ["//server:__pkg__"], deps = [ ":array_buffer", + ":client_cc_proto", ":event", "//utility:vector2", ], @@ -87,6 +88,7 @@ cc_library( ":client", ":event", ":event_queue", + ":room_cc_proto", ":team", "//conway:structure_properties", ], @@ -128,6 +130,7 @@ cc_library( ":client", ":event", ":event_queue", + ":team_cc_proto", "//conway:structure", ], ) diff --git a/conway_rts/client.cc b/conway_rts/client.cc index 46b37cb..f970560 100644 --- a/conway_rts/client.cc +++ b/conway_rts/client.cc @@ -67,4 +67,10 @@ bool Client::SerializeToOstream(std::ostream& out) const { bool Client::ParseFromIstream(std::istream& in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::Client& Client::CopyToProtobuf(conway::Client &pb) const { + pb.set_name(name()); + pb.set_id(id()); + return pb; +} diff --git a/conway_rts/client.hh b/conway_rts/client.hh index 42755ea..669396b 100644 --- a/conway_rts/client.hh +++ b/conway_rts/client.hh @@ -3,7 +3,7 @@ #include -// #include "conway_rts/client.pb.h" +#include "conway_rts/client.pb.h" #include "conway_rts/event.hh" #include "utility/vector2.hh" @@ -32,6 +32,9 @@ class Client { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + conway::Client& CopyToProtobuf(conway::Client &pb) const; + + // accessors/mutators const std::string& name() const { return name_; } diff --git a/conway_rts/client_test.cc b/conway_rts/client_test.cc index 372bbf9..561584c 100644 --- a/conway_rts/client_test.cc +++ b/conway_rts/client_test.cc @@ -1,7 +1,5 @@ #include "conway_rts/client.hh" -#include - #include "gtest/gtest.h" TEST(Client, initialization) { @@ -34,4 +32,12 @@ TEST(Client, CreateBuildEvent_format) { ArrayBuffer expected_data {10000, 1234, 5678, building_id}; Event expected_event(time, 0, expected_data); EXPECT_EQ(result_event, expected_event); -} \ No newline at end of file +} + +TEST(Client, copy_into_proto) { + conway::Client pb; + Client client(1234, "hello world"); + client.CopyToProtobuf(pb); + EXPECT_EQ(pb.id(), client.id()); + EXPECT_EQ(pb.name(), client.name()); +} diff --git a/conway_rts/room.cc b/conway_rts/room.cc index 21e6a08..9619afa 100644 --- a/conway_rts/room.cc +++ b/conway_rts/room.cc @@ -174,4 +174,28 @@ bool Room::SerializeToOstream(std::ostream &out) const { bool Room::ParseFromIstream(std::istream &in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::Room& Room::CopyToProtobuf(conway::Room &pb, int id) const { + pb.set_id(id); + pb.set_name(name()); + grid().dimensions().CopyToProtobuf(*pb.mutable_dimensions()); + pb.clear_clients(); + for (const std::pair& client_entry : clients()) { + conway::Client *client_pb = pb.add_clients(); + client_entry.second.CopyToProtobuf(*client_pb); + } + pb.clear_teams(); + for (const std::pair& team_entry : teams()) { + conway::Team *team_pb = pb.add_teams(); + team_entry.second.CopyToProtobuf(*team_pb); + } + return pb; +} + +conway::RoomListing& Room::CopyToProtobuf(conway::RoomListing &pb, int id) const { + pb.set_id(id); + pb.set_name(name()); + pb.set_client_count(clients().size()); + return pb; +} diff --git a/conway_rts/room.hh b/conway_rts/room.hh index 7504d53..f81fb81 100644 --- a/conway_rts/room.hh +++ b/conway_rts/room.hh @@ -9,6 +9,7 @@ #include "conway_rts/client.hh" #include "conway_rts/event_queue.hh" #include "conway_rts/team.hh" +#include "conway_rts/room.pb.h" /// @brief game instance class Room { @@ -68,6 +69,9 @@ public: bool SerializeToOstream(std::ostream &out) const; bool ParseFromIstream(std::istream &in); + conway::Room& CopyToProtobuf(conway::Room &pb, int id) const; + conway::RoomListing& CopyToProtobuf(conway::RoomListing &pb, int id) const; + // accessor/mutators const std::string &name() const { return name_; } const std::vector &structure_lookup() const { diff --git a/conway_rts/team.cc b/conway_rts/team.cc index 977500c..ea4cfa5 100644 --- a/conway_rts/team.cc +++ b/conway_rts/team.cc @@ -141,4 +141,21 @@ bool Team::SerializeToOstream(std::ostream& out) const { bool Team::ParseFromIstream(std::istream& in) { in >> *this; return true; -} \ No newline at end of file +} + +conway::Team& Team::CopyToProtobuf(conway::Team& pb) const { + pb.set_id(id()); + // pb.set_name(name()); + pb.clear_name(); + pb.set_resources(resources()); + pb.set_income(income()); + pb.clear_members(); + for (const Client member : members()) { + conway::Client *client_pb = pb.add_members(); + member.CopyToProtobuf(*client_pb); + } + pb.clear_structures(); + /// TODO: no way to know what the structure_lookup table + /// is supposed to be?? + return pb; +} diff --git a/conway_rts/team.hh b/conway_rts/team.hh index e88cee3..02cbf74 100644 --- a/conway_rts/team.hh +++ b/conway_rts/team.hh @@ -9,6 +9,7 @@ #include "conway_rts/client.hh" #include "conway_rts/event.hh" #include "conway_rts/event_queue.hh" +#include "conway_rts/team.pb.h" /// @brief a single team and the actions it wants to do (controlled from client actions) class Team { @@ -66,6 +67,8 @@ class Team { bool SerializeToOstream(std::ostream& out) const; bool ParseFromIstream(std::istream& in); + conway::Team& CopyToProtobuf(conway::Team &pb) const; + // accessors/mutators int id() const { return id_; From 2b37dd253c2292e87fff8c105578cfa5554c53db Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 16:30:44 -0700 Subject: [PATCH 28/56] implement clients joining a room from room list --- client/src/App.tsx | 19 ++++--- client/src/RoomListing.tsx | 30 ++++++++++ client/src/Socket.ts | 113 +++++++++++++++++++++++++++++++++++++ client_wasm/conway.ts | 2 +- package.json | 1 + pnpm-lock.yaml | 7 +++ server/main.cpp | 51 +++++++++++++---- 7 files changed, 203 insertions(+), 20 deletions(-) create mode 100644 client/src/RoomListing.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 21116cd..0ece371 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,27 +1,28 @@ import { useEffect, useState } from "react"; import SocketLogger from "./SocketLogger"; import SocketUtils from "./SocketUtils"; +import { ClientWebSocket } from "./Socket"; +import RoomListing from "./RoomListing"; const server_url = "ws://localhost:3000"; function App() { - const [socket, setSocket] = useState(null); + const [clientSocket, setClientSocket] = useState( + null, + ); useEffect(() => { - console.log("Creating websocket"); - const s = new WebSocket(server_url); - s.onopen = ({ timeStamp }) => console.log(timeStamp, "opened"); - s.onclose = ({ timeStamp }) => console.log(timeStamp, "closed"); - s.addEventListener("message", ({ data }) => console.log(data)); - setSocket(s); + const s = new ClientWebSocket(server_url); + setClientSocket(s); return () => s?.close(); }, []); return ( <>
Hello world
- {socket && } - {socket && } + {clientSocket && } + {clientSocket && } + {clientSocket && } ); } diff --git a/client/src/RoomListing.tsx b/client/src/RoomListing.tsx new file mode 100644 index 0000000..896dcfb --- /dev/null +++ b/client/src/RoomListing.tsx @@ -0,0 +1,30 @@ +import { useEffect, useState } from "react"; +import { RoomListData, UseClientSocket } from "./Socket"; + +function RoomListing({ clientSocket }: UseClientSocket) { + const [rooms, setRooms] = useState([]); + + useEffect(() => { + const processEvent = (event: CustomEvent) => { + setRooms(event.detail); + console.log(event.detail); + }; + clientSocket.addEventListener("roomList", processEvent); + return () => clientSocket.removeEventListener("roomList", processEvent); + }, []); + + return ( + <> +
+ {rooms.map(({ name, id, size }, index) => ( +
+ {name} ({size})
#{id}
+ +
+ ))} +
+ + ); +} + +export default RoomListing; diff --git a/client/src/Socket.ts b/client/src/Socket.ts index b2a68c0..50d6fcd 100644 --- a/client/src/Socket.ts +++ b/client/src/Socket.ts @@ -3,6 +3,12 @@ */ import * as Proto from "@/proto"; +import { Vector2 } from "@/lib"; +import { TypedEventTarget } from "typescript-event-target"; + +export type UseClientSocket = { + clientSocket: ClientWebSocket; +}; export function register(socket: WebSocket, name: string) { const event = Proto.server.Event.create(); @@ -30,3 +36,110 @@ export function joinRoom(socket: WebSocket, roomId: number) { event.action.roomJoin.roomId = roomId; socket.send(Proto.server.Event.encode(event).finish()); } + +export interface RegisterData { + name: string; + acknowledged: boolean; +} +export interface RoomDescription { + id: number; + name: string; + size: number; +} +export type RoomListData = RoomDescription[]; + +/** + * Sent to client when they join a room. Contains + * room initialization data. + */ +export interface RoomCreateData { + id: number; + name: string; + dimensions: Vector2; +} + +interface ClientWebSocketEventMap { + register: CustomEvent; + roomList: CustomEvent; + roomCreate: CustomEvent; + roomJoin: CustomEvent; + roomLeave: CustomEvent; +} + +export class ClientWebSocket extends TypedEventTarget { + public readonly socket: WebSocket; + constructor(url: string) { + super(); + this.socket = new WebSocket(url); + this.socket.addEventListener("message", (event: MessageEvent) => + this.processServerEvent(event), + ); + } + + /** + * Closes the socket. + */ + public close() { + this.socket.close(); + } + + /** + * Sends a request to the server to join a room. + * @param roomId which room to join + */ + public joinRoom(roomId: number) { + joinRoom(this.socket, roomId); + } + + /** + * Process server event, handler of WebSocket::message event + * @param serverEvent MessageEvent to process + */ + private async processServerEvent(serverEvent: MessageEvent) { + const eventIssues = Proto.server.Event.verify(serverEvent); + if (eventIssues !== null) throw new Error(eventIssues); + if (serverEvent.data instanceof Blob) { + const arrayBuffer = await serverEvent.data.arrayBuffer(); + const view = new Uint8Array(arrayBuffer); + const event = Proto.server.Event.decode(view); + switch (event.event) { + case "register": + { + const name = event.register?.name ?? "?"; + const acknowledged = event.register?.acknowledged ?? false; + const detail = { name, acknowledged }; + this.dispatchTypedEvent( + "register", + new CustomEvent("register", { detail }), + ); + } + break; + case "roomList": + { + const rooms = event.roomList?.rooms ?? []; + this.dispatchTypedEvent( + "roomList", + new CustomEvent("roomList", { + detail: rooms.map((room) => ({ + name: room.name ?? "?", + id: room.id ?? -1, + size: room.clientCount ?? -1, + })), + }), + ); + } + break; + case "action": + { + this.processActionEvent(event.action as Proto.conway.Event); + console.log(event.action); + } + break; + default: + throw new Error(`Invalid event type <${event.event}>`); + } + } + } + + private processActionEvent(actionEvent: Proto.conway.Event) {} +} diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index 148b0bb..8cecaa1 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -9,7 +9,7 @@ interface Type extends Function { new (...args: any[]): T; // eslint-disable-line @typescript-eslint/no-explicit-any } -interface Vector2 { +export interface Vector2 { x: () => number; y: () => number; set_x: (x: number) => void; diff --git a/package.json b/package.json index 1196505..4326256 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "protobufjs": "^7.2.5", "protobufjs-cli": "^1.1.2", "typescript": "^5.3.3", + "typescript-event-target": "^1.1.0", "vite": "^5.0.10", "vitest": "^1.1.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5acc1fd..753db3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,6 +52,9 @@ devDependencies: typescript: specifier: ^5.3.3 version: 5.3.3 + typescript-event-target: + specifier: ^1.1.0 + version: 1.1.0 vite: specifier: ^5.0.10 version: 5.0.10 @@ -2453,6 +2456,10 @@ packages: engines: {node: '>=10'} dev: true + /typescript-event-target@1.1.0: + resolution: {integrity: sha512-PMrzUVryhnUq2n8M7tjNHNRuIHlUqly5RfGltBTpPCdVpbytgALTRDegF/t6+mFmmtBVhOqEYlbjVNBxwabIug==} + dev: true + /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} diff --git a/server/main.cpp b/server/main.cpp index d24bb73..2b847c9 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -17,8 +17,11 @@ typedef uWS::WebSocket WebSocket; // Note: make sure you're using C++20 for VScode intellisense int main() { + const Vector2 kDefaultDimensions(100, 100); + int room_instance_count = 1; std::map rooms; - rooms[-1] = Room("kitchen", Vector2(100, 100)); + rooms[-1] = Room("kitchen", kDefaultDimensions); + rooms[-2] = Room("cellar", kDefaultDimensions); uWS::SSLApp({ .key_file_name = "misc/key.pem", @@ -50,7 +53,11 @@ int main() { server::Event event; if (event.ParseFromArray(&*message.begin(), message.size())) { - std::cout << "[rx] event\n"; + std::cout << "[rx] event #" << user_data.client.id(); + // the first processed event MUST be to register + if (!(event.has_register_() || user_data.IsInitialized())) { + return; + } switch (event.event_case()) { case server::Event::EventCase::EVENT_NOT_SET: std::cout << "unset event??"; @@ -67,11 +74,9 @@ int main() { } case server::Event::EventCase::kRoomList: { event.mutable_room_list()->clear_rooms(); - for (const auto& [room_key, room] : rooms) { - conway::RoomListing &room_listing = *event.mutable_room_list()->add_rooms(); - room_listing.set_id(room_key); - room_listing.set_client_count(room.clients().size()); - room_listing.set_name(room.name()); + for (const auto& [room_id, room] : rooms) { + conway::RoomListing *room_listing = event.mutable_room_list()->add_rooms(); + room.CopyToProtobuf(*room_listing, room_id); } std::string ack; event.SerializeToString(&ack); @@ -81,10 +86,36 @@ int main() { case server::Event::EventCase::kAction: { switch (event.action().event_case()) { case conway::Event::EventCase::EVENT_NOT_SET: break; - case conway::Event::EventCase::kRoomCreate: { - break; - } + case conway::Event::EventCase::kRoomCreate: case conway::Event::EventCase::kRoomJoin: { + std::cout << " {create}"; + if (user_data.room == nullptr) { // don't allow making multiple empty rooms + int room_id = -1; + if (event.action().event_case() == conway::Event::EventCase::kRoomCreate) { + // create room + room_id = ++room_instance_count; + rooms[room_id] = Room("Test room", kDefaultDimensions); + } else { + // join existing room + room_id = event.action().room_join().room_id(); + if (rooms.count(room_id) == 0) { + // invalid room_id (doesn't exist) + break; + } + } + Room& room = rooms[room_id]; + user_data.room = &room; + + const int team_id = room.AddTeam({user_data.client}); + user_data.team = room.GetTeam(team_id); + + server::Event event; + conway::Room *room_pb = event.mutable_action()->mutable_room_create()->mutable_room(); + room.CopyToProtobuf(*room_pb, room_id); + std::string response; + event.SerializeToString(&response); + ws->send(response); + } break; } case conway::Event::EventCase::kRoomLeave: { From 7808216f6277391e2423f0ee0a78eaad49e0259f Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 17:39:36 -0700 Subject: [PATCH 29/56] bump emsdk version to fix windows FileExistsError --- WORKSPACE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index dfd57be..91dfbb3 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -43,11 +43,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") ######################## # Emscripten toolchain # ######################## -http_archive( +git_repository( name = "emsdk", - sha256 = "5dd94e557b720800a60387ec078bf3b3a527cbd916ad74a696fe399f1544474f", - strip_prefix = "emsdk-3.1.46/bazel", - url = "https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.46.tar.gz", + commit = "9347bc393b94a17b93450bbc98bc3f66cef2aeb0", # 3.1.51 + 2 commits (to patch windows bazel wasm_cc_binary) + remote = "git@github.com:emscripten-core/emsdk.git", + strip_prefix = "bazel", ) load("@emsdk//:deps.bzl", emsdk_deps = "deps") From 060201efefde29adc45dca11183310b254ea73ae Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 18:30:37 -0700 Subject: [PATCH 30/56] fix -Wreorder-ctr (enabled in emcc) --- conway_rts/client.hh | 2 +- conway_rts/team.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conway_rts/client.hh b/conway_rts/client.hh index 669396b..fcb4312 100644 --- a/conway_rts/client.hh +++ b/conway_rts/client.hh @@ -43,8 +43,8 @@ class Client { } private: - std::string name_; int id_; + std::string name_; }; #endif // CONWAY_INCLUDE_CLIENT_HPP \ No newline at end of file diff --git a/conway_rts/team.hh b/conway_rts/team.hh index 02cbf74..55bb031 100644 --- a/conway_rts/team.hh +++ b/conway_rts/team.hh @@ -101,8 +101,8 @@ class Team { /// @brief events from `Client`s to process EventQueue event_queue_; - int resources_ = 0; int income_ = 0; + int resources_ = 0; /// @brief when the last income update was processed int last_income_update_ = 0; From c3ef9f44e950d09fa6efd8ce5d93a1241cf2c148 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 20:49:05 -0700 Subject: [PATCH 31/56] add //conway_rts:client and //conway_rts:team emscripten bindings support added for the -fno-exceptions that wasm_cc_binary rule enables --- client_wasm/BUILD.bazel | 4 +++- client_wasm/bindings.cpp | 51 ++++++++++++++++++++++++++++++++++++++++ conway_rts/BUILD.bazel | 15 +++++++++--- conway_rts/client.cc | 2 ++ conway_rts/client.hh | 4 ++++ conway_rts/team.cc | 26 ++++++++++++++++---- 6 files changed, 94 insertions(+), 8 deletions(-) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index d6603d9..9eba89a 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -41,7 +41,9 @@ cc_binary( "//conway:life_grid", "//conway:structure_properties", "//conway:structure", - # "//hello-world/cpp:localtime", + "//conway_rts:client", + # "//conway_rts:room", + "//conway_rts:team", ], # this isn't a problem as long as you don't wildcard... wildcarding still breaks this though ) diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index 1fab425..9b42e4f 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -3,6 +3,9 @@ #include "conway/life_grid.hh" #include "conway/structure.hh" #include "conway/structure_properties.hh" +#include "conway_rts/client.hh" +// #include "conway_rts/room.hh" +#include "conway_rts/team.hh" #include "utility/vector2.hh" EMSCRIPTEN_BINDINGS(Utility) { @@ -47,3 +50,51 @@ EMSCRIPTEN_BINDINGS(Conway) { .function("position", &Structure::position) .function("properties", &Structure::properties); } + +EMSCRIPTEN_BINDINGS(ConwayRTS) { + emscripten::class_("Client") + .constructor() + .function("name", &Client::name) + .function("id", &Client::id); + + /* + emscripten::class_("Room") + .constructor() + .function("Initialize", &Room::Initialize) + .function("SetName", &Room::SetName) + .function("AddClient", &Room::AddClient) + .function("AddTeam", &Room::AddTeam) + .function("AddToTeam", &Room::AddToTeam) + .function("RemoveFromTeam", &Room::RemoveFromTeam) + .function("RemoveClient", &Room::RemoveClient) + .function("LoadStructures", &Room::LoadStructures) + .function("Tick", &Room::Tick) + .function("name", &Room::name) + .function("structure_lookup", &Room::structure_lookup) + .function("time", &Room::time) + .function("clients", &Room::clients) + .function("teams", &Room::teams) + .function("grid", &Room::grid) + .function("event_queue", &Room::event_queue); + */ + + emscripten::class_("Team") + .constructor<>() + .constructor() + .constructor &>() + .function("AddMember", &Team::AddMember) + .function("GetLeader", &Team::GetLeader) // these methods seem to break emcc + .function("SetLeader", &Team::SetLeader) + .function("AddEventToQueue", &Team::AddEventToQueue) + .function("Tick", &Team::Tick) + .function("CheckStructureIntegrity", &Team::CheckStructureIntegrity) + .function("AddStructure", &Team::AddStructure) + .function("id", &Team::id) + .function("members", &Team::members) + .function("structures", &Team::structures) + .function("event_queue", &Team::event_queue) + .function("resources", &Team::resources) + .function("income", &Team::income) + .function("last_income_update", &Team::last_income_update) + ; +} diff --git a/conway_rts/BUILD.bazel b/conway_rts/BUILD.bazel index 855dc21..82b7152 100644 --- a/conway_rts/BUILD.bazel +++ b/conway_rts/BUILD.bazel @@ -12,7 +12,10 @@ cc_library( name = "client", srcs = ["client.cc"], hdrs = ["client.hh"], - visibility = ["//server:__pkg__"], + visibility = [ + "//client_wasm:__pkg__", + "//server:__pkg__", + ], deps = [ ":array_buffer", ":client_cc_proto", @@ -83,7 +86,10 @@ cc_library( name = "room", srcs = ["room.cc"], hdrs = ["room.hh"], - visibility = ["//server:__pkg__"], + visibility = [ + "//client_wasm:__pkg__", + "//server:__pkg__", + ], deps = [ ":client", ":event", @@ -125,7 +131,10 @@ cc_library( srcs = ["team.cc"], hdrs = ["team.hh"], copts = CC_OPTS, - visibility = ["//server:__pkg__"], + visibility = [ + "//client_wasm:__pkg__", + "//server:__pkg__", + ], deps = [ ":client", ":event", diff --git a/conway_rts/client.cc b/conway_rts/client.cc index f970560..ac953f1 100644 --- a/conway_rts/client.cc +++ b/conway_rts/client.cc @@ -74,3 +74,5 @@ conway::Client& Client::CopyToProtobuf(conway::Client &pb) const { pb.set_id(id()); return pb; } + +Client Client::placeholder = Client(-1, ""); diff --git a/conway_rts/client.hh b/conway_rts/client.hh index fcb4312..0adbc56 100644 --- a/conway_rts/client.hh +++ b/conway_rts/client.hh @@ -42,6 +42,10 @@ class Client { return id_; } + // placeholder class that can be returned when needed + // such as when you can't throw an exception since -fno-exception + static Client placeholder; + private: int id_; std::string name_; diff --git a/conway_rts/team.cc b/conway_rts/team.cc index ea4cfa5..177ed43 100644 --- a/conway_rts/team.cc +++ b/conway_rts/team.cc @@ -48,7 +48,13 @@ bool Team::AddMember(const Client& new_member) { } const Client& Team::GetLeader() const { - if(members().empty()) throw std::logic_error("cannot Team::GetLeader(), no members in team"); + if(members().empty()) { +#ifdef __cpp_exceptions + throw std::logic_error("cannot Team::GetLeader(), no members in team"); +#else + return Client::placeholder; +#endif // __cpp_exceptions + } return members().front(); } @@ -79,9 +85,21 @@ void Team::Tick(const int current_time, EventQueue& next_queue, const LifeGrid& event_queue_.pop_front(); // client event validation before passing to the next queue (room) - if(event.data().size() != 4) throw std::logic_error("invalid event data"); + if(event.data().size() != 4) { +#ifdef __cpp_exceptions + throw std::logic_error("invalid event data"); +#else + continue; +#endif // __cpp_exceptions + } const int building_id = event.data()[3]; - if(building_id < 0 || building_id >= event.data().size()) throw std::logic_error("invalid event building_id"); + if(building_id < 0 || building_id >= event.data().size()) { +#ifdef __cpp_exceptions + throw std::logic_error("invalid event building_id"); +#else + continue; +#endif // __cpp_exceptions + } const StructureProperties& props = structure_lookup[building_id]; const int cost = props.grid().dimensions().x() * props.grid().dimensions().y() + props.activation_cost(); const Structure new_structure(props, Vector2(event.data()[1], event.data()[2])); @@ -150,7 +168,7 @@ conway::Team& Team::CopyToProtobuf(conway::Team& pb) const { pb.set_resources(resources()); pb.set_income(income()); pb.clear_members(); - for (const Client member : members()) { + for (const Client& member : members()) { conway::Client *client_pb = pb.add_members(); member.CopyToProtobuf(*client_pb); } From 9ea84bc80673b2d9d0ab7f0822bca326b442e380 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 21:11:34 -0700 Subject: [PATCH 32/56] add //conway_rts:room emscripten bindings --- client_wasm/BUILD.bazel | 2 +- client_wasm/bindings.cpp | 4 +--- conway_rts/room.cc | 28 +++++++++++----------------- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index 9eba89a..3161f78 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -42,7 +42,7 @@ cc_binary( "//conway:structure_properties", "//conway:structure", "//conway_rts:client", - # "//conway_rts:room", + "//conway_rts:room", "//conway_rts:team", ], # this isn't a problem as long as you don't wildcard... wildcarding still breaks this though diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index 9b42e4f..4f4026a 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -4,7 +4,7 @@ #include "conway/structure.hh" #include "conway/structure_properties.hh" #include "conway_rts/client.hh" -// #include "conway_rts/room.hh" +#include "conway_rts/room.hh" #include "conway_rts/team.hh" #include "utility/vector2.hh" @@ -57,7 +57,6 @@ EMSCRIPTEN_BINDINGS(ConwayRTS) { .function("name", &Client::name) .function("id", &Client::id); - /* emscripten::class_("Room") .constructor() .function("Initialize", &Room::Initialize) @@ -76,7 +75,6 @@ EMSCRIPTEN_BINDINGS(ConwayRTS) { .function("teams", &Room::teams) .function("grid", &Room::grid) .function("event_queue", &Room::event_queue); - */ emscripten::class_("Team") .constructor<>() diff --git a/conway_rts/room.cc b/conway_rts/room.cc index 9619afa..3f48559 100644 --- a/conway_rts/room.cc +++ b/conway_rts/room.cc @@ -50,9 +50,7 @@ bool Room::AddClient(const Client &client) { int Room::AddTeam(const std::vector &team_members) { ++team_instance_count; Team new_team(team_instance_count); - if (teams_.count(new_team.id())) { - throw std::runtime_error("team id exists"); - } + assert(!(teams_.count(new_team.id())) && "team id shouldn't exist"); const int new_team_id = new_team.id(); teams_[new_team.id()] = std::move(new_team); @@ -81,8 +79,8 @@ bool Room::RemoveFromTeam(int team_id, const Client &client) { if (teams_.count(team_id) == 0) { return false; } - throw std::exception("not implemented, depends on Team::RemoveMember but " - "this doesn't exist yet"); + assert("not implemented, depends on Team::RemoveMember but this doesn't exist yet"); + return false; // return teams_[team_id].RemoveMember(client); } @@ -111,20 +109,16 @@ void Room::Tick(EventQueue &next_queue) { while (!event_queue().empty() && event_queue().front().time() <= time()) { const Event event = std::move(event_queue_.front()); event_queue_.pop_front(); - if (event.data().size() != 4) { - throw std::logic_error("invalid event data"); - } + assert(!(event.data().size() != 4) && "invalid event data"); const int building_id = event.data()[3]; - if (building_id < 0 || building_id >= event.data().size()) { - throw std::logic_error("invalid event building_id"); - } + assert(!(building_id < 0 || building_id >= event.data().size()) && "invalid event building_id"); const StructureProperties &props = structure_lookup()[building_id]; const Vector2 position(event.data()[1], event.data()[2]); - const int refund = - props.grid().dimensions().x() * props.grid().dimensions().y() - - grid().Compare(props.grid(), position); + // const int refund = + // props.grid().dimensions().x() * props.grid().dimensions().y() - + // grid().Compare(props.grid(), position); grid_.Load(props.grid(), position); next_queue.push_back(event); @@ -162,7 +156,7 @@ std::ostream &operator<<(std::ostream &out, const Room &rhs) { } std::istream &operator>>(std::istream &in, Room &rhs) { - throw "not implemented"; + assert("not implemented"); return in; } @@ -181,12 +175,12 @@ conway::Room& Room::CopyToProtobuf(conway::Room &pb, int id) const { pb.set_name(name()); grid().dimensions().CopyToProtobuf(*pb.mutable_dimensions()); pb.clear_clients(); - for (const std::pair& client_entry : clients()) { + for (const std::pair& client_entry : clients()) { conway::Client *client_pb = pb.add_clients(); client_entry.second.CopyToProtobuf(*client_pb); } pb.clear_teams(); - for (const std::pair& team_entry : teams()) { + for (const std::pair& team_entry : teams()) { conway::Team *team_pb = pb.add_teams(); team_entry.second.CopyToProtobuf(*team_pb); } From de02cb6368aa6b9465800d998940ead9fc3eb694 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 21:57:18 -0700 Subject: [PATCH 33/56] cleanup //client_wasm BUILD, format --- client_wasm/BUILD.bazel | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index 3161f78..1734e75 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -9,13 +9,13 @@ DEFAULT_EMSCRIPTEN_LINKOPTS = [ # "-flto", # Specify lto (has to be set on for compiler as well) "--bind", # Compiles the source code using the Embind bindings to connect C/C++ and JavaScript "--closure 1", # Run the closure compiler - # "--embind-emit-tsd interface.d.ts", # emit typescript types file + # "--embind-emit-tsd interface.d.ts", # emit typescript types file, doesn't work (doesn't seem to be supported) # "-s EXPORTED_FUNCTIONS=_free,_malloc", "-s MALLOC=emmalloc", # Switch to using the much smaller implementation "-s ALLOW_MEMORY_GROWTH=1", "-s USE_PTHREADS=0", # Disable pthreads "-s ASSERTIONS=0", # Turn off assertions - "-s EXPORT_ES6=1", # Export as es6 module, used for rollup + "-s EXPORT_ES6=1", # Export as es6 module, used for rollup "-s MODULARIZE=1", # Allows us to manually invoke the initializatio of wasm "-s EXPORT_NAME=createModule", # Not used, but good to specify # "-s USE_ES6_IMPORT_META=1", # needed for somethingggg --- Disable loading from import meta since we use rollup @@ -37,19 +37,17 @@ cc_binary( # headers etc. Therefore, we hide it from wildcards. tags = ["manual"], deps = [ - "//utility:vector2", "//conway:life_grid", - "//conway:structure_properties", "//conway:structure", + "//conway:structure_properties", "//conway_rts:client", "//conway_rts:room", "//conway_rts:team", + "//utility:vector2", ], - # this isn't a problem as long as you don't wildcard... wildcarding still breaks this though ) wasm_cc_binary( name = "conway_lib_wasm", cc_target = ":conway_lib_wasm_embind", - # simd = True, # adding this fixed FileExistsError, but it appeared again and removing this also fixed that error? ) From 9947ca64208b51ec9ee2f5530c8f642a96a781c6 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 22:22:45 -0700 Subject: [PATCH 34/56] handle client-server RoomCreate and RoomJoin events --- client/src/App.tsx | 2 ++ client/src/Lobby.tsx | 38 ++++++++++++++++++++ client/src/Socket.ts | 58 +++++++++++++++++++++++++++--- client/src/protobufDecoder.ts | 11 ++++++ client_wasm/conway.ts | 67 +++++++++++++++++++++++++++-------- server/main.cpp | 13 +++++++ 6 files changed, 171 insertions(+), 18 deletions(-) create mode 100644 client/src/Lobby.tsx create mode 100644 client/src/protobufDecoder.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 0ece371..cc0cd47 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,6 +3,7 @@ import SocketLogger from "./SocketLogger"; import SocketUtils from "./SocketUtils"; import { ClientWebSocket } from "./Socket"; import RoomListing from "./RoomListing"; +import Lobby from "./Lobby"; const server_url = "ws://localhost:3000"; @@ -23,6 +24,7 @@ function App() { {clientSocket && } {clientSocket && } {clientSocket && } + {clientSocket && } ); } diff --git a/client/src/Lobby.tsx b/client/src/Lobby.tsx new file mode 100644 index 0000000..f15d05a --- /dev/null +++ b/client/src/Lobby.tsx @@ -0,0 +1,38 @@ +import { useEffect, useState } from "react"; +import { RoomCreateData, RoomJoinData, UseClientSocket } from "./Socket"; +import { Client } from "@/lib"; + +function Lobby({ clientSocket }: UseClientSocket) { + const [name, setName] = useState(""); + const [members, setMembers] = useState([]); + + useEffect(() => { + const processEvent = (event: CustomEvent) => { + const { name, clients } = event.detail; + setName(name); + setMembers(clients); + }; + clientSocket.addEventListener("roomCreate", processEvent); + return () => clientSocket.removeEventListener("roomCreate", processEvent); + }, []); + + useEffect(() => { + const processEvent = (event: CustomEvent) => { + const { newClient } = event.detail; + setMembers((members) => [...members, newClient]); + }; + clientSocket.addEventListener("roomJoin", processEvent); + return () => clientSocket.removeEventListener("roomJoin", processEvent); + }, []); + + return ( + <> +
+ Room name: {name}
+ Members: {members.map(({ name }) => name).join(", ")} +
+ + ); +} + +export default Lobby; diff --git a/client/src/Socket.ts b/client/src/Socket.ts index 50d6fcd..1a26390 100644 --- a/client/src/Socket.ts +++ b/client/src/Socket.ts @@ -2,9 +2,10 @@ * Methods for interacting with the server. */ +import { Client, Team, Vector2 } from "@/lib"; import * as Proto from "@/proto"; -import { Vector2 } from "@/lib"; import { TypedEventTarget } from "typescript-event-target"; +import { decodePbClient } from "./protobufDecoder"; export type UseClientSocket = { clientSocket: ClientWebSocket; @@ -56,13 +57,23 @@ export interface RoomCreateData { id: number; name: string; dimensions: Vector2; + clients: Client[]; + teams: Team[]; +} + +/** + * Sent to client when someone else joins a room. + * Contains the joining client's data. + */ +export interface RoomJoinData { + newClient: Client; } interface ClientWebSocketEventMap { register: CustomEvent; roomList: CustomEvent; - roomCreate: CustomEvent; - roomJoin: CustomEvent; + roomCreate: CustomEvent; + roomJoin: CustomEvent; roomLeave: CustomEvent; } @@ -141,5 +152,44 @@ export class ClientWebSocket extends TypedEventTarget { } } - private processActionEvent(actionEvent: Proto.conway.Event) {} + private processActionEvent(actionEvent: Proto.conway.Event) { + switch (actionEvent.event) { + case "roomCreate": + { + const event = actionEvent.roomCreate; + this.dispatchTypedEvent( + "roomCreate", + new CustomEvent("roomCreate", { + detail: { + id: event?.room?.id ?? -1, + name: event?.room?.name ?? "", + dimensions: { x: 0, y: 0 }, + clients: event?.room?.clients?.map(decodePbClient) ?? [], + teams: [], + }, + }), + ); + } + break; + case "roomJoin": + { + const event = actionEvent.roomJoin; + this.dispatchTypedEvent( + "roomJoin", + new CustomEvent("roomJoin", { + detail: { + newClient: decodePbClient(event?.client), + }, + }), + ); + } + break; + case "roomLeave": + break; + case "baseInitialize": + break; + case "baseBuild": + break; + } + } } diff --git a/client/src/protobufDecoder.ts b/client/src/protobufDecoder.ts new file mode 100644 index 0000000..baf6354 --- /dev/null +++ b/client/src/protobufDecoder.ts @@ -0,0 +1,11 @@ +import { Client } from "@/lib"; +import * as Proto from "@/proto"; + +export function decodePbClient( + pb: Proto.conway.IClient | null | undefined, +): Client { + return { + id: pb?.id ?? -1, + name: pb?.name ?? "", + }; +} diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index 8cecaa1..01ef60d 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -10,10 +10,8 @@ interface Type extends Function { } export interface Vector2 { - x: () => number; - y: () => number; - set_x: (x: number) => void; - set_y: (y: number) => void; + x: number; + y: number; } enum LifeState { @@ -21,20 +19,61 @@ enum LifeState { ALIVE, } -interface LifeGrid { - dimensions: () => Vector2; - GetCell: (position: Vector2) => boolean; - SetCell: (position: Vector2) => void; - ResetCell: (position: Vector2) => void; - Load: (payload: LifeGrid, position: Vector2) => void; - Compare: (payload: LifeGrid, position: Vector2) => number; - Tick: () => void; +export interface LifeGrid { + dimensions: Vector2; + contents: boolean[][]; +} + +export interface StructureProperties { + grid: LifeGrid; + name: string; + activationCost: number; + income: number; + buildArea: number; + checks: Vector2[]; +} + +export interface Structure { + position: Vector2; + properties: StructureProperties; +} + +export interface Client { + id: number; + name: string; +} + +export interface Team { + id: number; + name: string; + resources: number; + income: number; + members: Client[]; + structures: Structure[]; +} + +namespace wasm { + export interface Vector2 { + x: () => number; + y: () => number; + set_x: (x: number) => void; + set_y: (y: number) => void; + } + export interface LifeGrid { + dimensions: () => Vector2; + GetCell: (position: Vector2) => boolean; + SetCell: (position: Vector2) => void; + ResetCell: (position: Vector2) => void; + Load: (payload: LifeGrid, position: Vector2) => void; + Compare: (payload: LifeGrid, position: Vector2) => number; + Tick: () => void; + } } interface ConwayLib { - Vector2: Type; + Vector2: Type; LifeState: typeof LifeState; - LifeGrid: Type; + LifeGrid: Type; } const Conway = (await ConwayWasm()) as ConwayLib; diff --git a/server/main.cpp b/server/main.cpp index 2b847c9..e47f7ef 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -20,6 +20,7 @@ int main() { const Vector2 kDefaultDimensions(100, 100); int room_instance_count = 1; std::map rooms; + std::map> room_subscriptions; rooms[-1] = Room("kitchen", kDefaultDimensions); rooms[-2] = Room("cellar", kDefaultDimensions); @@ -115,6 +116,18 @@ int main() { std::string response; event.SerializeToString(&response); ws->send(response); + + // broadcast to all other room members + server::Event join_event; + user_data.client.CopyToProtobuf(*join_event.mutable_action()->mutable_room_join()->mutable_client()); + std::string new_member_message; + join_event.SerializeToString(&new_member_message); + for (WebSocket* client_ws : room_subscriptions[room_id]) { + client_ws->send(new_member_message); + } + + // then add the ws of the client who just joined to the mailing list + room_subscriptions[room_id].insert(ws); } break; } From 71dac41c6ff9f8425d1f62344e10740b14d627d8 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 23:32:29 -0700 Subject: [PATCH 35/56] change A: () => Z format to A(): Z --- client_wasm/Room.spec.ts | 13 +++++++++++ client_wasm/bindings.cpp | 12 ++++++---- client_wasm/conway.ts | 49 +++++++++++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 client_wasm/Room.spec.ts diff --git a/client_wasm/Room.spec.ts b/client_wasm/Room.spec.ts new file mode 100644 index 0000000..437d109 --- /dev/null +++ b/client_wasm/Room.spec.ts @@ -0,0 +1,13 @@ +/** + * Test emscripten compiled //conway_rts:room + */ + +import { expect, test } from "vitest"; +import Conway from "@/lib"; + +test("constructor", () => { + const room = new Conway.Room("TEST", new Conway.Vector2(1000, 1500)); + expect(room.name()).toBe("TEST"); + expect(room.grid().dimensions().x()).toBe(1000); + expect(room.grid().dimensions().y()).toBe(1500); +}); diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index 4f4026a..9b3def9 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -23,6 +23,7 @@ EMSCRIPTEN_BINDINGS(Conway) { .value("DEAD", LifeState::DEAD) .value("ALIVE", LifeState::ALIVE); + emscripten::register_vector>("Bool2DVector"); emscripten::class_("LifeGrid") .constructor() .function("dimensions", &LifeGrid::dimensions) @@ -32,8 +33,9 @@ EMSCRIPTEN_BINDINGS(Conway) { .function("Load", &LifeGrid::Load) .function("Compare", &LifeGrid::Compare) .function("Tick", &LifeGrid::Tick); - - emscripten::class_("StructureProperties") + + emscripten::register_vector("Vector2Vector"); + emscripten::class_("StructureProperties"); .constructor &>() .function("name", &StructureProperties::name) @@ -52,6 +54,8 @@ EMSCRIPTEN_BINDINGS(Conway) { } EMSCRIPTEN_BINDINGS(ConwayRTS) { + emscripten::register_vector("StructurePropertiesVector"); + emscripten::class_("Client") .constructor() .function("name", &Client::name) @@ -69,12 +73,12 @@ EMSCRIPTEN_BINDINGS(ConwayRTS) { .function("LoadStructures", &Room::LoadStructures) .function("Tick", &Room::Tick) .function("name", &Room::name) - .function("structure_lookup", &Room::structure_lookup) + .function("structureLookup", &Room::structure_lookup) .function("time", &Room::time) .function("clients", &Room::clients) .function("teams", &Room::teams) .function("grid", &Room::grid) - .function("event_queue", &Room::event_queue); + .function("eventQueue", &Room::event_queue); emscripten::class_("Team") .constructor<>() diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index 01ef60d..aaed484 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -53,27 +53,54 @@ export interface Team { } namespace wasm { + export interface HasDestructor { + delete(): void; + } export interface Vector2 { - x: () => number; - y: () => number; - set_x: (x: number) => void; - set_y: (y: number) => void; + x(): number; + y(): number; + set_x(x: number): void; + set_y(y: number): void; } export interface LifeGrid { - dimensions: () => Vector2; - GetCell: (position: Vector2) => boolean; - SetCell: (position: Vector2) => void; - ResetCell: (position: Vector2) => void; - Load: (payload: LifeGrid, position: Vector2) => void; - Compare: (payload: LifeGrid, position: Vector2) => number; - Tick: () => void; + dimensions(): Vector2; + GetCell(position: Vector2): boolean; + SetCell(position: Vector2): void; + ResetCell(position: Vector2): void; + Load(payload: LifeGrid, position: Vector2): void; + Compare(payload: LifeGrid, position: Vector2): number; + Tick(): void; + } + export interface Client { + name(): string; + id(): number; + } + export interface Room { + Initialize(): void; + SetName(newName: string): void; + // AddClient(client: Client): boolean; + AddTeam(teamMembers: Client[]): number; + // AddToTeam(teamId: Team, client: Client): boolean; + GetTeam(teamId: number): Team; + LoadStructures(structures: StructureProperties[]): void; + Tick(): void; + name(): string; + structureLookup(): StructureProperties[]; + time(): number; + // clients(): any; + // teams(): any; + grid(): LifeGrid; } + export interface Team {} } interface ConwayLib { Vector2: Type; LifeState: typeof LifeState; LifeGrid: Type; + Client: Type; + Room: Type; + Team: Type; } const Conway = (await ConwayWasm()) as ConwayLib; From 4320fc89a722ae63fe59f1816b9166163f413924 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Tue, 2 Jan 2024 23:34:14 -0700 Subject: [PATCH 36/56] add delete() method to ts interfaces --- client_wasm/conway.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index aaed484..456e64a 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -56,13 +56,13 @@ namespace wasm { export interface HasDestructor { delete(): void; } - export interface Vector2 { + export interface Vector2 extends HasDestructor { x(): number; y(): number; set_x(x: number): void; set_y(y: number): void; } - export interface LifeGrid { + export interface LifeGrid extends HasDestructor { dimensions(): Vector2; GetCell(position: Vector2): boolean; SetCell(position: Vector2): void; @@ -71,11 +71,11 @@ namespace wasm { Compare(payload: LifeGrid, position: Vector2): number; Tick(): void; } - export interface Client { + export interface Client extends HasDestructor { name(): string; id(): number; } - export interface Room { + export interface Room extends HasDestructor { Initialize(): void; SetName(newName: string): void; // AddClient(client: Client): boolean; @@ -91,7 +91,7 @@ namespace wasm { // teams(): any; grid(): LifeGrid; } - export interface Team {} + export interface Team extends HasDestructor {} } interface ConwayLib { From fc81e27613df1943c81d3e9937b64d3d0cb9b38c Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Thu, 4 Jan 2024 14:12:25 -0700 Subject: [PATCH 37/56] fill in missing emscripten bindings specifically, enough to build typescript bindings, but this doesn't seem to be supported for emscripten-bazel so the manual conway.ts (types file with wasm and clientside types) was improved for now --- client_wasm/BUILD.bazel | 42 +++++++++++++---- client_wasm/Room.spec.ts | 18 ++++++++ client_wasm/bindings.cpp | 57 +++++++++++++++-------- client_wasm/conway.ts | 98 +++++++++++++++++++++++++++++++++------- 4 files changed, 169 insertions(+), 46 deletions(-) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index 1734e75..5dce7b9 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -28,6 +28,16 @@ WASM_LINKOPTS = [ "-s WASM=1", # Specify wasm output ] +EMSCRIPTEN_CC_DEPS = [ + "//conway:life_grid", + "//conway:structure", + "//conway:structure_properties", + "//conway_rts:client", + "//conway_rts:room", + "//conway_rts:team", + "//utility:vector2", +] + cc_binary( name = "conway_lib_wasm_embind", srcs = ["bindings.cpp"], @@ -36,18 +46,32 @@ cc_binary( # This target won't build successfully on its own because of missing emscripten # headers etc. Therefore, we hide it from wildcards. tags = ["manual"], - deps = [ - "//conway:life_grid", - "//conway:structure", - "//conway:structure_properties", - "//conway_rts:client", - "//conway_rts:room", - "//conway_rts:team", - "//utility:vector2", - ], + deps = EMSCRIPTEN_CC_DEPS, ) wasm_cc_binary( name = "conway_lib_wasm", cc_target = ":conway_lib_wasm_embind", ) + +cc_binary( + name = "conway_lib_wasm_embind_ts", + srcs = ["bindings.cpp"], + # features = ["emcc_debug_link"], # idk what this does + linkopts = [ + "--bind", + "-lembind", + "--embind-emit-tsd conway_lib_wasm_embind_ts.js", + "-s EXPORTED_FUNCTIONS=_malloc,_free", + ], + # This target won't build successfully on its own because of missing emscripten + # headers etc. Therefore, we hide it from wildcards. + tags = ["manual"], + deps = EMSCRIPTEN_CC_DEPS, +) + +wasm_cc_binary( + name = "conway_lib_wasm_ts", + cc_target = ":conway_lib_wasm_embind_ts", + tags = ["manual"], # emscripten ts in bazel currently not supported +) diff --git a/client_wasm/Room.spec.ts b/client_wasm/Room.spec.ts index 437d109..97e3bc3 100644 --- a/client_wasm/Room.spec.ts +++ b/client_wasm/Room.spec.ts @@ -10,4 +10,22 @@ test("constructor", () => { expect(room.name()).toBe("TEST"); expect(room.grid().dimensions().x()).toBe(1000); expect(room.grid().dimensions().y()).toBe(1500); + room.delete(); +}); + +test("map wasm", () => { + const room = new Conway.Room("TEST", new Conway.Vector2(10, 10)); + const c1 = new Conway.Client(1, "test"); + const teamMembers = new Conway.ClientVector(); + teamMembers.push_back(c1); + room.AddTeam(teamMembers); + // console.log(room.clients()); + // console.log(room.clients().get(1)); + // console.log(room.teams().get(1).members().get(0)); + + const strGrid = new Conway.LifeGrid(new Conway.Vector2(3, 3)); + const strChecks = new Conway.Vector2Vector(); + const strProp = new Conway.StructureProperties("a", 0, 0, 0, strGrid, strChecks); // prettier-ignore + const str = new Conway.Structure(strProp, new Conway.Vector2(5, 5)); + expect(str.checkIntegrity(room.grid())).toBe(true); }); diff --git a/client_wasm/bindings.cpp b/client_wasm/bindings.cpp index 9b3def9..dd8913e 100644 --- a/client_wasm/bindings.cpp +++ b/client_wasm/bindings.cpp @@ -8,6 +8,26 @@ #include "conway_rts/team.hh" #include "utility/vector2.hh" +EMSCRIPTEN_BINDINGS(cc_stl) { + emscripten::class_>("BoolVector"); + emscripten::class_>>("Bool2DVector"); + // emscripten::register_vector>("Bool2DVector"); + emscripten::register_vector("Vector2Vector"); + emscripten::register_vector("ClientVector"); + emscripten::register_vector("StructurePropertiesVector"); + emscripten::register_vector( + "IntVector"); // needed for std::map::keys() + emscripten::register_map("IntClientMap"); + emscripten::register_map("IntTeamMap"); + emscripten::class_>( + "StructureKeyVector"); // needed for + // std::map::keys() + emscripten::register_map("StructureMap"); + emscripten::class_("StructureKey"); + emscripten::class_("EventWithTime"); + emscripten::class_("EventQueue"); +} + EMSCRIPTEN_BINDINGS(Utility) { emscripten::class_("Vector2") .constructor<>() @@ -23,7 +43,6 @@ EMSCRIPTEN_BINDINGS(Conway) { .value("DEAD", LifeState::DEAD) .value("ALIVE", LifeState::ALIVE); - emscripten::register_vector>("Bool2DVector"); emscripten::class_("LifeGrid") .constructor() .function("dimensions", &LifeGrid::dimensions) @@ -33,34 +52,32 @@ EMSCRIPTEN_BINDINGS(Conway) { .function("Load", &LifeGrid::Load) .function("Compare", &LifeGrid::Compare) .function("Tick", &LifeGrid::Tick); - - emscripten::register_vector("Vector2Vector"); - emscripten::class_("StructureProperties"); + + emscripten::class_("StructureProperties") .constructor &>() .function("name", &StructureProperties::name) - .function("activation_cost", &StructureProperties::activation_cost) + .function("activationCost", &StructureProperties::activation_cost) .function("grid", &StructureProperties::grid) .function("income", &StructureProperties::income) - .function("build_area", &StructureProperties::build_area) + .function("buildArea", &StructureProperties::build_area) .function("checks", &StructureProperties::checks); emscripten::class_("Structure") .constructor() - .function("CheckIntegrity", &Structure::CheckIntegrity) + .function("getKey", &Structure::GetKey) + .function("checkIntegrity", &Structure::CheckIntegrity) .function("active", &Structure::active) .function("position", &Structure::position) .function("properties", &Structure::properties); } EMSCRIPTEN_BINDINGS(ConwayRTS) { - emscripten::register_vector("StructurePropertiesVector"); - emscripten::class_("Client") .constructor() .function("name", &Client::name) .function("id", &Client::id); - + emscripten::class_("Room") .constructor() .function("Initialize", &Room::Initialize) @@ -84,19 +101,19 @@ EMSCRIPTEN_BINDINGS(ConwayRTS) { .constructor<>() .constructor() .constructor &>() - .function("AddMember", &Team::AddMember) - .function("GetLeader", &Team::GetLeader) // these methods seem to break emcc - .function("SetLeader", &Team::SetLeader) - .function("AddEventToQueue", &Team::AddEventToQueue) - .function("Tick", &Team::Tick) - .function("CheckStructureIntegrity", &Team::CheckStructureIntegrity) - .function("AddStructure", &Team::AddStructure) + .function("addMember", &Team::AddMember) + .function("getLeader", + &Team::GetLeader) // these methods seem to break emcc + .function("setLeader", &Team::SetLeader) + // .function("addEventToQueue", &Team::AddEventToQueue) + // .function("tick", &Team::Tick) + .function("checkStructureIntegrity", &Team::CheckStructureIntegrity) + .function("addStructure", &Team::AddStructure) .function("id", &Team::id) .function("members", &Team::members) .function("structures", &Team::structures) - .function("event_queue", &Team::event_queue) + // .function("event_queue", &Team::event_queue) .function("resources", &Team::resources) .function("income", &Team::income) - .function("last_income_update", &Team::last_income_update) - ; + .function("lastIncomeUpdate", &Team::last_income_update); } diff --git a/client_wasm/conway.ts b/client_wasm/conway.ts index 456e64a..8b3f70c 100644 --- a/client_wasm/conway.ts +++ b/client_wasm/conway.ts @@ -56,6 +56,18 @@ namespace wasm { export interface HasDestructor { delete(): void; } + interface AssociativeContainer extends HasDestructor { + size(): number; + set(key: K, value: V): boolean; + get(key: K): V; + } + export interface Map extends AssociativeContainer { + keys(): Vector; + } + export interface Vector extends AssociativeContainer { + push_back(value: T): void; + } + export interface Vector2 extends HasDestructor { x(): number; y(): number; @@ -71,6 +83,23 @@ namespace wasm { Compare(payload: LifeGrid, position: Vector2): number; Tick(): void; } + export interface StructureProperties extends HasDestructor { + name(): string; + activationCost(): number; + grid(): LifeGrid; + income(): number; + buildArea(): number; + checks(): Vector; + } + /// a std::tuple of 4 ints + interface StructureKey {} + export interface Structure extends HasDestructor { + getKey(): StructureKey; + checkIntegrity(base: LifeGrid): boolean; + active(): boolean; + position(): Vector2; + properties(): StructureProperties; + } export interface Client extends HasDestructor { name(): string; id(): number; @@ -78,30 +107,65 @@ namespace wasm { export interface Room extends HasDestructor { Initialize(): void; SetName(newName: string): void; - // AddClient(client: Client): boolean; - AddTeam(teamMembers: Client[]): number; - // AddToTeam(teamId: Team, client: Client): boolean; + AddClient(client: Client): boolean; + AddTeam(teamMembers: Vector): number; + AddToTeam(teamId: Team, client: Client): boolean; GetTeam(teamId: number): Team; - LoadStructures(structures: StructureProperties[]): void; + LoadStructures(structures: Vector): void; Tick(): void; name(): string; - structureLookup(): StructureProperties[]; + structureLookup(): Vector; time(): number; - // clients(): any; - // teams(): any; + clients(): Map; + teams(): Map; grid(): LifeGrid; } - export interface Team extends HasDestructor {} -} + export interface Team extends HasDestructor { + checkStructureIntegrity(grid: LifeGrid): void; + addStructure(structure: Structure): void; + id(): number; + members(): Vector; + structures(): Map; + resources(): number; + income(): number; + lastIncomeUpdate(): number; + } -interface ConwayLib { - Vector2: Type; - LifeState: typeof LifeState; - LifeGrid: Type; - Client: Type; - Room: Type; - Team: Type; + export interface ConwayLib { + Vector2: { + new (x: number, y: number): Vector2; + }; + LifeState: typeof LifeState; + LifeGrid: { + new (dimensions: Vector2): LifeGrid; + }; + StructureProperties: { + new ( + name: string, + activationCost: number, + income: number, + buildArea: number, + grid: LifeGrid, + checks: Vector, + ): StructureProperties; + }; + Structure: { + new (properties: StructureProperties, position: Vector2): Structure; + }; + Client: { + new (id: number, name: string): Client; + }; + Room: { + new (name: string, dimensions: Vector2): Room; + }; + Team: { + new (id: number, members: Vector): Team; + }; + Vector2Vector: Type>; + ClientVector: Type>; + StructurePropertiesVector: Type>; + } } -const Conway = (await ConwayWasm()) as ConwayLib; +const Conway = (await ConwayWasm()) as wasm.ConwayLib; export default Conway; From 2acf052d8d62d63ace315d37b0416c5f4d68fcc7 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Thu, 4 Jan 2024 14:38:05 -0700 Subject: [PATCH 38/56] switch @usocket git_repository to http_archive --- WORKSPACE | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 91dfbb3..cced675 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -73,11 +73,12 @@ http_archive( url = "https://github.com/uNetworking/uWebSockets/archive/refs/tags/v20.51.0.tar.gz", ) -git_repository( +http_archive( name = "usockets", build_file = "@//externals:BUILD.usockets.bazel", - commit = "8cd4cb66eb061b2594ca114b9ea1ead64613ad4b", # v0.8.6 - remote = "git@github.com:uNetworking/uSockets.git", + integrity = "sha256-FuuhM90z6t4vX43YdhLAS13XEQZuBHHGDWQaL2qYjxY=", + strip_prefix = "uSockets-0.8.6", + url = "https://github.com/uNetworking/uSockets/archive/refs/tags/v0.8.6.tar.gz", ) git_repository( From de52266ddf3c4274100582a1a7041b1706ff3127 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 00:13:14 -0700 Subject: [PATCH 39/56] handle negative offset out of bounds --- conway/life_grid.cc | 1 + conway/life_grid_test.cc | 105 +++++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/conway/life_grid.cc b/conway/life_grid.cc index c732092..20c1974 100644 --- a/conway/life_grid.cc +++ b/conway/life_grid.cc @@ -60,6 +60,7 @@ void LifeGrid::ResetCell(const Vector2& coordinate) { bool LifeGrid::Load(const LifeGrid& life_grid, const Vector2& offset) { const Vector2 bottom_right = offset + life_grid.dimensions_; + if(offset.x() < 0 || offset.y() < 0) return false; if(bottom_right.x() > dimensions_.x() || bottom_right.y() > dimensions_.y()) return false; int oy = offset.y(); diff --git a/conway/life_grid_test.cc b/conway/life_grid_test.cc index 7fecde0..e02d7ab 100644 --- a/conway/life_grid_test.cc +++ b/conway/life_grid_test.cc @@ -4,15 +4,15 @@ TEST(LifeGrid, initialization) { LifeGrid grid(5, 5); - for(int i = 0; i < 5; ++i) { - for(int j = 0; j < 5; ++j) { + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { EXPECT_EQ(grid.GetCell(Vector2(i, j)), LifeState::DEAD); } } } TEST(LifeGrid, initialization_small_nonzero) { - const std::vector> base {{true, false}, {false, false}}; + const std::vector> base{{true, false}, {false, false}}; const LifeGrid grid(base); EXPECT_EQ(grid.GetCell(Vector2(0, 0)), LifeState::ALIVE); EXPECT_EQ(grid.GetCell(Vector2(1, 0)), LifeState::DEAD); @@ -21,41 +21,76 @@ TEST(LifeGrid, initialization_small_nonzero) { } TEST(LifeGrid, load_small) { - const std::vector> single {{true}}; + const std::vector> single{{true}}; LifeGrid grid(5, 5), small_grid(single); - for(int i = 0; i < 5; i ++){ - for(int j = 0; j < 5; j ++){ + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { grid.Load(small_grid, Vector2(i, j)); - EXPECT_EQ(grid.GetCell(Vector2(i, j)), LifeState::ALIVE) - << "loadTemplate should've updated the cell to alive" - " at " << Vector2(i, j); + EXPECT_EQ(grid.GetCell(Vector2(i, j)), LifeState::ALIVE) + << "loadTemplate should've updated the cell to alive" + " at " + << Vector2(i, j); } } } -TEST(LifeGrid, istream_extraction){ +TEST(LifeGrid, partial_out_of_bounds) { + LifeGrid grid(3, 2), payload({{1, 1}, {1, 1}}); + LifeGrid empty(grid); + + grid = empty; + EXPECT_EQ(grid.Load(payload, {-2, -2}), false) << "did not place"; + EXPECT_EQ(grid, empty) + << "the 2x2 placed at (-2, -2) is completely out of bounds"; + + grid = empty; + EXPECT_EQ(grid.Load(payload, {-1, -1}), false) << "did not place"; + EXPECT_EQ(grid, empty) + << "the 2x2 block placed at (-1, -1) is partially out of bounds"; + + grid = empty; + EXPECT_EQ(grid.Load(payload, {-1, 1}), false) << "did not place"; + EXPECT_EQ(grid, empty) + << "the 2x2 block placed at (-1, 1) is partially out of bounds"; + + grid = empty; + EXPECT_EQ(grid.Load(payload, {2, 1}), false) << "did not place"; + EXPECT_EQ(grid, empty) + << "the 2x2 block placed at (2, -1) is partially out of bounds"; + + grid = empty; + EXPECT_EQ(grid.Load(payload, {2, 1}), false) << "did not place"; + EXPECT_EQ(grid, empty) + << "the 2x2 block placed at (2, 1) is partially out of bounds"; + + grid = empty; + EXPECT_EQ(grid.Load(payload, {1, 0}), true) << "should've placed"; + EXPECT_EQ(grid, LifeGrid({{0, 1, 1}, {0, 1, 1}})) + << "the 2x2 block placed at (1, 0) is in bounds"; +} + +TEST(LifeGrid, istream_extraction) { std::istringstream in("3 3 ### #.# .#."); LifeGrid grid; in >> grid; - std::vector> expected { - {true, true, true}, - {true, false, true}, - {false, true, false}, + std::vector> expected{ + {true, true, true}, + {true, false, true}, + {false, true, false}, }; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - EXPECT_EQ(grid.GetCell(Vector2(i, j)), expected[j][i] ? LifeState::ALIVE : LifeState::DEAD); + EXPECT_EQ(grid.GetCell(Vector2(i, j)), + expected[j][i] ? LifeState::ALIVE : LifeState::DEAD); } } } TEST(LifeGrid, templateDifference) { - std::istringstream in( - "1 1 # " - "3 5 ##### ###.. ..### " - "2 2 ## ## " - "3 3 ### ... ###" - ); + std::istringstream in("1 1 # " + "3 5 ##### ###.. ..### " + "2 2 ## ## " + "3 3 ### ... ###"); LifeGrid single, large, block, hamburger; in >> single >> large >> block >> hamburger; @@ -63,25 +98,23 @@ TEST(LifeGrid, templateDifference) { LifeGrid grid(5, 5); EXPECT_EQ(grid.Compare(single, Vector2(0, 0)), 1) - << "initial board is all dead"; + << "initial board is all dead"; EXPECT_EQ(grid.Compare(large, Vector2(0, 0)), 11) - << "initial board is all dead so there should be 5+3+3=11 alive" - "from the template (that aren't alive on the board)"; + << "initial board is all dead so there should be 5+3+3=11 alive" + "from the template (that aren't alive on the board)"; grid.Load(block, Vector2(1, 1)); EXPECT_EQ(grid.Compare(block, Vector2(1, 1)), 0) - << "template should've been loaded correctly (so now difference is zero)"; + << "template should've been loaded correctly (so now difference is zero)"; EXPECT_EQ(grid.Compare(block, Vector2(0, 0)), 3) - << "only one of the original set cells is alive in this shifted offset"; - EXPECT_EQ(grid.Compare(hamburger, Vector2(0, 0)), 3+2+1); + << "only one of the original set cells is alive in this shifted offset"; + EXPECT_EQ(grid.Compare(hamburger, Vector2(0, 0)), 3 + 2 + 1); } -TEST(LifeGrid, nextGeneration){ - std::istringstream in( - "3 3 .#. ..# ### " - "4 3 ... #.# .## .#. " - "4 3 ... ..# #.# .## " - "4 4 .... .#.. ..## .##. " - ); +TEST(LifeGrid, nextGeneration) { + std::istringstream in("3 3 .#. ..# ### " + "4 3 ... #.# .## .#. " + "4 3 ... ..# #.# .## " + "4 4 .... .#.. ..## .##. "); // .#.. .... .... .... .... // ..#. #.#. ..#. .#.. ..#. // ###. .##. #.#. ..## ...# @@ -92,7 +125,7 @@ TEST(LifeGrid, nextGeneration){ LifeGrid grid(10, 10); Vector2 offset(2, 2); grid.Load(glider1, offset); - for(int steps = 0; steps < 3; steps ++){ + for (int steps = 0; steps < 3; steps++) { EXPECT_EQ(grid.Compare(glider1, offset), 0); grid.Tick(); EXPECT_EQ(grid.Compare(glider2, offset), 0); @@ -119,7 +152,7 @@ TEST(LifeGrid, copy_into_proto) { glider.CopyToProtobuf(pb); EXPECT_EQ(pb.dimensions().x(), 3); EXPECT_EQ(pb.dimensions().y(), 4); - const std::vector expected { 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0 }; + const std::vector expected{0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0}; for (int i = 0; i < 12; ++i) { EXPECT_EQ(pb.flat_grid().at(i), expected[i]) << "checking i=" << i; } From 42ef6cb6d1a7d34bec329652da4d87102fd7aa1a Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 00:15:29 -0700 Subject: [PATCH 40/56] setup basic client that uses life_grid wasm and allows loading --- client/src/App.tsx | 2 + client/src/GameClient.tsx | 155 ++++++++++++++++++++++++++++++++++++++ package.json | 3 +- pnpm-lock.yaml | 23 ++++++ 4 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 client/src/GameClient.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index cc0cd47..796844b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -4,6 +4,7 @@ import SocketUtils from "./SocketUtils"; import { ClientWebSocket } from "./Socket"; import RoomListing from "./RoomListing"; import Lobby from "./Lobby"; +import GameClient from "./GameClient"; const server_url = "ws://localhost:3000"; @@ -25,6 +26,7 @@ function App() { {clientSocket && } {clientSocket && } {clientSocket && } + {clientSocket && } ); } diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx new file mode 100644 index 0000000..69afcf9 --- /dev/null +++ b/client/src/GameClient.tsx @@ -0,0 +1,155 @@ +import { useEffect } from "react"; +import { RoomCreateData, UseClientSocket } from "./Socket"; +import { P5CanvasInstance, ReactP5Wrapper } from "@p5-wrapper/react"; +import Conway from "@/lib"; + +function GameClient({ clientSocket }: UseClientSocket) { + const grid = new Conway.LifeGrid(new Conway.Vector2(100, 100)); + const structureTypes = [ + [ + [1, 1], + [1, 1], + ], + [ + [0, 1, 0], + [0, 0, 1], + [1, 1, 1], + ], + [ + [0, 0, 0, 0, 0, 0, 1, 0], + [1, 1, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 1, 1], + ], + ].map((baseGrid) => { + const n = baseGrid.length; + const m = baseGrid[0].length; + const layout = new Conway.LifeGrid(new Conway.Vector2(m, n)); + for (let i = 0; i < n; ++i) { + for (let j = 0; j < m; ++j) { + if (baseGrid[i][j]) { + layout.SetCell(new Conway.Vector2(j, i)); + } + } + } + return new Conway.StructureProperties("A", 0, 0, 0, layout, new Conway.Vector2Vector()); // prettier-ignore + }); + let display = ""; + + useEffect(() => { + const processEvent = (event: CustomEvent) => { + const { name } = event.detail; + display = name; + }; + clientSocket.addEventListener("roomCreate", processEvent); + return () => clientSocket.removeEventListener("roomCreate", processEvent); + }, []); + + let timeMax = 0; + useEffect(() => { + const interval = setInterval(() => ++timeMax, 50); + return () => clearInterval(interval); + }, []); + + function sketch(p5: P5CanvasInstance) { + p5.preload = () => {}; + p5.setup = () => { + p5.createCanvas(600, 400, p5.P2D); + p5.rectMode(p5.CENTER); + p5.textAlign(p5.CENTER, p5.CENTER); + }; + let cameraX = 0; + let cameraY = 0; + let time = 0; + const SIDE_LENGTH = 10; + function renderLifeGrid( + grid: InstanceType, + offsetI: number = 0, + offsetJ: number = 0, + aliveColor: number | number[] = 255, + deadColor: number | number[] = 0, + ) { + const n = grid.dimensions().y(); + const m = grid.dimensions().x(); + const padding = 1; + const i_begin = ~~Math.max(0, cameraY / SIDE_LENGTH - padding - offsetI); + const i_end = ~~Math.min( + n, + (cameraY + p5.height) / SIDE_LENGTH + padding - offsetI, + ); + const j_begin = ~~Math.max(0, cameraX / SIDE_LENGTH - padding - offsetJ); + const j_end = ~~Math.min( + m, + (cameraX + p5.width) / SIDE_LENGTH + padding - offsetJ, + ); + p5.push(); + p5.translate(offsetJ * SIDE_LENGTH, offsetI * SIDE_LENGTH); + for (let i = i_begin; i < i_end; ++i) { + for (let j = j_begin; j < j_end; ++j) { + p5.fill( + grid.GetCell(new Conway.Vector2(j, i)) ? aliveColor : deadColor, + ); + p5.rect( + j * SIDE_LENGTH, + i * SIDE_LENGTH, + SIDE_LENGTH - 1, + SIDE_LENGTH - 1, + ); + } + } + p5.pop(); + } + let mousePressed = false; + p5.mousePressed = () => (mousePressed = true); + p5.keyPressed = () => ++timeMax; + p5.draw = () => { + if (time < timeMax) { + ++time; + grid.Tick(); + } + p5.background(250); + p5.noStroke(); + if (p5.mouseIsPressed) { + cameraX -= p5.mouseX - p5.pmouseX; + cameraY -= p5.mouseY - p5.pmouseY; + } + p5.push(); + p5.translate(-cameraX, -cameraY); + const mouseI = Math.max( + 0, + Math.min(100, Math.round((p5.mouseY + cameraY) / SIDE_LENGTH)), + ); + const mouseJ = Math.max( + 0, + Math.min(100, Math.round((p5.mouseX + cameraX) / SIDE_LENGTH)), + ); + renderLifeGrid(grid, 0, 0); + const selectedStructureType = structureTypes[1]; + renderLifeGrid( + selectedStructureType.grid(), + mouseI - (selectedStructureType.grid().dimensions().y() >> 1), + mouseJ - (selectedStructureType.grid().dimensions().x() >> 1), + ); + if (mousePressed) { + const i = mouseI - (selectedStructureType.grid().dimensions().y() >> 1); + const j = mouseJ - (selectedStructureType.grid().dimensions().x() >> 1); + grid.Load(selectedStructureType.grid(), new Conway.Vector2(j, i)); + } + // renderLifeGrid(structureTypes[2].grid(), 0, 0); + p5.pop(); + p5.fill(200); + p5.rect(100, 15, 50, 30); + p5.fill(255, 0, 0); + p5.text(p5.frameRate().toFixed(1) + "FPS", 100, 10); + p5.text(`${mouseJ},${mouseI}`, 100, 20); + mousePressed = false; + }; + } + + return ( + <> + + + ); +} + +export default GameClient; diff --git a/package.json b/package.json index 4326256..54003df 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "conway-rts", + "name": "conway-rts-client", "version": "0.0.1", "description": "a realtime strategy game based on conway's game of life", "main": "index.js", @@ -49,6 +49,7 @@ "vitest": "^1.1.0" }, "dependencies": { + "@p5-wrapper/react": "^4.3.2", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 753db3d..7dc5050 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@p5-wrapper/react': + specifier: ^4.3.2 + version: 4.3.2(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -627,6 +630,18 @@ packages: fastq: 1.16.0 dev: true + /@p5-wrapper/react@4.3.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-m7MoL63grVVyu39czEi3V3tz29VM4zzd4K9MjNWZ5t7jb4itgHpw6FCiSG2JzU1SqElKgo9ny8NliavPWYYdrw==} + peerDependencies: + react: '>= 18.2.0' + react-dom: '>= 18.2.0' + dependencies: + microdiff: 1.3.2 + p5: 1.9.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@protobufjs/aspromise@1.1.2: resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} dev: true @@ -1934,6 +1949,10 @@ packages: engines: {node: '>= 8'} dev: true + /microdiff@1.3.2: + resolution: {integrity: sha512-pKy60S2febliZIbwdfEQKTtL5bLNxOyiRRmD400gueYl9XcHyNGxzHSlJWn9IMHwYXT0yohPYL08+bGozVk8cQ==} + dev: false + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -2069,6 +2088,10 @@ packages: p-limit: 3.1.0 dev: true + /p5@1.9.0: + resolution: {integrity: sha512-+5/hz0ZokCDf7BMMAeemE7FIo7gFZK7ImL62acHLXZwerGjqj+171bnaAWj4aCFCx6fwysAr2U7/AKuPyPhehA==} + dev: false + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} From ddde14ebcb68a4882616c534764e2a7eb5ec5461 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 00:17:30 -0700 Subject: [PATCH 41/56] don't track MODULE.bazel.lock for now --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0bca6d0..cafbd84 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,7 @@ CMakeCache.txt bazel-bin bazel-conway-rts bazel-out -bazel-testlogs \ No newline at end of file +bazel-testlogs + +# removeMODULE.bazel.lock after modules have been migrated +MODULE.bazel.lock From 99ba8469d1f601ae6c68a0ffbf8d55894d09160c Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 13:00:41 -0700 Subject: [PATCH 42/56] update dependencies installation instructions for windows --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70e5093..b9cee07 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,22 @@ # conway-rts conway rts cyber project (but still being designed) +## Dependencies + +### Windows +- [chocolately](https://chocolatey.org/install) + - [bazelisk](https://github.com/bazelbuild/bazelisk) + - [git](https://community.chocolatey.org/packages/git.install) + - [python](https://community.chocolatey.org/packages/python/3.12.1) + - [nvm](https://community.chocolatey.org/packages/nvm) +- [VS Build Tools, C++ compiler](https://visualstudio.microsoft.com/downloads/?q=build+tools#build-tools-for-visual-studio-2022) + +### Linux +- [bazelisk](https://github.com/bazelbuild/bazelisk) +- *work in progress + ## Running -Install [bazelisk](https://github.com/bazelbuild/bazelisk) ``` -bazelisk # installs bazel with specific version bazel query ... # list targets bazel test ... # run all tests bazel build ... # build all targets (config pattern still applies) From 6b2500cc3065f7401da45f6c1a5fade98b787a83 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 16:08:39 -0800 Subject: [PATCH 43/56] remove unneeded c++17 flag in linux for boringssl --- externals/BUILD.boringssl.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/BUILD.boringssl.bazel b/externals/BUILD.boringssl.bazel index 920fe3e..c590789 100644 --- a/externals/BUILD.boringssl.bazel +++ b/externals/BUILD.boringssl.bazel @@ -1,6 +1,6 @@ CC_OPTS = select({ "@bazel_tools//src/conditions:windows": ["/std:c++17"], - "//conditions:default": ["--std=c++17"], + "//conditions:default": [], }) cc_library( From a15ac5550f4e99a22e520d218db5ce69284f7ee4 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 16:10:16 -0800 Subject: [PATCH 44/56] linux compatibility for @libuv tested on ubuntu 22.04.3 LTS desktop --- externals/BUILD.libuv.bazel | 78 ++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/externals/BUILD.libuv.bazel b/externals/BUILD.libuv.bazel index 88e4604..f3be01c 100644 --- a/externals/BUILD.libuv.bazel +++ b/externals/BUILD.libuv.bazel @@ -1,12 +1,63 @@ """ https://github.com/libuv/help/issues/83 https://github.com/bazelregistry/libuv/commit/b8e9b269bc7294f485e4be030b3340017745ca58 +https://github.com/libuv/libuv/blob/v1.47.0/CMakeLists.txt """ _win_srcs = glob(["src/win/**/*"]) _unix_srcs = glob(["src/unix/**/*"]) +SRCS_COMMON = [ + "src/fs-poll.c", + "src/idna.c", + "src/inet.c", + "src/random.c", + "src/strscpy.c", + "src/strtok.c", + "src/thread-common.c", + "src/threadpool.c", + "src/timer.c", + "src/uv-common.c", + "src/uv-data-getter-setters.c", + "src/version.c", +] + +SRCS_UNIX = SRCS_COMMON + [ + "src/unix/async.c", + "src/unix/core.c", + "src/unix/dl.c", + "src/unix/fs.c", + "src/unix/getaddrinfo.c", + "src/unix/getnameinfo.c", + "src/unix/loop-watcher.c", + "src/unix/loop.c", + "src/unix/pipe.c", + "src/unix/poll.c", + "src/unix/process.c", + "src/unix/random-devurandom.c", + "src/unix/signal.c", + "src/unix/stream.c", + "src/unix/tcp.c", + "src/unix/thread.c", + "src/unix/tty.c", + "src/unix/udp.c", +] + +SRCS_DARWIN_ANDROID_LINUX = SRCS_UNIX + ["src/unix/proctitle.c"] + +SRCS_LINUX = SRCS_DARWIN_ANDROID_LINUX + [ + "src/unix/linux.c", + "src/unix/procfs-exepath.c", + "src/unix/random-getrandom.c", + "src/unix/random-sysctl-linux.c", +] + +DEFS_LINUX = [ + "_GNU_SOURCE", + "_POSIX_C_SOURCE=200112", +] + _srcs = glob(["src/*"]) _win_linkopts = [ @@ -25,16 +76,33 @@ cc_library( name = "libuv", srcs = select({ "@bazel_tools//src/conditions:windows": _srcs + _win_srcs, - "//conditions:default": _srcs + _unix_srcs, + "@bazel_tools//src/conditions:linux": SRCS_LINUX, + "//conditions:default": _srcs + _unix_srcs, # doesn't work since it includes too many (different Unix's) + }), + hdrs = glob([ + "include/**/*", + "src/*.h", + ]) + select({ + "@bazel_tools//src/conditions:linux": glob(["src/unix/*.h"]), + "//conditions:default": [], + }), + defines = select({ + "@bazel_tools//src/conditions:windows": [], + "@bazel_tools//src/conditions:linux": DEFS_LINUX, + "//conditions:default": [], }), - hdrs = glob(["include/**/*"]), includes = [ - "include/", - "src/", + "include", + "src", ], linkopts = select({ "@bazel_tools//src/conditions:windows": _win_linkopts, - "//conditions:default": [], + "//conditions:default": [ + "-lpthread", + "-ldl", + "-lrt", + ], }), + linkstatic = True, visibility = ["//visibility:public"], ) From f981f80e6c03bcb7f2b189ff5e257f4dd40c73fd Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 16:19:24 -0800 Subject: [PATCH 45/56] reduce COPTS c++ version for @lsquic --- externals/BUILD.lsquic.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/externals/BUILD.lsquic.bazel b/externals/BUILD.lsquic.bazel index 9444bbf..ef179ad 100644 --- a/externals/BUILD.lsquic.bazel +++ b/externals/BUILD.lsquic.bazel @@ -4,8 +4,8 @@ lsquic bazel build file # c++17 needed for sys/uio.h on windows CC_OPTS = select({ - "@bazel_tools//src/conditions:windows": ["/std:c++20"], - "//conditions:default": ["--std=c++20"], + "@bazel_tools//src/conditions:windows": ["/std:c++17"], + "//conditions:default": [], }) cc_library( From 95db335b817e3772c670fca2c221bbba2bdef8ef Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 16:19:54 -0800 Subject: [PATCH 46/56] add files neccessary for linux to build @lsquic --- externals/BUILD.lsquic.bazel | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/externals/BUILD.lsquic.bazel b/externals/BUILD.lsquic.bazel index ef179ad..f6d270a 100644 --- a/externals/BUILD.lsquic.bazel +++ b/externals/BUILD.lsquic.bazel @@ -1,5 +1,9 @@ """ lsquic bazel build file + +lsquic v3.2.0 +https://github.com/litespeedtech/lsquic/blob/v3.2.0/CMakeLists.txt +https://github.com/litespeedtech/lsquic/blob/v3.2.0/src/liblsquic/CMakeLists.txt """ # c++17 needed for sys/uio.h on windows @@ -10,10 +14,11 @@ CC_OPTS = select({ cc_library( name = "lsquic", - srcs = glob(["src/liblsquic/ls*.c"]) + ["src/liblsquic/ls-qpack/lsqpack.c"], + srcs = glob(["src/liblsquic/lsquic_*.c"]) + ["src/liblsquic/ls-qpack/lsqpack.c"], hdrs = glob([ "include/*.h", "src/**/*.h", + "src/liblsquic/common_*", ]), copts = CC_OPTS, defines = select({ From d7621c65f570b8e3bb8fc440914212d14b077e59 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 16:20:25 -0800 Subject: [PATCH 47/56] fix linux COPTS --- cc_config.bzl | 2 +- externals/BUILD.usockets.bazel | 2 +- externals/BUILD.uwebsockets.bazel | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cc_config.bzl b/cc_config.bzl index 20b66ed..d0af4a7 100644 --- a/cc_config.bzl +++ b/cc_config.bzl @@ -4,5 +4,5 @@ CC Compiler options to be used for all project files. CC_OPTS = select({ "@bazel_tools//src/conditions:windows": ["/std:c++20"], - "//conditions:default": ["--std=c++20"], + "//conditions:default": ["-std=c++20"], }) diff --git a/externals/BUILD.usockets.bazel b/externals/BUILD.usockets.bazel index 025f27d..d4147a2 100644 --- a/externals/BUILD.usockets.bazel +++ b/externals/BUILD.usockets.bazel @@ -1,6 +1,6 @@ CC_OPTS = select({ "@bazel_tools//src/conditions:windows": ["/std:c++17"], - "//conditions:default": ["--std=c++17"], + "//conditions:default": ["-std=c++17"], }) cc_library( diff --git a/externals/BUILD.uwebsockets.bazel b/externals/BUILD.uwebsockets.bazel index ea73c57..094a493 100644 --- a/externals/BUILD.uwebsockets.bazel +++ b/externals/BUILD.uwebsockets.bazel @@ -1,6 +1,6 @@ CC_OPTS = select({ "@bazel_tools//src/conditions:windows": ["/std:c++17"], - "//conditions:default": ["--std=c++17"], + "//conditions:default": ["-std=c++17"], }) cc_library( From 4622a72e3bb3ad1ba4da09c71f4422cff88f1fcd Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 22:52:00 -0700 Subject: [PATCH 48/56] document linux dependencies tfw outdated compiler --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b9cee07..8f75a29 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ conway rts cyber project (but still being designed) ### Linux - [bazelisk](https://github.com/bazelbuild/bazelisk) -- *work in progress +- gcc version 8+ (using c++2a), ideally gcc version 10+ (for c++20 support) + - [gsrc](https://www.gnu.org/software/gsrc/) can be used if pre-built binaries aren't available. ## Running ``` From 8d0f57ff83e4152112209eb9ca2c2ff29bb25794 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:03:07 -0700 Subject: [PATCH 49/56] add colored corners for rotational orientation --- client/src/GameClient.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx index 69afcf9..b611ffc 100644 --- a/client/src/GameClient.tsx +++ b/client/src/GameClient.tsx @@ -83,6 +83,10 @@ function GameClient({ clientSocket }: UseClientSocket) { ); p5.push(); p5.translate(offsetJ * SIDE_LENGTH, offsetI * SIDE_LENGTH); + p5.fill(255, 0, 0); + p5.rect(0, 0, SIDE_LENGTH + 2, SIDE_LENGTH + 2); + p5.fill(0, 255, 0); + p5.rect((m - 1) * SIDE_LENGTH, (n - 1) * SIDE_LENGTH, SIDE_LENGTH + 2, SIDE_LENGTH + 2); // prettier-ignore for (let i = i_begin; i < i_end; ++i) { for (let j = j_begin; j < j_end; ++j) { p5.fill( From 5819be485b24f83f0e07d505f2f88b91ab55339e Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:03:25 -0700 Subject: [PATCH 50/56] swap colors to lessen constrast --- client/src/GameClient.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx index b611ffc..6e6740c 100644 --- a/client/src/GameClient.tsx +++ b/client/src/GameClient.tsx @@ -65,8 +65,8 @@ function GameClient({ clientSocket }: UseClientSocket) { grid: InstanceType, offsetI: number = 0, offsetJ: number = 0, - aliveColor: number | number[] = 255, - deadColor: number | number[] = 0, + aliveColor: number | number[] = 0, + deadColor: number | number[] = 255, ) { const n = grid.dimensions().y(); const m = grid.dimensions().x(); From db5317078c08f05bbd55f7909d271fb54a713224 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:23:12 -0700 Subject: [PATCH 51/56] bounds checking for LifeGrid::Compare --- conway/life_grid.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conway/life_grid.cc b/conway/life_grid.cc index 20c1974..8f7757f 100644 --- a/conway/life_grid.cc +++ b/conway/life_grid.cc @@ -74,6 +74,10 @@ bool LifeGrid::Load(const LifeGrid& life_grid, const Vector2& offset) { } int LifeGrid::Compare(const LifeGrid& life_grid, const Vector2& offset) const { + const Vector2 bottom_right = offset + life_grid.dimensions_; + if(offset.x() < 0 || offset.y() < 0) return -1; + if(bottom_right.x() > dimensions_.x() || bottom_right.y() > dimensions_.y()) return -1; + int diff_count = 0; int oy = offset.y(); int ox = offset.x(); From 3b10545ad3affe52e38e4a54cb6e2aa034dd5945 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:31:03 -0700 Subject: [PATCH 52/56] add preview data and structure switching --- client/src/GameClient.tsx | 59 +++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx index 6e6740c..4ab5366 100644 --- a/client/src/GameClient.tsx +++ b/client/src/GameClient.tsx @@ -10,6 +10,13 @@ function GameClient({ clientSocket }: UseClientSocket) { [1, 1], [1, 1], ], + [ + [1, 1, 0, 1, 1], + [1, 1, 0, 1, 1], + [0, 0, 0, 0, 0], + [1, 1, 0, 1, 1], + [1, 1, 0, 1, 1], + ], [ [0, 1, 0], [0, 0, 1], @@ -33,6 +40,7 @@ function GameClient({ clientSocket }: UseClientSocket) { } return new Conway.StructureProperties("A", 0, 0, 0, layout, new Conway.Vector2Vector()); // prettier-ignore }); + let selectedStructureType = structureTypes[0]; let display = ""; useEffect(() => { @@ -57,6 +65,15 @@ function GameClient({ clientSocket }: UseClientSocket) { p5.rectMode(p5.CENTER); p5.textAlign(p5.CENTER, p5.CENTER); }; + p5.keyTyped = () => { + const { keyCode } = p5; + if (keyCode >= 49 && keyCode <= 57) { + const id = keyCode - 49; + if (id < structureTypes.length) { + selectedStructureType = structureTypes[id]; + } + } + }; let cameraX = 0; let cameraY = 0; let time = 0; @@ -65,9 +82,11 @@ function GameClient({ clientSocket }: UseClientSocket) { grid: InstanceType, offsetI: number = 0, offsetJ: number = 0, - aliveColor: number | number[] = 0, - deadColor: number | number[] = 255, + aliveColor: number | number[] = [0], + deadColor: number | number[] = [255], ) { + if (aliveColor instanceof Number) aliveColor = [aliveColor as number]; + if (deadColor instanceof Number) deadColor = [deadColor as number]; const n = grid.dimensions().y(); const m = grid.dimensions().x(); const padding = 1; @@ -83,14 +102,16 @@ function GameClient({ clientSocket }: UseClientSocket) { ); p5.push(); p5.translate(offsetJ * SIDE_LENGTH, offsetI * SIDE_LENGTH); - p5.fill(255, 0, 0); + p5.fill(255, 0, 0, 100); // corner markers p5.rect(0, 0, SIDE_LENGTH + 2, SIDE_LENGTH + 2); - p5.fill(0, 255, 0); + p5.fill(0, 255, 0, 100); p5.rect((m - 1) * SIDE_LENGTH, (n - 1) * SIDE_LENGTH, SIDE_LENGTH + 2, SIDE_LENGTH + 2); // prettier-ignore for (let i = i_begin; i < i_end; ++i) { for (let j = j_begin; j < j_end; ++j) { p5.fill( - grid.GetCell(new Conway.Vector2(j, i)) ? aliveColor : deadColor, + ...((grid.GetCell(new Conway.Vector2(j, i)) + ? aliveColor + : deadColor) as number[]), ); p5.rect( j * SIDE_LENGTH, @@ -127,19 +148,33 @@ function GameClient({ clientSocket }: UseClientSocket) { Math.min(100, Math.round((p5.mouseX + cameraX) / SIDE_LENGTH)), ); renderLifeGrid(grid, 0, 0); - const selectedStructureType = structureTypes[1]; + + const placeI = + mouseI - (selectedStructureType.grid().dimensions().y() >> 1); + const placeJ = + mouseJ - (selectedStructureType.grid().dimensions().x() >> 1); + const placeVector = new Conway.Vector2(placeJ, placeI); renderLifeGrid( selectedStructureType.grid(), - mouseI - (selectedStructureType.grid().dimensions().y() >> 1), - mouseJ - (selectedStructureType.grid().dimensions().x() >> 1), + placeI, + placeJ, + [50, 0, 0, 100], + [255, 200, 200, 100], ); + p5.push(); + p5.fill(0); + p5.text(selectedStructureType.name(), mouseJ * SIDE_LENGTH, (placeI-1) * SIDE_LENGTH); // prettier-ignore + p5.textAlign(p5.RIGHT, p5.CENTER); + p5.translate((placeJ - 0.5) * SIDE_LENGTH, 0); + p5.text(grid.Compare(selectedStructureType.grid(), placeVector), 0, placeI * SIDE_LENGTH); // prettier-ignore + p5.text(selectedStructureType.activationCost(), 0, (placeI+1) * SIDE_LENGTH); // prettier-ignore + p5.text(selectedStructureType.income() + "/t", 0, (placeI+2) * SIDE_LENGTH); // prettier-ignore + p5.pop(); if (mousePressed) { - const i = mouseI - (selectedStructureType.grid().dimensions().y() >> 1); - const j = mouseJ - (selectedStructureType.grid().dimensions().x() >> 1); - grid.Load(selectedStructureType.grid(), new Conway.Vector2(j, i)); + grid.Load(selectedStructureType.grid(), placeVector); } - // renderLifeGrid(structureTypes[2].grid(), 0, 0); p5.pop(); + p5.fill(200); p5.rect(100, 15, 50, 30); p5.fill(255, 0, 0); From cbb8a47c351961413ca31aea773f529cbfcd196e Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:34:16 -0700 Subject: [PATCH 53/56] use spacebar to place instead of onclick (since drag is used to look around) --- client/src/GameClient.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx index 4ab5366..07db7fc 100644 --- a/client/src/GameClient.tsx +++ b/client/src/GameClient.tsx @@ -65,9 +65,12 @@ function GameClient({ clientSocket }: UseClientSocket) { p5.rectMode(p5.CENTER); p5.textAlign(p5.CENTER, p5.CENTER); }; + let placeNow = false; p5.keyTyped = () => { const { keyCode } = p5; - if (keyCode >= 49 && keyCode <= 57) { + if (keyCode === 32) { + placeNow = true; + } else if (keyCode >= 49 && keyCode <= 57) { const id = keyCode - 49; if (id < structureTypes.length) { selectedStructureType = structureTypes[id]; @@ -125,7 +128,6 @@ function GameClient({ clientSocket }: UseClientSocket) { } let mousePressed = false; p5.mousePressed = () => (mousePressed = true); - p5.keyPressed = () => ++timeMax; p5.draw = () => { if (time < timeMax) { ++time; @@ -170,8 +172,9 @@ function GameClient({ clientSocket }: UseClientSocket) { p5.text(selectedStructureType.activationCost(), 0, (placeI+1) * SIDE_LENGTH); // prettier-ignore p5.text(selectedStructureType.income() + "/t", 0, (placeI+2) * SIDE_LENGTH); // prettier-ignore p5.pop(); - if (mousePressed) { + if (placeNow) { grid.Load(selectedStructureType.grid(), placeVector); + placeNow = false; } p5.pop(); @@ -181,6 +184,7 @@ function GameClient({ clientSocket }: UseClientSocket) { p5.text(p5.frameRate().toFixed(1) + "FPS", 100, 10); p5.text(`${mouseJ},${mouseI}`, 100, 20); mousePressed = false; + placeNow = false; }; } From e7c54411c0eb3f772a6c3fccb33a6c71cc13f7e1 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:48:36 -0700 Subject: [PATCH 54/56] increase grid size --- client/src/GameClient.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/GameClient.tsx b/client/src/GameClient.tsx index 07db7fc..bbee7c5 100644 --- a/client/src/GameClient.tsx +++ b/client/src/GameClient.tsx @@ -4,7 +4,7 @@ import { P5CanvasInstance, ReactP5Wrapper } from "@p5-wrapper/react"; import Conway from "@/lib"; function GameClient({ clientSocket }: UseClientSocket) { - const grid = new Conway.LifeGrid(new Conway.Vector2(100, 100)); + const grid = new Conway.LifeGrid(new Conway.Vector2(200, 200)); const structureTypes = [ [ [1, 1], @@ -143,11 +143,11 @@ function GameClient({ clientSocket }: UseClientSocket) { p5.translate(-cameraX, -cameraY); const mouseI = Math.max( 0, - Math.min(100, Math.round((p5.mouseY + cameraY) / SIDE_LENGTH)), + Math.min(grid.dimensions().y(), Math.round((p5.mouseY + cameraY) / SIDE_LENGTH)), ); const mouseJ = Math.max( 0, - Math.min(100, Math.round((p5.mouseX + cameraX) / SIDE_LENGTH)), + Math.min(grid.dimensions().x(), Math.round((p5.mouseX + cameraX) / SIDE_LENGTH)), ); renderLifeGrid(grid, 0, 0); From 29fab374baba640638b2b9ae85fc99224aaf97df Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Fri, 5 Jan 2024 23:49:00 -0700 Subject: [PATCH 55/56] enable compiler optimizations for emscripten --- client_wasm/BUILD.bazel | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client_wasm/BUILD.bazel b/client_wasm/BUILD.bazel index 5dce7b9..0018ccc 100644 --- a/client_wasm/BUILD.bazel +++ b/client_wasm/BUILD.bazel @@ -9,6 +9,7 @@ DEFAULT_EMSCRIPTEN_LINKOPTS = [ # "-flto", # Specify lto (has to be set on for compiler as well) "--bind", # Compiles the source code using the Embind bindings to connect C/C++ and JavaScript "--closure 1", # Run the closure compiler + "-O3", # "--embind-emit-tsd interface.d.ts", # emit typescript types file, doesn't work (doesn't seem to be supported) # "-s EXPORTED_FUNCTIONS=_free,_malloc", "-s MALLOC=emmalloc", # Switch to using the much smaller implementation @@ -52,6 +53,7 @@ cc_binary( wasm_cc_binary( name = "conway_lib_wasm", cc_target = ":conway_lib_wasm_embind", + simd = True, ) cc_binary( From 0bb56364f3df3a555194a8f4ec7bd15e7d6641a8 Mon Sep 17 00:00:00 2001 From: NovaSagittarii Date: Sun, 21 Apr 2024 00:41:12 -0700 Subject: [PATCH 56/56] update emscripten deps version --- WORKSPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WORKSPACE b/WORKSPACE index cced675..7e0e23f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -56,7 +56,7 @@ emsdk_deps() load("@emsdk//:emscripten_deps.bzl", emsdk_emscripten_deps = "emscripten_deps") -emsdk_emscripten_deps(emscripten_version = "3.1.46") +emsdk_emscripten_deps(emscripten_version = "3.1.51") load("@emsdk//:toolchains.bzl", "register_emscripten_toolchains")