mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 11:12:23 +01:00
9.1.4
This commit is contained in:
27
backend/structure/affiliate.js
Normal file
27
backend/structure/affiliate.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
|
||||
express.get("/affiliate/api/public/affiliates/slug/:slug", async (req, res) => {
|
||||
const SupportedCodes = require("./../responses/SAC.json");
|
||||
var ValidCode = false;
|
||||
|
||||
SupportedCodes.forEach(code => {
|
||||
if (req.params.slug.toLowerCase() == code.toLowerCase()) {
|
||||
ValidCode = true;
|
||||
return res.json({
|
||||
"id": code,
|
||||
"slug": code,
|
||||
"displayName": code,
|
||||
"status": "ACTIVE",
|
||||
"verified": false
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
if (ValidCode == false) {
|
||||
res.status(404);
|
||||
res.json({});
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
162
backend/structure/cloudstorage.js
Normal file
162
backend/structure/cloudstorage.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const crypto = require("crypto");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.use((req, res, next) => {
|
||||
// Get raw body in encoding latin1 for ClientSettings
|
||||
if (req.originalUrl.toLowerCase().startsWith("/fortnite/api/cloudstorage/user/") && req.method == "PUT") {
|
||||
req.rawBody = "";
|
||||
req.setEncoding("latin1");
|
||||
|
||||
req.on("data", (chunk) => req.rawBody += chunk);
|
||||
req.on("end", () => next());
|
||||
}
|
||||
else return next();
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/cloudstorage/system", async (req, res) => {
|
||||
const memory = functions.GetVersionInfo(req);
|
||||
|
||||
if (memory.build >= 9.40 && memory.build <= 10.40) {
|
||||
return res.status(404).end();
|
||||
}
|
||||
|
||||
const dir = path.join(__dirname, "..", "CloudStorage")
|
||||
var CloudFiles = [];
|
||||
|
||||
fs.readdirSync(dir).forEach(name => {
|
||||
if (name.toLowerCase().endsWith(".ini")) {
|
||||
const ParsedFile = fs.readFileSync(path.join(dir, name), 'utf-8');
|
||||
const ParsedStats = fs.statSync(path.join(dir, name));
|
||||
|
||||
CloudFiles.push({
|
||||
"uniqueFilename": name,
|
||||
"filename": name,
|
||||
"hash": crypto.createHash('sha1').update(ParsedFile).digest('hex'),
|
||||
"hash256": crypto.createHash('sha256').update(ParsedFile).digest('hex'),
|
||||
"length": ParsedFile.length,
|
||||
"contentType": "application/octet-stream",
|
||||
"uploaded": ParsedStats.mtime,
|
||||
"storageType": "S3",
|
||||
"storageIds": {},
|
||||
"doNotCache": true
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
res.json(CloudFiles)
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/cloudstorage/system/:file", async (req, res) => {
|
||||
const file = path.join(__dirname, "..", "CloudStorage", req.params.file);
|
||||
if (fs.existsSync(file)) {
|
||||
const ParsedFile = fs.readFileSync(file);
|
||||
|
||||
return res.status(200).send(ParsedFile).end();
|
||||
} else {
|
||||
res.status(200);
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/cloudstorage/user/*/:file", async (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"))) {
|
||||
fs.mkdirSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"));
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
res.set("Content-Type", "application/octet-stream")
|
||||
|
||||
if (req.params.file.toLowerCase() != "clientsettings.sav") {
|
||||
return res.status(404).json({
|
||||
"error": "file not found"
|
||||
});
|
||||
}
|
||||
|
||||
const memory = functions.GetVersionInfo(req);
|
||||
|
||||
var currentBuildID = memory.CL;
|
||||
|
||||
let file;
|
||||
if (process.env.LOCALAPPDATA) file = path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
else file = path.join(__dirname, "..", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
|
||||
if (fs.existsSync(file)) {
|
||||
const ParsedFile = fs.readFileSync(file);
|
||||
|
||||
return res.status(200).send(ParsedFile).end();
|
||||
} else {
|
||||
res.status(200);
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/cloudstorage/user/:accountId", async (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"))) {
|
||||
fs.mkdirSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"));
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
res.set("Content-Type", "application/json")
|
||||
|
||||
const memory = functions.GetVersionInfo(req);
|
||||
|
||||
var currentBuildID = memory.CL;
|
||||
|
||||
let file;
|
||||
if (process.env.LOCALAPPDATA) file = path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
else file = path.join(__dirname, "..", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
|
||||
if (fs.existsSync(file)) {
|
||||
const ParsedFile = fs.readFileSync(file, 'latin1');
|
||||
const ParsedStats = fs.statSync(file);
|
||||
|
||||
return res.json([{
|
||||
"uniqueFilename": "ClientSettings.Sav",
|
||||
"filename": "ClientSettings.Sav",
|
||||
"hash": crypto.createHash('sha1').update(ParsedFile).digest('hex'),
|
||||
"hash256": crypto.createHash('sha256').update(ParsedFile).digest('hex'),
|
||||
"length": Buffer.byteLength(ParsedFile),
|
||||
"contentType": "application/octet-stream",
|
||||
"uploaded": ParsedStats.mtime,
|
||||
"storageType": "S3",
|
||||
"storageIds": {},
|
||||
"accountId": req.params.accountId,
|
||||
"doNotCache": true
|
||||
}]);
|
||||
} else {
|
||||
return res.json([]);
|
||||
}
|
||||
})
|
||||
|
||||
express.put("/fortnite/api/cloudstorage/user/*/:file", async (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"))) {
|
||||
fs.mkdirSync(path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings"));
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
if (req.params.file.toLowerCase() != "clientsettings.sav") {
|
||||
return res.status(404).json({
|
||||
"error": "file not found"
|
||||
});
|
||||
}
|
||||
|
||||
const memory = functions.GetVersionInfo(req);
|
||||
|
||||
var currentBuildID = memory.CL;
|
||||
|
||||
let file;
|
||||
if (process.env.LOCALAPPDATA) file = path.join(process.env.LOCALAPPDATA, "LawinServer", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
else file = path.join(__dirname, "..", "ClientSettings", `ClientSettings-${currentBuildID}.Sav`);
|
||||
|
||||
fs.writeFileSync(file, req.rawBody, 'latin1');
|
||||
res.status(204).end();
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
24
backend/structure/contentpages.js
Normal file
24
backend/structure/contentpages.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.get("/content/api/pages/*", async (req, res) => {
|
||||
const contentpages = functions.getContentPages(req);
|
||||
|
||||
res.json(contentpages)
|
||||
})
|
||||
|
||||
express.post("/api/v1/fortnite-br/surfaces/motd/target", async (req, res) => {
|
||||
const motdTarget = JSON.parse(JSON.stringify(require("./../responses/Athena/motdTarget.json")));
|
||||
|
||||
try {
|
||||
motdTarget.contentItems.forEach(item => {
|
||||
item.contentFields.title = item.contentFields.title[req.body.language];
|
||||
item.contentFields.body = item.contentFields.body[req.body.language];
|
||||
})
|
||||
} catch (err) {}
|
||||
|
||||
res.json(motdTarget)
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
45
backend/structure/discovery.js
Normal file
45
backend/structure/discovery.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const discovery = require("./../responses/Athena/Discovery/discovery_frontend.json");
|
||||
|
||||
express.post("*/discovery/surface/*", async (req, res) => {
|
||||
res.json(discovery);
|
||||
})
|
||||
|
||||
express.post("/links/api/fn/mnemonic", async (req, res) => {
|
||||
var MnemonicArray = [];
|
||||
|
||||
for (var i in discovery.Panels[0].Pages[0].results) {
|
||||
MnemonicArray.push(discovery.Panels[0].Pages[0].results[i].linkData)
|
||||
}
|
||||
|
||||
res.json(MnemonicArray);
|
||||
})
|
||||
|
||||
express.get("/links/api/fn/mnemonic/:playlist/related", async (req, res) => {
|
||||
var response = {
|
||||
"parentLinks": [],
|
||||
"links": {}
|
||||
};
|
||||
|
||||
if (req.params.playlist) {
|
||||
for (var i in discovery.Panels[0].Pages[0].results) {
|
||||
var linkData = discovery.Panels[0].Pages[0].results[i].linkData;
|
||||
if (linkData.mnemonic == req.params.playlist) {
|
||||
response.links[req.params.playlist] = linkData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json(response);
|
||||
})
|
||||
|
||||
express.get("/links/api/fn/mnemonic/*", async (req, res) => {
|
||||
for (var i in discovery.Panels[0].Pages[0].results) {
|
||||
if (discovery.Panels[0].Pages[0].results[i].linkData.mnemonic == req.url.split("/").slice(-1)[0]) {
|
||||
res.json(discovery.Panels[0].Pages[0].results[i].linkData);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
122
backend/structure/friends.js
Normal file
122
backend/structure/friends.js
Normal file
@@ -0,0 +1,122 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const fs = require("fs");
|
||||
const friendslist = require("./../responses/friendslist.json");
|
||||
const friendslist2 = require("./../responses/friendslist2.json");
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.get("/friends/api/v1/*/settings", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/friends/api/v1/*/blocklist", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
express.get("/friends/api/public/friends/:accountId", async (req, res) => {
|
||||
const memory = functions.GetVersionInfo(req);
|
||||
|
||||
if (!friendslist.find(i => i.accountId == req.params.accountId)) {
|
||||
var FriendObject = {
|
||||
"accountId": req.params.accountId,
|
||||
"status": "ACCEPTED",
|
||||
"direction": "OUTBOUND",
|
||||
"created": new Date().toISOString(),
|
||||
"favorite": false
|
||||
};
|
||||
|
||||
friendslist.push(FriendObject)
|
||||
friendslist2.friends.push({
|
||||
"accountId": FriendObject.accountId,
|
||||
"groups": [],
|
||||
"mutual": 0,
|
||||
"alias": "",
|
||||
"note": "",
|
||||
"favorite": FriendObject.favorite,
|
||||
"created": FriendObject.created
|
||||
})
|
||||
|
||||
functions.sendXmppMessageToAll({
|
||||
"payload": FriendObject,
|
||||
"type": "com.epicgames.friends.core.apiobjects.Friend",
|
||||
"timestamp": FriendObject.created
|
||||
})
|
||||
|
||||
functions.sendXmppMessageToAll({
|
||||
"type": "FRIENDSHIP_REQUEST",
|
||||
"timestamp": FriendObject.created,
|
||||
"from": FriendObject.accountId,
|
||||
"status": FriendObject.status
|
||||
})
|
||||
|
||||
fs.writeFileSync("./responses/friendslist.json", JSON.stringify(friendslist, null, 2));
|
||||
fs.writeFileSync("./responses/friendslist2.json", JSON.stringify(friendslist2, null, 2));
|
||||
}
|
||||
|
||||
if (memory.season >= 7) {
|
||||
var friends = JSON.parse(JSON.stringify(friendslist))
|
||||
friends.splice(friendslist.findIndex(i => i.accountId == req.params.accountId), 1)
|
||||
return res.json(friends);
|
||||
}
|
||||
|
||||
res.json(friendslist)
|
||||
})
|
||||
|
||||
express.get("/friends/api/v1/:accountId/summary", async (req, res) => {
|
||||
if (!friendslist2.friends.find(i => i.accountId == req.params.accountId)) {
|
||||
var FriendObject = {
|
||||
"accountId": req.params.accountId,
|
||||
"groups": [],
|
||||
"mutual": 0,
|
||||
"alias": "",
|
||||
"note": "",
|
||||
"favorite": false,
|
||||
"created": new Date().toISOString()
|
||||
};
|
||||
|
||||
friendslist2.friends.push(FriendObject)
|
||||
friendslist.push({
|
||||
"accountId": FriendObject.accountId,
|
||||
"status": "ACCEPTED",
|
||||
"direction": "OUTBOUND",
|
||||
"created": FriendObject.created,
|
||||
"favorite": FriendObject.favorite
|
||||
})
|
||||
|
||||
functions.sendXmppMessageToAll({
|
||||
"payload": {
|
||||
"accountId": FriendObject.accountId,
|
||||
"status": "ACCEPTED",
|
||||
"direction": "OUTBOUND",
|
||||
"created": FriendObject.created,
|
||||
"favorite": FriendObject.favorite
|
||||
},
|
||||
"type": "com.epicgames.friends.core.apiobjects.Friend",
|
||||
"timestamp": FriendObject.created
|
||||
})
|
||||
|
||||
functions.sendXmppMessageToAll({
|
||||
"type": "FRIENDSHIP_REQUEST",
|
||||
"timestamp": FriendObject.created,
|
||||
"from": FriendObject.accountId,
|
||||
"status": "ACCEPTED"
|
||||
})
|
||||
|
||||
fs.writeFileSync("./responses/friendslist.json", JSON.stringify(friendslist, null, 2));
|
||||
fs.writeFileSync("./responses/friendslist2.json", JSON.stringify(friendslist2, null, 2));
|
||||
}
|
||||
|
||||
res.json(friendslist2)
|
||||
})
|
||||
|
||||
express.get("/friends/api/public/list/fortnite/*/recentPlayers", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
express.get("/friends/api/public/blocklist/*", async (req, res) => {
|
||||
res.json({
|
||||
"blockedUsers": []
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
374
backend/structure/functions.js
Normal file
374
backend/structure/functions.js
Normal file
@@ -0,0 +1,374 @@
|
||||
const XMLBuilder = require("xmlbuilder");
|
||||
const uuid = require("uuid");
|
||||
|
||||
async function sleep(ms) {
|
||||
await new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, ms);
|
||||
})
|
||||
}
|
||||
|
||||
function GetVersionInfo(req) {
|
||||
var memory = {
|
||||
season: 0,
|
||||
build: 0.0,
|
||||
CL: "",
|
||||
lobby: ""
|
||||
}
|
||||
|
||||
if (req.headers["user-agent"])
|
||||
{
|
||||
var CL = "";
|
||||
|
||||
try {
|
||||
var BuildID = req.headers["user-agent"].split("-")[3].split(",")[0]
|
||||
if (!Number.isNaN(Number(BuildID))) {
|
||||
CL = BuildID;
|
||||
}
|
||||
|
||||
if (Number.isNaN(Number(BuildID))) {
|
||||
var BuildID = req.headers["user-agent"].split("-")[3].split(" ")[0]
|
||||
if (!Number.isNaN(Number(BuildID))) {
|
||||
CL = BuildID;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
var BuildID = req.headers["user-agent"].split("-")[1].split("+")[0]
|
||||
if (!Number.isNaN(Number(BuildID))) {
|
||||
CL = BuildID;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
try {
|
||||
var Build = req.headers["user-agent"].split("Release-")[1].split("-")[0];
|
||||
|
||||
if (Build.split(".").length == 3) {
|
||||
Value = Build.split(".");
|
||||
Build = Value[0] + "." + Value[1] + Value[2];
|
||||
}
|
||||
|
||||
memory.season = Number(Build.split(".")[0]);
|
||||
memory.build = Number(Build);
|
||||
memory.CL = CL;
|
||||
memory.lobby = `LobbySeason${memory.season}`;
|
||||
|
||||
if (Number.isNaN(memory.season)) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (err) {
|
||||
memory.season = 2;
|
||||
memory.build = 2.0;
|
||||
memory.CL = CL;
|
||||
memory.lobby = "LobbyWinterDecor";
|
||||
}
|
||||
}
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
function getItemShop() {
|
||||
const catalog = JSON.parse(JSON.stringify(require("./../responses/catalog.json")));
|
||||
const CatalogConfig = require("./../Config/catalog_config.json");
|
||||
|
||||
try {
|
||||
for (var value in CatalogConfig) {
|
||||
if (Array.isArray(CatalogConfig[value].itemGrants)) {
|
||||
if (CatalogConfig[value].itemGrants.length != 0) {
|
||||
const CatalogEntry = {"devName":"","offerId":"","fulfillmentIds":[],"dailyLimit":-1,"weeklyLimit":-1,"monthlyLimit":-1,"categories":[],"prices":[{"currencyType":"MtxCurrency","currencySubType":"","regularPrice":0,"finalPrice":0,"saleExpiration":"9999-12-02T01:12:00Z","basePrice":0}],"meta":{"SectionId":"Featured","TileSize":"Small"},"matchFilter":"","filterWeight":0,"appStoreId":[],"requirements":[],"offerType":"StaticPrice","giftInfo":{"bIsEnabled":false,"forcedGiftBoxTemplateId":"","purchaseRequirements":[],"giftRecordIds":[]},"refundable":true,"metaInfo":[{"key":"SectionId","value":"Featured"},{"key":"TileSize","value":"Small"}],"displayAssetPath":"","itemGrants":[],"sortPriority":0,"catalogGroupPriority":0};
|
||||
|
||||
if (value.toLowerCase().startsWith("daily")) {
|
||||
catalog.storefronts.forEach((storefront, i) => {
|
||||
if (storefront.name == "BRDailyStorefront") {
|
||||
CatalogEntry.requirements = [];
|
||||
CatalogEntry.itemGrants = [];
|
||||
|
||||
for (var x in CatalogConfig[value].itemGrants) {
|
||||
if (typeof CatalogConfig[value].itemGrants[x] == "string") {
|
||||
if (CatalogConfig[value].itemGrants[x].length != 0) {
|
||||
CatalogEntry.devName = CatalogConfig[value].itemGrants[0]
|
||||
CatalogEntry.offerId = CatalogConfig[value].itemGrants[0]
|
||||
|
||||
CatalogEntry.requirements.push({ "requirementType": "DenyOnItemOwnership", "requiredId": CatalogConfig[value].itemGrants[x], "minQuantity": 1 })
|
||||
CatalogEntry.itemGrants.push({ "templateId": CatalogConfig[value].itemGrants[x], "quantity": 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CatalogEntry.prices[0].basePrice = CatalogConfig[value].price
|
||||
CatalogEntry.prices[0].regularPrice = CatalogConfig[value].price
|
||||
CatalogEntry.prices[0].finalPrice = CatalogConfig[value].price
|
||||
|
||||
// Make featured items appear on the left side of the screen
|
||||
CatalogEntry.sortPriority = -1
|
||||
|
||||
if (CatalogEntry.itemGrants.length != 0) {
|
||||
catalog.storefronts[i].catalogEntries.push(CatalogEntry);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (value.toLowerCase().startsWith("featured")) {
|
||||
catalog.storefronts.forEach((storefront, i) => {
|
||||
if (storefront.name == "BRWeeklyStorefront") {
|
||||
CatalogEntry.requirements = [];
|
||||
CatalogEntry.itemGrants = [];
|
||||
|
||||
for (var x in CatalogConfig[value].itemGrants) {
|
||||
if (typeof CatalogConfig[value].itemGrants[x] == "string") {
|
||||
if (CatalogConfig[value].itemGrants[x].length != 0) {
|
||||
CatalogEntry.devName = CatalogConfig[value].itemGrants[0]
|
||||
CatalogEntry.offerId = CatalogConfig[value].itemGrants[0]
|
||||
|
||||
CatalogEntry.requirements.push({ "requirementType": "DenyOnItemOwnership", "requiredId": CatalogConfig[value].itemGrants[x], "minQuantity": 1 })
|
||||
CatalogEntry.itemGrants.push({ "templateId": CatalogConfig[value].itemGrants[x], "quantity": 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CatalogEntry.prices[0].basePrice = CatalogConfig[value].price
|
||||
CatalogEntry.prices[0].regularPrice = CatalogConfig[value].price
|
||||
CatalogEntry.prices[0].finalPrice = CatalogConfig[value].price
|
||||
|
||||
CatalogEntry.meta.TileSize = "Normal"
|
||||
CatalogEntry.metaInfo[1].value = "Normal"
|
||||
|
||||
if (CatalogEntry.itemGrants.length != 0) {
|
||||
catalog.storefronts[i].catalogEntries.push(CatalogEntry);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
return catalog;
|
||||
}
|
||||
|
||||
function getTheater(req) {
|
||||
const memory = GetVersionInfo(req);
|
||||
|
||||
var theater = JSON.stringify(require("./../responses/Campaign/worldstw.json"));
|
||||
var Season = "Season" + memory.season;
|
||||
|
||||
try {
|
||||
if (memory.build >= 15.30) {
|
||||
theater = theater.replace(/\/Game\//ig, "\/SaveTheWorld\/");
|
||||
theater = theater.replace(/\"DataTable\'\/SaveTheWorld\//ig, "\"DataTable\'\/Game\/");
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
var hour = date.getHours();
|
||||
|
||||
// Set the 24-hour StW mission refresh date for version season 9 and above
|
||||
if (memory.season >= 9) {
|
||||
date.setHours(23, 59, 59, 999);
|
||||
} else {
|
||||
// Set the 6-hour StW mission refresh date for versions below season 9
|
||||
if (hour < 6) {
|
||||
date.setHours(5, 59, 59, 999);
|
||||
} else if (hour < 12) {
|
||||
date.setHours(11, 59, 59, 999);
|
||||
} else if (hour < 18) {
|
||||
date.setHours(17, 59, 59, 999);
|
||||
} else {
|
||||
date.setHours(23, 59, 59, 999);
|
||||
}
|
||||
}
|
||||
|
||||
date = date.toISOString();
|
||||
|
||||
theater = theater.replace(/2017-07-25T23:59:59.999Z/ig, date);
|
||||
} catch (err) {}
|
||||
|
||||
theater = JSON.parse(theater)
|
||||
|
||||
if (theater.hasOwnProperty("Seasonal")) {
|
||||
if (theater.Seasonal.hasOwnProperty(Season)) {
|
||||
theater.theaters = theater.theaters.concat(theater.Seasonal[Season].theaters);
|
||||
theater.missions = theater.missions.concat(theater.Seasonal[Season].missions);
|
||||
theater.missionAlerts = theater.missionAlerts.concat(theater.Seasonal[Season].missionAlerts);
|
||||
}
|
||||
delete theater.Seasonal;
|
||||
}
|
||||
|
||||
return theater;
|
||||
}
|
||||
|
||||
function getContentPages(req) {
|
||||
const memory = GetVersionInfo(req);
|
||||
|
||||
const contentpages = JSON.parse(JSON.stringify(require("./../responses/contentpages.json")));
|
||||
|
||||
var Language = "en";
|
||||
|
||||
if (req.headers["accept-language"]) {
|
||||
if (req.headers["accept-language"].includes("-") && req.headers["accept-language"] != "es-419" && req.headers["accept-language"] != "pt-BR") {
|
||||
Language = req.headers["accept-language"].split("-")[0];
|
||||
} else {
|
||||
Language = req.headers["accept-language"];
|
||||
}
|
||||
}
|
||||
|
||||
const modes = ["saveTheWorldUnowned", "battleRoyale", "creative", "saveTheWorld"];
|
||||
const news = ["savetheworldnews", "battleroyalenews"]
|
||||
const motdnews = ["battleroyalenews", "battleroyalenewsv2"]
|
||||
|
||||
try {
|
||||
modes.forEach(mode => {
|
||||
contentpages.subgameselectdata[mode].message.title = contentpages.subgameselectdata[mode].message.title[Language]
|
||||
contentpages.subgameselectdata[mode].message.body = contentpages.subgameselectdata[mode].message.body[Language]
|
||||
})
|
||||
} catch (err) {}
|
||||
|
||||
try {
|
||||
if (memory.build < 5.30) {
|
||||
news.forEach(mode => {
|
||||
contentpages[mode].news.messages[0].image = "https://fortnite-public-service-prod11.ol.epicgames.com/images/discord-s.png";
|
||||
contentpages[mode].news.messages[1].image = "https://fortnite-public-service-prod11.ol.epicgames.com/images/lawin-s.png";
|
||||
})
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
try {
|
||||
motdnews.forEach(news => {
|
||||
contentpages[news].news.motds.forEach(motd => {
|
||||
motd.title = motd.title[Language];
|
||||
motd.body = motd.body[Language];
|
||||
})
|
||||
})
|
||||
} catch (err) {}
|
||||
|
||||
try {
|
||||
const backgrounds = contentpages.dynamicbackgrounds.backgrounds.backgrounds;
|
||||
const season = `season${memory.season}${memory.season >= 21 ? "00" : ""}`;
|
||||
backgrounds[0].stage = season;
|
||||
backgrounds[1].stage = season;
|
||||
|
||||
if (memory.season == 10) {
|
||||
backgrounds[0].stage = "seasonx";
|
||||
backgrounds[1].stage = "seasonx";
|
||||
} else if (memory.build == 11.31 || memory.build == 11.40) {
|
||||
backgrounds[0].stage = "Winter19";
|
||||
backgrounds[1].stage = "Winter19";
|
||||
} else if (memory.build == 19.01) {
|
||||
backgrounds[0].stage = "winter2021";
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp19-lobby-xmas-2048x1024-f85d2684b4af.png";
|
||||
contentpages.subgameinfo.battleroyale.image = "https://cdn2.unrealengine.com/19br-wf-subgame-select-512x1024-16d8bb0f218f.jpg";
|
||||
contentpages.specialoffervideo.bSpecialOfferEnabled = "true";
|
||||
} else if (memory.season == 20) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp20-lobby-2048x1024-d89eb522746c.png";
|
||||
if (memory.build == 20.40) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp20-40-armadillo-glowup-lobby-2048x2048-2048x2048-3b83b887cc7f.jpg";
|
||||
}
|
||||
} else if (memory.season == 21) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/s21-lobby-background-2048x1024-2e7112b25dc3.jpg";
|
||||
if (memory.build == 21.10) {
|
||||
backgrounds[0].stage = "season2100";
|
||||
}
|
||||
if (memory.build == 21.30) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/nss-lobbybackground-2048x1024-f74a14565061.jpg";
|
||||
backgrounds[0].stage = "season2130";
|
||||
}
|
||||
} else if (memory.season == 22) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp22-lobby-square-2048x2048-2048x2048-e4e90c6e8018.jpg";
|
||||
} else if (memory.season == 23) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp23-lobby-2048x1024-2048x1024-26f2c1b27f63.png";
|
||||
if (memory.build == 23.10) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp23-winterfest-lobby-square-2048x2048-2048x2048-277a476e5ca6.png";
|
||||
contentpages.specialoffervideo.bSpecialOfferEnabled = "true";
|
||||
}
|
||||
} else if (memory.season == 24) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-ch4s2-bp-lobby-4096x2048-edde08d15f7e.jpg"
|
||||
}
|
||||
|
||||
else if (memory.season == 25) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/s25-lobby-4k-4096x2048-4a832928e11f.jpg";
|
||||
backgrounds[1].backgroundimage = "https://cdn2.unrealengine.com/fn-shop-ch4s3-04-1920x1080-785ce1d90213.png";
|
||||
if (memory.build == 25.11) {
|
||||
backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-s25-14dos-lobby-4096x2048-2be24969eee3.jpg";
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {}
|
||||
|
||||
return contentpages;
|
||||
}
|
||||
|
||||
function MakeSurvivorAttributes(templateId) {
|
||||
const SurvivorData = require("./../responses/Campaign/survivorData.json");
|
||||
var SurvivorAttributes = {
|
||||
"level": 1,
|
||||
"item_seen": false,
|
||||
"squad_slot_idx": -1,
|
||||
"building_slot_used": -1
|
||||
};
|
||||
|
||||
if (SurvivorData.fixedAttributes.hasOwnProperty(templateId)) {
|
||||
SurvivorAttributes = {...SurvivorAttributes, ...SurvivorData.fixedAttributes[templateId]};
|
||||
}
|
||||
|
||||
if (!SurvivorAttributes.hasOwnProperty("gender")) {
|
||||
SurvivorAttributes.gender = (Math.floor(Math.random() * (1 - 3) + 3)).toString(); // Set a random survivor gender ("1" = male, "2" = female)
|
||||
}
|
||||
|
||||
if (!SurvivorAttributes.hasOwnProperty("managerSynergy")) {
|
||||
var randomNumber = Math.floor(Math.random() * SurvivorData.bonuses.length);
|
||||
SurvivorAttributes.set_bonus = SurvivorData.bonuses[randomNumber];
|
||||
}
|
||||
|
||||
if (!SurvivorAttributes.hasOwnProperty("personality")) {
|
||||
var randomNumber = Math.floor(Math.random() * SurvivorData.personalities.length);
|
||||
SurvivorAttributes.personality = SurvivorData.personalities[randomNumber];
|
||||
}
|
||||
|
||||
if (!SurvivorAttributes.hasOwnProperty("portrait")) {
|
||||
portraitFactor = SurvivorAttributes.personality;
|
||||
if (SurvivorAttributes.hasOwnProperty("managerSynergy")) {
|
||||
portraitFactor = SurvivorAttributes.managerSynergy;
|
||||
}
|
||||
|
||||
var gender = SurvivorAttributes.gender
|
||||
var randomNumber = Math.floor(Math.random() * SurvivorData.portraits[portraitFactor][gender].length);
|
||||
SurvivorAttributes.portrait = SurvivorData.portraits[portraitFactor][gender][randomNumber];
|
||||
}
|
||||
|
||||
return SurvivorAttributes;
|
||||
}
|
||||
|
||||
function MakeID() {
|
||||
return uuid.v4();
|
||||
}
|
||||
|
||||
function sendXmppMessageToAll(body) {
|
||||
if (global.Clients) {
|
||||
if (typeof body == "object") body = JSON.stringify(body);
|
||||
|
||||
global.Clients.forEach(ClientData => {
|
||||
ClientData.client.send(XMLBuilder.create("message")
|
||||
.attribute("from", "xmpp-admin@prod.ol.epicgames.com")
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("to", ClientData.jid)
|
||||
.element("body", `${body}`).up().toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function DecodeBase64(str) {
|
||||
return Buffer.from(str, 'base64').toString()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sleep,
|
||||
GetVersionInfo,
|
||||
getItemShop,
|
||||
getTheater,
|
||||
getContentPages,
|
||||
MakeSurvivorAttributes,
|
||||
MakeID,
|
||||
sendXmppMessageToAll,
|
||||
DecodeBase64
|
||||
}
|
||||
47
backend/structure/lightswitch.js
Normal file
47
backend/structure/lightswitch.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
|
||||
express.get("/lightswitch/api/service/Fortnite/status", async (req, res) => {
|
||||
res.json({
|
||||
"serviceInstanceId": "fortnite",
|
||||
"status": "UP",
|
||||
"message": "Fortnite is online",
|
||||
"maintenanceUri": null,
|
||||
"overrideCatalogIds": [
|
||||
"a7f138b2e51945ffbfdacc1af0541053"
|
||||
],
|
||||
"allowedActions": [],
|
||||
"banned": false,
|
||||
"launcherInfoDTO": {
|
||||
"appName": "Fortnite",
|
||||
"catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5",
|
||||
"namespace": "fn"
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/lightswitch/api/service/bulk/status", async (req, res) => {
|
||||
res.json(
|
||||
[{
|
||||
"serviceInstanceId": "fortnite",
|
||||
"status": "UP",
|
||||
"message": "fortnite is up.",
|
||||
"maintenanceUri": null,
|
||||
"overrideCatalogIds": [
|
||||
"a7f138b2e51945ffbfdacc1af0541053"
|
||||
],
|
||||
"allowedActions": [
|
||||
"PLAY",
|
||||
"DOWNLOAD"
|
||||
],
|
||||
"banned": false,
|
||||
"launcherInfoDTO": {
|
||||
"appName": "Fortnite",
|
||||
"catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5",
|
||||
"namespace": "fn"
|
||||
}
|
||||
}]
|
||||
)
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
494
backend/structure/main.js
Normal file
494
backend/structure/main.js
Normal file
@@ -0,0 +1,494 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.get("/clearitemsforshop", async (req, res) => {
|
||||
res.set("Content-Type", "text/plain");
|
||||
|
||||
const athena = require("./../profiles/athena.json");
|
||||
const CatalogConfig = require("./../Config/catalog_config.json");
|
||||
var StatChanged = false;
|
||||
|
||||
for (var value in CatalogConfig) {
|
||||
for (var i in CatalogConfig[value].itemGrants) {
|
||||
if (Array.isArray(CatalogConfig[value].itemGrants)) {
|
||||
for (var key in athena.items) {
|
||||
if (typeof CatalogConfig[value].itemGrants[i] == "string") {
|
||||
if (CatalogConfig[value].itemGrants[i].length != 0) {
|
||||
if (CatalogConfig[value].itemGrants[i].toLowerCase() == athena.items[key].templateId.toLowerCase()) {
|
||||
delete athena.items[key]
|
||||
|
||||
StatChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StatChanged == true) {
|
||||
athena.rvn += 1;
|
||||
athena.commandRevision += 1;
|
||||
|
||||
fs.writeFileSync("./profiles/athena.json", JSON.stringify(athena, null, 2));
|
||||
|
||||
res.send('Success');
|
||||
} else {
|
||||
res.send('Failed, there are no items to remove')
|
||||
}
|
||||
})
|
||||
|
||||
express.get("/eulatracking/api/shared/agreements/fn*", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/friendcodes/*/epic", async (req, res) => {
|
||||
res.json([{
|
||||
"codeId": "L4WINS3RV3R",
|
||||
"codeType": "CodeToken:FounderFriendInvite",
|
||||
"dateCreated": "2024-04-02T21:37:00.420Z"
|
||||
},
|
||||
{
|
||||
"codeId": "playeereq",
|
||||
"codeType": "CodeToken:FounderFriendInvite_XBOX",
|
||||
"dateCreated": "2024-04-02T21:37:00.420Z"
|
||||
},
|
||||
{
|
||||
"codeId": "lawinscodelol",
|
||||
"codeType": "CodeToken:MobileInvite",
|
||||
"dateCreated": "2024-04-02T21:37:00.420Z"
|
||||
}])
|
||||
})
|
||||
|
||||
express.get("/launcher/api/public/distributionpoints/", async (req, res) => {
|
||||
res.json({
|
||||
"distributions": [
|
||||
"https://epicgames-download1.akamaized.net/",
|
||||
"https://download.epicgames.com/",
|
||||
"https://download2.epicgames.com/",
|
||||
"https://download3.epicgames.com/",
|
||||
"https://download4.epicgames.com/",
|
||||
"https://lawinserver.ol.epicgames.com/"
|
||||
]
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/launcher/api/public/assets/*", async (req, res) => {
|
||||
res.json({
|
||||
"appName": "FortniteContentBuilds",
|
||||
"labelName": "LawinServer",
|
||||
"buildVersion": "++Fortnite+Release-20.00-CL-19458861-Windows",
|
||||
"catalogItemId": "5cb97847cee34581afdbc445400e2f77",
|
||||
"expires": "9999-12-31T23:59:59.999Z",
|
||||
"items": {
|
||||
"MANIFEST": {
|
||||
"signature": "LawinServer",
|
||||
"distribution": "https://lawinserver.ol.epicgames.com/",
|
||||
"path": "Builds/Fortnite/Content/CloudDir/LawinServer.manifest",
|
||||
"hash": "55bb954f5596cadbe03693e1c06ca73368d427f3",
|
||||
"additionalDistributions": []
|
||||
},
|
||||
"CHUNKS": {
|
||||
"signature": "LawinServer",
|
||||
"distribution": "https://lawinserver.ol.epicgames.com/",
|
||||
"path": "Builds/Fortnite/Content/CloudDir/LawinServer.manifest",
|
||||
"additionalDistributions": []
|
||||
}
|
||||
},
|
||||
"assetId": "FortniteContentBuilds"
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/Builds/Fortnite/Content/CloudDir/*.manifest", async (req, res) => {
|
||||
res.set("Content-Type", "application/octet-stream")
|
||||
|
||||
const manifest = fs.readFileSync(path.join(__dirname, "..", "responses", "CloudDir", "LawinServer.manifest"));
|
||||
|
||||
res.status(200).send(manifest).end();
|
||||
})
|
||||
|
||||
express.get("/Builds/Fortnite/Content/CloudDir/*.chunk", async (req, res) => {
|
||||
res.set("Content-Type", "application/octet-stream")
|
||||
|
||||
const chunk = fs.readFileSync(path.join(__dirname, "..", "responses", "CloudDir", "LawinServer.chunk"));
|
||||
|
||||
res.status(200).send(chunk).end();
|
||||
})
|
||||
|
||||
express.get("/Builds/Fortnite/Content/CloudDir/*.ini", async (req, res) => {
|
||||
const ini = fs.readFileSync(path.join(__dirname, "..", "responses", "CloudDir", "Full.ini"));
|
||||
|
||||
res.status(200).send(ini).end();
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/grant_access/*", async (req, res) => {
|
||||
res.json({});
|
||||
res.status(204);
|
||||
})
|
||||
|
||||
express.post("/api/v1/user/setting", async (req, res) => {
|
||||
res.json([]);
|
||||
})
|
||||
|
||||
express.get("/waitingroom/api/waitingroom", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/socialban/api/public/v1/*", async (req, res) => {
|
||||
res.json({
|
||||
"bans": [],
|
||||
"warnings": []
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/events/tournamentandhistory/*/EU/WindowsClient", async (req, res) => {
|
||||
res.json({});
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/statsv2/account/:accountId", async (req, res) => {
|
||||
res.json({
|
||||
"startTime": 0,
|
||||
"endTime": 0,
|
||||
"stats": {},
|
||||
"accountId": req.params.accountId
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/statsproxy/api/statsv2/account/:accountId", async (req, res) => {
|
||||
res.json({
|
||||
"startTime": 0,
|
||||
"endTime": 0,
|
||||
"stats": {},
|
||||
"accountId": req.params.accountId
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/stats/accountId/:accountId/bulk/window/alltime", async (req, res) => {
|
||||
res.json({
|
||||
"startTime": 0,
|
||||
"endTime": 0,
|
||||
"stats": {},
|
||||
"accountId": req.params.accountId
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/feedback/*", async (req, res) => {
|
||||
res.status(200);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/statsv2/query", async (req, res) => {
|
||||
res.json([]);
|
||||
})
|
||||
|
||||
express.post("/statsproxy/api/statsv2/query", async (req, res) => {
|
||||
res.json([]);
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/events/v2/setSubgroup/*", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/enabled_features", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
express.get("/api/v1/events/Fortnite/download/*", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/twitch/*", async (req, res) => {
|
||||
res.status(200);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/world/info", async (req, res) => {
|
||||
const worldstw = functions.getTheater(req);
|
||||
|
||||
res.json(worldstw)
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/chat/*/*/*/pc", async (req, res) => {
|
||||
res.json({ "GlobalChatRooms": [{"roomName":"lawinserverglobal"}] })
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/chat/*/recommendGeneralChatRooms/pc", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/presence/api/v1/_/*/last-online", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/receipts/v1/account/*/receipts", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/leaderboards/cohort/:accountId", async (req, res) => {
|
||||
res.json({
|
||||
"accountId": req.params.accountId,
|
||||
"cohortAccounts": [
|
||||
req.params.accountId,
|
||||
"Lawin",
|
||||
"TI93",
|
||||
"PRO100KatYT",
|
||||
"Playeereq",
|
||||
"Matteoki"
|
||||
],
|
||||
"expiresAt": "9999-12-31T00:00:00.000Z",
|
||||
"playlist": req.query.playlist
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/leaderboards/type/group/stat/:statName/window/:statWindow", async (req, res) => {
|
||||
var entries = [];
|
||||
|
||||
for (var i = 0; i < req.body.length; i++) {
|
||||
entries.push({
|
||||
"accountId": req.body[i],
|
||||
"value": Math.floor(Math.random() * 68) + 1
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
"entries": entries,
|
||||
"statName": req.params.statName,
|
||||
"statWindow": req.params.statWindow
|
||||
})
|
||||
res.end();
|
||||
});
|
||||
|
||||
express.post("/fortnite/api/leaderboards/type/global/stat/:statName/window/:statWindow", async (req, res) => {
|
||||
var HeroNames = [
|
||||
"Hawk",
|
||||
"Banshee",
|
||||
"Wildcat",
|
||||
"Jonsey",
|
||||
"Spitfire",
|
||||
"Ramirez",
|
||||
"Headhunter",
|
||||
"Renegade",
|
||||
"Harper",
|
||||
"Knox",
|
||||
"Hype",
|
||||
"Bull",
|
||||
"Hazard",
|
||||
"Penny",
|
||||
"Izza",
|
||||
"Kyle",
|
||||
"Luna",
|
||||
"Crash",
|
||||
"Edge",
|
||||
"Scorpion",
|
||||
"Scorch",
|
||||
"Ken",
|
||||
"Mari",
|
||||
"Sarah",
|
||||
"Grizzly",
|
||||
"Eagle Eye",
|
||||
"Southie",
|
||||
"A.C.",
|
||||
"Buzz",
|
||||
"Quinn",
|
||||
"Jess",
|
||||
"Deadeye"
|
||||
]
|
||||
|
||||
var entries = [];
|
||||
|
||||
for (var i = 0; i < HeroNames.length; i++) {
|
||||
entries.push({
|
||||
"accountId": HeroNames[i],
|
||||
"value": Math.floor(Math.random() * 68) + 1
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
"entries": entries,
|
||||
"statName": req.params.statName,
|
||||
"statWindow": req.params.statWindow
|
||||
})
|
||||
res.end();
|
||||
});
|
||||
|
||||
express.get("/fortnite/api/game/v2/homebase/allowed-name-chars", async (req, res) => {
|
||||
res.json({
|
||||
"ranges": [
|
||||
48,
|
||||
57,
|
||||
65,
|
||||
90,
|
||||
97,
|
||||
122,
|
||||
192,
|
||||
255,
|
||||
260,
|
||||
265,
|
||||
280,
|
||||
281,
|
||||
286,
|
||||
287,
|
||||
304,
|
||||
305,
|
||||
321,
|
||||
324,
|
||||
346,
|
||||
347,
|
||||
350,
|
||||
351,
|
||||
377,
|
||||
380,
|
||||
1024,
|
||||
1279,
|
||||
1536,
|
||||
1791,
|
||||
4352,
|
||||
4607,
|
||||
11904,
|
||||
12031,
|
||||
12288,
|
||||
12351,
|
||||
12352,
|
||||
12543,
|
||||
12592,
|
||||
12687,
|
||||
12800,
|
||||
13055,
|
||||
13056,
|
||||
13311,
|
||||
13312,
|
||||
19903,
|
||||
19968,
|
||||
40959,
|
||||
43360,
|
||||
43391,
|
||||
44032,
|
||||
55215,
|
||||
55216,
|
||||
55295,
|
||||
63744,
|
||||
64255,
|
||||
65072,
|
||||
65103,
|
||||
65281,
|
||||
65470,
|
||||
131072,
|
||||
173791,
|
||||
194560,
|
||||
195103
|
||||
],
|
||||
"singlePoints": [
|
||||
32,
|
||||
39,
|
||||
45,
|
||||
46,
|
||||
95,
|
||||
126
|
||||
],
|
||||
"excludedPoints": [
|
||||
208,
|
||||
215,
|
||||
222,
|
||||
247
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/datarouter/api/v1/public/data", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.post("/api/v1/assets/Fortnite/*/*", async (req, res) => {
|
||||
if (req.body.hasOwnProperty("FortCreativeDiscoverySurface") && req.body.FortCreativeDiscoverySurface == 0) {
|
||||
const discovery_api_assets = require("./../responses/Athena/Discovery/discovery_api_assets.json");
|
||||
res.json(discovery_api_assets)
|
||||
}
|
||||
else {
|
||||
res.json({
|
||||
"FortCreativeDiscoverySurface": {
|
||||
"meta": {
|
||||
"promotion": req.body.FortCreativeDiscoverySurface || 0
|
||||
},
|
||||
"assets": {}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
express.get("/region", async (req, res) => {
|
||||
res.json({
|
||||
"continent": {
|
||||
"code": "EU",
|
||||
"geoname_id": 6255148,
|
||||
"names": {
|
||||
"de": "Europa",
|
||||
"en": "Europe",
|
||||
"es": "Europa",
|
||||
"fr": "Europe",
|
||||
"ja": "ヨーロッパ",
|
||||
"pt-BR": "Europa",
|
||||
"ru": "Европа",
|
||||
"zh-CN": "欧洲"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"geoname_id": 2635167,
|
||||
"is_in_european_union": false,
|
||||
"iso_code": "GB",
|
||||
"names": {
|
||||
"de": "UK",
|
||||
"en": "United Kingdom",
|
||||
"es": "RU",
|
||||
"fr": "Royaume Uni",
|
||||
"ja": "英国",
|
||||
"pt-BR": "Reino Unido",
|
||||
"ru": "Британия",
|
||||
"zh-CN": "英国"
|
||||
}
|
||||
},
|
||||
"subdivisions": [
|
||||
{
|
||||
"geoname_id": 6269131,
|
||||
"iso_code": "ENG",
|
||||
"names": {
|
||||
"de": "England",
|
||||
"en": "England",
|
||||
"es": "Inglaterra",
|
||||
"fr": "Angleterre",
|
||||
"ja": "イングランド",
|
||||
"pt-BR": "Inglaterra",
|
||||
"ru": "Англия",
|
||||
"zh-CN": "英格兰"
|
||||
}
|
||||
},
|
||||
{
|
||||
"geoname_id": 3333157,
|
||||
"iso_code": "KEC",
|
||||
"names": {
|
||||
"en": "Royal Kensington and Chelsea"
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
// Parental Controls
|
||||
express.all("/v1/epic-settings/public/users/*/values", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/br-inventory/account/*", async (req, res) => {
|
||||
res.json({
|
||||
"stash": {
|
||||
"globalcash": 5000
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
77
backend/structure/matchmaker.js
Normal file
77
backend/structure/matchmaker.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const functions = require("./functions.js");
|
||||
|
||||
module.exports = async (ws) => {
|
||||
const ticketId = functions.MakeID().replace(/-/gi, "");
|
||||
const matchId = functions.MakeID().replace(/-/gi, "");
|
||||
const sessionId = functions.MakeID().replace(/-/gi, "");
|
||||
|
||||
Connecting();
|
||||
Waiting();
|
||||
Queued();
|
||||
SessionAssignment();
|
||||
Join();
|
||||
|
||||
function Connecting() {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
state: "Connecting",
|
||||
},
|
||||
name: "StatusUpdate",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function Waiting() {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
totalPlayers: 1,
|
||||
connectedPlayers: 1,
|
||||
state: "Waiting",
|
||||
},
|
||||
name: "StatusUpdate",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function Queued() {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
ticketId: ticketId,
|
||||
queuedPlayers: 0,
|
||||
estimatedWaitSec: 0,
|
||||
status: {},
|
||||
state: "Queued",
|
||||
},
|
||||
name: "StatusUpdate",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function SessionAssignment() {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
matchId: matchId,
|
||||
state: "SessionAssignment",
|
||||
},
|
||||
name: "StatusUpdate",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function Join() {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
payload: {
|
||||
matchId: matchId,
|
||||
sessionId: sessionId,
|
||||
joinDelaySec: 1,
|
||||
},
|
||||
name: "Play",
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
87
backend/structure/matchmaking.js
Normal file
87
backend/structure/matchmaking.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const iniparser = require("ini");
|
||||
const config = iniparser.parse(fs.readFileSync(path.join(__dirname, "..", "Config", "config.ini")).toString());
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.get("/fortnite/api/matchmaking/session/findPlayer/*", async (req, res) => {
|
||||
res.status(200);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/matchmakingservice/ticket/player/*", async (req, res) => {
|
||||
res.cookie("currentbuildUniqueId", req.query.bucketId.split(":")[0]);
|
||||
|
||||
res.json({
|
||||
"serviceUrl": "ws://127.0.0.1:80",
|
||||
"ticketType": "mms-player",
|
||||
"payload": "69=",
|
||||
"signature": "420="
|
||||
})
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/game/v2/matchmaking/account/:accountId/session/:sessionId", async (req, res) => {
|
||||
res.json({
|
||||
"accountId": req.params.accountId,
|
||||
"sessionId": req.params.sessionId,
|
||||
"key": "AOJEv8uTFmUh7XM2328kq9rlAzeQ5xzWzPIiyKn2s7s="
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/matchmaking/session/:session_id", async (req, res) => {
|
||||
res.json({
|
||||
"id": req.params.session_id,
|
||||
"ownerId": functions.MakeID().replace(/-/ig, "").toUpperCase(),
|
||||
"ownerName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968",
|
||||
"serverName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968",
|
||||
"serverAddress": config.GameServer.ip,
|
||||
"serverPort": Number(config.GameServer.port),
|
||||
"maxPublicPlayers": 220,
|
||||
"openPublicPlayers": 175,
|
||||
"maxPrivatePlayers": 0,
|
||||
"openPrivatePlayers": 0,
|
||||
"attributes": {
|
||||
"REGION_s": "EU",
|
||||
"GAMEMODE_s": "FORTATHENA",
|
||||
"ALLOWBROADCASTING_b": true,
|
||||
"SUBREGION_s": "GB",
|
||||
"DCID_s": "FORTNITE-LIVEEUGCEC1C2E30UBRCORE0A-14840880",
|
||||
"tenant_s": "Fortnite",
|
||||
"MATCHMAKINGPOOL_s": "Any",
|
||||
"STORMSHIELDDEFENSETYPE_i": 0,
|
||||
"HOTFIXVERSION_i": 0,
|
||||
"PLAYLISTNAME_s": "Playlist_DefaultSolo",
|
||||
"SESSIONKEY_s": functions.MakeID().replace(/-/ig, "").toUpperCase(),
|
||||
"TENANT_s": "Fortnite",
|
||||
"BEACONPORT_i": 15009
|
||||
},
|
||||
"publicPlayers": [],
|
||||
"privatePlayers": [],
|
||||
"totalPlayers": 45,
|
||||
"allowJoinInProgress": false,
|
||||
"shouldAdvertise": false,
|
||||
"isDedicated": false,
|
||||
"usesStats": false,
|
||||
"allowInvites": false,
|
||||
"usesPresence": false,
|
||||
"allowJoinViaPresence": true,
|
||||
"allowJoinViaPresenceFriendsOnly": false,
|
||||
"buildUniqueId": req.cookies.currentbuildUniqueId || "0", // buildUniqueId is different for every build, this uses the netver of the version you're currently using
|
||||
"lastUpdated": new Date().toISOString(),
|
||||
"started": false
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/matchmaking/session/*/join", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/matchmaking/session/matchMakingRequest", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
7964
backend/structure/mcp.js
Normal file
7964
backend/structure/mcp.js
Normal file
File diff suppressed because it is too large
Load Diff
60
backend/structure/party.js
Normal file
60
backend/structure/party.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const functions = require("./functions.js");
|
||||
|
||||
express.get("/party/api/v1/Fortnite/user/*", async (req, res) => {
|
||||
res.json({
|
||||
"current": [],
|
||||
"pending": [],
|
||||
"invites": [],
|
||||
"pings": []
|
||||
});
|
||||
})
|
||||
|
||||
express.post("/party/api/v1/Fortnite/parties", async (req, res) => {
|
||||
if (!req.body.join_info) return res.json({});
|
||||
if (!req.body.join_info.connection) return res.json({});
|
||||
|
||||
res.json({
|
||||
"id": functions.MakeID().replace(/-/ig, ""),
|
||||
"created_at": new Date().toISOString(),
|
||||
"updated_at": new Date().toISOString(),
|
||||
"config": {
|
||||
"type": "DEFAULT",
|
||||
...req.body.config,
|
||||
"discoverability": "ALL",
|
||||
"sub_type": "default",
|
||||
"invite_ttl": 14400,
|
||||
"intention_ttl": 60
|
||||
},
|
||||
"members": [{
|
||||
"account_id": (req.body.join_info.connection.id || "").split("@prod")[0],
|
||||
"meta": req.body.join_info.meta || {},
|
||||
"connections": [
|
||||
{
|
||||
"id": req.body.join_info.connection.id || "",
|
||||
"connected_at": new Date().toISOString(),
|
||||
"updated_at": new Date().toISOString(),
|
||||
"yield_leadership": false,
|
||||
"meta": req.body.join_info.connection.meta || {}
|
||||
}
|
||||
],
|
||||
"revision": 0,
|
||||
"updated_at": new Date().toISOString(),
|
||||
"joined_at": new Date().toISOString(),
|
||||
"role": "CAPTAIN"
|
||||
}],
|
||||
"applicants": [],
|
||||
"meta": req.body.meta || {},
|
||||
"invites": [],
|
||||
"revision": 0,
|
||||
"intentions": []
|
||||
})
|
||||
})
|
||||
|
||||
express.all("/party/api/v1/Fortnite/parties/*", async (req, res) => {
|
||||
res.status(204)
|
||||
res.end()
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
22
backend/structure/privacy.js
Normal file
22
backend/structure/privacy.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const fs = require("fs");
|
||||
const privacy = require("./../responses/privacy.json");
|
||||
|
||||
express.get("/fortnite/api/game/v2/privacy/account/:accountId", async (req, res) => {
|
||||
privacy.accountId = req.params.accountId;
|
||||
|
||||
res.json(privacy);
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/privacy/account/:accountId", async (req, res) => {
|
||||
privacy.accountId = req.params.accountId;
|
||||
privacy.optOutOfPublicLeaderboards = req.body.optOutOfPublicLeaderboards;
|
||||
|
||||
fs.writeFileSync("./responses/privacy.json", JSON.stringify(privacy, null, 2));
|
||||
|
||||
res.json(privacy);
|
||||
res.end();
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
23
backend/structure/storefront.js
Normal file
23
backend/structure/storefront.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const functions = require("./functions.js");
|
||||
const catalog = functions.getItemShop();
|
||||
const keychain = require("./../responses/keychain.json");
|
||||
|
||||
express.get("/fortnite/api/storefront/v2/catalog", async (req, res) => {
|
||||
if (req.headers["user-agent"].includes("2870186")) {
|
||||
return res.status(404).end();
|
||||
}
|
||||
|
||||
res.json(catalog);
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/storefront/v2/keychain", async (req, res) => {
|
||||
res.json(keychain)
|
||||
})
|
||||
|
||||
express.get("/catalog/api/shared/bulk/offers", async (req, res) => {
|
||||
res.json({});
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
1427
backend/structure/timeline.js
Normal file
1427
backend/structure/timeline.js
Normal file
File diff suppressed because it is too large
Load Diff
193
backend/structure/user.js
Normal file
193
backend/structure/user.js
Normal file
@@ -0,0 +1,193 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const iniparser = require("ini");
|
||||
const config = iniparser.parse(fs.readFileSync(path.join(__dirname, "..", "Config", "config.ini")).toString());
|
||||
var Memory_CurrentAccountID = config.Config.displayName;
|
||||
|
||||
express.get("/account/api/public/account", async (req, res) => {
|
||||
var response = [];
|
||||
|
||||
if (typeof req.query.accountId == "string") {
|
||||
var accountId = req.query.accountId;
|
||||
if (accountId.includes("@")) accountId = accountId.split("@")[0];
|
||||
|
||||
response.push({
|
||||
"id": accountId,
|
||||
"displayName": accountId,
|
||||
"externalAuths": {}
|
||||
})
|
||||
}
|
||||
|
||||
if (Array.isArray(req.query.accountId)) {
|
||||
for (var x in req.query.accountId) {
|
||||
var accountId = req.query.accountId[x];
|
||||
if (accountId.includes("@")) accountId = accountId.split("@")[0];
|
||||
|
||||
response.push({
|
||||
"id": accountId,
|
||||
"displayName": accountId,
|
||||
"externalAuths": {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
res.json(response)
|
||||
})
|
||||
|
||||
express.get("/account/api/public/account/:accountId", async (req, res) => {
|
||||
if (config.Config.bUseConfigDisplayName == false) {
|
||||
Memory_CurrentAccountID = req.params.accountId;
|
||||
}
|
||||
|
||||
if (Memory_CurrentAccountID.includes("@")) Memory_CurrentAccountID = Memory_CurrentAccountID.split("@")[0];
|
||||
|
||||
res.json({
|
||||
"id": req.params.accountId,
|
||||
"displayName": Memory_CurrentAccountID,
|
||||
"name": "Lawin",
|
||||
"email": Memory_CurrentAccountID + "@lawin.com",
|
||||
"failedLoginAttempts": 0,
|
||||
"lastLogin": new Date().toISOString(),
|
||||
"numberOfDisplayNameChanges": 0,
|
||||
"ageGroup": "UNKNOWN",
|
||||
"headless": false,
|
||||
"country": "US",
|
||||
"lastName": "Server",
|
||||
"preferredLanguage": "en",
|
||||
"canUpdateDisplayName": false,
|
||||
"tfaEnabled": false,
|
||||
"emailVerified": true,
|
||||
"minorVerified": false,
|
||||
"minorExpected": false,
|
||||
"minorStatus": "NOT_MINOR",
|
||||
"cabinedMode": false,
|
||||
"hasHashedEmail": false
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/sdk/v1/*", async (req, res) => {
|
||||
const sdk = require("./../responses/sdkv1.json");
|
||||
res.json(sdk)
|
||||
})
|
||||
|
||||
express.post("/auth/v1/oauth/token", async (req, res) => {
|
||||
res.json({
|
||||
"access_token": "lawinstokenlol",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 28800,
|
||||
"expires_at": "9999-12-31T23:59:59.999Z",
|
||||
"deployment_id": "lawinsdeploymentidlol",
|
||||
"organization_id": "lawinsorganizationidlol",
|
||||
"product_id": "prod-fn",
|
||||
"sandbox_id": "fn"
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/epic/id/v2/sdk/accounts", async (req, res) => {
|
||||
res.json([{
|
||||
"accountId": Memory_CurrentAccountID,
|
||||
"displayName": Memory_CurrentAccountID,
|
||||
"preferredLanguage": "en",
|
||||
"cabinedMode": false,
|
||||
"empty": false
|
||||
}])
|
||||
})
|
||||
|
||||
express.post("/epic/oauth/v2/token", async (req, res) => {
|
||||
res.json({
|
||||
"scope": "basic_profile friends_list openid presence",
|
||||
"token_type": "bearer",
|
||||
"access_token": "lawinstokenlol",
|
||||
"expires_in": 28800,
|
||||
"expires_at": "9999-12-31T23:59:59.999Z",
|
||||
"refresh_token": "lawinstokenlol",
|
||||
"refresh_expires_in": 86400,
|
||||
"refresh_expires_at": "9999-12-31T23:59:59.999Z",
|
||||
"account_id": Memory_CurrentAccountID,
|
||||
"client_id": "lawinsclientidlol",
|
||||
"application_id": "lawinsapplicationidlol",
|
||||
"selected_account_id": Memory_CurrentAccountID,
|
||||
"id_token": "lawinstokenlol"
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/account/api/public/account/*/externalAuths", async (req, res) => {
|
||||
res.json([])
|
||||
})
|
||||
|
||||
express.delete("/account/api/oauth/sessions/kill", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.delete("/account/api/oauth/sessions/kill/*", async (req, res) => {
|
||||
res.status(204);
|
||||
res.end();
|
||||
})
|
||||
|
||||
express.get("/account/api/oauth/verify", async (req, res) => {
|
||||
res.json({
|
||||
"token": "lawinstokenlol",
|
||||
"session_id": "3c3662bcb661d6de679c636744c66b62",
|
||||
"token_type": "bearer",
|
||||
"client_id": "lawinsclientidlol",
|
||||
"internal_client": true,
|
||||
"client_service": "fortnite",
|
||||
"account_id": Memory_CurrentAccountID,
|
||||
"expires_in": 28800,
|
||||
"expires_at": "9999-12-02T01:12:01.100Z",
|
||||
"auth_method": "exchange_code",
|
||||
"display_name": Memory_CurrentAccountID,
|
||||
"app": "fortnite",
|
||||
"in_app_id": Memory_CurrentAccountID,
|
||||
"device_id": "lawinsdeviceidlol"
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/account/api/oauth/token", async (req, res) => {
|
||||
if (config.Config.bUseConfigDisplayName == false) {
|
||||
Memory_CurrentAccountID = req.body.username || "LawinServer"
|
||||
}
|
||||
|
||||
if (Memory_CurrentAccountID.includes("@")) Memory_CurrentAccountID = Memory_CurrentAccountID.split("@")[0];
|
||||
|
||||
res.json({
|
||||
"access_token": "lawinstokenlol",
|
||||
"expires_in": 28800,
|
||||
"expires_at": "9999-12-02T01:12:01.100Z",
|
||||
"token_type": "bearer",
|
||||
"refresh_token": "lawinstokenlol",
|
||||
"refresh_expires": 86400,
|
||||
"refresh_expires_at": "9999-12-02T01:12:01.100Z",
|
||||
"account_id": Memory_CurrentAccountID,
|
||||
"client_id": "lawinsclientidlol",
|
||||
"internal_client": true,
|
||||
"client_service": "fortnite",
|
||||
"displayName": Memory_CurrentAccountID,
|
||||
"app": "fortnite",
|
||||
"in_app_id": Memory_CurrentAccountID,
|
||||
"device_id": "lawinsdeviceidlol"
|
||||
})
|
||||
})
|
||||
|
||||
express.post("/account/api/oauth/exchange", async (req, res) => {
|
||||
res.json({})
|
||||
})
|
||||
|
||||
express.get("/account/api/epicdomains/ssodomains", async (req, res) => {
|
||||
res.json([
|
||||
"unrealengine.com",
|
||||
"unrealtournament.com",
|
||||
"fortnite.com",
|
||||
"epicgames.com"
|
||||
])
|
||||
})
|
||||
|
||||
express.post("/fortnite/api/game/v2/tryPlayOnPlatform/account/*", async (req, res) => {
|
||||
res.setHeader("Content-Type", "text/plain");
|
||||
res.send(true);
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
59
backend/structure/version.js
Normal file
59
backend/structure/version.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const Express = require("express");
|
||||
const express = Express.Router();
|
||||
|
||||
express.get("/fortnite/api/version", async (req, res) => {
|
||||
res.json({
|
||||
"app": "fortnite",
|
||||
"serverDate": new Date().toISOString(),
|
||||
"overridePropertiesVersion": "unknown",
|
||||
"cln": "17951730",
|
||||
"build": "444",
|
||||
"moduleName": "Fortnite-Core",
|
||||
"buildDate": "2021-10-27T21:00:51.697Z",
|
||||
"version": "18.30",
|
||||
"branch": "Release-18.30",
|
||||
"modules": {
|
||||
"Epic-LightSwitch-AccessControlCore": {
|
||||
"cln": "17237679",
|
||||
"build": "b2130",
|
||||
"buildDate": "2021-08-19T18:56:08.144Z",
|
||||
"version": "1.0.0",
|
||||
"branch": "trunk"
|
||||
},
|
||||
"epic-xmpp-api-v1-base": {
|
||||
"cln": "5131a23c1470acbd9c94fae695ef7d899c1a41d6",
|
||||
"build": "b3595",
|
||||
"buildDate": "2019-07-30T09:11:06.587Z",
|
||||
"version": "0.0.1",
|
||||
"branch": "master"
|
||||
},
|
||||
"epic-common-core": {
|
||||
"cln": "17909521",
|
||||
"build": "3217",
|
||||
"buildDate": "2021-10-25T18:41:12.486Z",
|
||||
"version": "3.0",
|
||||
"branch": "TRUNK"
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/v2/versioncheck/*", async (req, res) => {
|
||||
res.json({
|
||||
"type": "NO_UPDATE"
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/v2/versioncheck*", async (req, res) => {
|
||||
res.json({
|
||||
"type": "NO_UPDATE"
|
||||
})
|
||||
})
|
||||
|
||||
express.get("/fortnite/api/versioncheck*", async (req, res) => {
|
||||
res.json({
|
||||
"type": "NO_UPDATE"
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = express;
|
||||
275
backend/structure/xmpp.js
Normal file
275
backend/structure/xmpp.js
Normal file
@@ -0,0 +1,275 @@
|
||||
const WebSocket = require("ws").Server;
|
||||
const XMLBuilder = require("xmlbuilder");
|
||||
const XMLParser = require("xml-parser");
|
||||
|
||||
const functions = require("./../structure/functions.js");
|
||||
const matchmaker = require("./matchmaker.js");
|
||||
|
||||
const port = 80;
|
||||
|
||||
const wss = new WebSocket({ port: port }, () => console.log("XMPP and Matchmaker started listening on port", port));
|
||||
wss.on("error", (err) => {
|
||||
console.log("XMPP and Matchmaker \x1b[31mFAILED\x1b[0m to start hosting.");
|
||||
})
|
||||
|
||||
|
||||
global.Clients = [];
|
||||
|
||||
wss.on('connection', async (ws) => {
|
||||
ws.on('error', () => {});
|
||||
|
||||
if (ws.protocol.toLowerCase() != "xmpp") return matchmaker(ws);
|
||||
|
||||
var accountId = "";
|
||||
var jid = "";
|
||||
var id = "";
|
||||
var ID = functions.MakeID();
|
||||
var Authenticated = false;
|
||||
|
||||
ws.on('message', async (message) => {
|
||||
if (Buffer.isBuffer(message)) message = message.toString();
|
||||
const msg = XMLParser(message);
|
||||
if (!msg.root) return Error(ws);
|
||||
|
||||
switch (msg.root.name) {
|
||||
case "open":
|
||||
ws.send(XMLBuilder.create("open")
|
||||
.attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-framing")
|
||||
.attribute("from", "prod.ol.epicgames.com")
|
||||
.attribute("id", ID)
|
||||
.attribute("version", "1.0")
|
||||
.attribute("xml:lang", "en").toString())
|
||||
|
||||
if (Authenticated == true) {
|
||||
ws.send(XMLBuilder.create("stream:features").attribute("xmlns:stream", "http://etherx.jabber.org/streams")
|
||||
.element("ver").attribute("xmlns", "urn:xmpp:features:rosterver").up()
|
||||
.element("starttls").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls").up()
|
||||
.element("bind").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind").up()
|
||||
.element("compression").attribute("xmlns", "http://jabber.org/features/compress")
|
||||
.element("method", "zlib").up().up()
|
||||
.element("session").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session").up().toString())
|
||||
} else {
|
||||
ws.send(XMLBuilder.create("stream:features").attribute("xmlns:stream", "http://etherx.jabber.org/streams")
|
||||
.element("mechanisms").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl")
|
||||
.element("mechanism", "PLAIN").up().up()
|
||||
.element("ver").attribute("xmlns", "urn:xmpp:features:rosterver").up()
|
||||
.element("starttls").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls").up()
|
||||
.element("compression").attribute("xmlns", "http://jabber.org/features/compress")
|
||||
.element("method", "zlib").up().up()
|
||||
.element("auth").attribute("xmlns", "http://jabber.org/features/iq-auth").up().toString())
|
||||
}
|
||||
break;
|
||||
|
||||
case "auth":
|
||||
if (!msg.root.content) return Error(ws);
|
||||
if (!functions.DecodeBase64(msg.root.content)) return Error(ws);
|
||||
if (!functions.DecodeBase64(msg.root.content).includes("\u0000")) return Error(ws);
|
||||
var decodedBase64 = functions.DecodeBase64(msg.root.content).split("\u0000");
|
||||
|
||||
if (global.Clients.find(i => i.accountId == decodedBase64[1])) return Error(ws);
|
||||
|
||||
accountId = decodedBase64[1];
|
||||
|
||||
if (decodedBase64 && accountId && decodedBase64.length == 3) {
|
||||
Authenticated = true;
|
||||
|
||||
console.log(`An xmpp client with the account id ${accountId} has logged in.`);
|
||||
|
||||
ws.send(XMLBuilder.create("success").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl").toString());
|
||||
} else {
|
||||
return Error(ws);
|
||||
}
|
||||
break;
|
||||
|
||||
case "iq":
|
||||
switch (msg.root.attributes.id) {
|
||||
case "_xmpp_bind1":
|
||||
if (!msg.root.children.find(i => i.name == "bind")) return;
|
||||
if (!msg.root.children.find(i => i.name == "bind").children.find(i => i.name == "resource")) return;
|
||||
var resource = msg.root.children.find(i => i.name == "bind").children.find(i => i.name == "resource").content;
|
||||
jid = `${accountId}@prod.ol.epicgames.com/${resource}`;
|
||||
id = `${accountId}@prod.ol.epicgames.com`;
|
||||
|
||||
ws.send(XMLBuilder.create("iq")
|
||||
.attribute("to", jid)
|
||||
.attribute("id", "_xmpp_bind1")
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("type", "result")
|
||||
.element("bind")
|
||||
.attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind")
|
||||
.element("jid", jid).up().up().toString())
|
||||
break;
|
||||
|
||||
case "_xmpp_session1":
|
||||
if (!global.Clients.find(i => i.client == ws)) return Error(ws);
|
||||
var xml = XMLBuilder.create("iq")
|
||||
.attribute("to", jid)
|
||||
.attribute("from", "prod.ol.epicgames.com")
|
||||
.attribute("id", "_xmpp_session1")
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("type", "result");
|
||||
|
||||
ws.send(xml.toString());
|
||||
getPresenceFromAll(ws);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!global.Clients.find(i => i.client == ws)) return Error(ws);
|
||||
var xml = XMLBuilder.create("iq")
|
||||
.attribute("to", jid)
|
||||
.attribute("from", "prod.ol.epicgames.com")
|
||||
.attribute("id", msg.root.attributes.id)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("type", "result");
|
||||
|
||||
ws.send(xml.toString());
|
||||
}
|
||||
break;
|
||||
|
||||
case "message":
|
||||
if (!global.Clients.find(i => i.client == ws)) return Error(ws);
|
||||
if (!msg.root.children.find(i => i.name == "body")) return;
|
||||
var body = msg.root.children.find(i => i.name == "body").content;
|
||||
|
||||
if (msg.root.attributes.type) {
|
||||
if (msg.root.attributes.type == "chat") {
|
||||
if (!msg.root.attributes.to) return;
|
||||
var receiver = global.Clients.find(i => i.id == msg.root.attributes.to);
|
||||
var sender = global.Clients.find(i => i.client == ws);
|
||||
if (!receiver || !sender) return;
|
||||
if (receiver == sender) return;
|
||||
|
||||
receiver.client.send(XMLBuilder.create("message")
|
||||
.attribute("to", receiver.jid)
|
||||
.attribute("from", sender.jid)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("type", "chat")
|
||||
.element("body", body).up().toString())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ifJSON(body)) {
|
||||
var object = JSON.parse(body);
|
||||
|
||||
if (object.hasOwnProperty("type")) {
|
||||
if (typeof object.type == "string") {
|
||||
switch (object.type.toLowerCase()) {
|
||||
case "com.epicgames.party.invitation":
|
||||
if (!msg.root.attributes.to) return;
|
||||
var sender = global.Clients.find(i => i.client == ws);
|
||||
var receiver = global.Clients.find(i => i.id == msg.root.attributes.to);
|
||||
if (!receiver) return;
|
||||
|
||||
receiver.client.send(XMLBuilder.create("message")
|
||||
.attribute("from", sender.jid)
|
||||
.attribute("id", msg.root.attributes.id)
|
||||
.attribute("to", receiver.jid)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.element("body", body).up().toString())
|
||||
break;
|
||||
|
||||
default:
|
||||
ws.send(XMLBuilder.create("message")
|
||||
.attribute("from", jid)
|
||||
.attribute("id", msg.root.attributes.id)
|
||||
.attribute("to", jid)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.element("body", body).up().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "presence":
|
||||
if (!global.Clients.find(i => i.client == ws)) return Error(ws);
|
||||
if (!msg.root.children.find(i => i.name == "status")) return;
|
||||
if (!ifJSON(msg.root.children.find(i => i.name == "status").content)) return;
|
||||
var body = msg.root.children.find(i => i.name == "status").content;
|
||||
var away = false;
|
||||
if (msg.root.children.find(i => i.name == "show")) away = true;
|
||||
|
||||
updatePresenceForAll(ws, body, away, false)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!global.Clients.find(i => i.client == ws)) {
|
||||
if (accountId && jid && ID && id && Authenticated == true) {
|
||||
global.Clients.push({ "client": ws, "accountId": accountId, "jid": jid, "id": id, "lastPresenceUpdate": { "away": false, "status": "{}" } });
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ws.on('close', () => RemoveClient(ws))
|
||||
})
|
||||
|
||||
function RemoveClient(ws) {
|
||||
if (global.Clients.find(i => i.client == ws)) {
|
||||
updatePresenceForAll(ws, "{}", false, true);
|
||||
|
||||
console.log(`An xmpp client with the account id ${global.Clients.find(i => i.client == ws).accountId} has logged out.`);
|
||||
|
||||
global.Clients.splice(global.Clients.findIndex(i => i.client == ws), 1);
|
||||
}
|
||||
}
|
||||
|
||||
function Error(ws) {
|
||||
ws.send(XMLBuilder.create("close").attribute("xmlns", "urn:ietf:params:xml:ns:xmpp-framing").toString());
|
||||
ws.close();
|
||||
}
|
||||
|
||||
function updatePresenceForAll(ws, body, away, offline) {
|
||||
if (global.Clients.find(i => i.client == ws)) {
|
||||
var SenderData = global.Clients.find(i => i.client == ws);
|
||||
var SenderIndex = global.Clients.findIndex(i => i.client == ws);
|
||||
global.Clients[SenderIndex].lastPresenceUpdate.away = away;
|
||||
global.Clients[SenderIndex].lastPresenceUpdate.status = body;
|
||||
|
||||
global.Clients.forEach(ClientData => {
|
||||
var xml = XMLBuilder.create("presence")
|
||||
.attribute("to", ClientData.jid)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("from", SenderData.jid)
|
||||
|
||||
if (offline == true) xml = xml.attribute("type", "unavailable");
|
||||
else xml = xml.attribute("type", "available")
|
||||
|
||||
if (away == true) xml = xml.element("show", "away").up().element("status", body).up();
|
||||
else xml = xml.element("status", body).up();
|
||||
|
||||
ClientData.client.send(xml.toString())
|
||||
})
|
||||
} else {
|
||||
return Error(ws);
|
||||
}
|
||||
}
|
||||
|
||||
function getPresenceFromAll(ws) {
|
||||
if (global.Clients.find(i => i.client == ws)) {
|
||||
var SenderData = global.Clients.find(i => i.client == ws);
|
||||
|
||||
global.Clients.forEach(ClientData => {
|
||||
var xml = XMLBuilder.create("presence")
|
||||
.attribute("to", SenderData.jid)
|
||||
.attribute("xmlns", "jabber:client")
|
||||
.attribute("from", ClientData.jid)
|
||||
|
||||
if (ClientData.lastPresenceUpdate.away == true) xml = xml.attribute("type", "available").element("show", "away").up().element("status", ClientData.lastPresenceUpdate.status).up();
|
||||
else xml = xml.attribute("type", "available").element("status", ClientData.lastPresenceUpdate.status).up();
|
||||
|
||||
SenderData.client.send(xml.toString())
|
||||
})
|
||||
} else {
|
||||
return Error(ws);
|
||||
}
|
||||
}
|
||||
|
||||
function ifJSON(str) {
|
||||
try {
|
||||
JSON.parse(str)
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user