From 76715a89bab0a648ce11f588b3449fed2e9fae8d Mon Sep 17 00:00:00 2001 From: theo-doree <95889170+theo-doree@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:55:03 -0500 Subject: [PATCH] Initial Commit --- .env.example | 16 + .gitignore | 3 + README.md | 105 ++++ package-lock.json | 973 ++++++++++++++++++++++++++++++++++ package.json | 24 + src/index.js | 40 ++ src/lib/RCCService.wsdl | 811 ++++++++++++++++++++++++++++ src/lib/classes/GameJob.js | 51 ++ src/lib/classes/Job.js | 56 ++ src/lib/classes/RCCService.js | 56 ++ src/lib/classes/RenderJob.js | 347 ++++++++++++ src/lib/logger.js | 12 + src/lib/randport.js | 22 + src/lua/bodyshot.lua | 29 + src/lua/clothing.lua | 24 + src/lua/gameserver.lua | 76 +++ src/lua/head.lua | 33 ++ src/lua/headshot.lua | 41 ++ src/lua/mesh.lua | 23 + src/lua/place.lua | 23 + src/lua/texture.lua | 15 + src/lua/xml.lua | 23 + src/routes/game/execute.js | 19 + src/routes/game/renew.js | 14 + src/routes/game/running.js | 19 + src/routes/game/start.js | 21 + src/routes/game/status.js | 14 + src/routes/game/stop.js | 14 + src/routes/index.js | 23 + src/routes/render/asset.js | 26 + src/routes/render/clothing.js | 16 + src/routes/render/game.js | 16 + src/routes/render/head.js | 16 + src/routes/render/mesh.js | 16 + src/routes/render/texture.js | 16 + src/routes/render/user.js | 36 ++ 36 files changed, 3069 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/index.js create mode 100644 src/lib/RCCService.wsdl create mode 100644 src/lib/classes/GameJob.js create mode 100644 src/lib/classes/Job.js create mode 100644 src/lib/classes/RCCService.js create mode 100644 src/lib/classes/RenderJob.js create mode 100644 src/lib/logger.js create mode 100644 src/lib/randport.js create mode 100644 src/lua/bodyshot.lua create mode 100644 src/lua/clothing.lua create mode 100644 src/lua/gameserver.lua create mode 100644 src/lua/head.lua create mode 100644 src/lua/headshot.lua create mode 100644 src/lua/mesh.lua create mode 100644 src/lua/place.lua create mode 100644 src/lua/texture.lua create mode 100644 src/lua/xml.lua create mode 100644 src/routes/game/execute.js create mode 100644 src/routes/game/renew.js create mode 100644 src/routes/game/running.js create mode 100644 src/routes/game/start.js create mode 100644 src/routes/game/status.js create mode 100644 src/routes/game/stop.js create mode 100644 src/routes/index.js create mode 100644 src/routes/render/asset.js create mode 100644 src/routes/render/clothing.js create mode 100644 src/routes/render/game.js create mode 100644 src/routes/render/head.js create mode 100644 src/routes/render/mesh.js create mode 100644 src/routes/render/texture.js create mode 100644 src/routes/render/user.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8bbbab3 --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +PORT=64989 +RCCSERVICE= + +BASE_URL=http://www.warrior.rip + +ARBITER_TOKEN= +ARBITER_KEY= + +RENDER_USER_WIDTH=720 +RENDER_USER_HEIGHT=720 + +RENDER_ASSET_WIDTH=720 +RENDER_ASSET_HEIGHT=720 + +RENDER_PLACE_WIDTH=854 +RENDER_PLACE_HEIGHT=480 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c27e9ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +build +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..632247f --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# bingle-arbiter + +The Bingle arbiter is designed to be used with almost any revival backend. + +It comes preloaded with some Lua scripts made by kinery and jackd900. + +You **will** have to replace/modify these scripts when implementing for your own projects. + +Set your desired settings in `.env.example`, then rename it to `.env`. + +## Routes + +### GET /render/asset/:id + +
+200 OK + +``` +iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC... +``` + +
+ +### GET /render/asset/3d/:id + +
+200 OK + +```json +{ + "camera": { + "position": { "x": 0, "y": 0, "z": 0 }, + "direction": { "x": 0, "y": 0, "z": 0 } + }, + "AABB": { + "min": { "x": 0, "y": 0, "z": 0 }, + "max": { "x": 0, "y": 0, "z": 0 } + }, + "files": { + "scene.obj": { "content": "..." }, + "scene.mtl": { "content": "..." }, + "Handle1Tex.png": { "content": "..." } + } +} +``` + +
+ +### GET /render/texture/:id + +
+200 OK + +``` +iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC... +``` + +
+ +### GET /render/user/headshot/:id + +
+200 OK + +``` +iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC... +``` + +
+ +### GET /render/user/bodyshot/:id + +
+200 OK + +``` +iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC... +``` + +
+ +### GET /render/user/3d/:id + +
+200 OK + +```json +{ + "camera": { + "position": { "x": 0, "y": 0, "z": 0 }, + "direction": { "x": 0, "y": 0, "z": 0 } + }, + "AABB": { + "min": { "x": 0, "y": 0, "z": 0 }, + "max": { "x": 0, "y": 0, "z": 0 } + }, + "files": { + "scene.obj": { "content": "..." }, + "scene.mtl": { "content": "..." }, + "Handle1Tex.png": { "content": "..." } + } +} +``` + +
diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5f640fa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,973 @@ +{ + "name": "bingle-arbiter", + "version": "0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bingle-arbiter", + "version": "0.1", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2", + "chalk": "^4.1.2", + "dotenv": "^16.0.3", + "express": "^4.18.2", + "soap": "^1.0.0", + "wait-port": "^1.0.4" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.6.tgz", + "integrity": "sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/axios-ntlm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.3.1.tgz", + "integrity": "sha512-YhjZj6UUzFzGirh7SiKbyvoXCWiZFMjjx2WJ8ouUUGNrqw/QgTc4H3M+7a6CTGENfLgXi2OiEhVeHmqoCffdYQ==", + "dependencies": { + "axios": "^1.2.0", + "dev-null": "^0.1.1" + } + }, + "node_modules/axios-ntlm/node_modules/axios": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.3.tgz", + "integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dev-null": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", + "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==" + }, + "node_modules/dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "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==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "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==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.2.5.tgz", + "integrity": "sha512-GRGDJTWAZ3H+umZbF2bKcqjsTov25zgon1St05ziKdiSw3kxvI+meMJrXx3ylRmuSADOpviSakBuS4yvGCGnSg==", + "dependencies": { + "dezalgo": "1.0.3", + "hexoid": "1.0.0", + "once": "1.4.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "engines": { + "node": ">=8" + } + }, + "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==", + "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/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "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==" + }, + "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==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "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==", + "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==", + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/soap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/soap/-/soap-1.0.0.tgz", + "integrity": "sha512-GB5GuKjWFtAPP0IaM4tKUvdF4dFlaz4iujLPg0DsCy9qAAgm5/g+SI+P9e6igWWwXZ74CC5Uo0VEEz8lte0CJA==", + "dependencies": { + "axios-ntlm": "^1.2.0", + "debug": "^4.3.2", + "formidable": "^3.2.4", + "get-stream": "^6.0.1", + "lodash": "^4.17.21", + "sax": ">=0.6", + "strip-bom": "^3.0.0", + "uuid": "^8.3.2", + "whatwg-mimetype": "3.0.0", + "xml-crypto": "^3.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "axios": "^0.27.2" + } + }, + "node_modules/soap/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/soap/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=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==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wait-port": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.0.4.tgz", + "integrity": "sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw==", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wait-port/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/wait-port/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "engines": { + "node": ">=12" + } + }, + "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==" + }, + "node_modules/xml-crypto": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.0.1.tgz", + "integrity": "sha512-7XrwB3ujd95KCO6+u9fidb8ajvRJvIfGNWD0XLJoTWlBKz+tFpUzEYxsN+Il/6/gHtEs1RgRh2RH+TzhcWBZUw==", + "dependencies": { + "@xmldom/xmldom": "^0.8.5", + "xpath": "0.0.32" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xpath": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", + "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", + "engines": { + "node": ">=0.6.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5092ecb --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "bingle-arbiter", + "version": "0.1", + "main": "src/index.js", + "scripts": { + "start": "node .", + "dev": "nodemon . --exec \"clear; npm run start\"" + }, + "repository": { + "type": "git", + "url": "git@calones.xyz:Bingle/arbiter.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2", + "chalk": "^4.1.2", + "dotenv": "^16.0.3", + "express": "^4.18.2", + "soap": "^1.0.0", + "wait-port": "^1.0.4" + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..e419a76 --- /dev/null +++ b/src/index.js @@ -0,0 +1,40 @@ +require("dotenv").config() +const express = require("express") +const app = express() + +const logger = require("./lib/logger.js") + +if (process.platform == "linux") logger.warn("Game hosting might not be fully compatible with Linux") + +app.use(({ query }, response, next) => { + if (!query.key) return response.status(400).json({ error: "Missing key in query" }) + if (query.key !== process.env.ARBITER_KEY) return response.status(403).json({ error: "Incorrect key in query" }) + next() +}) + +app.use("/game/start", require("./routes/game/start.js")) +app.use("/game/stop", require("./routes/game/stop.js")) +app.use("/game/running", require("./routes/game/running.js")) +app.use("/game/renew", require("./routes/game/renew.js")) +app.use("/game/status", require("./routes/game/status.js")) +app.use("/game/execute", require("./routes/game/execute.js")) + +app.use("/render/asset", require("./routes/render/asset.js")) +app.use("/render/game", require("./routes/render/game.js")) +app.use("/render/texture", require("./routes/render/texture.js")) +app.use("/render/user", require("./routes/render/user.js")) +app.use("/render/clothing", require("./routes/render/clothing.js")) +app.use("/render/head", require("./routes/render/head.js")) +app.use("/render/mesh", require("./routes/render/mesh.js")) + +app.use("*", require("./routes/index.js")) + +process.on("uncaughtException", (err) => logger.error(err.message)) +global.games = new Map() +setInterval(() => { + global.games.forEach(async (value, key) => { + if (!(await value.Running())) value.Stop() + }) +}, 15000) + +app.listen(process.env.PORT, () => logger.boot(`Listening on http://127.0.0.1:${process.env.PORT}/`)) \ No newline at end of file diff --git a/src/lib/RCCService.wsdl b/src/lib/RCCService.wsdl new file mode 100644 index 0000000..b58c3f0 --- /dev/null +++ b/src/lib/RCCService.wsdl @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/lib/classes/GameJob.js b/src/lib/classes/GameJob.js new file mode 100644 index 0000000..1cc4528 --- /dev/null +++ b/src/lib/classes/GameJob.js @@ -0,0 +1,51 @@ +const axios = require("axios") +const { readFile } = require("fs/promises") + +const Job = require("./Job.js") +const logger = require("../logger.js") +const randport = require("../randport.js") + +class GameJob extends Job { + constructor() { + super({ expirationInSeconds: 360 }) + } + + StartGame(id) { + return new Promise(async (resolve, reject) => { + this.placeId = id + + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + if (!this.client) await this.CreateClient() + + logger.info(`[${this.id}] GameJob started for ${id}`) + + const port = await randport.udp() + + this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/gameserver.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + { type: "LUA_TSTRING", value: "Place" }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TNUMBER", value: port }, + ], + }, + }).catch((e) => reject(e)) + + resolve(port) + }) + } + + async Running() { + const result = await this.Execute("IsRunning", "return true").catch((_) => _) + return !result?.message + } +} + +module.exports = GameJob diff --git a/src/lib/classes/Job.js b/src/lib/classes/Job.js new file mode 100644 index 0000000..4757f84 --- /dev/null +++ b/src/lib/classes/Job.js @@ -0,0 +1,56 @@ +const soap = require("soap") +const { randomUUID } = require("crypto") + +const RCCService = require("./RCCService.js") +const logger = require("../logger.js") + +class Job extends RCCService { + constructor({ id = randomUUID(), expirationInSeconds = 60, category = 0, cores = 1 } = {}) { + super() + this.id = id + this.expirationInSeconds = expirationInSeconds + this.category = category + this.cores = cores + } + + async CreateClient() { + this.client = await soap.createClientAsync(__dirname + "/../RCCService.wsdl", {}, `http://127.0.0.1:${this.port}/`) + return this.client + } + + async OpenJobEx(script) { + if (!this.client) throw new Error("There is no client") + return await this.client.OpenJobExAsync({ + job: { + id: this.id, + expirationInSeconds: this.expirationInSeconds, + category: this.category, + cores: this.cores, + }, + script, + }) + } + + async CloseJob() { + if (!this.client) return true + return await this.client.CloseJobAsync({ jobID: this.id }) + } + + async RenewLease(expirationInSeconds) { + if (!this.client) throw new Error("There is no client") + logger.info(`[${this.id}] Job renewed to ${expirationInSeconds} seconds`) + return await this.client.RenewLeaseAsync({ jobID: this.id, expirationInSeconds }) + } + + async Execute(name, script) { + if (!this.client) throw new Error("There is no client") + return await this.client.ExecuteAsync({ jobID: this.id, script: { name, script, arguments: {} } }) + } + + async GetStatus() { + if (!this.client) throw new Error("There is no client") + return await this.client.GetStatusAsync({}) + } +} + +module.exports = Job diff --git a/src/lib/classes/RCCService.js b/src/lib/classes/RCCService.js new file mode 100644 index 0000000..ee1cb40 --- /dev/null +++ b/src/lib/classes/RCCService.js @@ -0,0 +1,56 @@ +const EventEmitter = require("events") +const child_process = require("child_process") +const waitPort = require("wait-port") + +const logger = require("../../lib/logger.js") +const randport = require("../../lib/randport.js") + +const chalk = require("chalk") + +class RCCService extends EventEmitter { + constructor() { + super() + } + + Start() { + return new Promise(async (resolve, reject) => { + try { + this.port = await randport.tcp() + + if (process.platform == "win32") { + this.proc = child_process.spawn("RCCService.exe", ["-Console", "-PlaceId:-1", `-Port`, this.port], { cwd: process.env.RCCSERVICE, stdio: "inherit" }) + } else { + this.proc = child_process.spawn("wine", ["RCCService.exe", "-Console", "-PlaceId:-1", `-Port`, this.port], { cwd: process.env.RCCSERVICE, stdio: "inherit" }) + } + + this.proc.once("spawn", async () => { + logger.info(`${chalk.gray(`[${this.port}]`)} RCCService instance spawned`) + const { open } = await waitPort({ host: "127.0.0.1", port: this.port, timeout: 5000, output: "silent" }).catch((e) => console.log(e)) + if (!open || this.proc.exitCode !== null) { + this.proc.kill() + logger.error(`${chalk.gray(`[${this.port}]`)} RCCService could not listen`) + return resolve(false) + } + + this.started = true + return resolve(true) + }) + + this.proc.once("exit", () => { + this.proc.kill() + logger.info(`${chalk.gray(`[${this.port}]`)} RCCService instance exited`) + }) + } catch (_) { + resolve(false) + } + }) + } + + // sigma ?? + Stop(signal = "SIGTERM") { + if (!this.proc) return + return this.proc.kill(signal) + } +} + +module.exports = RCCService diff --git a/src/lib/classes/RenderJob.js b/src/lib/classes/RenderJob.js new file mode 100644 index 0000000..49db58c --- /dev/null +++ b/src/lib/classes/RenderJob.js @@ -0,0 +1,347 @@ +const { readFile } = require("fs/promises") +const chalk = require("chalk") + +const Job = require("./Job.js") +const logger = require("../logger.js") +const { randomUUID } = require("crypto") + +class RenderJob extends Job { + constructor() { + super() + } + + async RenderHeadshot(id) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Headshot RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/headshot.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Headshot" }, + { type: "LUA_TSTRING", value: "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + ], + }, + }).catch((e) => false) + + logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Headshot RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderBodyshot(id, three_d = false) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Bodyshot RenderJob started for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Bodyshot RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/bodyshot.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Bodyshot" }, + { type: "LUA_TSTRING", value: three_d ? "OBJ" : "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + ], + }, + }).catch((e) => false) + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Bodyshot RenderJob finished for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Bodyshot RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderAsset(id, three_d = false) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Asset RenderJob started for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Asset RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/xml.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Asset" }, + { type: "LUA_TSTRING", value: three_d ? "OBJ" : "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TBOOLEAN", value: "true" }, + ], + }, + }).catch((e) => false) + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Asset RenderJob finished for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Asset RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderPlace(id) { + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Place RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/place.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Place" }, + { type: "LUA_TSTRING", value: "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_PLACE_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_PLACE_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TSTRING", value: process.env.ARBITER_TOKEN }, + ], + }, + }).catch((e) => false) + + logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Place RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderTexture(id) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + logger.info(`[${this.id}] Texture RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/texture.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Texture" }, + { type: "LUA_TSTRING", value: "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_USER_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + ], + }, + }).catch((e) => false) + + logger.info(`[${this.id}] Headshot RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderClothing(id, three_d = false) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Clothing RenderJob started for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Clothing RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/clothing.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Clothing" }, + { type: "LUA_TSTRING", value: three_d ? "OBJ" : "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TBOOLEAN", value: "true" }, + ], + }, + }).catch((e) => false) + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Clothing RenderJob finished for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Clothing RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderHead(id, three_d = false) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Head RenderJob started for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Head RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/head.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Head" }, + { type: "LUA_TSTRING", value: three_d ? "OBJ" : "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TBOOLEAN", value: "true" }, + ], + }, + }).catch((e) => false) + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Head RenderJob finished for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Head RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } + + async RenderMesh(id, three_d = false) { + this.id = randomUUID() + + const running = this.started + if (!running) { + const started = await this.Start() + if (!started) throw new Error("RCCService failed to start") + } + + if (!this.client) await this.CreateClient() + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Mesh RenderJob started for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Mesh RenderJob started for ${id}`) + + const result = await this.OpenJobEx({ + name: this.id, + script: await readFile(__dirname + "/../../lua/mesh.lua", { encoding: "utf-8" }), + arguments: { + LuaValue: [ + { type: "LUA_TSTRING", value: this.id }, + + { type: "LUA_TSTRING", value: "Mesh" }, + { type: "LUA_TSTRING", value: three_d ? "OBJ" : "PNG" }, + + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_WIDTH }, + { type: "LUA_TNUMBER", value: process.env.RENDER_ASSET_HEIGHT }, + + { type: "LUA_TSTRING", value: process.env.BASE_URL }, + { type: "LUA_TNUMBER", value: id }, + { type: "LUA_TBOOLEAN", value: "true" }, + ], + }, + }).catch((e) => false) + + if (three_d) logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} 3D Mesh RenderJob finished for ${id}`) + else logger.info(`${chalk.gray(`${chalk.gray(`[${this.id}]`)}`)} Mesh RenderJob finished for ${id}`) + + this.Stop() + + if (!result) return false + return result[0]?.OpenJobExResult?.LuaValue[0]?.value + } +} + +module.exports = RenderJob diff --git a/src/lib/logger.js b/src/lib/logger.js new file mode 100644 index 0000000..38adbe7 --- /dev/null +++ b/src/lib/logger.js @@ -0,0 +1,12 @@ +const chalk = require("chalk") + +const logger = { + // Omg just like tadah :D (Tadahjak face) + boot: (_) => console.log(chalk.greenBright("[BOOT]"), _), + info: (_) => console.log(chalk.blue("[INFO]"), _), + success: (_) => console.log(chalk.green("[SUCCESS]"), _), + warn: (_) => console.log(chalk.yellow("[WARN]"), _), + error: (_) => console.log(chalk.red("[ERROR]"), _), +} + +module.exports = logger diff --git a/src/lib/randport.js b/src/lib/randport.js new file mode 100644 index 0000000..0da19fa --- /dev/null +++ b/src/lib/randport.js @@ -0,0 +1,22 @@ +const net = require("net") +const dgram = require("dgram") + +exports.tcp = () => { + return new Promise((resolve) => { + const server = net.createServer() + server.listen(0, () => { + const port = server.address().port + server.close((err) => resolve(port)) + }) + }) +} + +exports.udp = () => { + return new Promise((resolve) => { + const server = dgram.createSocket("udp4") + server.bind(Math.random() * (60_000 - 50_000) + 50_000, () => { + const port = server.address().port + server.close((err) => resolve(port)) + }) + }) +} diff --git a/src/lua/bodyshot.lua b/src/lua/bodyshot.lua new file mode 100644 index 0000000..092241c --- /dev/null +++ b/src/lua/bodyshot.lua @@ -0,0 +1,29 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... +print(("[%s] Started RenderJob for type '%s' with assetId %d ..."):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local Player = game.Players:CreateLocalPlayer(0) +Player.CharacterAppearance = ("%s/Character/%d"):format(baseUrl, assetId) +Player:LoadCharacter(false) + +game:GetService("RunService"):Run() + +Player.Character.Animate.Disabled = true +Player.Character.Torso.Anchored = true + +local gear = Player.Backpack:GetChildren()[1] +if gear then + gear.Parent = Player.Character + Player.Character.Torso["Right Shoulder"].CurrentAngle = math.rad(90) +end + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result diff --git a/src/lua/clothing.lua b/src/lua/clothing.lua new file mode 100644 index 0000000..ffddfa8 --- /dev/null +++ b/src/lua/clothing.lua @@ -0,0 +1,24 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d"):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local Player = game.Players:CreateLocalPlayer(0) +Player.CharacterAppearance = ("%s/clothing/%d"):format(baseUrl, assetId) +Player:LoadCharacter(false) + +game:GetService("RunService"):Run() + +Player.Character.Animate.Disabled = true +Player.Character.Torso.Anchored = true + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/gameserver.lua b/src/lua/gameserver.lua new file mode 100644 index 0000000..a875969 --- /dev/null +++ b/src/lua/gameserver.lua @@ -0,0 +1,76 @@ +local jobId, type, baseUrl, placeId, port = ... + +------------------- UTILITY FUNCTIONS -------------------------- +function waitForChild(parent, childName) + while true do + local child = parent:findFirstChild(childName) + if child then return child end + parent.ChildAdded:wait() + end +end + +-----------------------------------END UTILITY FUNCTIONS ------------------------- + +-----------------------------------"CUSTOM" SHARED CODE---------------------------------- + +pcall(function() settings().Network.UseInstancePacketCache = true end) +pcall(function() settings().Network.UsePhysicsPacketCache = true end) +pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end) + +settings().Network.PhysicsSend = Enum.PhysicsSendMethod.TopNErrors +settings().Network.ExperimentalPhysicsEnabled = true +settings().Network.WaitingForCharacterLogRate = 100 +pcall(function() settings().Diagnostics:LegacyScriptMode() end) + +-----------------------------------START GAME SHARED SCRIPT------------------------------ + +local scriptContext = game:GetService("ScriptContext") +pcall(function() scriptContext:AddStarterScript(37801172) end) +scriptContext.ScriptsDisabled = true + +game:SetPlaceID(placeId, false) +game:GetService("ChangeHistoryService"):SetEnabled(false) + +local ns = game:GetService("NetworkServer") + +if baseUrl ~= nil then + pcall(function() game:GetService("Players"):SetAbuseReportUrl(baseUrl .. "/AbuseReport/InGameChatHandler.ashx") end) + pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/Asset/") end) + pcall(function() game:GetService("ContentProvider"):SetBaseUrl(baseUrl .. "/") end) + pcall(function() game:GetService("Players"):SetChatFilterUrl(baseUrl .. "/Game/ChatFilter.ashx") end) + + game:GetService("BadgeService"):SetPlaceId(placeId) + game:GetService("BadgeService"):SetIsBadgeLegalUrl("") + game:GetService("InsertService"):SetBaseSetsUrl(baseUrl .. "/Game/Tools/InsertAsset.ashx?nsets=10&type=base") + game:GetService("InsertService"):SetUserSetsUrl(baseUrl .. "/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d") + game:GetService("InsertService"):SetCollectionUrl(baseUrl .. "/Game/Tools/InsertAsset.ashx?sid=%d") + game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/Asset/?id=%d") + game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") + + pcall(function() loadfile(baseUrl .. "/Game/LoadPlaceInfo.ashx?PlaceId=" .. placeId)() end) + pcall(function() game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(true) end) +end + +settings().Diagnostics.LuaRamLimit = 0 + +game:GetService("Players").PlayerAdded:connect(function(player) + print("Player " .. player.userId .. " added") +end) + +game:GetService("Players").PlayerRemoving:connect(function(player) + print("Player " .. player.userId .. " leaving") +end) + +if placeId ~= nil and baseUrl ~= nil then + wait() + game:Load(baseUrl .. "/asset/?id=" .. placeId) +end + +ns:Start(port) + +scriptContext:SetTimeout(10) +scriptContext.ScriptsDisabled = false + +------------------------------END START GAME SHARED SCRIPT-------------------------- + +game:GetService("RunService"):Run() diff --git a/src/lua/head.lua b/src/lua/head.lua new file mode 100644 index 0000000..59a36eb --- /dev/null +++ b/src/lua/head.lua @@ -0,0 +1,33 @@ +local jobId, headType, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d"):format(jobId, headType, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local Player = game.Players:CreateLocalPlayer(0) +Player.CharacterAppearance = ("%s/clothing/%d"):format(baseUrl, assetId) +Player:LoadCharacter(false) + +local function removeBodyParts() + for _, part in pairs(Player.Character:GetChildren()) do + if part:IsA("BasePart") and part.Name ~= "Head" then + part:Destroy() + end + end +end + +removeBodyParts() + +game:GetService("RunService"):Run() + +Player.Character.Animate.Disabled = true + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/headshot.lua b/src/lua/headshot.lua new file mode 100644 index 0000000..d1c6457 --- /dev/null +++ b/src/lua/headshot.lua @@ -0,0 +1,41 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d ..."):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local Player = game.Players:CreateLocalPlayer(0) +Player.CharacterAppearance = ("%s/Character/%d"):format(baseUrl, assetId) +Player:LoadCharacter(false) + +game:GetService("RunService"):Run() + +Player.Character.Animate.Disabled = true +Player.Character.Torso.Anchored = true + +-- Headshot Camera +local FOV = 52.5 +local AngleOffsetX = 0 +local AngleOffsetY = 0 +local AngleOffsetZ = 0 + +local CameraAngle = Player.Character.Head.CFrame * CFrame.new(AngleOffsetX, AngleOffsetY, AngleOffsetZ) +local CameraPosition = Player.Character.Head.CFrame + Vector3.new(0, 0, 0) + (CFrame.Angles(0, -0.2, 0).lookVector.unit * 3) + +local Camera = Instance.new("Camera", Player.Character) +Camera.Name = "ThumbnailCamera" +Camera.CameraType = Enum.CameraType.Scriptable + +Camera.CoordinateFrame = CFrame.new(CameraPosition.p, CameraAngle.p) +Camera.FieldOfView = FOV +workspace.CurrentCamera = Camera + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/mesh.lua b/src/lua/mesh.lua new file mode 100644 index 0000000..d7bf58c --- /dev/null +++ b/src/lua/mesh.lua @@ -0,0 +1,23 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d ..."):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local meshPart = Instance.new("Part", workspace) +meshPart.Anchored = true +meshPart.Size = Vector3.new(10, 10, 10) + +local mesh = Instance.new("SpecialMesh", meshPart) +mesh.MeshType = "FileMesh" +mesh.MeshId = ("%s/asset?id=%d"):format(baseUrl, assetId) + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/place.lua b/src/lua/place.lua new file mode 100644 index 0000000..5dee862 --- /dev/null +++ b/src/lua/place.lua @@ -0,0 +1,23 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d ..."):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) + +-- do this twice for security +game:GetService("ScriptContext").ScriptsDisabled = true +game:GetService("StarterGui").ShowDevelopmentGui = false + +game:Load(("%s/asset/?id=%d"):format(baseUrl, assetId)) + +game:GetService("ScriptContext").ScriptsDisabled = true +game:GetService("StarterGui").ShowDevelopmentGui = false + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, false) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/texture.lua b/src/lua/texture.lua new file mode 100644 index 0000000..5aaf1b6 --- /dev/null +++ b/src/lua/texture.lua @@ -0,0 +1,15 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d"):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):ClickTexture("rbxassetid://" .. assetId, format, x, y) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/lua/xml.lua b/src/lua/xml.lua new file mode 100644 index 0000000..e9a960d --- /dev/null +++ b/src/lua/xml.lua @@ -0,0 +1,23 @@ +local jobId, type, format, x, y, baseUrl, assetId = ... + +print(("[%s] Started RenderJob for type '%s' with assetId %d ..."):format(jobId, type, assetId)) + +game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl .. "/asset/") +game:GetService("InsertService"):SetAssetUrl(baseUrl .. "/asset/?id=%d") +game:GetService("InsertService"):SetAssetVersionUrl(baseUrl .. "/Asset/?assetversionid=%d") +game:GetService("ContentProvider"):SetBaseUrl(baseUrl) +game:GetService("ScriptContext").ScriptsDisabled = true + +local asset = game:GetObjects(("%s/asset/?id=%d"):format(baseUrl, assetId))[1] +asset.Parent = workspace + +local thumbnailCamera = asset:FindFirstChild("ThumbnailCamera") +if thumbnailCamera ~= nil and thumbnailCamera.ClassName == "Camera" then + workspace.CurrentCamera = thumbnailCamera +end + +print(("[%s] Rendering ..."):format(jobId)) +local result = game:GetService("ThumbnailGenerator"):Click(format, x, y, true) +print(("[%s] Done!"):format(jobId)) + +return result \ No newline at end of file diff --git a/src/routes/game/execute.js b/src/routes/game/execute.js new file mode 100644 index 0000000..6ea0070 --- /dev/null +++ b/src/routes/game/execute.js @@ -0,0 +1,19 @@ +const { randomUUID } = require("crypto") +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.use(express.json()) + +app.post("/:id", async (request, response) => { + const game = global.games.get(request.params.id) + if (!game) return response.status(404).json({ error: "Game is not running" }) + + const { script } = request.body + const jobResponse = await game.Execute(randomUUID(), script) + + return response.json({ response: jobResponse }) +}) + +module.exports = app diff --git a/src/routes/game/renew.js b/src/routes/game/renew.js new file mode 100644 index 0000000..68e6538 --- /dev/null +++ b/src/routes/game/renew.js @@ -0,0 +1,14 @@ +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.get("/:id/:expire", async (request, response) => { + const game = global.games.get(request.params.id) + if (!game) return response.status(404).json({ error: "Game is not running" }) + + await game.RenewLease(request.params.expire) + return response.json({ success: true }) +}) + +module.exports = app diff --git a/src/routes/game/running.js b/src/routes/game/running.js new file mode 100644 index 0000000..a1f97a0 --- /dev/null +++ b/src/routes/game/running.js @@ -0,0 +1,19 @@ +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.get("/:id", async (request, response) => { + const game = global.games.get(request.params.id) + if (!game) return response.json(false) + + const running = await game.Running() + if (!running && game) { + game.Stop() + return response.json(false) + } + + return response.json(true) +}) + +module.exports = app diff --git a/src/routes/game/start.js b/src/routes/game/start.js new file mode 100644 index 0000000..0049888 --- /dev/null +++ b/src/routes/game/start.js @@ -0,0 +1,21 @@ +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.get("/:id", async (request, response) => { + const game = global.games.get(request.params.id) + if (game) return response.status(400).json({ error: "Game is running" }) + + const job = new GameJob() + const result = await job.StartGame(request.params.id).catch((_) => _) + + global.games.set(request.params.id, job) + job.proc.once("exit", () => { + global.games.delete(request.params.id) + }) + + return response.json({ success: true }) +}) + +module.exports = app diff --git a/src/routes/game/status.js b/src/routes/game/status.js new file mode 100644 index 0000000..7c4aeed --- /dev/null +++ b/src/routes/game/status.js @@ -0,0 +1,14 @@ +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.get("/:id", async (request, response) => { + const game = global.games.get(request.params.id) + if (!game) return response.status(404).json({ error: "Game is not running" }) + + const status = await game.GetStatus() + return response.json(status[0]?.GetStatusResult) +}) + +module.exports = app diff --git a/src/routes/game/stop.js b/src/routes/game/stop.js new file mode 100644 index 0000000..be676b5 --- /dev/null +++ b/src/routes/game/stop.js @@ -0,0 +1,14 @@ +const express = require("express") +const app = express.Router() + +const GameJob = require("../../lib/classes/GameJob.js") + +app.get("/:id", async (request, response) => { + const game = global.games.get(request.params.id) + if (!game) return response.status(404).json({ error: "Game is not running" }) + + game.Stop() + return response.json({ success: true }) +}) + +module.exports = app diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..26804f0 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,23 @@ +const express = require("express") +const app = express.Router() + +function getGameIds() { + let gameIds = [] + + global.games.forEach((value, key) => { + gameIds.push(value.placeId) + }) + + return gameIds +} + +app.get("/", (request, response) => { + return response.status(200).json({ + runningGamesCount: global.games.size, + runningGames: getGameIds(), + }) +}) + +app.all("*", (request, response) => response.status(404).json({ status: 404 })) + +module.exports = app diff --git a/src/routes/render/asset.js b/src/routes/render/asset.js new file mode 100644 index 0000000..7f0eca1 --- /dev/null +++ b/src/routes/render/asset.js @@ -0,0 +1,26 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const asset = await job.RenderAsset(params.id).catch((_) => _) + if (asset?.message) return response.status(500).json({ error: asset.message }) + + return response.end(asset); +}) + +app.get("/:id/3d", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const three_d = await job.RenderAsset(params.id, true).catch((_) => _) + if (three_d?.message) return response.status(500).json({ error: three_d.message }) + + return response.json(JSON.parse(three_d)) +}) + +module.exports = app diff --git a/src/routes/render/clothing.js b/src/routes/render/clothing.js new file mode 100644 index 0000000..6111a49 --- /dev/null +++ b/src/routes/render/clothing.js @@ -0,0 +1,16 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const clothing = await job.RenderClothing(params.id).catch((_) => _) + if (clothing?.message) return response.status(500).json({ error: clothing.message }) + + return response.end(clothing) +}) + +module.exports = app diff --git a/src/routes/render/game.js b/src/routes/render/game.js new file mode 100644 index 0000000..a2e4c51 --- /dev/null +++ b/src/routes/render/game.js @@ -0,0 +1,16 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const game = await job.RenderPlace(params.id).catch((_) => _) + if (game?.message) return response.status(500).json({ error: game.message }) + + return response.end(game) +}) + +module.exports = app diff --git a/src/routes/render/head.js b/src/routes/render/head.js new file mode 100644 index 0000000..3abd12f --- /dev/null +++ b/src/routes/render/head.js @@ -0,0 +1,16 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const head = await job.RenderHead(params.id).catch((_) => _) + if (head?.message) return response.status(500).json({ error: head.message }) + + return response.end(head) +}) + +module.exports = app diff --git a/src/routes/render/mesh.js b/src/routes/render/mesh.js new file mode 100644 index 0000000..d2edc73 --- /dev/null +++ b/src/routes/render/mesh.js @@ -0,0 +1,16 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const mesh = await job.RenderMesh(params.id).catch((_) => _) + if (mesh?.message) return response.status(500).json({ error: mesh.message }) + + return response.end(mesh) +}) + +module.exports = app diff --git a/src/routes/render/texture.js b/src/routes/render/texture.js new file mode 100644 index 0000000..6bd0fc7 --- /dev/null +++ b/src/routes/render/texture.js @@ -0,0 +1,16 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const texture = await job.RenderTexture(params.id).catch((_) => _) + if (texture?.message) return response.status(500).json({ error: texture.message }) + + return response.end(texture) +}) + +module.exports = app diff --git a/src/routes/render/user.js b/src/routes/render/user.js new file mode 100644 index 0000000..f0a532e --- /dev/null +++ b/src/routes/render/user.js @@ -0,0 +1,36 @@ +const express = require("express") +const app = express.Router() + +const RenderJob = require("../../lib/classes/RenderJob.js") + +app.get("/:id/bodyshot", async (request, response) => { + const { params } = request + const job = new RenderJob() + + const bodyshot = await job.RenderBodyshot(params.id).catch((_) => _) + if (bodyshot?.message) return response.status(500).json({ error: bodyshot.message }) + + return response.end(bodyshot); +}) + +app.get("/:id/headshot", async (request, response) => { + const { params } = request + const job = new RenderJob() + + const headshot = await job.RenderHeadshot(params.id).catch((_) => _) + if (headshot?.message) return response.status(500).json({ error: headshot.message }) + + return response.end(headshot); +}) + +app.get("/:id/3d", async (request, response) => { + const { params, query } = request + const job = new RenderJob() + + const three_d = await job.RenderBodyshot(params.id, true).catch((_) => _) + if (three_d?.message) return response.status(500).json({ error: three_d.message }) + + return response.json(JSON.parse(three_d)) +}) + +module.exports = app