diff --git a/authserver/index.js b/authserver/index.js new file mode 100644 index 0000000..2c88ee5 --- /dev/null +++ b/authserver/index.js @@ -0,0 +1,102 @@ +import express from "express"; +import bodyParser from "body-parser"; +import crypto from "crypto"; + +const app = express(); +app.use(bodyParser.json()); + +// In-memory stores +const users = {}; // { userId: username } +const sessions = {}; // { token: userId } + +// Basic HTML UI +const HTML_PAGE = ` + + + + Fake Auth API (Express) + + + +

Fake Auth API

+
+
+
+ +
+

Users

+
+ + + + +`; + +// === Routes === + +// UI page +app.get("/", (req, res) => res.send(HTML_PAGE)); + +// Get users +app.get("/api/users", (req, res) => res.json(users)); + +// Add user +app.post("/api/users", (req, res) => { + const { id, name } = req.body; + if (!id || !name) return res.status(400).json({ error: "Missing id or name" }); + users[id] = name; + res.json({ status: "ok" }); +}); + +// Create token +app.post("/api/create_token/:uid", (req, res) => { + const uid = req.params.uid; + if (!users[uid]) return res.status(404).json({ error: "User not found" }); + const token = crypto.randomBytes(8).toString("hex"); + sessions[token] = uid; + res.json({ token }); +}); + +// Verify token +app.get("/api/verify/:token", (req, res) => { + const uid = sessions[req.params.token]; + if (!uid) return res.status(404).json({ error: "Invalid token" }); + res.json({ id: uid, name: users[uid] }); +}); + +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => console.log(`[FakeAuth] Running on http://localhost:${PORT}`)); diff --git a/authserver/package-lock.json b/authserver/package-lock.json new file mode 100644 index 0000000..0caa45f --- /dev/null +++ b/authserver/package-lock.json @@ -0,0 +1,842 @@ +{ + "name": "authserver", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "authserver", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^5.1.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/authserver/package.json b/authserver/package.json new file mode 100644 index 0000000..ed4ea05 --- /dev/null +++ b/authserver/package.json @@ -0,0 +1,14 @@ +{ + "name": "authserver", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "express": "^5.1.0" + } +} diff --git a/js/clientmain.js b/js/clientmain.js index c0065d5..e0eb1ab 100644 --- a/js/clientmain.js +++ b/js/clientmain.js @@ -5,7 +5,7 @@ import { ReplicatorService } from "./instances/ReplicatorService.js"; import { RenderService } from "./instances/RenderService.js"; import { Workspace } from "./instances/Workspace.js"; import { DebugTextService } from "./instances/DebugTextService.js"; // Client-only debug overlay manager - +import { Players } from "./instances/Players.js"; const dm = new DataModel(); dm.SetParent(null); // root @@ -16,6 +16,11 @@ ws.SetParent(dm); const net = new NetworkService(); net.SetParent(dm); net.isServer = false; // client mode + +// Create a players container (client-side crippled version) +const players = new Players(net, true); +players.SetParent(dm); + await net.connect("ws://localhost:8080"); // connect to server const render = new RenderService(dm); @@ -27,4 +32,17 @@ const replication = new ReplicatorService(dm, net); // Automatically parents to // Create DebugTextService const debugText = new DebugTextService(); -debugText.SetParent(dm); \ No newline at end of file +debugText.SetParent(dm); + +setTimeout(() => { + console.log("Prompting for token..."); + let sessionToken = prompt("Enter your session token: (any string will do for this test except 'fake')"); + console.log("Got token:", sessionToken); + if (sessionToken) { + const encoder = new TextEncoder(); + const payload = encoder.encode(sessionToken); + net.send(0x01, 0x03, payload); + } else { + console.warn("No token entered — not sending packet."); + } +}, 3000); diff --git a/js/core/Auth.js b/js/core/Auth.js new file mode 100644 index 0000000..4b6521a --- /dev/null +++ b/js/core/Auth.js @@ -0,0 +1,36 @@ +export class Auth { + constructor(baseUrl = "http://localhost:5000") { + this.baseUrl = baseUrl; + } + + /** + * Verify a session token against the fake auth API. + * Returns the user ID if valid, or null if invalid. + */ + async verifySessionToken(token) { + try { + const res = await fetch(`${this.baseUrl}/api/verify/${encodeURIComponent(token)}`); + if (!res.ok) return null; + const data = await res.json(); + return data.id ?? null; + } catch (err) { + console.warn("[Auth] verifySessionToken error:", err); + return null; + } + } + + /** + * Look up the user's display name by ID. + */ + async getUserNameById(id) { + try { + const res = await fetch(`${this.baseUrl}/api/users`); + if (!res.ok) return "Unknown"; + const data = await res.json(); + return data[id] ?? `User${id}`; + } catch (err) { + console.warn("[Auth] getUserNameById error:", err); + return `User${id}`; + } + } +} diff --git a/js/core/AuthStub.js b/js/core/AuthStub.js new file mode 100644 index 0000000..115a447 --- /dev/null +++ b/js/core/AuthStub.js @@ -0,0 +1,28 @@ +// For whoever is going to use this engine later, please modify this file to actually do authentication. + +export class Auth { + /** + * Verifies a session token and returns a user ID. + * In this dummy version, it just returns a random numeric ID. + * + * @param {string} token - The player's session token. + * @returns {number|null} - The verified user ID, or null if invalid. + */ + verifySessionToken(token) { + // Dummy implementation: always "verifies" successfully. + if (token === "fake") return null; // Simulate invalid token + return Math.floor(Math.random() * 1000); + } + + /** + * Gets a user's display name by their ID. + * Replace this with a real lookup (e.g., database or API call). + * + * @param {number|string} userId - The player's user ID. + * @returns {string} - The player's display name. + */ + getUserNameById(userId) { + // Dummy implementation + return `User${userId}`; + } +} diff --git a/js/core/DataModel.js b/js/core/DataModel.js index 9bf50a1..176a4c3 100644 --- a/js/core/DataModel.js +++ b/js/core/DataModel.js @@ -10,6 +10,16 @@ export class DataModel extends Instance { return this.Children.find(c => c.ClassName === name) || null; } GetDataModel() {return this;} // Override to return self + + LuaBridge () { + const obj = super.LuaBridge(); + obj.GetService = (name) => { + if (name === "RunService") name = "RenderService"; // Alias + const service = this.GetService(name); + return service ? service.LuaBridge() : null; + } + } + } // Just a container for the whole instance tree // cuz this is an Entity Component System, we need a root entity // Assume all children of DataModel are services or top-level game objects \ No newline at end of file diff --git a/js/core/util.js b/js/core/util.js index 0df4624..b5c78e3 100644 --- a/js/core/util.js +++ b/js/core/util.js @@ -134,6 +134,7 @@ function eulerDecoder(arr) { // Float32Array(3) to THREE.Euler const encoders = { Vector3: { encode: v3Encoder, decode: v3Decoder }, Euler: { encode: eulerEncoder, decode: eulerDecoder }, + bool: { encode: (bool)=>bool, decode:(bool)=>bool} // Add more as needed }; diff --git a/js/instances/BaseService.js b/js/instances/BaseService.js new file mode 100644 index 0000000..128db05 --- /dev/null +++ b/js/instances/BaseService.js @@ -0,0 +1,13 @@ +import { Instance } from "./Instance.js"; +export class BaseService extends Instance { + constructor(name) { + super(name); + } + SetParent(parent) { + if (this.Parent === null) { + super.SetParent(parent); + return; + } // Only accept it once + throw new Error("Cannot reparent a service instance."); + } +} \ No newline at end of file diff --git a/js/instances/DebugTextService.js b/js/instances/DebugTextService.js index 8578f13..4a86b48 100644 --- a/js/instances/DebugTextService.js +++ b/js/instances/DebugTextService.js @@ -1,7 +1,8 @@ import { Instance } from "./Instance.js"; +import { BaseService } from "./BaseService.js"; import { BO3ScriptSignal } from "../core/BO3ScriptSignal.js"; -export class DebugTextService extends Instance { +export class DebugTextService extends BaseService { constructor() { super("DebugTextService"); diff --git a/js/instances/ExampleService.js b/js/instances/ExampleService.js index 210c9e6..f943949 100644 --- a/js/instances/ExampleService.js +++ b/js/instances/ExampleService.js @@ -1,5 +1,6 @@ import { Instance } from "./Instance.js"; -export class ExampleService extends Instance { +import { BaseService } from "./BaseService.js"; +export class ExampleService extends BaseService { constructor() { super("ExampleService"); } diff --git a/js/instances/Instance.js b/js/instances/Instance.js index 41286d9..686d098 100644 --- a/js/instances/Instance.js +++ b/js/instances/Instance.js @@ -5,7 +5,7 @@ let instanceCounter = 0; export class Instance { constructor(className = "Instance") { this.ClassName = className; - this.Name = `${className}${instanceCounter++}`; + this.Name = `${className}`; this.Parent = null; this.Children = []; this.InstanceId = guidGenerator(); // Unique identifier, used for replication @@ -120,4 +120,21 @@ export class Instance { console.warn("Destroy: No DataModel found in ancestry. Are you destroying an unparented instance?"); } } // Garbage collected after this + + LuaBridge(exclude, visited = new Set()) { // Convert to a plain object for LuaBridge. Should be overridden in subclasses to add properties. + if (visited.has(this.InstanceId)) { + // Already visited, avoid circular reference + return null; + } + visited.add(this.InstanceId); + const obj = { + ClassName: this.ClassName, + Name: this.Name, + InstanceId: this.InstanceId, + Properties: {}, + Children: this.Children.filter(c => c !== exclude).map(c => c.LuaBridge(this, visited)).filter(Boolean), + Parent: this.Parent && this.Parent !== exclude ? this.Parent.LuaBridge(this, visited) : null // Avoid circular reference by excluding self + }; + return obj; + } } diff --git a/js/instances/NetworkService.js b/js/instances/NetworkService.js index ed6d78a..862be52 100644 --- a/js/instances/NetworkService.js +++ b/js/instances/NetworkService.js @@ -9,6 +9,7 @@ if (typeof window !== "undefined") { WebSocketServer = wsModule.WebSocketServer; } import { Instance } from "./Instance.js"; +import { BaseService } from "./BaseService.js"; import { BO3ScriptSignal } from "../core/BO3ScriptSignal.js"; /** @@ -17,7 +18,7 @@ import { BO3ScriptSignal } from "../core/BO3ScriptSignal.js"; * - Binary framed packets: [uint32 length][ptype][psub][payload] * - Fires PacketReceived signal for incoming packets */ -export class NetworkService extends Instance { +export class NetworkService extends BaseService { constructor() { super("NetworkService"); @@ -132,6 +133,17 @@ export class NetworkService extends Instance { console.warn("[NetworkService] Disconnected from server.") alert("Disconnected from server! If this was unintentional, please refresh the page. You might have been kicked by the server.\n\n(If you are the server host, check if the servers running.)"); }; + // Register 0xFF kick handler + this.registerHandler(0x00, 0xFF, (payload) => { + let reason = "Kicked by server."; + try { + reason = new TextDecoder().decode(payload); + } catch (e) { + // ignore + } + console.warn("[NetworkService] Kicked by server:", reason); + alert("You have been kicked by the server:\n\n" + reason); + }); }); } @@ -151,6 +163,7 @@ export class NetworkService extends Instance { this.wss.on("connection", (ws) => { this.clients.set(ws, {}); + ws.isAuthed = false; // Determine whether to send any data except auth packets console.log("[NetworkService] New client connected. Total clients:", this.clients.size); ws.binaryType = "arraybuffer"; @@ -167,10 +180,12 @@ export class NetworkService extends Instance { }); } - broadcast(ptype, psub, payload) { + broadcast(ptype, psub, payload,ignoreUnauthed,ignoreCheck) { if (!this.isServer || !this.wss) return; const packet = this.encodePacket(ptype, psub, payload); for (const ws of this.clients.keys()) { + if(ignoreUnauthed && !ws.isAuthed) continue; + if (ignoreCheck && ignoreCheck(ws)) continue; if (ws.readyState === WebSocket.OPEN) ws.send(packet); } } @@ -181,6 +196,17 @@ export class NetworkService extends Instance { if (ws.readyState === WebSocket.OPEN) ws.send(packet); } + KickClientLater(ws,code=4000,reason="") { + // Send a kick packet + this.sendToClient(ws, 0x00, 0xFF, new TextEncoder().encode(reason)); + setTimeout(() => { + if (ws.readyState === WebSocket.OPEN) { + ws.close(code,reason); + } + }, 500); // Slight delay to ensure any pending packets are sent + } + + Broadcast(ptype, psub, payload) { console.warn("[NetworkService] Broadcast is deprecated, use broadcast() instead."); this.broadcast(ptype, psub, payload); diff --git a/js/instances/Player.js b/js/instances/Player.js new file mode 100644 index 0000000..7b90150 --- /dev/null +++ b/js/instances/Player.js @@ -0,0 +1,11 @@ + +import { Instance } from "./Instance.js"; +export class Player extends Instance { + constructor( id, name, ws ) { + super("Player"); + this.InstanceId = id; // To fix jank with replication system + this.UserId = id; // For compatibility + this.Name = name; + this.ws = ws; // WebSocket or client reference + } +} \ No newline at end of file diff --git a/js/instances/Players.js b/js/instances/Players.js new file mode 100644 index 0000000..b11c605 --- /dev/null +++ b/js/instances/Players.js @@ -0,0 +1,105 @@ +import { NetworkService } from "./NetworkService.js"; +import { Player } from "./Player.js"; +import { BaseService } from "./BaseService.js"; +import { Auth } from "../core/Auth.js"; +/** + * Players manager + * - Tracks connected players after successful auth (0x01 0x03) + */ +export class Players extends BaseService { + constructor(networkService,isClient) { + super("Players"); + this.InstanceId = "Players"; // For replication system + if (isClient) { + this.networkService = networkService; + this.networkService.registerHandler(0x00, 0x04, (payload) => { + // Auth response from server, parse it + const decoder = new TextDecoder(); + const msg = decoder.decode(payload); + console.debug("[Players] Received auth response from server:", msg); + if (msg.startsWith("AUTH_SUCCESS|")) { + const playerName = msg.split("|")[1]; + const id = msg.split("|")[2]; + console.debug(`[Players] You have logged in as ${playerName}`); + // Create a local Player instance + const player = new Player( id, playerName, null ); + this.players = new Map(); + this.players.set(null, player); // single local player + this.LocalPlayer = player; + player.SetParent(this); + } else { + console.warn("[Players] Auth failed or unknown response:", msg); + } + }); + return; + } + this.networkService = networkService; + this.players = new Map(); // id -> Player + this.authenticator = new Auth(); + + // Listen for auth packets + this.networkService.registerHandler(0x01, 0x03, async (payload, ws) => { + if (ws.isAuthed) return; // Already authed, ignore additional auth attempts + console.debug("[Players] Received auth packet:", "CENSORED"); + // Assume payload is a string (player name) + let token; + try { + // Decode bytes + const decoder = new TextDecoder(); + token = decoder.decode(payload); + } catch (e) { + console.warn("[Players] Invalid auth payload:", payload); + networkService.KickClientLater(ws, 4000, "Invalid auth payload, please try again."); + + return; + } + // Verify token (dummy implementation) + const id = await this.authenticator.verifySessionToken(token); + if (id === null) { + // Kick off the client + networkService.KickClientLater(ws, 4001, "Authentication failed; are you logged in?"); + // Reason must be understandable as its shown to the user + return; + } + console.debug(`[Players] Client authenticated as ${id}`); + ws.isAuthed = true; // Mark ws as authed + // Create Player instance + const name = await this.authenticator.getUserNameById(id); + const player = new Player(id, name, ws ); + this.players.set(ws, player); + player.SetParent(this); + // Listen for disconnection + ws.on("close", () => { + this.players.delete(ws); + player.Destroy(); + console.debug(`[Players] Player ${name} (${id}) disconnected.`); + }); + console.debug(`[Players] Player ${name} (${id}) connected.`); + // Send auth success packet + const encoder = new TextEncoder(); + const successPayload = encoder.encode("AUTH_SUCCESS|" + name + "|" + id); + this.networkService.sendToClient(ws, 0x00, 0x04, successPayload); + }); + } + + /** + * Get all connected players + */ + getAll() { + return Array.from(this.players.values()); + } + + /** + * Remove player by ws + */ + removeByWs(ws) { + this.players.delete(ws); + } + + /** + * Get player by ws + */ + getByWs(ws) { + return this.players.get(ws); + } +} \ No newline at end of file diff --git a/js/instances/RenderService.js b/js/instances/RenderService.js index a20a108..002241a 100644 --- a/js/instances/RenderService.js +++ b/js/instances/RenderService.js @@ -4,7 +4,8 @@ import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { Instance } from "./Instance.js"; import { BO3ScriptSignal } from "../core/BO3ScriptSignal.js"; import * as CANNON from "cannon-es"; -export class RenderService extends Instance { +import { BaseService } from "./BaseService.js"; +export class RenderService extends BaseService { constructor() { super("RenderService"); diff --git a/js/instances/ReplicatorService.js b/js/instances/ReplicatorService.js index d770b4f..f084d6c 100644 --- a/js/instances/ReplicatorService.js +++ b/js/instances/ReplicatorService.js @@ -1,6 +1,7 @@ import { Instance } from "./Instance.js"; +import { BaseService } from "./BaseService.js"; import { sha256, encoders } from "../core/util.js"; // Since SubtleCrypto isnt available in http only, we use a JS implementation -export class ReplicatorService extends Instance { +export class ReplicatorService extends BaseService { constructor(datamodel, network) { super("ReplicatorService"); // Get tickloop from RenderService @@ -98,6 +99,9 @@ export class ReplicatorService extends Instance { }); } else { this._renderService.Heartbeat.Connect((dt) => this._onHeartbeat(dt)); + this._networkService.registerHandler(0x01, 0x02, (payload,ws) => { + }); // Server side replicator reciever, will be used later for client to server replication + // aka what roblox does for certain instances like under the player character } this.SetParent(datamodel); this.lastClearedStatCheck = 0; // If above 1 second, clear stats @@ -132,9 +136,14 @@ export class ReplicatorService extends Instance { const toReplicate = []; // Get workspace and its children const workspace = this.Parent.GetService("Workspace"); + const players = this.Parent.GetService("Players"); if (workspace) { toReplicate.push(...workspace.Children); } + if (players) { + toReplicate.push(...players.Children); // Replicate player instances too + // (so players can see each other) + } // Send over the pipe for (const inst of toReplicate) { // Check property hashes to see if anything changed @@ -175,7 +184,8 @@ export class ReplicatorService extends Instance { key === "scene" || key === "controls" || key === "physicsWorld" || - key === "loader" + key === "loader" || + key === "LuaState" // Script Lua state ) { continue; } @@ -199,7 +209,14 @@ export class ReplicatorService extends Instance { // Send to all clients let outpay = JSON.stringify(payload); this.bytesSent += outpay.length + 2; // +2 for ptype and psub - this._networkService.broadcast(0x00, 0x02, outpay); // Refer to ptype and psub definitions to see what these mean + this._networkService.broadcast(0x00, 0x02, outpay,true,(ws) => { + const player = players ? players.getByWs(ws) : null; + // Dont send to the player if its their own character + if (player && inst.InstanceId === player.InstanceId) { + return true; + } + return false; + }); // Refer to ptype and psub definitions to see what these mean // Update the _lastHash property to avoid re-sending unchanged data inst._lastHash = hash; //console.log(`Replicated instance ${inst.Name} (${inst.InstanceId}) to clients.`); diff --git a/js/instances/Script.js b/js/instances/Script.js new file mode 100644 index 0000000..e022ab2 --- /dev/null +++ b/js/instances/Script.js @@ -0,0 +1,63 @@ +import { lua, lauxlib, lualib, to_luastring } from "fengari"; +import { ScriptSchedulerService } from "./ScriptSchedulerService.js"; +import { Instance } from "./Instance.js"; // your engine's instance class + +// Import interop from fengari-interop +import { push, luaopen_js } from "fengari-interop"; + +export class Script extends Instance { + constructor(sourceCode) { + super("Script"); + this.Source = sourceCode; + this.LuaState = null; + } + + Run() { + const L = lauxlib.luaL_newstate(); + luaopen_js(L); + lualib.luaL_openlibs(L); + + // Bridge all engine APIs + this._registerGlobals(L); + + // Compile the source + const status = lauxlib.luaL_loadstring(L, to_luastring(this.Source)); + if (status !== lua.LUA_OK) { + const err = lua.lua_tojsstring(L, -1); + throw new Error("Lua load error: " + err); + } + + // Create a coroutine thread + const thread = lua.lua_newthread(L); + + // Schedule with scheduler + this._getScheduler().scheduleFengariCoroutine(thread, L); + this.LuaState = L; + } + + _registerGlobals(L) { + // game global + push(L, globalThis.game); + lua.lua_setglobal(L, to_luastring("game")); + + // task + wait bridge + lua.lua_pushjsfunction(L, () => { + const secs = lua.lua_isnumber(L, 1) ? lua.lua_tonumber(L, 1) : 0; + const yieldVal = JSON.stringify({ wait: secs }); + lua.lua_pushstring(L, to_luastring(yieldVal)); + return lua.lua_yield(L, 1); + }); + lua.lua_setglobal(L, to_luastring("task_wait")); + + // Lua API helpers + lua.lua_pushjsfunction(L, () => { + const inst = new Instance("Part"); // example + return inst.LuaBridge(); // converts to Lua table/proxy + }); + lua.lua_setglobal(L, to_luastring("Instance_new")); + } + + _getScheduler() { + return game.GetService("ScriptSchedulerService"); + } +} diff --git a/js/instances/ScriptSchedulerService.js b/js/instances/ScriptSchedulerService.js new file mode 100644 index 0000000..97e1dc1 --- /dev/null +++ b/js/instances/ScriptSchedulerService.js @@ -0,0 +1,111 @@ +import { lua, lauxlib, lualib, to_luastring } from "fengari"; +const lua_resume = lua.lua_resume; +const LUA_OK = lua.LUA_OK; +const LUA_YIELD = lua.LUA_YIELD; +import { BaseService } from "./BaseService.js"; +export class ScriptSchedulerService extends BaseService { + constructor() { + super("ScriptSchedulerService"); + this._scheduled = []; + this._waiting = []; + this._running = false; + } + + // Add a script (generator function) to the scheduler + schedule(scriptGen) { + if (typeof scriptGen === "function") { + const gen = scriptGen(); + this._scheduled.push({ gen, wakeTime: 0 }); + } else if (scriptGen && typeof scriptGen.next === "function") { + this._scheduled.push({ gen: scriptGen, wakeTime: 0 }); + } else { + throw new Error("Script must be a generator or generator function"); + } + } + + // Add a Fengari coroutine (thread) to the scheduler + scheduleFengariCoroutine(luaThread, luaState) { + // luaThread: the coroutine (thread) object from Fengari + // luaState: the Lua state (L) from Fengari + console.debug("Scheduling Fengari coroutine"); + this._scheduled.push({ fengari: true, thread: luaThread, state: luaState, wakeTime: 0 }); + } + + // Main update loop, call this every frame with dt (delta time in seconds) + step(dt) { + const now = Date.now(); + // Wake up any waiting scripts whose time has come + for (let i = this._waiting.length - 1; i >= 0; i--) { + const item = this._waiting[i]; + if (now >= item.wakeTime) { + this._scheduled.push(item); + this._waiting.splice(i, 1); + console.log("Woke up routine",i,"from waiting."); + } + } + // Run scheduled scripts + for (let i = this._scheduled.length - 1; i >= 0; i--) { + const item = this._scheduled[i]; + if (item.fengari) { + // Fengari coroutine + const L = item.state; + const thread = item.thread; + // Resume the coroutine + const status = lua_resume(thread, L, 0); + if (status === LUA_OK) { + // Coroutine finished + this._scheduled.splice(i, 1); + console.log("Woke up routine",i,"from fengari, finished."); + } else if (status === LUA_YIELD) { + // Check for yield value (wait) + // You must implement a way for Lua to yield with a wait time, e.g. coroutine.yield({wait=seconds}) + // For now, we assume the top of the stack is a table with a 'wait' field + const luaTable = lua.lua_tojsstring(L, -1); + let waitSeconds = 0; + try { + // Try to parse the yielded value as JSON (if you yield a JSON string) + const val = JSON.parse(luaTable); + if (val && val.wait) waitSeconds = val.wait; + } catch (e) { } + if (waitSeconds > 0) { + this._waiting.push({ ...item, wakeTime: now + waitSeconds * 1000 }); + this._scheduled.splice(i, 1); + } + // Otherwise, just continue next frame + } else { + // Error or unknown status + this._scheduled.splice(i, 1); + } + } else { + // JavaScript generator + const { value, done } = item.gen.next(); + if (done) { + this._scheduled.splice(i, 1); + console.log("Woke up routine",i,"from JS generator, finished."); + } else if (value && value.wait) { + // Script yielded with {wait: seconds} + const ms = value.wait * 1000; + this._waiting.push({ gen: item.gen, wakeTime: now + ms }); + this._scheduled.splice(i, 1); + } + } + } + } + + // Utility for scripts to yield for a certain time (in seconds) + static wait(seconds) { + return { wait: seconds }; + } +} + +// Example usage: +// function* myScript() { +// console.log("Script start"); +// yield ScriptSchedulerService.wait(1); +// console.log("1 second later"); +// yield ScriptSchedulerService.wait(2); +// console.log("2 more seconds later"); +// } +// const scheduler = new ScriptSchedulerService(); +// scheduler.schedule(myScript); +// setInterval(() => scheduler.step(1/60), 1000/60); \ No newline at end of file diff --git a/js/instances/ServiceService.js b/js/instances/ServiceService.js new file mode 100644 index 0000000..f65bc4a --- /dev/null +++ b/js/instances/ServiceService.js @@ -0,0 +1,117 @@ +// ServiceService.js +import { BaseService } from "./BaseService.js"; +import { NetworkService } from "./NetworkService.js"; +import { RenderService } from "./RenderService.js"; +import { ReplicatorService } from "./ReplicatorService.js"; +import { ScriptSchedulerService } from "./ScriptSchedulerService.js"; +import { Players } from "./Players.js"; +import { Workspace } from "./Workspace.js"; +import { DataModel } from "../core/DataModel.js"; +import * as THREE from "three"; + +export class ServiceService extends BaseService { + constructor() { + super("ServiceService"); + this._services = new Map(); + } + + register(name, ctor, ...args) { + if (this._services.has(name)) return this._services.get(name); + const instance = new ctor(...args); + this._services.set(name, instance); + return instance; + } + + GetService(name) { + return this._services.get(name); + } + GetDataModel() { + return globalThis.game; + } + + initAll(port) { + // === Core DataModel === + const dm = this.register("DataModel", DataModel); + dm.SetParent(null); + this.SetParent(dm); // Parent myself to the DataModel + globalThis.game = dm; + + // === Workspace === + const ws = this.register("Workspace", Workspace); + ws.SetParent(dm); + + // === RenderService === + const render = this.register("RenderService", RenderService, dm); + render.isServer = true; + render.start(); + render.SetParent(dm); + + // === Networking === + const net = this.register("NetworkService", NetworkService); + net.listen(port); + net.SetParent(dm); + + // === Players === + const players = this.register("Players", Players, net); + players.SetParent(dm); + + // === ReplicatorService === + const replication = this.register("ReplicatorService", ReplicatorService, dm, net); + + // === ScriptSchedulerService === + const scriptScheduler = this.register( + "ScriptSchedulerService", + ScriptSchedulerService, + dm + ); + scriptScheduler.SetParent(dm); + // Connect to RenderService for update loop + render.Stepped.Connect((dt) => { + scriptScheduler.step(dt); + }); + + // Cross-link + replication.Players = players; + players.Replicator = replication; + + // === Example echo handler === + net.registerHandler(0x01, 0x00, (payload, client) => { + console.log("Echo:", new TextDecoder().decode(payload)); + net.sendToClient(client, 0x01, 0x01, payload); + }); + + // === Example test parts === + //this._spawnDemoScene(ws); + + console.log("[ServiceService] Initialized all services."); + } + + async _spawnDemoScene(ws) { + const { LerpedBasePart } = await import("./LerpedBasePart.js"); + + const ground = new LerpedBasePart(); + ground.Size = new THREE.Vector3(10, 1, 10); + ground.Position.set(0, -0.5, 0); + ground.setAnchored(true); + ground.SetParent(ws); + ground.updateSizes(); + + let angle = 0; + setInterval(() => { + angle += 0.01; + ground.Orientation.set(0, angle, 0); + ground.Position.set( + 5 * Math.sin(angle * 2), + -0.5, + 5 * Math.cos(angle * 2) + ); + }, 16); + + const part = new LerpedBasePart(); + part.Size = new THREE.Vector3(1, 1, 1); + part.Position.set(10, 5, 0); + part.setAnchored(false); + part.SetParent(ws); + part.updateSizes(); + } +} diff --git a/js/instances/Workspace.js b/js/instances/Workspace.js index c3d81f0..20233cd 100644 --- a/js/instances/Workspace.js +++ b/js/instances/Workspace.js @@ -1,5 +1,6 @@ +import { BaseService } from "./BaseService.js"; import { Instance } from "./Instance.js"; -export class Workspace extends Instance { +export class Workspace extends BaseService { constructor() { super("Workspace"); this.InstanceId = "WorkspaceRoot"; // Fixed ID for Workspace (Bad idea to replicate services over network?) diff --git a/js/place.json b/js/place.json new file mode 100644 index 0000000..a359b8b --- /dev/null +++ b/js/place.json @@ -0,0 +1,40 @@ +{ + "objects": [ + { + "InstanceId": "2", + "ClassName": "BasePart", + "Name": "Ground", + "ParentId": "WorkspaceRoot", + "properties": { + "Color": { "_type": "Color", "value": 65280 }, + "Size": { "_type": "Vector3", "value": { "x": 50, "y": 1, "z": 50 } }, + "Position": { "_type": "Vector3", "value": { "x": 0, "y": -0.5, "z": 0 } }, + "Anchored": { "_type": "bool", "value": true } + } + }, + { + "InstanceId": "3", + "ClassName": "BasePart", + "Name": "FallingCube", + "ParentId": "WorkspaceRoot", + "properties": { + "Color": { "_type": "Color", "value": 16711680 }, + "Size": { "_type": "Vector3", "value": { "x": 2, "y": 2, "z": 2 } }, + "Position": { "_type": "Vector3", "value": { "x": 0, "y": 15, "z": 0 } }, + "Anchored": { "_type": "bool", "value": false } + } + }, + { + "InstanceId": "4", + "ClassName": "BasePart", + "Name": "Wall", + "ParentId": "WorkspaceRoot", + "properties": { + "Color": { "_type": "Color", "value": 255 }, + "Size": { "_type": "Vector3", "value": { "x": 1, "y": 10, "z": 20 } }, + "Position": { "_type": "Vector3", "value": { "x": 10, "y": 5, "z": 0 } }, + "Anchored": { "_type": "bool", "value": true } + } + } + ] +} diff --git a/js/serverParams.js b/js/serverParams.js new file mode 100644 index 0000000..4cdd90f --- /dev/null +++ b/js/serverParams.js @@ -0,0 +1,56 @@ +import { ServiceService } from "./instances/ServiceService.js"; + +import fs from 'fs'; +import { program } from "commander"; +import { sha256, encoders } from "./core/util.js"; +import { exit } from "process"; + +program + .option("-p, --port ", "Port to listen on", "8080") + .option("-a, --place ", "Place JSON file", "https://example.com/asset.php?id=1818"); +program.parse(); + +const options = program.opts(); +options.port = parseInt(options.port, 10); // optional, if you need a number +const Services = new ServiceService(options.port); +Services.initAll(); + +globalThis.Services = Services; // optional global shortcut + + +// Load from place.json + +// import FS + +(async () => { + const placeReq = await fetch(options.place); + if (!placeData.ok) { + console.error("Failed to fetch place data!"); + exit(1); + } + const placeData = await placeReq.json(); + // Create instances off this + for (let obj of placeData.objects) { + const parent = Services.GetDataModel().FindInstanceRecursively(obj.ParentId) + if (parent) { + import(`./instances/${obj.ClassName}.js`).then((module) => { + const NewClass = module[obj.ClassName]; + if (!NewClass) { + console.warn(`Init: Class ${obj.ClassName} not found for startup. Is this server up to date?`); + return; + } + const newInst = new NewClass(); + newInst.InstanceId = obj.InstanceId; // Set the same InstanceId + newInst.Name = obj.Name; + for (let prop in obj.properties) { + const type = prop._type + const value = prop.value + if (encoders[type]) obj.value = encoders[type].decode(value); + else obj.value = value; + }; + newInst.SetParent(parent) + }); + } + } + console.log("Server initialized and listening on port " + options.port.toString() + "!"); +})(); \ No newline at end of file diff --git a/js/servermain.js b/js/servermain.js index 295f14f..0f59d11 100644 --- a/js/servermain.js +++ b/js/servermain.js @@ -1,72 +1,22 @@ -import { NetworkService } from "./instances/NetworkService.js"; -import { RenderService } from "./instances/RenderService.js"; -import { ReplicatorService } from "./instances/ReplicatorService.js"; -import { DataModel } from "./core/DataModel.js"; -import { Workspace } from "./instances/Workspace.js"; -import * as THREE from "three"; +import { ServiceService } from "./instances/ServiceService.js"; -const dm = new DataModel(); -dm.SetParent(null); +const Services = new ServiceService(); +Services.initAll(); -// === Workspace === -const ws = new Workspace(); -ws.SetParent(dm); +globalThis.Services = Services; // optional global shortcut -// === RenderService (headless physics) === -const render = new RenderService(dm); -render.isServer = true; -render.start(); -render.SetParent(dm); +console.log("Server initialized and listening on port 8080."); -// === Networking === -const net = new NetworkService(); -net.listen(8080); -net.SetParent(dm); - -// Simple echo handler -net.registerHandler(0x01, 0x00, (payload, client) => { - console.log("Received from client:", new TextDecoder().decode(payload)); - net.sendToClient(client, 0x01, 0x01, payload); -}); - -// === Replication Service === -const replication = new ReplicatorService(dm, net); - -// === Test parts === -const { LerpedBasePart } = await import("./instances/LerpedBasePart.js"); - -// Falling cube -// Anchored ground -const ground = new LerpedBasePart(); -ground.Size = new THREE.Vector3(10, 1, 10); -ground.Position.set(0, -0.5, 0); -ground.setAnchored(true); -ground.SetParent(ws); -ground.updateSizes(); -// Spin ground -let angle = 0; -setInterval(() => { - angle += 0.01; - ground.Orientation.set(0, angle, 0); - ground.Position.set(5 * Math.sin(angle * 2), -0.5, 5 * Math.cos(angle * 2)); -}, 16); -/*setInterval(() => { - const part = new LerpedBasePart(); - part.Position.set(0, 35, 0); - part.SetParent(ws); - part.setAnchored(true); - part.updateVisual(1 / 60); // Ensure physics is initialized - setTimeout(() => { - part.updateBodyPosition(); // Reset physics to new position - part.setAnchored(false); - }, 2000); - part.updateSizes(); -}, 1000);*/ -// Falling cube singularity -const part = new LerpedBasePart(); -part.Size = new THREE.Vector3(1, 1, 1); -part.Position.set(10, 5, 0); -part.setAnchored(false); -part.SetParent(ws); -part.updateSizes(); +// Create a test script instance +import { Script } from "./instances/Script.js"; +const testScript = new Script(`print("Hello from Lua script!") +for i=1,5 do + print("Waiting...", i) + task_wait(1) -- wait 1 second +end +print("Lua script finished!")`); +// Parent to Workspace +const workspace = Services.GetService("Workspace"); +testScript.SetParent(workspace); +testScript.Run(); \ No newline at end of file diff --git a/netcodedocs/ptype-psub-defs.md b/netcodedocs/ptype-psub-defs.md index 742156e..eda733c 100644 --- a/netcodedocs/ptype-psub-defs.md +++ b/netcodedocs/ptype-psub-defs.md @@ -8,7 +8,13 @@ Byte 2 is PSub (packet subtype), which is the actual packet name. Each ptype can 0x00 = Clientbound (should never be sent by a client) 0x01 = Serverbound (should never be recieved by a client) -## PSub +## PSub for client 0x02 = Replication channel -0x01 = Echo (Server will return what you send) \ No newline at end of file +0x01 = Echo (Server will return what you send) +0x04 = Recv auth validness +0xFF = kick + +## PSub for server +0x03 = Recv auth session +0x02 = Same as client \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5fc7dcc..628b3ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,9 @@ "license": "ISC", "dependencies": { "cannon-es": "^0.20.0", + "commander": "^14.0.1", + "fengari": "^0.1.4", + "fengari-interop": "^0.1.3", "three": "^0.180.0", "ws": "^8.18.3" }, @@ -780,6 +783,15 @@ "integrity": "sha512-eZhWTZIkFOnMAJOgfXJa9+b3kVlvG+FX4mdkpePev/w/rP5V8NRquGyEozcjPfEoXUlb+p7d9SUcmDSn14prOA==", "license": "MIT" }, + "node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/esbuild": { "version": "0.25.10", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", @@ -840,6 +852,26 @@ } } }, + "node_modules/fengari": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fengari/-/fengari-0.1.4.tgz", + "integrity": "sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==", + "license": "MIT", + "dependencies": { + "readline-sync": "^1.4.9", + "sprintf-js": "^1.1.1", + "tmp": "^0.0.33" + } + }, + "node_modules/fengari-interop": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fengari-interop/-/fengari-interop-0.1.3.tgz", + "integrity": "sha512-EtZ+oTu3kEwVJnoymFPBVLIbQcCoy9uWCVnMA6h3M/RqHkUBsLYp29+RRHf9rKr6GwjubWREU1O7RretFIXjHw==", + "license": "MIT", + "peerDependencies": { + "fengari": "^0.1.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -874,6 +906,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -923,6 +964,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/rollup": { "version": "4.52.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", @@ -975,6 +1025,12 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/three": { "version": "0.180.0", "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", @@ -998,10 +1054,22 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 765510e..d691b16 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,14 @@ "description": "", "dependencies": { "cannon-es": "^0.20.0", + "commander": "^14.0.1", + "fengari": "^0.1.4", + "fengari-interop": "^0.1.3", "three": "^0.180.0", "ws": "^8.18.3" }, "devDependencies": { "vite": "^7.1.9" }, - "type":"module" + "type": "module" }