mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 19:22:22 +01:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70d83dc1c5 | ||
|
|
d53a577f0b | ||
|
|
c9ed6a5af3 | ||
|
|
4ea73d17c7 | ||
|
|
52abf5eb95 | ||
|
|
9c6cd6dd37 | ||
|
|
c3ede3b745 | ||
|
|
d2f0d176eb | ||
|
|
f9cf99a6b2 | ||
|
|
dc2d4c4377 | ||
|
|
5d8f6bf0fa | ||
|
|
9a000db3b7 | ||
|
|
4327541ac6 | ||
|
|
64dc971da4 | ||
|
|
d36da909ed | ||
|
|
90448eeaa1 | ||
|
|
b319479def | ||
|
|
d5e41ed646 | ||
|
|
9e20ec86e6 | ||
|
|
004fc41292 | ||
|
|
ee466df630 | ||
|
|
fdb1d694d9 | ||
|
|
0cfa4af236 | ||
|
|
d42946c44b | ||
|
|
0a59a32c1b | ||
|
|
2046cb14f6 | ||
|
|
e3f7a1d2cc | ||
|
|
cd6752ed3f | ||
|
|
e1df46efd9 | ||
|
|
dccd05e57f | ||
|
|
eb7745cc4d | ||
|
|
7d5e17642a | ||
|
|
6f91ad0404 | ||
|
|
0c38528e77 | ||
|
|
dfebe74518 | ||
|
|
bfe15e43d9 | ||
|
|
62dae468bf | ||
|
|
a9af28273a | ||
|
|
232bf8fbfc | ||
|
|
a787c4efc9 | ||
|
|
4c3fe9bc65 | ||
|
|
3f88d5ed80 | ||
|
|
582270849e | ||
|
|
1ef4e76768 | ||
|
|
cd8c8e6dd9 | ||
|
|
170a878e79 | ||
|
|
a2505011d9 | ||
|
|
3e2c2e96b1 | ||
|
|
00802ac6da | ||
|
|
1d68469297 | ||
|
|
83878db8c5 | ||
|
|
e3b8d7d182 | ||
|
|
45b8629207 | ||
|
|
2df1b81485 | ||
|
|
018ccb7f8e | ||
|
|
0d64251623 | ||
|
|
47adb572ea | ||
|
|
e3a42d6b81 | ||
|
|
e24f4e97b3 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
backend/**/* linguist-vendored
|
||||
17
README.md
17
README.md
@@ -1,14 +1,25 @@
|
||||

|
||||
|
||||
GUI and CLI Launcher for [Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0/)
|
||||
Join our discord at https://discord.gg/reboot
|
||||
|
||||
Join our [Discord](https://discord.gg/rebootmp)
|
||||
|
||||
Install the launcher easily from the [releases](https://github.com/Auties00/Reboot-Launcher/releases/) section
|
||||
|
||||
## Modules
|
||||
|
||||
- COMMON: Shared business logic for CLI and GUI modules
|
||||
|
||||
- CLI: Work in progress command line interface to host a Fortnite Server on a Windows VPS easily, developed in Dart
|
||||
|
||||
- GUI: Stable graphical user interface to play and host Fortnite S0-14
|
||||
|
||||
## Installation
|
||||
## Preview
|
||||
|
||||
Check the releases section
|
||||
- GUI
|
||||
|
||||

|
||||
|
||||
- CLI
|
||||
|
||||
Coming soon!
|
||||
|
||||
4
auth_backend/README.md
Normal file
4
auth_backend/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Backend
|
||||
Fork of LawinV1
|
||||
Awaiting rewrite in Dart
|
||||
Use build.bat to generate the executable
|
||||
@@ -4,6 +4,16 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const cookieParser = require("cookie-parser");
|
||||
|
||||
const audit = require('express-requests-logger')
|
||||
express.use(audit({
|
||||
request: {
|
||||
maxBodyLength: 150
|
||||
},
|
||||
response: {
|
||||
maxBodyLength: 150
|
||||
}
|
||||
}));
|
||||
|
||||
express.use(Express.json());
|
||||
express.use(Express.urlencoded({ extended: true }));
|
||||
express.use(Express.static('public'));
|
||||
@@ -25,7 +35,7 @@ express.use(require("./structure/matchmaking.js"));
|
||||
express.use(require("./structure/cloudstorage.js"));
|
||||
express.use(require("./structure/mcp.js"));
|
||||
|
||||
const port = process.env.PORT || 3551;
|
||||
const port = 3551;
|
||||
express.listen(port, () => {
|
||||
console.log("LawinServer started listening on port", port);
|
||||
|
||||
0
auth_backend/install_packages.bat
Normal file
0
auth_backend/install_packages.bat
Normal file
248
lawin/package-lock.json → auth_backend/package-lock.json
generated
248
lawin/package-lock.json → auth_backend/package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express": "^4.18.2",
|
||||
"express-requests-logger": "^4.0.0",
|
||||
"ini": "^2.0.0",
|
||||
"nexe": "^4.0.0-rc.6",
|
||||
"path": "^0.12.7",
|
||||
@@ -388,6 +389,23 @@
|
||||
"node": ">v0.4.12"
|
||||
}
|
||||
},
|
||||
"node_modules/bunyan": {
|
||||
"version": "1.8.15",
|
||||
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
|
||||
"integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==",
|
||||
"engines": [
|
||||
"node >=0.10.0"
|
||||
],
|
||||
"bin": {
|
||||
"bunyan": "bin/bunyan"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dtrace-provider": "~0.8",
|
||||
"moment": "^2.19.3",
|
||||
"mv": "~2",
|
||||
"safe-json-stringify": "~1"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -1063,6 +1081,19 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
"integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"nan": "^2.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer3": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
|
||||
@@ -1168,6 +1199,16 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-requests-logger": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-requests-logger/-/express-requests-logger-4.0.0.tgz",
|
||||
"integrity": "sha512-NHQptnDY0fceiTSWLnW0dbJSFlrvbFpCGHmY6LsTMmJLgkyO3x8qAJ+EsryQRMga20YH8Ynt/vnmg23QP07h1Q==",
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.14",
|
||||
"flat": "^5.0.2",
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
@@ -1292,6 +1333,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/flat": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
|
||||
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
|
||||
"bin": {
|
||||
"flat": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/fn.name": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||
@@ -1788,6 +1837,11 @@
|
||||
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
@@ -1973,6 +2027,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/moo-server": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz",
|
||||
@@ -2009,6 +2072,77 @@
|
||||
"readable-stream": "^3.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mv": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||
"integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "~0.5.1",
|
||||
"ncp": "~2.0.0",
|
||||
"rimraf": "~2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mv/node_modules/glob": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
|
||||
"integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mv/node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mv/node_modules/rimraf": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
|
||||
"integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"glob": "^6.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
"integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"ncp": "bin/ncp"
|
||||
}
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@@ -2534,6 +2668,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safe-json-stringify": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
|
||||
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
|
||||
@@ -3405,6 +3545,17 @@
|
||||
"wrench": "1.3.x"
|
||||
}
|
||||
},
|
||||
"bunyan": {
|
||||
"version": "1.8.15",
|
||||
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
|
||||
"integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==",
|
||||
"requires": {
|
||||
"dtrace-provider": "~0.8",
|
||||
"moment": "^2.19.3",
|
||||
"mv": "~2",
|
||||
"safe-json-stringify": "~1"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -3941,6 +4092,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
"integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"duplexer3": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz",
|
||||
@@ -4038,6 +4198,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"express-requests-logger": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-requests-logger/-/express-requests-logger-4.0.0.tgz",
|
||||
"integrity": "sha512-NHQptnDY0fceiTSWLnW0dbJSFlrvbFpCGHmY6LsTMmJLgkyO3x8qAJ+EsryQRMga20YH8Ynt/vnmg23QP07h1Q==",
|
||||
"requires": {
|
||||
"bunyan": "^1.8.14",
|
||||
"flat": "^5.0.2",
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"ext-list": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz",
|
||||
@@ -4130,6 +4300,11 @@
|
||||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"flat": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
|
||||
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
|
||||
},
|
||||
"fn.name": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||
@@ -4496,6 +4671,11 @@
|
||||
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
@@ -4626,6 +4806,12 @@
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"optional": true
|
||||
},
|
||||
"moo-server": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz",
|
||||
@@ -4645,6 +4831,62 @@
|
||||
"readable-stream": "^3.6.0"
|
||||
}
|
||||
},
|
||||
"mv": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||
"integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mkdirp": "~0.5.1",
|
||||
"ncp": "~2.0.0",
|
||||
"rimraf": "~2.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
|
||||
"integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
|
||||
"integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"glob": "^6.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"optional": true
|
||||
},
|
||||
"ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
"integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==",
|
||||
"optional": true
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@@ -5000,6 +5242,12 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
},
|
||||
"safe-json-stringify": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
|
||||
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
|
||||
"optional": true
|
||||
},
|
||||
"safe-stable-stringify": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
|
||||
@@ -12,7 +12,8 @@
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^8.5.0",
|
||||
"xml-parser": "^1.2.1",
|
||||
"xmlbuilder": "^15.1.1"
|
||||
"xmlbuilder": "^15.1.1",
|
||||
"express-requests-logger": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
File diff suppressed because it is too large
Load Diff
0
auth_backend/public/images/discord-s.png
Normal file
0
auth_backend/public/images/discord-s.png
Normal file
0
auth_backend/public/images/discord.png
Normal file
0
auth_backend/public/images/discord.png
Normal file
0
auth_backend/public/images/lawin-s.png
Normal file
0
auth_backend/public/images/lawin-s.png
Normal file
0
auth_backend/public/images/lawin.jpg
Normal file
0
auth_backend/public/images/lawin.jpg
Normal file
0
auth_backend/public/images/motd-s.png
Normal file
0
auth_backend/public/images/motd-s.png
Normal file
0
auth_backend/public/images/motd.png
Normal file
0
auth_backend/public/images/motd.png
Normal file
0
auth_backend/public/images/seasonx.png
Normal file
0
auth_backend/public/images/seasonx.png
Normal file
0
auth_backend/responses/CloudDir/LawinServer.chunk
Normal file
0
auth_backend/responses/CloudDir/LawinServer.chunk
Normal file
@@ -75,5 +75,12 @@
|
||||
"direction": "OUTBOUND",
|
||||
"created": "2024-05-31T19:50:04.738Z",
|
||||
"favorite": false
|
||||
},
|
||||
{
|
||||
"accountId": "Player724",
|
||||
"status": "ACCEPTED",
|
||||
"direction": "OUTBOUND",
|
||||
"created": "2024-06-24T20:15:48.062Z",
|
||||
"favorite": false
|
||||
}
|
||||
]
|
||||
@@ -98,6 +98,15 @@
|
||||
"note": "",
|
||||
"favorite": false,
|
||||
"created": "2024-05-31T19:50:04.738Z"
|
||||
},
|
||||
{
|
||||
"accountId": "Player724",
|
||||
"groups": [],
|
||||
"mutual": 0,
|
||||
"alias": "",
|
||||
"note": "",
|
||||
"favorite": false,
|
||||
"created": "2024-06-24T20:15:48.062Z"
|
||||
}
|
||||
],
|
||||
"incoming": [],
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7932,6 +7932,35 @@ express.post("/fortnite/api/game/v2/profile/*/client/SetHeroCosmeticVariants", a
|
||||
res.end();
|
||||
});
|
||||
|
||||
// any dedicated_server request
|
||||
express.post("/fortnite/api/game/v2/profile/*/dedicated_server/*", async (req, res) => {
|
||||
const profile = require(`./../profiles/${req.query.profileId || "athena"}.json`);
|
||||
|
||||
// do not change any of these or you will end up breaking it
|
||||
var ApplyProfileChanges = [];
|
||||
var BaseRevision = profile.rvn || 0;
|
||||
var QueryRevision = req.query.rvn || -1;
|
||||
|
||||
// this doesn't work properly on version v12.20 and above but whatever
|
||||
if (QueryRevision != BaseRevision) {
|
||||
ApplyProfileChanges = [{
|
||||
"changeType": "fullProfileUpdate",
|
||||
"profile": profile
|
||||
}];
|
||||
}
|
||||
|
||||
res.json({
|
||||
"profileRevision": profile.rvn || 0,
|
||||
"profileId": req.query.profileId || "athena",
|
||||
"profileChangesBaseRevision": BaseRevision,
|
||||
"profileChanges": ApplyProfileChanges,
|
||||
"profileCommandRevision": profile.commandRevision || 0,
|
||||
"serverTime": new Date().toISOString(),
|
||||
"responseVersion": 1
|
||||
})
|
||||
res.end();
|
||||
});
|
||||
|
||||
// any mcp request that doesn't have something assigned to it
|
||||
express.post("/fortnite/api/game/v2/profile/*/client/*", async (req, res) => {
|
||||
const profile = require(`./../profiles/${req.query.profileId || "athena"}.json`);
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:reboot_cli/src/game.dart';
|
||||
import 'package:reboot_cli/src/reboot.dart';
|
||||
import 'package:reboot_cli/src/server.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
late String? username;
|
||||
late bool host;
|
||||
late bool verbose;
|
||||
late String dll;
|
||||
late FortniteVersion version;
|
||||
late bool autoRestart;
|
||||
|
||||
void main(List<String> args) async {
|
||||
stdout.writeln("Reboot Launcher");
|
||||
stdout.writeln("Wrote by Auties00");
|
||||
stdout.writeln("Version 1.0");
|
||||
|
||||
kill();
|
||||
|
||||
var parser = ArgParser()
|
||||
..addOption("path", mandatory: true)
|
||||
..addOption("username")
|
||||
..addOption("server-type", allowed: ServerType.values.map((entry) => entry.name), defaultsTo: ServerType.embedded.name)
|
||||
..addOption("server-host")
|
||||
..addOption("server-port")
|
||||
..addOption("matchmaking-address")
|
||||
..addOption("dll", defaultsTo: rebootDllFile.path)
|
||||
..addFlag("update", defaultsTo: true, negatable: true)
|
||||
..addFlag("log", defaultsTo: false)
|
||||
..addFlag("host", defaultsTo: false)
|
||||
..addFlag("auto-restart", defaultsTo: false, negatable: true);
|
||||
var result = parser.parse(args);
|
||||
|
||||
dll = result["dll"];
|
||||
host = result["host"];
|
||||
username = result["username"] ?? kDefaultPlayerName;
|
||||
verbose = result["log"];
|
||||
version = FortniteVersion(name: "Dummy", location: Directory(result["path"]));
|
||||
|
||||
await downloadRequiredDLLs();
|
||||
if(result["update"]) {
|
||||
stdout.writeln("Updating reboot dll...");
|
||||
try {
|
||||
await downloadRebootDll(kRebootDownloadUrl);
|
||||
}catch(error){
|
||||
stderr.writeln("Cannot update reboot dll: $error");
|
||||
}
|
||||
}
|
||||
|
||||
stdout.writeln("Launching game...");
|
||||
var executable = version.gameExecutable;
|
||||
if(executable == null){
|
||||
throw Exception("Missing game executable at: ${version.location.path}");
|
||||
}
|
||||
|
||||
final serverHost = result["server-host"]?.trim();
|
||||
if(serverHost?.isEmpty == true){
|
||||
throw Exception("Missing host name");
|
||||
}
|
||||
|
||||
final serverPort = result["server-port"]?.trim();
|
||||
if(serverPort?.isEmpty == true){
|
||||
throw Exception("Missing port");
|
||||
}
|
||||
|
||||
final serverPortNumber = serverPort == null ? null : int.tryParse(serverPort);
|
||||
if(serverPort != null && serverPortNumber == null){
|
||||
throw Exception("Invalid port, use only numbers");
|
||||
}
|
||||
|
||||
var started = await startServerCli(
|
||||
serverHost,
|
||||
serverPortNumber,
|
||||
ServerType.values.firstWhere((element) => element.name == result["server-type"])
|
||||
);
|
||||
if(!started){
|
||||
stderr.writeln("Cannot start server!");
|
||||
return;
|
||||
}
|
||||
|
||||
writeMatchmakingIp(result["matchmaking-address"]);
|
||||
autoRestart = result["auto-restart"];
|
||||
await startGame();
|
||||
}
|
||||
437
cli/lib/main.dart
Normal file
437
cli/lib/main.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:interact_cli/interact_cli.dart';
|
||||
import 'package:reboot_cli/src/controller/config.dart';
|
||||
import 'package:reboot_cli/src/util/console.dart';
|
||||
import 'package:reboot_cli/src/util/extensions.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:tint/tint.dart';
|
||||
import 'package:version/version.dart';
|
||||
|
||||
const Command _buildList = Command(name: 'list', parameters: [], subCommands: []);
|
||||
const Command _buildImport = Command(name: 'import', parameters: ['version', 'path'], subCommands: []);
|
||||
const Command _buildDownload = Command(name: 'download', parameters: ['version', 'path'], subCommands: []);
|
||||
const Command _build = Command(name: 'versions', parameters: [], subCommands: [_buildList, _buildImport, _buildDownload]);
|
||||
const Command _play = Command(name: 'play', parameters: [], subCommands: []);
|
||||
const Command _host = Command(name: 'host', parameters: [], subCommands: []);
|
||||
const Command _backend = Command(name: 'backend', parameters: [], subCommands: []);
|
||||
final List<String> _versions = downloadableBuilds.map((build) => build.gameVersion).toList(growable: false);
|
||||
const String _playVersionAction = 'Play';
|
||||
const String _hostVersionAction = 'Host';
|
||||
const String _deleteVersionAction = 'Delete';
|
||||
const String _infoVersionAction = 'Info';
|
||||
const List<String> _versionActions = [_playVersionAction, _hostVersionAction, _deleteVersionAction, _infoVersionAction];
|
||||
|
||||
void main(List<String> args) async {
|
||||
enableLoggingToConsole = false;
|
||||
useDefaultPath = true;
|
||||
|
||||
print("""
|
||||
🎮 Reboot Launcher
|
||||
🔥 Launch, manage, and play Fortnite using Project Reboot!
|
||||
🚀 Developed by Auties00 - Version 10.0.7
|
||||
""".green());
|
||||
|
||||
final parser = ConsoleParser(
|
||||
commands: [
|
||||
_build,
|
||||
_play,
|
||||
_host,
|
||||
_backend
|
||||
]
|
||||
);
|
||||
final command = parser.parse(args);
|
||||
await _handleRootCommand(command);
|
||||
}
|
||||
|
||||
Future<void> _handleRootCommand(CommandCall? command) async {
|
||||
if(command == null) {
|
||||
await _askRootCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command.name) {
|
||||
case 'versions':
|
||||
await _handleBuildCommand(command.subCall);
|
||||
break;
|
||||
case 'play':
|
||||
_handlePlayCommand(command.subCall);
|
||||
break;
|
||||
case 'host':
|
||||
_handleHostCommand(command.subCall);
|
||||
break;
|
||||
case 'backend':
|
||||
_handleBackendCommand(command.subCall);
|
||||
break;
|
||||
default:
|
||||
await _askRootCommand();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _askRootCommand() async {
|
||||
final commands = [_build.name, _play.name, _host.name, _backend.name];
|
||||
final commandSelector = Select.withTheme(
|
||||
prompt: ' Select a command:',
|
||||
options: commands,
|
||||
theme: Theme.colorfulTheme.copyWith(inputPrefix: '❓', inputSuffix: '', successSuffix: '', errorPrefix: '❌')
|
||||
);
|
||||
await _handleRootCommand(CommandCall(name: commands[commandSelector.interact()]));
|
||||
}
|
||||
|
||||
Future<void> _handleBuildCommand(CommandCall? call) async {
|
||||
if(call == null) {
|
||||
_askBuildCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(call.name) {
|
||||
case 'import':
|
||||
await _handleBuildImportCommand(call);
|
||||
break;
|
||||
case 'download':
|
||||
_handleBuildDownloadCommand(call);
|
||||
break;
|
||||
case 'list':
|
||||
_handleBuildListCommand(call);
|
||||
break;
|
||||
default:
|
||||
_askBuildCommand();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleBuildListCommand(CommandCall commandCall) {
|
||||
List<FortniteVersion> versions;
|
||||
try {
|
||||
versions = readVersions();
|
||||
}catch(error) {
|
||||
print("❌ $error");
|
||||
return;
|
||||
}
|
||||
|
||||
if(versions.isEmpty) {
|
||||
print("❌ No versions found");
|
||||
return;
|
||||
}
|
||||
|
||||
final versionSelector = Select.withTheme(
|
||||
prompt: ' Select a version:',
|
||||
options: versions.map((version) => version.gameVersion).toList(growable: false),
|
||||
theme: Theme.colorfulTheme.copyWith(inputPrefix: '❓', inputSuffix: '', successSuffix: '', errorPrefix: '❌')
|
||||
);
|
||||
final version = versions[versionSelector.interact()];
|
||||
final actionSelector = Select.withTheme(
|
||||
prompt: ' Select an action:',
|
||||
options: _versionActions,
|
||||
theme: Theme.colorfulTheme.copyWith(inputPrefix: '❓', inputSuffix: '', successSuffix: '', errorPrefix: '❌')
|
||||
);
|
||||
final action = _versionActions[actionSelector.interact()];
|
||||
switch(action) {
|
||||
case _playVersionAction:
|
||||
break;
|
||||
case _hostVersionAction:
|
||||
break;
|
||||
case _deleteVersionAction:
|
||||
break;
|
||||
case _infoVersionAction:
|
||||
print('');
|
||||
print("""
|
||||
🏷️ ${"Version: ".cyan()} ${version.gameVersion}
|
||||
📁 ${"Location:".cyan()} ${version.location.path}
|
||||
""".green());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleBuildImportCommand(CommandCall call) async {
|
||||
final version = _getOrPromptVersion(call);
|
||||
if(version == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final path = await _getOrPromptPath(call, true);
|
||||
if(path == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final fortniteVersion = FortniteVersion(
|
||||
name: '',
|
||||
gameVersion: version,
|
||||
location: Directory(path)
|
||||
);
|
||||
writeVersion(fortniteVersion);
|
||||
print('');
|
||||
print('✅ Imported build: ${version.green()}');
|
||||
}
|
||||
|
||||
String? _getOrPromptVersion(CommandCall call) {
|
||||
final version = call.parameters['version'];
|
||||
if(version != null) {
|
||||
final result = version.trim();
|
||||
if (_versions.contains(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
print('');
|
||||
print("❌ Unknown version: $result");
|
||||
return null;
|
||||
}
|
||||
|
||||
stdout.write('❓ Type a version: ');
|
||||
final result = runAutoComplete(_autocompleteVersion).trim();
|
||||
if(_versions.contains(result)) {
|
||||
print('✅ Type a version: ${result.green()}');
|
||||
return result;
|
||||
}
|
||||
|
||||
print('');
|
||||
print("❌ Unknown version: $version");
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String?> _getOrPromptPath(CommandCall call, bool existing) async {
|
||||
var path = call.parameters['path'];
|
||||
if(path != null) {
|
||||
final result = path.trim();
|
||||
final check = await _checkBuildPath(result, existing);
|
||||
if(!check) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
stdout.write('❓ Type a path: ');
|
||||
final result = runAutoComplete(_autocompletePath).trim();
|
||||
final check = await _checkBuildPath(result, existing);
|
||||
if(!check) {
|
||||
return null;
|
||||
}
|
||||
|
||||
print('✅ Type a path: ${result.green()}');
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> _checkBuildPath(String path, bool existing) async {
|
||||
final directory = Directory(path);
|
||||
if(!directory.existsSync()) {
|
||||
if(existing) {
|
||||
print('');
|
||||
print("❌ Unknown path: $path");
|
||||
return false;
|
||||
}else {
|
||||
directory.createSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
final checker = Spinner.withTheme(
|
||||
icon: '✅',
|
||||
rightPrompt: (status) {
|
||||
switch(status) {
|
||||
case SpinnerStateType.inProgress:
|
||||
return 'Looking for FortniteClient-Win64-Shipping.exe...';
|
||||
case SpinnerStateType.done:
|
||||
return 'Finished looking for FortniteClient-Win64-Shipping.exe';
|
||||
case SpinnerStateType.failed:
|
||||
return 'Failed to look for FortniteClient-Win64-Shipping.exe';
|
||||
}
|
||||
},
|
||||
theme: Theme.colorfulTheme.copyWith(successSuffix: '', errorPrefix: '❌', spinners: '🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛'.split(' '))
|
||||
).interact();
|
||||
|
||||
final files = await findFiles(directory, "FortniteClient-Win64-Shipping.exe")
|
||||
.withMinimumDuration(const Duration(seconds: 1));
|
||||
if(files.isEmpty) {
|
||||
print("❌ Cannot find FortniteClient-Win64-Shipping.exe in $path");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(files.length > 1) {
|
||||
print("❌ There must be only one executable named FortniteClient-Win64-Shipping.exe in $path");
|
||||
return false;
|
||||
}
|
||||
|
||||
checker.done();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String? _autocompleteVersion(String input) => input.isEmpty ? null : _versions.firstWhereOrNull((version) => version.toLowerCase().startsWith(input.toLowerCase()));
|
||||
|
||||
String? _autocompletePath(String path) {
|
||||
try {
|
||||
if (path.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String separator = Platform.isWindows ? '\\' : '/';
|
||||
path = path.replaceAll('\\', separator);
|
||||
|
||||
if (FileSystemEntity.isFileSync(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(FileSystemEntity.isDirectorySync(path)) {
|
||||
return path.endsWith(separator) ? null : path + separator;
|
||||
}
|
||||
|
||||
final lastSeparatorIndex = path.lastIndexOf(separator);
|
||||
String directoryPath;
|
||||
String partialName;
|
||||
String prefixPath;
|
||||
if (lastSeparatorIndex == -1) {
|
||||
directoryPath = '';
|
||||
partialName = path;
|
||||
prefixPath = '';
|
||||
} else {
|
||||
directoryPath = path.substring(0, lastSeparatorIndex);
|
||||
partialName = path.substring(lastSeparatorIndex + 1);
|
||||
prefixPath = path.substring(0, lastSeparatorIndex + 1);
|
||||
if (directoryPath.isEmpty && lastSeparatorIndex == 0) {
|
||||
directoryPath = separator;
|
||||
} else if (directoryPath.isEmpty) {
|
||||
directoryPath = '.';
|
||||
}
|
||||
}
|
||||
|
||||
final dir = Directory(directoryPath);
|
||||
if (!dir.existsSync()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final entries = dir.listSync();
|
||||
final matches = <FileSystemEntity>[];
|
||||
for (var entry in entries) {
|
||||
final name = entry.path.split(separator).last;
|
||||
if (name.startsWith(partialName)) {
|
||||
matches.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
matches.sort((a, b) {
|
||||
final aIsDir = a is Directory;
|
||||
final bIsDir = b is Directory;
|
||||
|
||||
if (aIsDir != bIsDir) {
|
||||
return aIsDir ? -1 : 1;
|
||||
}
|
||||
|
||||
final aName = a.path.split(separator).last;
|
||||
final bName = b.path.split(separator).last;
|
||||
|
||||
if (aName.length != bName.length) {
|
||||
return aName.length - bName.length;
|
||||
}
|
||||
|
||||
return aName.compareTo(bName);
|
||||
});
|
||||
|
||||
final bestMatch = matches.first;
|
||||
final bestMatchName = bestMatch.path.split(separator).last;
|
||||
|
||||
var result = prefixPath + bestMatchName;
|
||||
if (bestMatch is Directory) {
|
||||
result = result.endsWith(separator) ? result : result + separator;
|
||||
}
|
||||
return result;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _handleBuildDownloadCommand(CommandCall call) async {
|
||||
final version = _getOrPromptVersion(call);
|
||||
if(version == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parsedVersion = Version.parse(version);
|
||||
final build = downloadableBuilds.firstWhereOrNull((build) => Version.parse(build.gameVersion) == parsedVersion);
|
||||
if(build == null) {
|
||||
print('');
|
||||
print("❌ Cannot find mirror for version: $parsedVersion");
|
||||
return;
|
||||
}
|
||||
|
||||
final path = await _getOrPromptPath(call, false);
|
||||
if(path == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
double progress = 0;
|
||||
bool extracting = false;
|
||||
final downloader = Spinner.withTheme(
|
||||
icon: '✅',
|
||||
rightPrompt: (status) => status != SpinnerStateType.inProgress ? 'Finished ${extracting ? 'extracting' : 'downloading'} ${parsedVersion.toString()}' : '${extracting ? 'Extracting' : 'Downloading'} ${parsedVersion.toString()} (${progress.round()}%)...',
|
||||
theme: Theme.colorfulTheme.copyWith(successSuffix: '', errorPrefix: '❌', spinners: '🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛'.split(' '))
|
||||
).interact();
|
||||
final parsedDirectory = Directory(path);
|
||||
final receivePort = ReceivePort();
|
||||
SendPort? sendPort;
|
||||
receivePort.listen((message) {
|
||||
if(message is FortniteBuildDownloadProgress) {
|
||||
if(message.progress >= 100) {
|
||||
sendPort?.send(kStopBuildDownloadSignal);
|
||||
stopDownloadServer();
|
||||
downloader.done();
|
||||
receivePort.close();
|
||||
final fortniteVersion = FortniteVersion(
|
||||
name: "dummy",
|
||||
gameVersion: version,
|
||||
location: parsedDirectory
|
||||
);
|
||||
writeVersion(fortniteVersion);
|
||||
print('');
|
||||
print('✅ Downloaded build: ${version.green()}');
|
||||
}else {
|
||||
progress = message.progress;
|
||||
extracting = message.extracting;
|
||||
}
|
||||
}else if(message is SendPort) {
|
||||
sendPort = message;
|
||||
}else {
|
||||
sendPort?.send(kStopBuildDownloadSignal);
|
||||
stopDownloadServer();
|
||||
downloader.done();
|
||||
receivePort.close();
|
||||
print("❌ Cannot download build: $message");
|
||||
}
|
||||
});
|
||||
final options = FortniteBuildDownloadOptions(
|
||||
build,
|
||||
parsedDirectory,
|
||||
receivePort.sendPort
|
||||
);
|
||||
await downloadArchiveBuild(options);
|
||||
}
|
||||
|
||||
void _askBuildCommand() {
|
||||
final commands = [_buildList.name, _buildImport.name, _buildDownload.name];
|
||||
final commandSelector = Select.withTheme(
|
||||
prompt: ' Select a version command:',
|
||||
options: commands,
|
||||
theme: Theme.colorfulTheme.copyWith(inputPrefix: '❓', inputSuffix: '', successSuffix: '', errorPrefix: '❌')
|
||||
);
|
||||
_handleBuildCommand(CommandCall(name: commands[commandSelector.interact()]));
|
||||
}
|
||||
|
||||
void _handlePlayCommand(CommandCall? call) {
|
||||
|
||||
}
|
||||
|
||||
void _handleHostCommand(CommandCall? call) {
|
||||
|
||||
}
|
||||
|
||||
void _handleBackendCommand(CommandCall? call) {
|
||||
|
||||
}
|
||||
0
cli/lib/src/command/backend.dart
Normal file
0
cli/lib/src/command/backend.dart
Normal file
0
cli/lib/src/command/commands.dart
Normal file
0
cli/lib/src/command/commands.dart
Normal file
0
cli/lib/src/command/config.dart
Normal file
0
cli/lib/src/command/config.dart
Normal file
0
cli/lib/src/command/host.dart
Normal file
0
cli/lib/src/command/host.dart
Normal file
0
cli/lib/src/command/play.dart
Normal file
0
cli/lib/src/command/play.dart
Normal file
0
cli/lib/src/command/versions.dart
Normal file
0
cli/lib/src/command/versions.dart
Normal file
34
cli/lib/src/controller/config.dart
Normal file
34
cli/lib/src/controller/config.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
List<FortniteVersion> readVersions() {
|
||||
final file = _versionsFile;
|
||||
if(!file.existsSync()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
Iterable decodedVersionsJson = jsonDecode(file.readAsStringSync());
|
||||
return decodedVersionsJson
|
||||
.map((entry) {
|
||||
try {
|
||||
return FortniteVersion.fromJson(entry);
|
||||
}catch(error) {
|
||||
throw "Cannot parse version: $error";
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
}catch(error) {
|
||||
throw "Cannot parse versions: $error";
|
||||
}
|
||||
}
|
||||
|
||||
void writeVersion(FortniteVersion version) {
|
||||
final versions = readVersions();
|
||||
versions.add(version);
|
||||
_versionsFile.writeAsString(jsonEncode(versions.map((version) => version.toJson()).toList()));
|
||||
}
|
||||
|
||||
File get _versionsFile => File('${installationDirectory.path}/versions.json');
|
||||
@@ -1,123 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:process_run/process_run.dart';
|
||||
import 'package:reboot_cli/cli.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
Process? _gameProcess;
|
||||
Process? _launcherProcess;
|
||||
Process? _eacProcess;
|
||||
|
||||
Future<void> startGame() async {
|
||||
await _startLauncherProcess(version);
|
||||
await _startEacProcess(version);
|
||||
|
||||
var executable = await version.gameExecutable;
|
||||
if (executable == null) {
|
||||
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
|
||||
}
|
||||
|
||||
if (username == null) {
|
||||
username = "Reboot${host ? 'Host' : 'Player'}";
|
||||
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
|
||||
}
|
||||
|
||||
_gameProcess = await Process.start(executable.path, createRebootArgs(username!, "", host, host, ""))
|
||||
..exitCode.then((_) => _onClose())
|
||||
..stdOutput.forEach((line) => _onGameOutput(line, dll, host, verbose));
|
||||
_injectOrShowError("cobalt.dll");
|
||||
}
|
||||
|
||||
|
||||
Future<void> _startLauncherProcess(FortniteVersion dummyVersion) async {
|
||||
if (dummyVersion.launcherExecutable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_launcherProcess = await Process.start(dummyVersion.launcherExecutable!.path, []);
|
||||
suspend(_launcherProcess!.pid);
|
||||
}
|
||||
|
||||
Future<void> _startEacProcess(FortniteVersion dummyVersion) async {
|
||||
if (dummyVersion.eacExecutable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_eacProcess = await Process.start(dummyVersion.eacExecutable!.path, []);
|
||||
suspend(_eacProcess!.pid);
|
||||
}
|
||||
|
||||
void _onGameOutput(String line, String dll, bool hosting, bool verbose) {
|
||||
if(verbose) {
|
||||
stdout.writeln(line);
|
||||
}
|
||||
|
||||
handleGameOutput(
|
||||
line: line,
|
||||
host: hosting,
|
||||
onDisplayAttached: () {}, // TODO: Support virtual desktops
|
||||
onLoggedIn: onLoggedIn,
|
||||
onMatchEnd: onMatchEnd,
|
||||
onShutdown: onShutdown,
|
||||
onTokenError: onTokenError,
|
||||
onBuildCorrupted: onBuildCorrupted
|
||||
);
|
||||
|
||||
if (line.contains(kShutdownLine)) {
|
||||
_onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(kCannotConnectErrors.any((element) => line.contains(element))){
|
||||
stderr.writeln("The backend doesn't work! Token expired");
|
||||
_onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(line.contains("Region ")){
|
||||
if(hosting) {
|
||||
_injectOrShowError(dll, false);
|
||||
}else {
|
||||
_injectOrShowError("console.dll");
|
||||
}
|
||||
|
||||
_injectOrShowError("memory.dll");
|
||||
}
|
||||
}
|
||||
|
||||
void _kill() {
|
||||
_gameProcess?.kill(ProcessSignal.sigabrt);
|
||||
_launcherProcess?.kill(ProcessSignal.sigabrt);
|
||||
_eacProcess?.kill(ProcessSignal.sigabrt);
|
||||
}
|
||||
|
||||
Future<void> _injectOrShowError(String binary, [bool locate = true]) async {
|
||||
if (_gameProcess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stdout.writeln("Injecting $binary...");
|
||||
var dll = locate ? File("${dllsDirectory.path}\\$binary") : File(binary);
|
||||
if(!dll.existsSync()){
|
||||
throw Exception("Cannot inject $dll: missing file");
|
||||
}
|
||||
|
||||
await injectDll(_gameProcess!.pid, dll);
|
||||
} catch (exception) {
|
||||
throw Exception("Cannot inject binary: $binary");
|
||||
}
|
||||
}
|
||||
|
||||
void _onClose() {
|
||||
_kill();
|
||||
sleep(const Duration(seconds: 3));
|
||||
stdout.writeln("The game was closed");
|
||||
if(autoRestart){
|
||||
stdout.writeln("Restarting automatically game");
|
||||
startGame();
|
||||
return;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
// TODO: Use github
|
||||
const String _baseDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968021373169674/cobalt.dll";
|
||||
const String _consoleDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968095033524234/console.dll";
|
||||
const String _memoryFixDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968141556756581/memory.dll";
|
||||
const String _embeddedConfigDownload = "https://cdn.discordapp.com/attachments/1026121175878881290/1040679319351066644/embedded.zip";
|
||||
|
||||
Future<void> downloadRequiredDLLs() async {
|
||||
stdout.writeln("Downloading necessary components...");
|
||||
var consoleDll = File("${dllsDirectory.path}\\console.dll");
|
||||
if(!consoleDll.existsSync()){
|
||||
var response = await http.get(Uri.parse(_consoleDownload));
|
||||
if(response.statusCode != 200){
|
||||
throw Exception("Cannot download console.dll");
|
||||
}
|
||||
|
||||
await consoleDll.writeAsBytes(response.bodyBytes);
|
||||
}
|
||||
|
||||
var craniumDll = File("${dllsDirectory.path}\\cobalt.dll");
|
||||
if(!craniumDll.existsSync()){
|
||||
var response = await http.get(Uri.parse(_baseDownload));
|
||||
if(response.statusCode != 200){
|
||||
throw Exception("Cannot download cobalt.dll");
|
||||
}
|
||||
|
||||
await craniumDll.writeAsBytes(response.bodyBytes);
|
||||
}
|
||||
|
||||
var memoryFixDll = File("${dllsDirectory.path}\\memory.dll");
|
||||
if(!memoryFixDll.existsSync()){
|
||||
var response = await http.get(Uri.parse(_memoryFixDownload));
|
||||
if(response.statusCode != 200){
|
||||
throw Exception("Cannot download memory.dll");
|
||||
}
|
||||
|
||||
await memoryFixDll.writeAsBytes(response.bodyBytes);
|
||||
}
|
||||
|
||||
if(!backendDirectory.existsSync()){
|
||||
var response = await http.get(Uri.parse(_embeddedConfigDownload));
|
||||
if(response.statusCode != 200){
|
||||
throw Exception("Cannot download embedded server config");
|
||||
}
|
||||
|
||||
var tempZip = File("${tempDirectory.path}/reboot_config.zip");
|
||||
await tempZip.writeAsBytes(response.bodyBytes);
|
||||
await extractFileToDisk(tempZip.path, backendDirectory.path);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_common/src/util/backend.dart' as server;
|
||||
|
||||
Future<bool> startServerCli(String? host, int? port, ServerType type) async {
|
||||
stdout.writeln("Starting backend server...");
|
||||
switch(type){
|
||||
case ServerType.local:
|
||||
var result = await pingBackend(host ?? kDefaultBackendHost, port ?? kDefaultBackendPort);
|
||||
if(result == null){
|
||||
throw Exception("Local backend server is not running");
|
||||
}
|
||||
|
||||
stdout.writeln("Detected local backend server");
|
||||
return true;
|
||||
case ServerType.embedded:
|
||||
stdout.writeln("Starting an embedded server...");
|
||||
await server.startEmbeddedBackend(false);
|
||||
var result = await pingBackend(host ?? kDefaultBackendHost, port ?? kDefaultBackendPort);
|
||||
if(result == null){
|
||||
throw Exception("Cannot start embedded server");
|
||||
}
|
||||
|
||||
return true;
|
||||
case ServerType.remote:
|
||||
if(host == null){
|
||||
throw Exception("Missing host for remote server");
|
||||
}
|
||||
|
||||
if(port == null){
|
||||
throw Exception("Missing host for remote server");
|
||||
}
|
||||
|
||||
stdout.writeln("Starting a reverse proxy to $host:$port");
|
||||
return await _changeReverseProxyState(host, port) != null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<HttpServer?> _changeReverseProxyState(String host, int port) async {
|
||||
try{
|
||||
var uri = await pingBackend(host, port);
|
||||
if(uri == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
return await server.startRemoteBackendProxy(uri);
|
||||
}catch(error){
|
||||
throw Exception("Cannot start reverse proxy");
|
||||
}
|
||||
}
|
||||
|
||||
void kill() async {
|
||||
try {
|
||||
await Process.run("taskkill", ["/f", "/im", "FortniteLauncher.exe"]);
|
||||
await Process.run("taskkill", ["/f", "/im", "FortniteClient-Win64-Shipping_EAC.exe"]);
|
||||
}catch(_){
|
||||
|
||||
}
|
||||
}
|
||||
160
cli/lib/src/util/console.dart
Normal file
160
cli/lib/src/util/console.dart
Normal file
@@ -0,0 +1,160 @@
|
||||
import 'dart:io';
|
||||
import 'package:dart_console/dart_console.dart';
|
||||
|
||||
typedef AutoComplete = String? Function(String);
|
||||
|
||||
class ConsoleParser {
|
||||
final List<Command> commands;
|
||||
|
||||
ConsoleParser({required this.commands});
|
||||
|
||||
CommandCall? parse(List<String> args) {
|
||||
var position = 0;
|
||||
var allowedCommands = _toMap(commands);
|
||||
var allowedParameters = <String>{};
|
||||
Command? command;
|
||||
CommandCall? head;
|
||||
CommandCall? tail;
|
||||
String? parameterName;
|
||||
while(position < args.length) {
|
||||
final current = args[position].toLowerCase();
|
||||
if(parameterName != null) {
|
||||
tail?.parameters[parameterName] = current;
|
||||
parameterName = null;
|
||||
}else if(allowedParameters.contains(current.toLowerCase())) {
|
||||
parameterName = current.substring(2);
|
||||
if(args.elementAtOrNull(position + 1) == '"') {
|
||||
position++;
|
||||
}
|
||||
}else {
|
||||
final newCommand = allowedCommands[current];
|
||||
if(newCommand != null) {
|
||||
final newCall = CommandCall(name: newCommand.name);
|
||||
if(head == null) {
|
||||
head = newCall;
|
||||
tail = newCall;
|
||||
}
|
||||
if(tail != null) {
|
||||
tail.subCall = newCall;
|
||||
}
|
||||
tail = newCall;
|
||||
command = newCommand;
|
||||
allowedCommands = _toMap(newCommand.subCommands);
|
||||
allowedParameters = _toParameters(command);
|
||||
}
|
||||
}
|
||||
position++;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
Set<String> _toParameters(Command? parent) => parent?.parameters
|
||||
.map((e) => '--${e.toLowerCase()}')
|
||||
.toSet() ?? {};
|
||||
|
||||
Map<String, Command> _toMap(List<Command> children) => Map.fromIterable(
|
||||
children,
|
||||
key: (command) => command.name.toLowerCase(),
|
||||
value: (command) => command
|
||||
);
|
||||
}
|
||||
|
||||
class Command {
|
||||
final String name;
|
||||
final List<String> parameters;
|
||||
final List<Command> subCommands;
|
||||
|
||||
const Command({required this.name, required this.parameters, required this.subCommands});
|
||||
|
||||
@override
|
||||
String toString() => 'Command{name: $name, parameters: $parameters, subCommands: $subCommands}';
|
||||
}
|
||||
|
||||
class Parameter {
|
||||
final String name;
|
||||
final bool Function(String) validator;
|
||||
|
||||
const Parameter({required this.name, required this.validator});
|
||||
|
||||
@override
|
||||
String toString() => 'Parameter{name: $name, validator: $validator}';
|
||||
}
|
||||
|
||||
class CommandCall {
|
||||
final String name;
|
||||
final Map<String, String> parameters;
|
||||
CommandCall? subCall;
|
||||
|
||||
CommandCall({required this.name}) : parameters = {};
|
||||
|
||||
@override
|
||||
String toString() => 'CommandCall{name: $name, parameters: $parameters, subCall: $subCall}';
|
||||
}
|
||||
|
||||
String runAutoComplete(AutoComplete completion) {
|
||||
final console = Console();
|
||||
console.rawMode = true;
|
||||
final position = console.cursorPosition!;
|
||||
|
||||
var currentInput = '';
|
||||
var running = true;
|
||||
var result = '';
|
||||
|
||||
while (running) {
|
||||
final key = console.readKey();
|
||||
switch (key.controlChar) {
|
||||
case ControlCharacter.ctrlC:
|
||||
running = false;
|
||||
break;
|
||||
case ControlCharacter.enter:
|
||||
_eraseUntil(console, position);
|
||||
console.write(currentInput);
|
||||
console.writeLine();
|
||||
result = currentInput;
|
||||
running = false;
|
||||
break;
|
||||
case ControlCharacter.tab:
|
||||
final suggestion = completion(currentInput);
|
||||
if (suggestion != null) {
|
||||
_eraseUntil(console, position);
|
||||
currentInput = suggestion;
|
||||
console.write(currentInput);
|
||||
}
|
||||
break;
|
||||
case ControlCharacter.backspace:
|
||||
if (currentInput.isNotEmpty) {
|
||||
currentInput = currentInput.substring(0, currentInput.length - 1);
|
||||
_eraseUntil(console, position);
|
||||
console.write(currentInput);
|
||||
_showSuggestion(console, position, currentInput, completion);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
currentInput += key.char;
|
||||
console.write(key.char);
|
||||
_showSuggestion(console, position, currentInput, completion);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void _eraseUntil(Console console, Coordinate position) {
|
||||
console.cursorPosition = position;
|
||||
stdout.write('\x1b[K');
|
||||
}
|
||||
|
||||
void _showSuggestion(Console console, Coordinate position, String input, AutoComplete completion) {
|
||||
final suggestion = completion(input);
|
||||
if(suggestion == null) {
|
||||
_eraseUntil(console, position);
|
||||
console.write(input);
|
||||
}else if(suggestion.length > input.length) {
|
||||
final remaining = suggestion.substring(input.length);
|
||||
final cursorPosition = console.cursorPosition;
|
||||
console.setForegroundColor(ConsoleColor.brightBlack);
|
||||
console.write(remaining);
|
||||
console.resetColorAttributes();
|
||||
console.cursorPosition = cursorPosition;
|
||||
}
|
||||
}
|
||||
20
cli/lib/src/util/extensions.dart
Normal file
20
cli/lib/src/util/extensions.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
extension IterableExtension<E> on Iterable<E> {
|
||||
E? firstWhereOrNull(bool test(E element)) {
|
||||
for (final element in this) {
|
||||
if (test(element)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
extension FutureExtension<T> on Future<T> {
|
||||
Future<T> withMinimumDuration(Duration duration) async {
|
||||
final result = await Future.wait([
|
||||
Future.delayed(duration),
|
||||
this
|
||||
]);
|
||||
return result.last;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user