Initial Commit

This commit is contained in:
theo-doree 2024-01-06 17:55:03 -05:00
commit 76715a89ba
36 changed files with 3069 additions and 0 deletions

16
.env.example Normal file
View File

@ -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

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
build
.env

105
README.md Normal file
View File

@ -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
<details>
<summary>200 OK</summary>
```
iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC...
```
</details>
### GET /render/asset/3d/:id
<details>
<summary>200 OK</summary>
```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": "..." }
}
}
```
</details>
### GET /render/texture/:id
<details>
<summary>200 OK</summary>
```
iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC...
```
</details>
### GET /render/user/headshot/:id
<details>
<summary>200 OK</summary>
```
iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC...
```
</details>
### GET /render/user/bodyshot/:id
<details>
<summary>200 OK</summary>
```
iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC...
```
</details>
### GET /render/user/3d/:id
<details>
<summary>200 OK</summary>
```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": "..." }
}
}
```
</details>

973
package-lock.json generated Normal file
View File

@ -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"
}
}
}
}

24
package.json Normal file
View File

@ -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"
}
}

40
src/index.js Normal file
View File

@ -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}/`))

811
src/lib/RCCService.wsdl Normal file
View File

@ -0,0 +1,811 @@
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://roblox.com/"
xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://roblox.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://roblox.com/">
<s:element name="HelloWorld">
<s:complexType />
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetVersion">
<s:complexType />
</s:element>
<s:element name="GetVersionResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetVersionResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetStatus">
<s:complexType />
</s:element>
<s:element name="GetStatusResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetStatusResult" type="tns:Status" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Status">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="version" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="environmentCount" type="s:int" />
</s:sequence>
</s:complexType>
<s:element name="OpenJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="OpenJobEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Job">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="id" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="expirationInSeconds" type="s:double" />
<s:element minOccurs="1" maxOccurs="1" name="category" type="s:int" />
<s:element minOccurs="1" maxOccurs="1" name="cores" type="s:double" />
</s:sequence>
</s:complexType>
<s:complexType name="ScriptExecution">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="script" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="arguments" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfLuaValue">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="LuaValue" nillable="true"
type="tns:LuaValue" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfJob">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Job" nillable="true" type="tns:Job" />
</s:sequence>
</s:complexType>
<s:complexType name="LuaValue">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="tns:LuaType" />
<s:element minOccurs="0" maxOccurs="1" name="value" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="table" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
<s:simpleType name="LuaType">
<s:restriction base="s:string">
<s:enumeration value="LUA_TNIL" />
<s:enumeration value="LUA_TBOOLEAN" />
<s:enumeration value="LUA_TNUMBER" />
<s:enumeration value="LUA_TSTRING" />
<s:enumeration value="LUA_TTABLE" />
</s:restriction>
</s:simpleType>
<s:element name="OpenJobResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="OpenJobResult" type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="OpenJobExResponse">
<s:complexType>
<s:sequence>
<s:element name="OpenJobExResult" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="RenewLease">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="expirationInSeconds" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="RenewLeaseResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="RenewLeaseResult" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Execute">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="ExecuteResult" nillable="true"
type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteExResponse">
<s:complexType>
<s:sequence>
<s:element name="ExecuteExResult" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseJobResponse">
<s:complexType />
</s:element>
<s:element name="BatchJob">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="BatchJobResult" nillable="true"
type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="job" type="tns:Job" />
<s:element minOccurs="1" maxOccurs="1" name="script" type="tns:ScriptExecution" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="BatchJobExResponse">
<s:complexType>
<s:sequence>
<s:element name="BatchJobExResult" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetExpiration">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetExpirationResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="GetExpirationResult" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetAllJobs">
<s:complexType />
</s:element>
<s:element name="GetAllJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="GetAllJobsResult" nillable="true"
type="tns:Job" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetAllJobsEx">
<s:complexType />
</s:element>
<s:element name="GetAllJobsExResponse">
<s:complexType>
<s:sequence>
<s:element name="GetAllJobsExResult" type="tns:ArrayOfJob" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseExpiredJobs">
<s:complexType />
</s:element>
<s:element name="CloseExpiredJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="CloseExpiredJobsResult" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CloseAllJobs">
<s:complexType />
</s:element>
<s:element name="CloseAllJobsResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="CloseAllJobsResult" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Diag">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="DiagResult" nillable="true"
type="tns:LuaValue" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagEx">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="type" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="jobID" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="DiagExResponse">
<s:complexType>
<s:sequence>
<s:element name="DiagExResult" type="tns:ArrayOfLuaValue" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld" />
</wsdl:message>
<wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse" />
</wsdl:message>
<wsdl:message name="GetVersionSoapIn">
<wsdl:part name="parameters" element="tns:GetVersion" />
</wsdl:message>
<wsdl:message name="GetVersionSoapOut">
<wsdl:part name="parameters" element="tns:GetVersionResponse" />
</wsdl:message>
<wsdl:message name="GetStatusSoapIn">
<wsdl:part name="parameters" element="tns:GetStatus" />
</wsdl:message>
<wsdl:message name="GetStatusSoapOut">
<wsdl:part name="parameters" element="tns:GetStatusResponse" />
</wsdl:message>
<wsdl:message name="OpenJobSoapIn">
<wsdl:part name="parameters" element="tns:OpenJob" />
</wsdl:message>
<wsdl:message name="OpenJobSoapOut">
<wsdl:part name="parameters" element="tns:OpenJobResponse" />
</wsdl:message>
<wsdl:message name="OpenJobExSoapIn">
<wsdl:part name="parameters" element="tns:OpenJobEx" />
</wsdl:message>
<wsdl:message name="OpenJobExSoapOut">
<wsdl:part name="parameters" element="tns:OpenJobExResponse" />
</wsdl:message>
<wsdl:message name="RenewLeaseSoapIn">
<wsdl:part name="parameters" element="tns:RenewLease" />
</wsdl:message>
<wsdl:message name="RenewLeaseSoapOut">
<wsdl:part name="parameters" element="tns:RenewLeaseResponse" />
</wsdl:message>
<wsdl:message name="ExecuteSoapIn">
<wsdl:part name="parameters" element="tns:Execute" />
</wsdl:message>
<wsdl:message name="ExecuteSoapOut">
<wsdl:part name="parameters" element="tns:ExecuteResponse" />
</wsdl:message>
<wsdl:message name="ExecuteExSoapIn">
<wsdl:part name="parameters" element="tns:ExecuteEx" />
</wsdl:message>
<wsdl:message name="ExecuteExSoapOut">
<wsdl:part name="parameters" element="tns:ExecuteExResponse" />
</wsdl:message>
<wsdl:message name="CloseJobSoapIn">
<wsdl:part name="parameters" element="tns:CloseJob" />
</wsdl:message>
<wsdl:message name="CloseJobSoapOut">
<wsdl:part name="parameters" element="tns:CloseJobResponse" />
</wsdl:message>
<wsdl:message name="BatchJobSoapIn">
<wsdl:part name="parameters" element="tns:BatchJob" />
</wsdl:message>
<wsdl:message name="BatchJobSoapOut">
<wsdl:part name="parameters" element="tns:BatchJobResponse" />
</wsdl:message>
<wsdl:message name="BatchJobExSoapIn">
<wsdl:part name="parameters" element="tns:BatchJobEx" />
</wsdl:message>
<wsdl:message name="BatchJobExSoapOut">
<wsdl:part name="parameters" element="tns:BatchJobExResponse" />
</wsdl:message>
<wsdl:message name="GetExpirationSoapIn">
<wsdl:part name="parameters" element="tns:GetExpiration" />
</wsdl:message>
<wsdl:message name="GetExpirationSoapOut">
<wsdl:part name="parameters" element="tns:GetExpirationResponse" />
</wsdl:message>
<wsdl:message name="GetAllJobsSoapIn">
<wsdl:part name="parameters" element="tns:GetAllJobs" />
</wsdl:message>
<wsdl:message name="GetAllJobsSoapOut">
<wsdl:part name="parameters" element="tns:GetAllJobsResponse" />
</wsdl:message>
<wsdl:message name="GetAllJobsExSoapIn">
<wsdl:part name="parameters" element="tns:GetAllJobsEx" />
</wsdl:message>
<wsdl:message name="GetAllJobsExSoapOut">
<wsdl:part name="parameters" element="tns:GetAllJobsExResponse" />
</wsdl:message>
<wsdl:message name="CloseExpiredJobsSoapIn">
<wsdl:part name="parameters" element="tns:CloseExpiredJobs" />
</wsdl:message>
<wsdl:message name="CloseExpiredJobsSoapOut">
<wsdl:part name="parameters" element="tns:CloseExpiredJobsResponse" />
</wsdl:message>
<wsdl:message name="CloseAllJobsSoapIn">
<wsdl:part name="parameters" element="tns:CloseAllJobs" />
</wsdl:message>
<wsdl:message name="CloseAllJobsSoapOut">
<wsdl:part name="parameters" element="tns:CloseAllJobsResponse" />
</wsdl:message>
<wsdl:message name="DiagSoapIn">
<wsdl:part name="parameters" element="tns:Diag" />
</wsdl:message>
<wsdl:message name="DiagSoapOut">
<wsdl:part name="parameters" element="tns:DiagResponse" />
</wsdl:message>
<wsdl:message name="DiagExSoapIn">
<wsdl:part name="parameters" element="tns:DiagEx" />
</wsdl:message>
<wsdl:message name="DiagExSoapOut">
<wsdl:part name="parameters" element="tns:DiagExResponse" />
</wsdl:message>
<wsdl:portType name="RCCServiceSoap">
<wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn" />
<wsdl:output message="tns:HelloWorldSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetVersion">
<wsdl:input message="tns:GetVersionSoapIn" />
<wsdl:output message="tns:GetVersionSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetStatus">
<wsdl:input message="tns:GetStatusSoapIn" />
<wsdl:output message="tns:GetStatusSoapOut" />
</wsdl:operation>
<wsdl:operation name="OpenJob">
<wsdl:input message="tns:OpenJobSoapIn" />
<wsdl:output message="tns:OpenJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<wsdl:input message="tns:OpenJobExSoapIn" />
<wsdl:output message="tns:OpenJobExSoapOut" />
</wsdl:operation>
<wsdl:operation name="RenewLease">
<wsdl:input message="tns:RenewLeaseSoapIn" />
<wsdl:output message="tns:RenewLeaseSoapOut" />
</wsdl:operation>
<wsdl:operation name="Execute">
<wsdl:input message="tns:ExecuteSoapIn" />
<wsdl:output message="tns:ExecuteSoapOut" />
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<wsdl:input message="tns:ExecuteExSoapIn" />
<wsdl:output message="tns:ExecuteExSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseJob">
<wsdl:input message="tns:CloseJobSoapIn" />
<wsdl:output message="tns:CloseJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="BatchJob">
<wsdl:input message="tns:BatchJobSoapIn" />
<wsdl:output message="tns:BatchJobSoapOut" />
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<wsdl:input message="tns:BatchJobExSoapIn" />
<wsdl:output message="tns:BatchJobExSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<wsdl:input message="tns:GetExpirationSoapIn" />
<wsdl:output message="tns:GetExpirationSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<wsdl:input message="tns:GetAllJobsSoapIn" />
<wsdl:output message="tns:GetAllJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<wsdl:input message="tns:GetAllJobsExSoapIn" />
<wsdl:output message="tns:GetAllJobsExSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<wsdl:input message="tns:CloseExpiredJobsSoapIn" />
<wsdl:output message="tns:CloseExpiredJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<wsdl:input message="tns:CloseAllJobsSoapIn" />
<wsdl:output message="tns:CloseAllJobsSoapOut" />
</wsdl:operation>
<wsdl:operation name="Diag">
<wsdl:input message="tns:DiagSoapIn" />
<wsdl:output message="tns:DiagSoapOut" />
</wsdl:operation>
<wsdl:operation name="DiagEx">
<wsdl:input message="tns:DiagExSoapIn" />
<wsdl:output message="tns:DiagExSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RCCServiceSoap" type="tns:RCCServiceSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://roblox.com/HelloWorld" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetVersion">
<soap:operation soapAction="http://roblox.com/GetVersion" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetStatus">
<soap:operation soapAction="http://roblox.com/GetStatus" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJob">
<soap:operation soapAction="http://roblox.com/OpenJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<soap:operation soapAction="http://roblox.com/OpenJobEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RenewLease">
<soap:operation soapAction="http://roblox.com/RenewLease" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Execute">
<soap:operation soapAction="http://roblox.com/Execute" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<soap:operation soapAction="http://roblox.com/ExecuteEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseJob">
<soap:operation soapAction="http://roblox.com/CloseJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJob">
<soap:operation soapAction="http://roblox.com/BatchJob" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<soap:operation soapAction="http://roblox.com/BatchJobEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<soap:operation soapAction="http://roblox.com/GetExpiration" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<soap:operation soapAction="http://roblox.com/GetAllJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<soap:operation soapAction="http://roblox.com/GetAllJobsEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<soap:operation soapAction="http://roblox.com/CloseExpiredJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<soap:operation soapAction="http://roblox.com/CloseAllJobs" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Diag">
<soap:operation soapAction="http://roblox.com/Diag" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="DiagEx">
<soap:operation soapAction="http://roblox.com/DiagEx" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="RCCServiceSoap12" type="tns:RCCServiceSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="HelloWorld">
<soap12:operation soapAction="http://roblox.com/HelloWorld" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetVersion">
<soap12:operation soapAction="http://roblox.com/GetVersion" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetStatus">
<soap12:operation soapAction="http://roblox.com/GetStatus" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJob">
<soap12:operation soapAction="http://roblox.com/OpenJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="OpenJobEx">
<soap12:operation soapAction="http://roblox.com/OpenJobEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RenewLease">
<soap12:operation soapAction="http://roblox.com/RenewLease" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Execute">
<soap12:operation soapAction="http://roblox.com/Execute" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ExecuteEx">
<soap12:operation soapAction="http://roblox.com/ExecuteEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseJob">
<soap12:operation soapAction="http://roblox.com/CloseJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJob">
<soap12:operation soapAction="http://roblox.com/BatchJob" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="BatchJobEx">
<soap12:operation soapAction="http://roblox.com/BatchJobEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetExpiration">
<soap12:operation soapAction="http://roblox.com/GetExpiration" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobs">
<soap12:operation soapAction="http://roblox.com/GetAllJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetAllJobsEx">
<soap12:operation soapAction="http://roblox.com/GetAllJobsEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseExpiredJobs">
<soap12:operation soapAction="http://roblox.com/CloseExpiredJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="CloseAllJobs">
<soap12:operation soapAction="http://roblox.com/CloseAllJobs" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Diag">
<soap12:operation soapAction="http://roblox.com/Diag" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="DiagEx">
<soap12:operation soapAction="http://roblox.com/DiagEx" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<service name="RCCServiceSoap">
<port name="RCCServiceSoapPort" binding="tns:RCCServiceSoap">
<soap:address location="AAHHHHH" />
</port>
</service>
</wsdl:definitions>

View File

@ -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

56
src/lib/classes/Job.js Normal file
View File

@ -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

View File

@ -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

View File

@ -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

12
src/lib/logger.js Normal file
View File

@ -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

22
src/lib/randport.js Normal file
View File

@ -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))
})
})
}

29
src/lua/bodyshot.lua Normal file
View File

@ -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

24
src/lua/clothing.lua Normal file
View File

@ -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

76
src/lua/gameserver.lua Normal file
View File

@ -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()

33
src/lua/head.lua Normal file
View File

@ -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

41
src/lua/headshot.lua Normal file
View File

@ -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

23
src/lua/mesh.lua Normal file
View File

@ -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

23
src/lua/place.lua Normal file
View File

@ -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

15
src/lua/texture.lua Normal file
View File

@ -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

23
src/lua/xml.lua Normal file
View File

@ -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

View File

@ -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

14
src/routes/game/renew.js Normal file
View File

@ -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

View File

@ -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

21
src/routes/game/start.js Normal file
View File

@ -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

14
src/routes/game/status.js Normal file
View File

@ -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

14
src/routes/game/stop.js Normal file
View File

@ -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

23
src/routes/index.js Normal file
View File

@ -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

View File

@ -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

View File

@ -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

16
src/routes/render/game.js Normal file
View File

@ -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

16
src/routes/render/head.js Normal file
View File

@ -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

16
src/routes/render/mesh.js Normal file
View File

@ -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

View File

@ -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

36
src/routes/render/user.js Normal file
View File

@ -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