<feat: New project structure>

<feat: New release>
This commit is contained in:
Alessandro Autiero
2023-09-02 15:34:15 +02:00
parent 64b33102f4
commit b41e22adeb
953 changed files with 1373072 additions and 0 deletions

View 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;

View File

@@ -0,0 +1,163 @@
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;

View 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/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;

View File

@@ -0,0 +1,27 @@
const Express = require("express");
const express = Express.Router();
const discovery = require("./../responses/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/*", 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
dependencies/lawin/structure/friends.js vendored Normal file
View 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;

View File

@@ -0,0 +1,318 @@
const XMLBuilder = require("xmlbuilder");
const uuid = require("uuid");
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/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().toISOString()
// Set the 24-hour StW mission refresh date for version season 9 and above
if (memory.season >= 9) {
date = date.split("T")[0] + "T23:59:59.999Z";
} else {
// Set the 6-hour StW mission refresh date for versions below season 9
if (date < (date.split("T")[0] + "T05:59:59.999Z")) {
date = date.split("T")[0] + "T05:59:59.999Z";
} else if (date < (date.split("T")[0] + "T11:59:59.999Z")) {
date = date.split("T")[0] + "T11:59:59.999Z";
} else if (date < (date.split("T")[0] + "T17:59:59.999Z")) {
date = date.split("T")[0] + "T17:59:59.999Z";
} else if (date < (date.split("T")[0] + "T23:59:59.999Z")) {
date = date.split("T")[0] + "T23:59:59.999Z";
}
}
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 {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].stage = `season${memory.season}`;
contentpages.dynamicbackgrounds.backgrounds.backgrounds[1].stage = `season${memory.season}`;
if (memory.season == 10) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].stage = "seasonx";
contentpages.dynamicbackgrounds.backgrounds.backgrounds[1].stage = "seasonx";
}
if (memory.build == 11.31 || memory.build == 11.40) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].stage = "Winter19";
contentpages.dynamicbackgrounds.backgrounds.backgrounds[1].stage = "Winter19";
}
if (memory.build == 19.01) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].stage = "winter2021";
contentpages.dynamicbackgrounds.backgrounds.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";
}
if (memory.season == 20) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp20-lobby-2048x1024-d89eb522746c.png";
if (memory.build == 20.40) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp20-40-armadillo-glowup-lobby-2048x2048-2048x2048-3b83b887cc7f.jpg"
}
}
if (memory.season == 21) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/s21-lobby-background-2048x1024-2e7112b25dc3.jpg"
if (memory.build == 21.30) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/nss-lobbybackground-2048x1024-f74a14565061.jpg";
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].stage = "season2130";
}
}
if (memory.season == 22) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp22-lobby-square-2048x2048-2048x2048-e4e90c6e8018.jpg"
}
if (memory.season == 23) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp23-lobby-2048x1024-2048x1024-26f2c1b27f63.png"
if (memory.build == 23.10) {
contentpages.dynamicbackgrounds.backgrounds.backgrounds[0].backgroundimage = "https://cdn2.unrealengine.com/t-bp23-winterfest-lobby-square-2048x2048-2048x2048-277a476e5ca6.png"
contentpages.specialoffervideo.bSpecialOfferEnabled = "true";
}
}
} catch (err) {}
return contentpages;
}
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 = {
GetVersionInfo,
getItemShop,
getTheater,
getContentPages,
MakeID,
sendXmppMessageToAll,
DecodeBase64
}

View 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;

384
dependencies/lawin/structure/main.js vendored Normal file
View File

@@ -0,0 +1,384 @@
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([])
})
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/*", async (req, res) => {
res.json([])
})
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/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"
}
}
]
})
})
module.exports = express;

View File

@@ -0,0 +1,82 @@
const { Server: WebSocket } = require("ws");
const crypto = require("crypto");
// Start listening websocket on port
const port = 8080;
const wss = new WebSocket({ port: port });
wss.on('listening', () => {
console.log(`Matchmaker started listening on port ${port}`);
});
wss.on('connection', async (ws) => {
if (ws.protocol.toLowerCase().includes("xmpp")) {
return ws.close();
}
// create hashes
const ticketId = crypto.createHash('md5').update(`1${Date.now()}`).digest('hex');
const matchId = crypto.createHash('md5').update(`2${Date.now()}`).digest('hex');
const sessionId = crypto.createHash('md5').update(`3${Date.now()}`).digest('hex');
// you can use setTimeout to send the websocket messages at certain times
setTimeout(Connecting, 200/* Milliseconds */);
setTimeout(Waiting, 1000); // 0.8 Seconds after Connecting
setTimeout(Queued, 2000); // 1 Second after Waiting
setTimeout(SessionAssignment, 6000); // 4 Seconds after Queued
setTimeout(Join, 8000); // 2 Seconds after SessionAssignment
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"
}));
}
});

View 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 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:8080",
"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) => {
const config = iniparser.parse(fs.readFileSync(path.join(__dirname, "..", "Config", "config.ini")).toString());
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;

7026
dependencies/lawin/structure/mcp.js vendored Normal file

File diff suppressed because it is too large Load Diff

60
dependencies/lawin/structure/party.js vendored Normal file
View 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
dependencies/lawin/structure/privacy.js vendored Normal file
View 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;

View 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;

1201
dependencies/lawin/structure/timeline.js vendored Normal file

File diff suppressed because it is too large Load Diff

191
dependencies/lawin/structure/user.js vendored Normal file
View File

@@ -0,0 +1,191 @@
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": "UNKNOWN"
})
})
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
dependencies/lawin/structure/version.js vendored Normal file
View 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;

269
dependencies/lawin/structure/xmpp.js vendored Normal file
View File

@@ -0,0 +1,269 @@
const WebSocket = require("ws").Server;
const XMLBuilder = require("xmlbuilder");
const XMLParser = require("xml-parser");
require("./../structure/matchmaker");
const functions = require("./../structure/functions.js");
const port = 80;
const wss = new WebSocket({ port: port }, () => console.log("XMPP started listening on port", port));
wss.on("error", (err) => {
console.log("XMPP \x1b[31mFAILED\x1b[0m to start hosting (NOTE: This should not affect LawinServer).");
})
global.Clients = [];
wss.on('connection', async (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;
}