├── .gitignore
├── FortniteGameConfig.json
├── LICENSE
├── LauncherAssets
├── Full.ini
├── Neonite.chunk
└── Neonite.manifest
├── README.md
├── Start Neonite.bat
├── api
└── controllers
│ ├── ApiController.js
│ ├── AuthController.js
│ ├── CloudStorageController.js
│ ├── DiscoveryController.js
│ ├── EosController.js
│ ├── FortniteGameController.js
│ ├── LockerController.js
│ ├── MatchMakingController.js
│ ├── PlayerController.js
│ ├── ProfileController.js
│ └── TimelineController.js
├── app.js
├── config.ini
├── config
├── MiniPass.json
├── apiRoutes.js
├── authRoutes.js
├── cloudstorageRoutes.js
├── defs.js
├── discoveryRoutes.js
├── eosRoutes.js
├── http.js
├── matchmakingRoutes.js
└── playerRoutes.js
├── discovery
├── discoveryMenuV1.json
└── discoveryMenuV2.json
├── hotfixes
├── DefaultEngine.ini
├── DefaultGame.ini
├── DefaultInput.ini
└── DefaultRuntimeOptions.ini
├── package-lock.json
├── package.json
├── profile.js
├── profile_template
└── profiles
│ ├── lockerv3.json
│ ├── lockerv4.json
│ ├── profile_athena.json
│ ├── profile_campaign.json
│ ├── profile_collection_book_people0.json
│ ├── profile_collection_book_schematics0.json
│ ├── profile_collections.json
│ ├── profile_common_core.json
│ ├── profile_common_public.json
│ ├── profile_creative.json
│ ├── profile_metadata.json
│ ├── profile_outpost0.json
│ ├── profile_profile0.json
│ └── profile_theater0.json
├── responses
├── FortniteAssets.json
├── catalog
│ ├── shopv1.json
│ ├── shopv2.json
│ └── shopv3.json
├── epic-settings.json
├── fortnitegame.json
└── keychain.json
└── structs
├── NeoLog.js
└── errors.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | profile/
3 | NodeMonRun.cmd
4 | ClientSettings/
--------------------------------------------------------------------------------
/FortniteGameConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "playlist_config": "Playlist_DefaultSolo",
3 | "playlist_settings":{
4 | "RespawnType": "InfiniteRespawn",
5 | "bUseMaxRespawnHeight": true,
6 | "bRespawnInAir": true,
7 | "bSkipWarmup": false,
8 | "bSkipAircraft": false,
9 | "bAllowWarmupPlayerStartInSetupPhase": true,
10 | "AirCraftBehavior": "Default",
11 | "bForceLTMLoadingScreenBackground": false,
12 | "LoadingScreenWidget": "",
13 | "MapScaleOverride": 1.0,
14 | "MapManagerClass": "",
15 | "SafeZoneStartUp": "StartsWithWarmUp",
16 | "bWarmUpInStorm": false,
17 | "bUseDefaultSupplyDrops": true,
18 | "bPlaylistUsesCustomCharacterParts":false,
19 | "CharactersToPreload":[{}],
20 | "CharacterFallbackTagsToPreload":[{}],
21 | "GameType":"BR",
22 | "MinPlayers": 1,
23 | "MaxPlayers": 99,
24 | "bUnderfillMatchmaking": false,
25 | "bOverrideMaxPlayers": false,
26 | "AdditionalLevels": [],
27 | "AdditionalLevelsServerOnly": [],
28 | "BuiltInGameFeaturePluginsToLoad": [],
29 | "GameFeaturePluginToActivateUntilDownloadedContentIsPresent": "",
30 | "TimeOfDayManager": "",
31 | "bIgnoreWeatherEvents": false,
32 | "ItemsToFullyLoad": [],
33 | "bIsDefaultPlaylist": true,
34 | "bUseCustomInGameState": false,
35 | "GameFeaturePluginURLsToLoad": []
36 | }
37 | }
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023-2025 Joshua Clarke (Hybrid)
4 |
5 | All Rights Reserved. You are not allowed to redistribute this software, or use
6 | the software to build derivative works based upon without prior written permission.
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a verbatim
9 | copy of this software and associated documentation files (the "Software"),
10 | This software is open-source only for Educational Purposes, if you learn\copy-over anything from it you must give appropriate credit,
11 | provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner.
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | We may revise this License at any time without prior notice.
17 | By using Neonite v2, you are agreeing to be bound by the current version of the license.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
26 |
--------------------------------------------------------------------------------
/LauncherAssets/Full.ini:
--------------------------------------------------------------------------------
1 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.de]
2 | DeltaDownloadSize="0"
3 | DownloadSize="0"
4 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteBR]
5 | DeltaDownloadSize="0"
6 | DownloadSize="0"
7 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteBROptional]
8 | DeltaDownloadSize="0"
9 | DownloadSize="0"
10 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,StartupOptional]
11 | DeltaDownloadSize="0"
12 | DownloadSize="0"
13 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Startup]
14 | DeltaDownloadSize="0"
15 | DownloadSize="314862"
16 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCreative]
17 | DeltaDownloadSize="0"
18 | DownloadSize="0"
19 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FrontEndOptional]
20 | DeltaDownloadSize="0"
21 | DownloadSize="0"
22 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FrontEnd]
23 | DeltaDownloadSize="0"
24 | DownloadSize="0"
25 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCampaign]
26 | DeltaDownloadSize="0"
27 | DownloadSize="0"
28 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.all]
29 | DeltaDownloadSize="0"
30 | DownloadSize="0"
31 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.zh-CNOptional]
32 | DeltaDownloadSize="0"
33 | DownloadSize="0"
34 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.ru]
35 | DeltaDownloadSize="0"
36 | DownloadSize="0"
37 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.plOptional]
38 | DeltaDownloadSize="0"
39 | DownloadSize="0"
40 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.it]
41 | DeltaDownloadSize="0"
42 | DownloadSize="0"
43 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.frOptional]
44 | DeltaDownloadSize="0"
45 | DownloadSize="0"
46 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.es-419Optional]
47 | DeltaDownloadSize="0"
48 | DownloadSize="0"
49 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.es-419]
50 | DeltaDownloadSize="0"
51 | DownloadSize="0"
52 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.deOptional]
53 | DeltaDownloadSize="0"
54 | DownloadSize="0"
55 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCreativeOptional]
56 | DeltaDownloadSize="0"
57 | DownloadSize="0"
58 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCampaignOptional]
59 | DeltaDownloadSize="0"
60 | DownloadSize="0"
61 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCampaignTutorialOptional]
62 | DeltaDownloadSize="0"
63 | DownloadSize="0"
64 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,FortniteCampaignTutorial]
65 | DeltaDownloadSize="0"
66 | DownloadSize="0"
67 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.allOptional]
68 | DeltaDownloadSize="0"
69 | DownloadSize="0"
70 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.zh-CN]
71 | DeltaDownloadSize="0"
72 | DownloadSize="0"
73 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.ruOptional]
74 | DeltaDownloadSize="0"
75 | DownloadSize="0"
76 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.pl]
77 | DeltaDownloadSize="0"
78 | DownloadSize="0"
79 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.itOptional]
80 | DeltaDownloadSize="0"
81 | DownloadSize="0"
82 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.fr]
83 | DeltaDownloadSize="0"
84 | DownloadSize="0"
85 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.es-ESOptional]
86 | DeltaDownloadSize="0"
87 | DownloadSize="0"
88 | [zWLs_nkItuSYf5pDOk6miVWwFbDtDA.manifest,Lang.es-ES]
89 | DeltaDownloadSize="0"
90 | DownloadSize="0"
91 |
--------------------------------------------------------------------------------
/LauncherAssets/Neonite.chunk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HybridFNBR/Neonite/70282cd6602e297af26ca52d24b23b25bc73ab5a/LauncherAssets/Neonite.chunk
--------------------------------------------------------------------------------
/LauncherAssets/Neonite.manifest:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HybridFNBR/Neonite/70282cd6602e297af26ca52d24b23b25bc73ab5a/LauncherAssets/Neonite.manifest
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 | ## About Neonite V2
14 |
15 | Neonite V2 is a popular private server written in [Node.js](https://nodejs.org/en/download/current/), aimed to provide a fun yet easy-to-use program for people wanting to customize their Fortnite experience.
16 | _This project was made for fun and it doesn't aim to harm the original game by any means, If you are an Epic Games employee and have any problems with this project, please do not hesitate to [contact us](#contact) through your official business email._
17 | _In acquiescence to Epic Games Inc. - Please note that access to all cosmetics for Neonite V2 has been stripped. If you want to use skins, please purchase them on Fortnite._
18 |
19 |
20 | ## Installation
21 |
22 | - Install the latest version of **[Node.js](https://nodejs.org/en/download/current/)**.
23 | - Open `Start Neonite.bat`, it should say `[Neonite]: v2.* is listening on port 5595!` (Do not close this while running Neonite V2!)
24 |
25 | ## FAQs
26 | * Can I go in-game?
27 | * Yes with Carbon on most past versions upto version 30.00 of fortnite, however with the latest version of fortnite(30.10+) Epic Games added new protections to stop you from launching
28 | directly with the shipping exe and only lobby is supported through a proxy method, check it out [here](https://discord.com/invite/X525zyJtaU).
29 | * Will I get banned?
30 | * No, you won't get banned because Neonite V2 doesn't connect to any Epic Games related services.
31 | * How do I play with my friends?
32 | * Neonite V2 is a locally-hosted project, meaning it has no party or friends functionality.
33 | * Why don't I see any cosmetics in my locker?
34 | * We removed cosmetics because Epic Games made it clear it does not like services that offer cosmetics for free. If you wish for skins you're welcome to add them yourself.
35 |
36 |
37 | ## Support
38 | Discord Server: [Carbon](https://discord.com/invite/X525zyJtaU) <- *you can get the launcher + backend alternatively from here*
39 |
40 |
41 |
42 | ## Credits
43 |
44 | ### Used APIs
45 |
[NiteStats API](https://nitestats.com/) developed by [VastBlast](https://github.com/VastBlast)
46 |
47 | ### Contributors
48 |
49 | | Contributor | Helped with |
50 | | ----------- | ----------- |
51 | | [Kemo](https://github.com/kem0o) | Original creator and maintainer |
52 | | [Hybrid](https://github.com/HybridFNBR) | Current maintainer |
53 | | [Andre](https://github.com/JustAndr3h) | Pull requests |
54 | | [Beat-YT](https://github.com/Beat-YT) | maintainer |
55 | | [Amrsatrio](https://github.com/Amrsatrio) | Write-up of profile.js and API-reversing |
56 | | [Kyiro](https://github.com/Kyiro) | Pull requests |
57 | | [iDrDoge](https://github.com/iDrDoge) | Pull requests |
58 | | [Tim](https://github.com/timjans01) | Improving this awesome page |
59 | | [Ayal](https://github.com/AyalX) | Management |
60 | | [Jacobb](https://github.com/Jacobb626) | Pull requests |
61 |
62 |
63 |
64 |
65 | ## License
66 |
67 | This project is licensed under the [Neo License](https://github.com/NeoniteDev/NeoniteV2/blob/main/LICENSE)
68 |
--------------------------------------------------------------------------------
/Start Neonite.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cd /d %~dp0
3 |
4 | where node >nul 2>&1
5 | if %ERRORLEVEL% NEQ 0 (
6 | echo NodeJS is not installed. Downloading NodeJS. Trying to install it automatically. Make sure to follow the installer instructions.
7 | powershell -Command "winget install OpenJS.NodeJS.LTS; if (!$?) { exit 1 }"
8 | if %ERRORLEVEL% NEQ 0 (
9 | echo Failed to install NodeJS automatically. Please install NodeJS manually. See https://nodejs.org/en/download/prebuilt-installer
10 | echo After installing NodeJS, run this script again to continue using NeoniteV2.
11 | pause
12 | exit /b 1
13 | )
14 | )
15 |
16 | if not exist node_modules (call npm i && call npm install sails -g)
17 |
18 | title NeoniteV2
19 |
20 | node app.js
21 |
22 | cmd /k
23 |
--------------------------------------------------------------------------------
/api/controllers/AuthController.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto');
2 | const {ApiException} = require('../../structs/errors');
3 | const errors = require("../../structs/errors");
4 | const jsonwebtoken = require('jsonwebtoken');
5 | var ini = require('ini')
6 | var fs = require('fs')
7 | const path = require("path");
8 | var config = ini.parse(fs.readFileSync(path.join(__dirname, '../../config.ini'), 'utf-8'));
9 | const {account} = require("../../config/defs")
10 |
11 |
12 | module.exports = {
13 | oauthToken: function(req, res){
14 | switch (req.body.grant_type) {
15 | case "password":
16 | if (!req.body.username) {
17 | throw new ApiException(errors.com.epicgames.common.oauth.invalid_request).with("username")
18 | }
19 | if (req.body.username.includes("@")) {
20 | account.displayName = req.body.username.split("@")[0]
21 | } else {
22 | account.displayName = req.body.username;
23 | }
24 | account.accountId = account.displayName.replace(/ /g, "_");
25 | break;
26 | case "exchange_code":
27 | if (!req.body.exchange_code) {
28 | throw new ApiException(errors.com.epicgames.common.oauth.invalid_request).with("exchange_code")
29 | }
30 |
31 | account.displayName = req.body.exchange_code;
32 | account.accountId = req.body.exchange_code;
33 | break;
34 | }
35 |
36 | if(config.bEnableOverride == true){
37 | account.displayName = config.username
38 | account.accountId = config.username
39 | }
40 |
41 | let token = jsonwebtoken.sign({
42 | "app": "prod-fn",
43 | "sub": account.accountId,
44 | "mver": false,
45 | "clid": "ec684b8c687f479fadea3cb2ad83f5c6",
46 | "dn": account.displayName,
47 | "am": "authorization_code",
48 | "pfpid": "prod-fn",
49 | "p": "eNqtWV1z2joQ/T8ZyJSE0kQzPPS2SW9n0jZz6dzpG7NYa6MiS7qSTMK/vyv5I4YGMMQPGRJsaT+0e85ZJdXWK+GRJVIX3HltIUPmNs5jzu4RfGGRf3USFB9fivg5HU0GhUP7IJxnab0ePyTXNzeTyRjfj8dXeHObXr9bXN9O+G0KH0Y31+xiOno/SA+Z+3f1KGHzGblIwCOfoV2j/QI5XgolglVUXniJOX0ySBJd0OdRu61Fbno1SK1AxR1LtFKYeKGVO7oHLTNuw3IwQ6s9hEXMFAspEiaFWg0TzZHeqV2qHjUePnu0CuTHwi/d/iSElB6P5pMk9/0MvRcqc79mvy4drMOmrTDJFacTAZIZ7bwB6zch8N6sfoOkttrfpl9/zOpNDVqnFTBXPep81mFx/a7XK1RM+yXaGToXzvle24/l09L09KZH/2cgc60O58VY/AclgsNZrPh7ITHU5OndFCrSokOVILs47qorFi6xwsRyn45GPcb9OHvfBF21VpdjkmnWyfHKynTSHGz1edwMy9EDBw9saeYhtLngrS49fR9aLKFQCZUU4/pJSQ2cgrivcvlJK0+J+asQkodTFSYat5hR1vfCQ/sk650+4zogoUfn71iq2AJ8sqQXLahVbIgLZug5WmN1RouDtaS0PqQ414L2yjUvJDJqfpFC4nsFgI+KWy14fe47gWUFWC6ACu36xWYeQsghuE+HGvuRohBqTQ+ntwMCfJCaHi2B+Ia44mrAKbnNcnBUCS5+n3ZnrFGLsbCQ4C0k0YPKU0qdoYbo0G5HI/mthepQ+AMuXKKJ1jbHc55YCkZbZig+ivsJnAnF8KSt5LHdVEoFItYB561OCUs6dR6aUHxrSrhtgPUlz0aEgqwAuHOLhH0TneeFCtRdcmrFeoxj6AA6HcuSJRDpSsLdOpgqgX/aZFLrVWHoAdLGTIBNRGjaDIcWSki4OnwmR/NbFV9Dlg7JyLIT9GrjKGdS6qcOqWn56Ug8OLYCYXWHpI4HROcWJGeFCVhD5RPkhguJ6+DlwUbZUVl3KhOq0Vktf7XF1OoIUyeUbunmUoT1m1h07UOkHwqLtNcah/iizPZJLIuG/Nnqne5HdYLTrrCElMj+K+j13cLjWBKotrHwCkn9JqMQrbyMGnA0KP9a4ab6vo0cr2veOy78wdM4RA/f2/Tw0g7UApCFZuhBlW9D6lULUs8AoNLeHqF88RZVwxaWWiQBFyrl9JS3x4z+iPKhUFCzZKt6dwrtLfaOMeLWY5Ckz8DG7wJ4EeQ2RfVoNf+u1QM15XGrzfKlMGfpVzrei5qMho3OrwUEnUUAuenkrX42SEMUM65xnljIVy2CrkOdUQvvKdgAc9QYPM5ehHNtgRa1xhZJndYjJ6NtCmttA6GRvK/Hvzj60WatPDhGOojatkvgpDg1YVkIJaqmrgEEKUAY4XoVnV/uv3/TC7K/Kzs10GAd9MY8/vKEoX6YBSHnlRaY+wDJnkqgV3eaeTXIgUqQh+1O4eb6l7nVMngV59JeB+sHoYrnHub1MNG0y6hT/Zwwjc/KwSUM5R3nyG0yedNcOCdlFgjznH4NTlRTl1C0D7EXbbOQ1DoRFI+vH5/XZydS93Wbuvu7glro5x8K/yiwV8ekV+ZUQzBZDjhnyJcti1Fc9wo5gb63MefA3hRYKrYK6AXC66+mf8wCpeahRGDUfPUArNOUtowP6IXn3JjWrNlhzAzFRxt0EFHkSu5OnBbaYmkfNcbxrZkCv4abl+5OnXlXvK2b61mpHVwzPJ9Mr8H5IbEa+RDvWEd9kknDJKeo35ersV4vlP9GKbS7nJUeVUrCI+SOqUaV9W5vu81eu6yhEwscC5nF8j8AHein8j4DKcNR51Qu0K2BqpULaMbQo4smB8Gv5Y3VhYnJbA10D1GU9XXNOScKad911hIpXMBk+DOQ8ac4r+5M5uXVynChwUb1tqNsXoNvq39jQv0eL0aH8Zp0LyjkQml792xoAfK9aqzq8IvWnVdGwppa7l11bUxc3SvOP87G53SgpGHzhyJ4aF/PVbdKo733Yc1tGMeod0b/A2wAVCM=",
50 | "iai": account.accountId,
51 | "sec": 1,
52 | "acr": "urn:epic:loa:aal1",
53 | "clsvc": "prod-fn",
54 | "t": "s",
55 | "scope": "basic_profile",
56 | "auth_time": 1725882476,
57 | "ic": true,
58 | "exp": 2147483647,
59 | "iat": 1725882476,
60 | "jti": "132fac2cc9c94fa08fdc3e65fef24f07"
61 | },"RS256", {keyid:"2022-06-14T06:17:57.047928700Z"})
62 | res.json({
63 | "access_token": `eg1~${token}`,
64 | "expires_in": 2147483647,
65 | "expires_at": "9999-12-31T23:59:59.999Z",
66 | "token_type": "bearer",
67 | "refresh_token": `eg1~${token}`,
68 | "refresh_expires": 2147483647,
69 | "refresh_expires_at": "9999-12-31T23:59:59.999Z",
70 | "account_id": account.accountId,
71 | "client_id": "ec684b8c687f479fadea3cb2ad83f5c6",
72 | "internal_client": true,
73 | "client_service": "prod-fn",
74 | "scope": [
75 | "basic_profile",
76 | "friends_list",
77 | "openid",
78 | "presence"
79 | ],
80 | "displayName": account.displayName,
81 | "app": "prod-fn",
82 | "in_app_id": account.accountId,
83 | "product_id": "prod-fn",
84 | "application_id": "fghi4567FNFBKFz3E4TROb0bmPS8h1GW",
85 | "acr": "urn:epic:loa:aal1",
86 | "auth_time": "1999-01-12T00:20:15.542Z"
87 | }).status(200).end();
88 | },
89 |
90 | verifyToken: function(req, res){
91 | const JWT = req.headers.authorization.replace("bearer eg1~", "")
92 | const JWTdecode = jsonwebtoken.decode(JWT)
93 | res.json({
94 | "token": req.headers.authorization,
95 | "session_id": `${crypto.randomBytes(32).toString("hex")}`,
96 | "token_type": "bearer",
97 | "client_id": "ec684b8c687f479fadea3cb2ad83f5c6",
98 | "internal_client": true,
99 | "client_service": "prod-fn",
100 | "account_id": JWTdecode["sub"],
101 | "expires_in": 2147483647,
102 | "expires_at": "9999-12-31T23:59:59.999Z",
103 | "auth_method": "authorization_code",
104 | "display_name": JWTdecode["sub"],
105 | "app": "prod-fn",
106 | "in_app_id": JWTdecode["sub"],
107 | "device_id": "89776e294d5c27ba1ef4e59fab402ea7",
108 | "scope": [
109 | "basic_profile",
110 | "friends_list",
111 | "openid",
112 | "presence"
113 | ],
114 | "product_id": "prod-fn",
115 | "sandbox_id": "fn",
116 | "deployment_id": "62a9473a2dca46b29ccf17577fcf42d7",
117 | "application_id": "fghi4567FNFBKFz3E4TROb0bmPS8h1GW",
118 | "acr": "urn:epic:loa:aal1",
119 | "auth_time": "1999-01-12T00:20:15.542Z"
120 | }).status(200).end()
121 | },
122 |
123 | killToken: function(req, res){
124 | res.status(204).end();
125 | },
126 |
127 | accountInfo: function(req, res){
128 | res.json({
129 | id: req.params.accountId,
130 | displayName: req.params.accountId,
131 | "email": "neonite@dev.com",
132 | "failedLoginAttempts": 0,
133 | "lastLogin": "",
134 | "numberOfDisplayNameChanges": 1,
135 | "dateOfBirth": "1999-01-01",
136 | "ageGroup": "ADULT",
137 | "headless": false,
138 | "country": "",
139 | "phoneNumber": "",
140 | "company": "Neonite", //neonite is now a company O:
141 | "preferredLanguage": "en",
142 | "lastDisplayNameChange": "",
143 | "canUpdateDisplayName": true,
144 | "tfaEnabled": true,
145 | "emailVerified": true,
146 | "minorVerified": false,
147 | "minorExpected": false,
148 | "minorStatus": "NOT_MINOR",
149 | "cabinedMode": false,
150 | "hasHashedEmail": false,
151 | "externalAuths": {},
152 |
153 | })
154 | },
155 |
156 | displayName: function(req, res){
157 | res.json({
158 | "id": req.params.displayName,
159 | "displayName": req.params.displayName,
160 | "externalAuths": {}
161 | });
162 | },
163 |
164 | discoveryToken: function(req, res){
165 | const useragent = req.headers["user-agent"];
166 | const regex = useragent.match(/\+\+Fortnite\+Release-\d+\.\d+/);
167 | res.json({
168 | "branchName" : regex[0],
169 | "appId" : "Fortnite",
170 | "token" : `${crypto.randomBytes(10).toString("hex")}=`
171 | })
172 | },
173 |
174 | publicAccount: function(req, res){
175 | res.json([{
176 | id: req.query.accountId,
177 | displayName: req.query.accountId,
178 | externalAuths: {}
179 | }])
180 | },
181 |
182 | externalAuths: function(req, res){
183 | res.json([])
184 | },
185 |
186 | tokenInfo: function(req, res){
187 | const base64 = req.headers.authorization.replace("Basic ", "")
188 | const decodedString = atob(base64);
189 | const [username, password] = decodedString.split(':')
190 | res.json({
191 | "active": true,
192 | "scope": "basic_profile openid offline_access",
193 | "token_type": "bearer",
194 | "expires_in": 2147483647,
195 | "expires_at": "9999-12-31T23:59:59.999Z",
196 | "account_id": account.accountId,
197 | "client_id": password,
198 | "application_id": "fghi45672f0QV6b6B1KntLd7JR7RFLWc"
199 | })
200 |
201 |
202 | },
203 |
204 | publicKey: function(req, res){
205 | let jwt = jsonwebtoken.sign({
206 | "account_id": account.accountId,
207 | "generated": 1731795408,
208 | "key_guid": "2e57bba7-4a7a-423c-b4b4-853acfcf019c",
209 | "kid": "20230621",
210 | "key": req.body.key,
211 | "expiration": "9999-12-31T23:59:59.999Z",
212 | "type": "legacy"
213 | },"EdDSA", {keyid:"20230621"})
214 | res.json({
215 | "key": req.body.key,
216 | "account_id": account.accountId,
217 | "key_guid": "2e57bba7-4a7a-423c-b4b4-853acfcf019c",
218 | "kid": "20230621",
219 | "expiration": "9999-12-31T23:59:59.999Z",
220 | "jwt": jwt,
221 | "type": "legacy"
222 | })
223 |
224 | },
225 |
226 | credentials: function(req, res){
227 | res.json({
228 | "username": "1742530227:00027b91959a4c57a1272efcc4d7480f",
229 | "password": crypto.randomBytes(16).toString("base64"),
230 | "ttl": 9999999,
231 | "uris": []
232 | })
233 | }
234 | }
--------------------------------------------------------------------------------
/api/controllers/CloudStorageController.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const crypto = require("crypto");
3 | const fs = require('fs');
4 | const hotfixPath = path.join(__dirname, '../../hotfixes/');
5 | const {getVersionInfo} = require("../../config/defs")
6 |
7 | module.exports = {
8 | cloudstoragesystem: async function (req, res) {
9 | const output = [];
10 | const dir = await fs.promises.opendir(hotfixPath);
11 | for await (const dirent of dir) {
12 | const fileName = dirent.name;
13 | const filePath = hotfixPath + fileName;
14 | const fileData = fs.readFileSync(filePath);
15 |
16 | output.push({
17 | "uniqueFilename": fileName,
18 | "filename": fileName,
19 | "hash": crypto.createHash("sha1").update(fileData).digest("hex"),
20 | "hash256": crypto.createHash("sha256").update(fileData).digest("hex"),
21 | "length": fileData.length,
22 | "contentType": "text/plain",
23 | "uploaded": fs.statSync(filePath).mtime,
24 | "storageType": "S3",
25 | "doNotCache": false
26 | });
27 | }
28 |
29 | res.json(output);
30 |
31 | },
32 |
33 | defaultGame: function(req, res){
34 | const {version, versionGlobal} = getVersionInfo(req);
35 | res.setHeader("content-type", "application/octet-stream")
36 | let DefaultGame = fs.readFileSync(path.join(__dirname, '../../hotfixes/DefaultGame.ini'), 'utf-8');
37 | if (versionGlobal >= 20) {
38 | DefaultGame = DefaultGame.replace(
39 | ";+CurveTable=/TacticalSprintGame/DataTables/TacticalSprintGameData;RowUpdate;Default.TacticalSprint.Sprint.Energy.CostPerSecond;0.0;0.0",
40 | "+CurveTable=/TacticalSprintGame/DataTables/TacticalSprintGameData;RowUpdate;Default.TacticalSprint.Sprint.Energy.CostPerSecond;0.0;0.0"
41 | );
42 | }
43 | const replacements = {
44 | "7.30": [
45 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Low, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
46 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Low, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
47 | ],
48 | "7.40": [
49 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_High, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
50 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_High, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
51 | ],
52 | "8.50": [
53 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Med, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
54 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Med, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
55 | ],
56 | "8.51": [
57 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Med, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
58 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Med, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
59 | ],
60 | "9.40": [
61 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Higher, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
62 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Higher, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
63 | ],
64 | "9.41": [
65 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Higher, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
66 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Higher, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
67 | ],
68 | "10.40": [
69 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Highest, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
70 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Highest, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
71 | ],
72 | "11.30": [
73 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Lowest, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
74 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_Lowest, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
75 | ],
76 | "12.41": [
77 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_High, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
78 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Music_High, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
79 | ],
80 | "12.61": [
81 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Fritter_64, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
82 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Fritter_64, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
83 | ],
84 | "14.60": [
85 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Junior_32, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))",
86 | "+FrontEndPlaylistData=(PlaylistName=Playlist_Junior_32, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))"
87 | ],
88 | };
89 |
90 | if (replacements[version]) {
91 | const [defaultvalue, replacedValue] = replacements[version];
92 | DefaultGame = DefaultGame.replace(defaultvalue, replacedValue);
93 | }
94 | res.send(DefaultGame)
95 | },
96 |
97 | config: function(req, res){
98 | return res.status(204).end()
99 | },
100 |
101 | defaultEngine: function(req, res){
102 | res.setHeader("content-type", "application/octet-stream")
103 | const {version} = getVersionInfo(req);
104 | let DefaultEngine = fs.readFileSync(path.join(__dirname, '../../hotfixes/DefaultEngine.ini'), 'utf-8');
105 | if (version == 32.11) {
106 | DefaultEngine = DefaultEngine.replace(
107 | ';Fort.Event.bForceOffLoadingScreen=1',
108 | 'Fort.Event.bForceOffLoadingScreen=1'
109 | );
110 | }
111 | res.send(DefaultEngine)
112 | },
113 |
114 | defaultRuntimeOptions: function(req, res){
115 | res.setHeader("content-type", "application/octet-stream")
116 | const {version} = getVersionInfo(req);
117 | let DefaultRuntimeOptions = fs.readFileSync(path.join(__dirname, '../../hotfixes/DefaultRuntimeOptions.ini'), 'utf-8');
118 | if (version == 26.20) {
119 | DefaultRuntimeOptions = DefaultRuntimeOptions.replace(
120 | ';+ExperimentalBucketPercentList=(ExperimentNum=27,Name="ShowMultiProductItemShop",BucketPercents=(100,0,0),WinningBucketIndex=-1)',
121 | '+ExperimentalBucketPercentList=(ExperimentNum=27,Name="ShowMultiProductItemShop",BucketPercents=(100,0,0),WinningBucketIndex=-1)'
122 | );
123 | }
124 | else if (version == 17.30) {
125 | DefaultRuntimeOptions = DefaultRuntimeOptions.replace(
126 | 'bEnableSocialTab=false',
127 | 'bEnableSocialTab=true'
128 | );
129 | }
130 | res.send(DefaultRuntimeOptions)
131 | },
132 |
133 | defaultInput: function(req, res){
134 | res.setHeader("content-type", "application/octet-stream")
135 | let DefaultInput = fs.readFileSync(path.join(__dirname, '../../hotfixes/DefaultInput.ini'), 'utf-8');
136 | res.send(DefaultInput)
137 | },
138 |
139 | user: function (req, res) {
140 | return res.json([])
141 | },
142 |
143 | userFile: function async(req, res, next) {
144 | res.status(200).send()
145 | },
146 |
147 | userPutFile:function (req, res, next) {
148 | res.status(200).send()
149 | },
150 | };
--------------------------------------------------------------------------------
/api/controllers/DiscoveryController.js:
--------------------------------------------------------------------------------
1 | const { default: axios } = require("axios");
2 | const { getVersionInfo, loadJSON } = require("../../config/defs")
3 | const discoveryv1 = loadJSON("../discovery/discoveryMenuV1.json");
4 | const discoveryv2 = loadJSON("../discovery/discoveryMenuV2.json")
5 | module.exports = {
6 |
7 | //first interation of discovery api
8 | discoveryv1: function(req, res){
9 | return res.json(discoveryv1);
10 | },
11 |
12 | //second interation of discovery api
13 | discoveryv2: function(req, res){
14 | const {version} = getVersionInfo(req);
15 | if(version >= 23.00){
16 | return res.json({
17 | "panels": [
18 | {
19 | "PanelName": "ByEpicNoBigBattle6Col",
20 | "Pages": [
21 | {
22 | "results": [
23 | {
24 | "lastVisited": null,
25 | "linkCode": "set_br_playlists",
26 | "isFavorite": false,
27 | "globalCCU": 1
28 | },
29 | {
30 | "lastVisited": null,
31 | "linkCode": "playlist_papaya",
32 | "isFavorite": false,
33 | "globalCCU": 1
34 | },
35 | ],
36 | "hasMore": false
37 | }
38 | ]
39 | }
40 | ],
41 | "testCohorts": [
42 | "testing"
43 | ]
44 | })}
45 | else{
46 | return res.json(discoveryv1);
47 | }
48 | },
49 |
50 | //thrid interation of discovery api - currently used
51 | discoveryv3: function(req, res){
52 | return res.json({
53 | "panels": [
54 | {
55 | "panelName": "Homebar",
56 | "panelDisplayName": "Homebar",
57 | "panelSubtitle": null,
58 | "featureTags": [
59 | "col:5",
60 | "homebar"
61 | ],
62 | "firstPage": {
63 | "results": [
64 | {
65 | "lastVisited": null,
66 | "linkCode": "reference_byepicnocompetitive_5",
67 | "isFavorite": false,
68 | "globalCCU": -1,
69 | "lockStatus": "UNLOCKED",
70 | "lockStatusReason": "NONE",
71 | "isVisible": true
72 | },
73 | ],
74 | "hasMore": true,
75 | "panelTargetName": null,
76 | "pageMarker": null
77 | },
78 | "panelType": "CuratedList",
79 | "playHistoryType": null
80 | },
81 | {
82 | "panelName": "ByEpicConvergenceBlastberry",
83 | "panelDisplayName": "By Epic",
84 | "panelSubtitle": "Islands created by Epic Games",
85 | "featureTags": [
86 | "col:5"
87 | ],
88 | "firstPage": {
89 | "results": [
90 | {
91 | "linkCode": "playlist_durian",
92 | "isFavorite": false,
93 | "globalCCU": 1,
94 | "lockStatus": "UNLOCKED",
95 | "lockStatusReason": "NONE",
96 | "isVisible": true
97 | },
98 | {
99 | "linkCode": "set_br_playlists",
100 | "isFavorite": false,
101 | "globalCCU": 1,
102 | "lockStatus": "UNLOCKED",
103 | "lockStatusReason": "NONE",
104 | "isVisible": true
105 | },
106 | {
107 | "linkCode": "playlist_beanstalk",
108 | "isFavorite": false,
109 | "globalCCU": 1,
110 | "lockStatus": "UNLOCKED",
111 | "lockStatusReason": "NONE",
112 | "isVisible": true
113 | },
114 | {
115 | "linkCode": "playlist_pilgrimquickplay",
116 | "isFavorite": false,
117 | "globalCCU": 1,
118 | "lockStatus": "UNLOCKED",
119 | "lockStatusReason": "NONE",
120 | "isVisible": true
121 | },
122 | {
123 | "linkCode": "playlist_juno",
124 | "isFavorite": false,
125 | "globalCCU": 1,
126 | "lockStatus": "UNLOCKED",
127 | "lockStatusReason": "NONE",
128 | "isVisible": true
129 | },
130 | {
131 | "lastVisited": null,
132 | "linkCode": "playlist_papaya",
133 | "isFavorite": false,
134 | "globalCCU": 1,
135 | "lockStatus": "UNLOCKED",
136 | "lockStatusReason": "NONE",
137 | "isVisible": true
138 | },
139 | {
140 | "lastVisited": null,
141 | "linkCode": "playlist_playgroundv2",
142 | "isFavorite": false,
143 | "globalCCU": 1,
144 | "lockStatus": "UNLOCKED",
145 | "lockStatusReason": "NONE",
146 | "isVisible": true
147 | }
148 | ],
149 | "hasMore": true,
150 | "panelTargetName": null,
151 | "pageMarker": null
152 | },
153 | "panelType": "AnalyticsList",
154 | "playHistoryType": null
155 | },
156 | {
157 | "panelName": "A-Spot",
158 | "panelDisplayName": "Remix: The Finale",
159 | "panelSubtitle": "Remix: The Finale",
160 | "featureTags": [
161 | "bannerItemRow"
162 | ],
163 | "firstPage": {
164 | "results": [
165 | {
166 | "lastVisited": null,
167 | "linkCode": "playlist_quail",
168 | "isFavorite": false,
169 | "globalCCU": -1,
170 | "lockStatus": "UNLOCKED",
171 | "lockStatusReason": "RATING_THRESHOLD",
172 | "isVisible": true,
173 | "favoriteStatus": "NONE"
174 | }
175 | ],
176 | "hasMore": false,
177 | "panelTargetName": null,
178 | "pageMarker": null
179 | },
180 | "panelType": "CuratedList",
181 | "playHistoryType": null,
182 | "panelContexts": {}
183 | },
184 | ]
185 | })
186 | },
187 |
188 | page: async function(req, res){
189 | /*const resultList = [];
190 | const results = (await axios.post('http://localhost:5595/api/v2/discovery/surface/CreativeDiscoverySurface_Frontend').catch(() => {})).data;
191 | results.panels.forEach(panel => {
192 | resultList.push(...panel.firstPage.results);
193 | });
194 | resultList.length = 0
195 | */
196 | res.json({
197 | "results": [],
198 | "hasMore": false,
199 | "panelTargetName": null,
200 | "pageMarker": null
201 | })
202 | //after implementing i realised its only ever good if you have a ton of ltms on one row where pages are needed, but for base neonite its not really an issue.
203 | },
204 |
205 | mnemonicLinks: function(req, res){
206 | const {version} = getVersionInfo(req);
207 | if(version >= 23.00){
208 | if (version > 27.11) {
209 | const durianIndex = discoveryv2.findIndex(i => i.mnemonic === "playlist_durian");
210 | discoveryv2[durianIndex].active = false;
211 | }
212 | if (version > 32.11) {
213 | const quailIndex = discoveryv2.findIndex(i => i.mnemonic === "playlist_quail");
214 | discoveryv2[quailIndex].active = false;
215 | }
216 | return res.json(discoveryv2)
217 | }
218 | else{
219 | const defaultResponse = discoveryv1.Panels[0].Pages[0].results.map(result => result.linkData);
220 | return res.json(defaultResponse);
221 | }
222 | },
223 |
224 | related: function(req, res){
225 | const relatedResponse = {
226 | parentLinks: [],
227 | links: {}
228 | }
229 | const findPlaylist = discoveryv2.findIndex(i => i.mnemonic === req.params.playlistId);
230 | if(discoveryv2[findPlaylist].metadata["sub_link_codes"]){
231 | relatedResponse.parentLinks.push(discoveryv2[findPlaylist])
232 | for (const subLinkCode of discoveryv2[findPlaylist].metadata.sub_link_codes) {
233 | const subPlaylist = discoveryv2.find(i => i.mnemonic === subLinkCode)
234 | relatedResponse.links[subLinkCode] = subPlaylist;
235 | }
236 | }
237 | else{
238 | relatedResponse.links[discoveryv2[findPlaylist].mnemonic] = discoveryv2[findPlaylist]
239 | }
240 | res.json(relatedResponse);
241 | },
242 |
243 |
244 | favoritesCheck: function(req, res){
245 | res.json({"results":[],"hasMore":false})
246 | },
247 |
248 | lockStatus: function(req, res){
249 | res.json({
250 | "results": [
251 | {
252 | "playerId": req.params.accountId,
253 | "linkCode": req.body["linkCodes"][0],
254 | "lockStatus": "UNLOCKED",
255 | "lockStatusReason": "NONE",
256 | "isVisible": true
257 | }
258 | ],
259 | "hasMore": false
260 | })
261 | },
262 |
263 | mnemonicPlaylist: function(req, res){
264 | const { version, versionGlobal } = getVersionInfo(req);
265 | if(versionGlobal <= 16){
266 | res.status(404).end();
267 | }
268 | else if(version >= 23.00){
269 | for (const result of discoveryv2) {
270 | if (result.mnemonic === req.params.playlistId) {
271 | return res.json(result);
272 | }
273 | }
274 | }
275 | else {
276 | for (const result of discoveryv1.Panels[0].Pages[0].results) {
277 | if (result.linkData.mnemonic === req.params.playlistId) {
278 | return res.json(result.linkData);
279 | }
280 | }
281 | }
282 | }
283 |
284 | }
285 |
--------------------------------------------------------------------------------
/api/controllers/LockerController.js:
--------------------------------------------------------------------------------
1 | const Profile = require("../../profile");
2 | const { v4: uuidv4 } = require("uuid");
3 | const NeoLog = require("../../structs/NeoLog");
4 | const fs = require("fs");
5 |
6 | module.exports = {
7 | lockerv3: async function(req, res){
8 | var accountId = req.params.accountId;
9 | const getOrCreateLockerProfile = () => {
10 | var lockerData = Profile.readLockerProfile(accountId, 3);
11 | if (!lockerData) {
12 | NeoLog.Error(`Locker Not Found for Account: ${accountId}, creating new Locker`);
13 | lockerData = Profile.readLockerTemplate(3);
14 |
15 | lockerData["activeLoadouts"].forEach(loadout => {
16 | loadout.creationTime = new Date().toISOString();
17 | loadout.accountId = accountId
18 | });
19 |
20 | if (!lockerData) {
21 | NeoLog.Error("An Error Occured Trying To Read Locker Template")
22 | }
23 | try {
24 | fs.mkdirSync(`./profile/${accountId}/profiles`, { recursive: true });
25 | Profile.saveLocker(accountId, 3, lockerData);
26 | } catch (e) {
27 | NeoLog.Error("Failed creating profile.");
28 | throw e;
29 | }
30 |
31 | }
32 | return res.json(lockerData)
33 |
34 | };
35 | getOrCreateLockerProfile()
36 |
37 | },
38 |
39 | lockerLoadoutV3: async function(req, res){
40 | var accountId = req.params.accountId;
41 | var lockerData = Profile.readLockerProfile(accountId, 3);
42 | const activeLoadout = lockerData["activeLoadouts"].find(loadout => loadout.loadoutType === req.params.loadoutType);
43 |
44 | if (!activeLoadout) {
45 | NeoLog.Error(`Invalid Loadout: ${req.params.loadoutType}`)
46 | }
47 | switch(req.params.loadout) {
48 | case "active-loadout":
49 | activeLoadout["loadoutSlots"] = req.body["loadoutSlots"];
50 | activeLoadout["updatedTime"] = new Date().toISOString();
51 | Profile.saveLocker(accountId, 3, lockerData);
52 | break;
53 |
54 | default:
55 | return NeoLog.Error("Invalid Loadout");
56 | }
57 |
58 | return res.json({
59 | "accountId": accountId,
60 | "athenaItemId": req.body["athenaItemId"],
61 | "creationTime": activeLoadout["creationTime"],
62 | "deploymentId": req.params.deploymentId,
63 | "loadoutShuffleType": activeLoadout["loadoutShuffleType"],
64 | "loadoutSlots": req.body["loadoutSlots"],
65 | "loadoutType": req.params.loadoutType,
66 | "updatedTime": activeLoadout["updatedTime"]
67 | })
68 | },
69 |
70 | lockerPresetV3: async function(req, res){
71 | var accountId = req.params.accountId;
72 | var lockerData = Profile.readLockerProfile(accountId, 3);
73 | let displayName = req.body["displayName"] || "";
74 |
75 | let existingPreset = lockerData["loadoutPresets"].find(preset =>
76 | preset.presetIndex === parseInt(req.params.presetIndex)
77 | );
78 |
79 | if (existingPreset) {
80 | existingPreset.athenaItemId = req.body.athenaItemId;
81 | existingPreset.loadoutSlots = req.body.loadoutSlots;
82 | existingPreset.updatedTime = new Date().toISOString();
83 | if (displayName) {
84 | existingPreset.displayName = displayName; //display name doesnt exist in the first request but most of the time does in the second request
85 | }
86 | Profile.saveLocker(accountId, 3, lockerData);
87 | res.json({
88 | "deploymentId": req.params.deploymentId,
89 | "accountId": accountId,
90 | "loadoutType": req.params.loadoutType,
91 | "presetId": existingPreset.presetId,
92 | "presetIndex": existingPreset.presetIndex,
93 | "athenaItemId": req.body.athenaItemId,
94 | "creationTime": existingPreset.creationTime,
95 | "updatedTime": existingPreset.updatedTime,
96 | "loadoutSlots": req.body.loadoutSlots,
97 | "displayName": displayName,
98 | "presetFavoriteStatus": "EMPTY"
99 | });
100 | }
101 | else {
102 | const presetId = uuidv4();
103 | lockerData["loadoutPresets"].push({
104 | "deploymentId": req.params.deploymentId,
105 | "accountId": accountId,
106 | "loadoutType": req.params.loadoutType,
107 | "presetId": presetId,
108 | "presetIndex": parseInt(req.params.presetIndex),
109 | "athenaItemId": req.body.athenaItemId,
110 | "creationTime": new Date().toISOString(),
111 | "updatedTime": new Date().toISOString(),
112 | "loadoutSlots": req.body.loadoutSlots,
113 | "displayName": displayName,
114 | "presetFavoriteStatus": "EMPTY"
115 | });
116 | Profile.saveLocker(accountId, 3, lockerData);
117 | return res.json({
118 | "deploymentId": req.params.deploymentId,
119 | "accountId": accountId,
120 | "loadoutType": req.params.loadoutType,
121 | "presetId": presetId,
122 | "presetIndex": parseInt(req.params.presetIndex),
123 | "athenaItemId": req.body.athenaItemId,
124 | "creationTime": new Date().toISOString(),
125 | "updatedTime": new Date().toISOString(),
126 | "loadoutSlots": req.body.loadoutSlots,
127 | "displayName": displayName,
128 | "presetFavoriteStatus": "EMPTY"
129 | });
130 | }
131 |
132 | },
133 |
134 | lockerv4: async function(req, res){
135 | var accountId = req.params.accountId;
136 | const getOrCreateLockerProfile = () => {
137 | var lockerData = Profile.readLockerProfile(accountId, 4);
138 | if (!lockerData) {
139 | NeoLog.Error(`Locker Not Found for Account: ${accountId}, creating new Locker`);
140 | lockerData = Profile.readLockerTemplate(4);
141 |
142 | lockerData["activeLoadoutGroup"].accountId = accountId
143 | lockerData["activeLoadoutGroup"].creationTime = new Date().toISOString();
144 | lockerData["activeLoadoutGroup"].updatedTime = new Date().toISOString()
145 |
146 | if (!lockerData) {
147 | NeoLog.Error("An Error Occured Trying To Read Locker Data")
148 | }
149 | try {
150 | fs.mkdirSync(`./profile/${accountId}/profiles`, { recursive: true });
151 | Profile.saveLocker(accountId, 4, lockerData);
152 | } catch (e) {
153 | NeoLog.Error("Failed creating profile.");
154 | throw e;
155 | }
156 |
157 | }
158 | return res.json(lockerData)
159 |
160 | };
161 | getOrCreateLockerProfile()
162 |
163 | },
164 |
165 | lockerLoadoutV4: function(req, res){
166 | var accountId = req.params.accountId;
167 | var lockerData = Profile.readLockerProfile(accountId, 4);
168 | if(req.body.equippedPresetId){
169 | lockerData["activeLoadoutGroup"].equippedPresetId = req.body.equippedPresetId
170 | }
171 | else if(!req.body.equippedPresetId){
172 | delete lockerData["activeLoadoutGroup"].equippedPresetId
173 | }
174 | lockerData["activeLoadoutGroup"].updatedTime = new Date().toISOString()
175 | lockerData["activeLoadoutGroup"].loadouts = req.body["loadouts"]
176 | Profile.saveLocker(accountId, 4, lockerData)
177 | return res.json(lockerData["activeLoadoutGroup"])
178 |
179 | },
180 |
181 | lockerGroupPresetV4: function(req, res){
182 | var accountId = req.params.accountId;
183 | var lockerData = Profile.readLockerProfile(accountId, 4);
184 | let displayName = req.body["displayName"]
185 | const presetId = uuidv4();
186 |
187 | let existingPreset = lockerData["loadoutGroupPresets"].find(preset =>
188 | preset.presetIndex === parseInt(req.params.presetIndex)
189 | );
190 | if(existingPreset){
191 | existingPreset.athenaItemId = req.body.athenaItemId;
192 | existingPreset.loadoutSlots = req.body.loadouts;
193 | existingPreset.updatedTime = new Date().toISOString();
194 | existingPreset.displayName = displayName;
195 | Profile.saveLocker(accountId, 4, lockerData);
196 | }
197 | else{
198 | lockerData["loadoutGroupPresets"].push({
199 | "accountId": accountId,
200 | "athenaItemId": req.body.athenaItemId,
201 | "creationTime": new Date().toISOString(),
202 | "deploymentId": req.params.deploymentId,
203 | "displayName": displayName,
204 | "loadouts": req.body.loadouts,
205 | "presetFavoriteStatus": "EMPTY",
206 | "presetId": presetId,
207 | "presetIndex": parseInt(req.params.presetIndex)
208 | });
209 | Profile.saveLocker(accountId, 4, lockerData);
210 | }
211 | return res.json({
212 | "accountId": accountId,
213 | "athenaItemId": req.body.athenaItemId,
214 | "creationTime": new Date().toISOString(),
215 | "deploymentId": req.params.deploymentId,
216 | "displayName": displayName,
217 | "loadouts": req.body.loadouts,
218 | "presetFavoriteStatus": "EMPTY",
219 | "presetId": presetId,
220 | "updatedTime": new Date().toISOString(),
221 | });
222 | },
223 |
224 |
225 | //will be implemented sometime in the future
226 | lockerPresetV4: async function(req, res){
227 | var accountId = req.params.accountId;
228 | var lockerData = Profile.readLockerProfile(accountId, 4);
229 | let displayName = req.body["displayName"] || "";
230 |
231 | let existingPreset = lockerData["loadoutPresets"].find(preset =>
232 | preset.presetIndex === parseInt(req.params.presetIndex) && preset.loadoutType == req.params.loadoutType
233 | );
234 |
235 | if (existingPreset) {
236 | existingPreset.athenaItemId = req.body.athenaItemId;
237 | existingPreset.loadoutSlots = req.body.loadoutSlots;
238 | existingPreset.updatedTime = new Date().toISOString();
239 | if (displayName) {
240 | existingPreset.displayName = displayName;
241 | }
242 | Profile.saveLocker(accountId, 4, lockerData);
243 | res.json({
244 | "deploymentId": req.params.deploymentId,
245 | "accountId": accountId,
246 | "loadoutType": req.params.loadoutType,
247 | "presetId": existingPreset.presetId,
248 | "presetIndex": existingPreset.presetIndex,
249 | "athenaItemId": req.body.athenaItemId,
250 | "creationTime": existingPreset.creationTime,
251 | "updatedTime": existingPreset.updatedTime,
252 | "loadoutSlots": req.body.loadoutSlots,
253 | "displayName": displayName,
254 | "presetFavoriteStatus": "EMPTY"
255 | });
256 | }
257 | else {
258 | const presetId = uuidv4();
259 | lockerData["loadoutPresets"].push({
260 | "deploymentId": req.params.deploymentId,
261 | "accountId": accountId,
262 | "loadoutType": req.params.loadoutType,
263 | "presetId": presetId,
264 | "presetIndex": parseInt(req.params.presetIndex),
265 | "athenaItemId": req.body.athenaItemId,
266 | "creationTime": new Date().toISOString(),
267 | "updatedTime": new Date().toISOString(),
268 | "loadoutSlots": req.body.loadoutSlots,
269 | "displayName": displayName,
270 | "presetFavoriteStatus": "EMPTY"
271 | });
272 | Profile.saveLocker(accountId, 4, lockerData);
273 | return res.json({
274 | "deploymentId": req.params.deploymentId,
275 | "accountId": accountId,
276 | "loadoutType": req.params.loadoutType,
277 | "presetId": presetId,
278 | "presetIndex": parseInt(req.params.presetIndex),
279 | "athenaItemId": req.body.athenaItemId,
280 | "creationTime": new Date().toISOString(),
281 | "updatedTime": new Date().toISOString(),
282 | "loadoutSlots": req.body.loadoutSlots,
283 | "displayName": displayName,
284 | "presetFavoriteStatus": "EMPTY"
285 | });
286 | }
287 |
288 | },
289 | }
--------------------------------------------------------------------------------
/api/controllers/MatchMakingController.js:
--------------------------------------------------------------------------------
1 | const {
2 | ApiException
3 | } = require('../../structs/errors');
4 | const NeoLog = require('../../structs/NeoLog')
5 | const {getVersionInfo} = require("../../config/defs")
6 |
7 | module.exports = {
8 |
9 | matchmakingTicket: function(req, res){
10 | const {version} = getVersionInfo(req);
11 | var ParsedBckt = {
12 | NetCL: "",
13 | Region: "",
14 | Playlist: "",
15 | HotfixVerion: -1
16 | }
17 |
18 | try {
19 | var splitted = req.query.bucketId.split(':');
20 | ParsedBckt.NetCL = splitted[0];
21 | ParsedBckt.HotfixVerion = splitted[1];
22 | ParsedBckt.Region = splitted[2];
23 | ParsedBckt.Playlist = splitted[3];
24 | }
25 | catch {
26 | throw new ApiException(errors.com.epicgames.fortnite.invalid_bucket_id);
27 | }
28 | finally {
29 | if (ParsedBckt.NetCL === "" || ParsedBckt.Region === "" || ParsedBckt.Playlist === "" || ParsedBckt.Region === -1) {
30 | throw new ApiException(errors.com.epicgames.fortnite.invalid_bucket_id).withMessage(`Failed to parse bucketId: '${req.query.bucketId}'`).with(req.query.bucketId)
31 | }
32 | }
33 | res.cookie("NetCL", ParsedBckt.NetCL);
34 | var data = {
35 | "playerId": req.params.accountId,
36 | "partyPlayerIds": [
37 | req.params.accountId,
38 | ],
39 | "bucketId": `FN:Live:${ParsedBckt.NetCL}:${ParsedBckt.HotfixVerion}:${ParsedBckt.Region}:${ParsedBckt.Playlist}:PC:public:1`,
40 | "attributes": {
41 | "player.userAgent": req.headers["user-agent"],
42 | "player.preferredSubregion": "None",
43 | "player.option.spectator": "false",
44 | "player.inputTypes": "",
45 | "playlist.revision": "1",
46 | "player.teamFormat": "fun"
47 | },
48 | "expireAt": new Date().addHours(1),
49 | "nonce": RandomString(32)
50 | }
51 | Object.entries(req.query).forEach(([key, value]) => {
52 | if (key == "player.subregions" && value.includes(',')) {
53 | data.attributes["player.preferredSubregion"] = value.split(',')[0];
54 | }
55 | data.attributes[key] = value;
56 | });
57 | var payload = Buffer.from(JSON.stringify(data, null, 0)).toString('base64');
58 | NeoLog.Log(`Matchmaking into ${ParsedBckt.Playlist}`)
59 | res.json({
60 | "serviceUrl": "ws://localhost:5595",
61 | "ticketType": "mms-player",
62 | "payload": payload,
63 | "signature": undefined
64 | });
65 | },
66 |
67 | matchmakingSession: function(req, res){
68 | res.json({
69 | "accountId": req.params.accountId,
70 | "sessionId": req.params.sessionId,
71 | "key": "none"
72 | })
73 | },
74 |
75 | matchmakingSession2: function(req, res){
76 | var BuildUniqueId = req.cookies["NetCL"];
77 | res.json({
78 | "id": req.params.sessionId,
79 | "ownerId": "Neonite",
80 | "ownerName": "Neonite",
81 | "serverName": "NeoniteV2",
82 | "serverAddress": "127.0.0.1",
83 | "serverPort": -1,
84 | "totalPlayers": 0,
85 | "maxPublicPlayers": 0,
86 | "openPublicPlayers": 0,
87 | "maxPrivatePlayers": 0,
88 | "openPrivatePlayers": 0,
89 | "attributes": {},
90 | "publicPlayers": [],
91 | "privatePlayers": [],
92 | "allowJoinInProgress": false,
93 | "shouldAdvertise": false,
94 | "isDedicated": false,
95 | "usesStats": false,
96 | "allowInvites": false,
97 | "usesPresence": false,
98 | "allowJoinViaPresence": true,
99 | "allowJoinViaPresenceFriendsOnly": false,
100 | "buildUniqueId": BuildUniqueId || "00000000",
101 | "lastUpdated": "2020-11-09T00:40:28.878Z",
102 | "started": false
103 | });
104 | },
105 |
106 | matchmakingSessionJoin: function(req, res){
107 | res.status(204).end()
108 | },
109 |
110 | waitingRoom: function(req, res){
111 | res.status(204).end();
112 | },
113 |
114 | findPlayer: function(req, res){
115 | res.json([])
116 | },
117 |
118 | verifyMatch: function(req, res){
119 | res.json({
120 | "account_id": req.body.account_id,
121 | "data": req.body.data,
122 | "allow":true
123 | })
124 | }
125 | }
126 |
127 | function RandomString(length) {
128 | var result = [];
129 | var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
130 | var charactersLength = characters.length;
131 | for (var i = 0; i < length; i++) {
132 | result.push(characters.charAt(Math.floor(Math.random() *
133 | charactersLength)));
134 | }
135 | return result.join('');
136 | }
--------------------------------------------------------------------------------
/api/controllers/PlayerController.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | parties: function(req, res){
4 | res.status(204).end();
5 | },
6 |
7 | localparty: function(req, res){
8 | res.json({
9 | "current": [],
10 | "pending": [],
11 | "invites": [],
12 | "pings": []
13 | })
14 | },
15 |
16 | partyMeta: function(req, res){
17 | res.status(204).end()
18 | },
19 |
20 | pings: function(req, res){
21 | res.json({
22 | sent_by: req.params.pingerId,
23 | sent_to: req.params.accountId,
24 | sent_at: new Date(),
25 | expires_at: new Date().addHours(1),
26 | meta: {}
27 | })
28 | },
29 |
30 | friendsSettings: function(req, res){
31 | res.json({
32 | acceptInvites: "public"
33 | })
34 | },
35 |
36 | friendsSummary: function(req, res){
37 | res.json({
38 | "friends": [{
39 | "accountId": req.params.accountId,
40 | "groups": [],
41 | "mutual": 0,
42 | "alias": "",
43 | "note": "",
44 | "favorite": true,
45 | "created": "2021-01-17T16:42:04.125Z"
46 | }],
47 | "incoming": [],
48 | "suggested": [],
49 | "blocklist": [],
50 | "settings": {
51 | "acceptInvites": "public"
52 | },
53 | "limitsReached": {
54 | "incoming": false,
55 | "outgoing": false,
56 | "accepted": false
57 | }
58 | })
59 | },
60 |
61 | friends: function(req, res){
62 | res.json([
63 | {
64 | accountId: req.params.accountId,
65 | status: 'ACCEPTED',
66 | direction: 'INBOUND',
67 | created: '2018-12-06T04:46:01.296Z',
68 | favorite: false
69 | }
70 | ]);
71 | },
72 |
73 | recentPlayers: function(req, res){
74 | res.json([])
75 | },
76 |
77 | stats: function(req, res){
78 | res.json({
79 | "startTime": 1698908409,
80 | "endTime": 1735714808,
81 | "stats": {
82 | "br_score_gamepad_m0_playlist_habanerosolo": 1871,
83 | "s27_social_bp_level": 1248,
84 | "br_matchesplayed_keyboardmouse_m0_playlist_playgroundv2": 1,
85 | "br_placetop10_gamepad_m0_playlist_habanerosolo": 1,
86 | "br_score_gamepad_m0_playlist_bots_defaultsquad": 3445,
87 | "br_lastmodified_keyboardmouse_m0_playlist_defaultsolo": 1699124004,
88 | "br_lastmodified_gamepad_m0_playlist_habanerosolo": 1699214141,
89 | "br_minutesplayed_keyboardmouse_m0_playlist_bots_defaultsquad": 1,
90 | "br_score_keyboardmouse_m0_playlist_playgroundv2": 17,
91 | "br_kills_gamepad_m0_playlist_bots_defaultsquad": 95,
92 | "br_score_keyboardmouse_m0_playlist_defaultsolo": 76,
93 | "br_matchesplayed_gamepad_m0_playlist_habanerosolo": 5,
94 | "br_minutesplayed_gamepad_m0_playlist_habanerosolo": 59,
95 | "br_playersoutlived_keyboardmouse_m0_playlist_bots_defaultsquad": 1,
96 | "br_placetop3_gamepad_m0_playlist_bots_defaultsquad": 5,
97 | "br_placetop6_gamepad_m0_playlist_bots_defaultsquad": 5,
98 | "br_matchesplayed_gamepad_m0_playlist_bots_defaultsquad": 5,
99 | "br_kills_gamepad_m0_playlist_habanerosolo": 30,
100 | "br_score_keyboardmouse_m0_playlist_bots_defaultsquad": 34,
101 | "br_lastmodified_gamepad_m0_playlist_bots_defaultsquad": 1699198715,
102 | "br_playersoutlived_gamepad_m0_playlist_habanerosolo": 376,
103 | "br_matchesplayed_keyboardmouse_m0_playlist_bots_defaultsquad": 2,
104 | "br_lastmodified_keyboardmouse_m0_playlist_playgroundv2": 1699015811,
105 | "br_placetop25_keyboardmouse_m0_playlist_defaultsolo": 1,
106 | "br_playersoutlived_gamepad_m0_playlist_bots_defaultsquad": 490,
107 | "br_placetop25_gamepad_m0_playlist_habanerosolo": 2,
108 | "br_lastmodified_keyboardmouse_m0_playlist_bots_defaultsquad": 1699094790,
109 | "br_placetop1_gamepad_m0_playlist_bots_defaultsquad": 5,
110 | "br_matchesplayed_keyboardmouse_m0_playlist_defaultsolo": 3,
111 | "br_minutesplayed_gamepad_m0_playlist_bots_defaultsquad": 101
112 | },
113 | "accountId": req.params.accountId
114 | })
115 | },
116 |
117 | presence: function(req, res){
118 | res.json([])
119 | },
120 |
121 | receipts: function(req, res){
122 | res.json([])
123 | },
124 |
125 | blocklist: function(req, res){
126 | res.json({
127 | blockedUsers: []
128 | })
129 | }
130 | }
--------------------------------------------------------------------------------
/api/controllers/ProfileController.js:
--------------------------------------------------------------------------------
1 | const Profile = require("../../profile");
2 | const errors = require("../../structs/errors");
3 | const { ApiException } = errors;
4 | const fs = require("fs");
5 | const { v4: uuidv4 } = require("uuid");
6 | const path = require('path');
7 | var ini = require('ini')
8 | const { getVersionInfo, MPLockerLoadout, CH1Fix, VersionFilter, loadJSON, stats, seasonPass, winterFest, winterFestPresents} = require("../../config/defs")
9 | let miniPassData = loadJSON("../config/MiniPass.json")
10 | Array.prototype.insert = function ( index, item ) {
11 | this.splice( index, 0, item );
12 | };
13 | const NeoLog = require("../../structs/NeoLog");
14 | var config = ini.parse(fs.readFileSync(path.join(__dirname, '../../config.ini'), 'utf-8'));
15 |
16 |
17 | module.exports = {
18 | mcp: function(req, res, next){
19 | res.setHeader("Content-Type", "application/json");
20 | var accountId = req.params.accountId;
21 | var athenprofile = Profile.readProfile(accountId, "athena")
22 | var commoncore = Profile.readProfile(accountId, "common_core")
23 | const { version, versionGlobal } = getVersionInfo(req);
24 | const getOrCreateProfile = profileId => {
25 | var profileData = Profile.readProfile(accountId, profileId);
26 |
27 | if (!profileData) {
28 | profileData = Profile.readProfileTemplate(profileId);
29 |
30 | if (!profileData) {
31 | NeoLog.Error("Failed to read profile template");
32 | throw next(new ApiException(errors.com.epicgames.modules.profiles.operation_forbidden).with(profileId));
33 | }
34 |
35 | profileData.created = profileData.updated = new Date().toISOString();
36 | profileData['_id'] = accountId;
37 | profileData.accountId = accountId;
38 |
39 | //creating profile if it doesn't exist
40 | try {
41 | fs.mkdirSync(`./profile/${accountId}/profiles`, { recursive: true });
42 | Profile.saveProfile(accountId, profileId, profileData);
43 | NeoLog.Log(`Created ${profileId} profile for ${accountId}`);
44 | } catch (e) {
45 | NeoLog.Error("Failed creating profile.");
46 | throw e;
47 | }
48 |
49 | }
50 |
51 | return {
52 | profileData,
53 | response: {
54 | "profileRevision": profileData.rvn || 1,
55 | "profileId": profileId,
56 | "profileChangesBaseRevision": profileData.rvn || 1,
57 | "profileChanges": [],
58 | "serverTime": new Date().toISOString(),
59 | "profileCommandRevision": profileData.commandRevision || 1,
60 | "responseVersion": 1
61 | }
62 | };
63 |
64 |
65 | };
66 | getOrCreateProfile("athena")
67 | var command = req.params.command;
68 | var profileId = req.query.profileId || "common_core";
69 | const { profileData, response } = getOrCreateProfile(profileId);
70 | const { profileChanges } = response;
71 | //const checkValidProfileID = (...validProfileIds) => checkValidProfileID0(command, profileId, next, ...validProfileIds); //not sure if ill need it but ill keep it just incase
72 |
73 | switch(command){
74 |
75 | case "CopyCosmeticLoadout": {
76 | //sourceIndex = 0 (Save)
77 | //sourceIndex > 0 (Load)
78 | let item;
79 |
80 | if (req.body.sourceIndex == 0) {
81 | const last_applied_loadout = profileData.stats.attributes["last_applied_loadout"]
82 | item = profileData.items[`neoset${req.body.targetIndex}_loadout`];
83 | profileData.items[`neoset${req.body.targetIndex}_loadout`] = profileData.items[last_applied_loadout];
84 | profileData.items[`neoset${req.body.targetIndex}_loadout`].attributes["locker_name"] = req.body.optNewNameForTarget;
85 | profileData.stats.attributes.loadouts[req.body.targetIndex] = `neoset${req.body.targetIndex}_loadout`;
86 | } else {
87 | item = profileData.items[`neoset${req.body.sourceIndex}_loadout`];
88 |
89 | if (!item) {
90 | throw next(new ApiException(errors.com.epicgames.fortnite.item_not_found).withMessage("Locker item {0} not found", req.body.lockerItem));
91 | }
92 |
93 | profileData.stats.attributes["active_loadout_index"] = req.body.sourceIndex;
94 | profileData.stats.attributes["last_applied_loadout"] = `neoset${req.body.sourceIndex}_loadout`;
95 | profileData.items["sandbox_loadout"].attributes["locker_slots_data"] = item.attributes["locker_slots_data"];
96 | }
97 | Profile.bumpRvn(profileData);
98 | response.profileRevision = profileData.rvn || 1;
99 | response.profileCommandRevision = profileData.commandRevision || 1
100 | Profile.saveProfile(accountId, profileId, profileData)
101 | response.profileChanges = [{
102 | "changeType": "fullProfileUpdate",
103 | "profile": profileData
104 | }];
105 | break;
106 | }
107 |
108 | case "DeleteCosmeticLoadout": {
109 | profileData.stats.attributes.loadouts[req.body.index] = "";
110 | Profile.bumpRvn(profileData);
111 | response.profileRevision = profileData.rvn || 1;
112 | response.profileCommandRevision = profileData.commandRevision || 1
113 | Profile.saveProfile(accountId, profileId, profileData)
114 | response.profileChanges = [{
115 | "changeType": "fullProfileUpdate",
116 | "profile": profileData
117 | }];
118 | break;
119 | }
120 |
121 | case "SetCosmeticLockerName": {
122 | const item = profileData.items[req.body.lockerItem];
123 |
124 | if (!item) {
125 | throw next(new ApiException(errors.com.epicgames.fortnite.item_not_found).withMessage("Locker item {0} not found", req.body.lockerItem));
126 | }
127 |
128 | if (typeof req.body.name === "string" && item.attributes.locker_name != req.body.name) {
129 | Profile.changeItemAttribute(profileData, req.body.lockerItem, "locker_name", req.body.name, profileChanges);
130 | }
131 | break;
132 | }
133 |
134 | case "SetRandomCosmeticLoadoutFlag": {
135 | break;
136 | }
137 |
138 | case "SetForcedIntroPlayed":{
139 | //{ forcedIntroName: 'Coconut' }
140 | response.profileChanges = [{
141 | "changeType": "fullProfileUpdate",
142 | "profile": profileData
143 | }];
144 | }
145 |
146 | case "RequestRestedStateIncrease":{
147 | var xpValue = profileData.stats.attributes["book_xp"] + req.body.restedXpGenAccumulated
148 |
149 | Profile.bumpRvn(profileData);
150 | response.profileRevision = profileData.rvn || 1;
151 | response.profileCommandRevision = profileData.commandRevision || 1
152 | Profile.modifyStat(profileData, "book_xp", xpValue)
153 |
154 | response.profileChanges = [{
155 | "changeType" : "statModified",
156 | "name" : "book_xp",
157 | "value" : xpValue
158 |
159 | }]
160 | break;
161 | }
162 |
163 | case "GetMcpTimeForLogin":{
164 | response.profileChanges = [{
165 | "changeType": "fullProfileUpdate",
166 | "profile": profileData
167 | }];
168 | break
169 | }
170 |
171 | case "IncrementNamedCounterStat":{
172 | break
173 | }
174 |
175 | case "ClientQuestLogin": {
176 | response.profileChanges = [{
177 | "changeType": "fullProfileUpdate",
178 | "profile": profileData
179 | }]
180 | break;
181 | }
182 |
183 | case "QuestLogin":{
184 | break;
185 | }
186 |
187 | case "AthenaPinQuest":{
188 | Profile.modifyStat(athenprofile, "pinned_quest", req.body.pinnedQuest)
189 | Profile.bumpRvn(profileData);
190 | response.profileRevision = profileData.rvn || 1;
191 | response.profileCommandRevision = profileData.commandRevision || 1
192 | response.profileChanges = [{
193 | "changeType" : "statModified",
194 | "name" : "pinned_quest",
195 | "value" : req.body.pinnedQuest
196 |
197 | }]
198 | break;
199 | }
200 |
201 | case "MarkNewQuestNotificationSent":{
202 | break;
203 | }
204 |
205 | case "MarkItemSeen": {
206 | req.body.itemIds.forEach(itemId => Profile.changeItemAttribute(profileData, itemId, "item_seen", true, profileChanges));
207 | break;
208 | }
209 |
210 | case "PopulatePrerolledOffers": {
211 | break;
212 | }
213 |
214 | case "PurchaseCatalogEntry": {
215 | const commoncore = Profile.readProfile(accountId, "common_core");
216 | let shop
217 | if (version >= 30.10) {
218 | shop = loadJSON("../responses/catalog/shopv3.json");
219 | }
220 | else if (version >= 26.30) {
221 | shop = loadJSON("../responses/catalog/shopv2.json");
222 | }
223 | else {
224 | shop = loadJSON("../responses/catalog/shopv1.json");
225 | }
226 |
227 |
228 | let catalogEntryToPurchase = null;
229 | for (let storefront of shop.storefronts) {
230 | /*if (!storefront.name.startsWith("BR")) {
231 | throw new Error("Unsupported");
232 | }*/
233 |
234 | for (catalogEntry of storefront.catalogEntries) {
235 | if (catalogEntry.offerId == req.body.offerId) {
236 | catalogEntryToPurchase = catalogEntry;
237 | }
238 | }
239 | }
240 |
241 |
242 | if (catalogEntryToPurchase == null) {
243 | throw next(new ApiException(errors.com.epicgames.modules.gamesubcatalog.catalog_out_of_date).with(req.body.offerId));
244 | }
245 |
246 | let grantToProfileId = "athena";
247 | const grantProfile = getOrCreateProfile(grantToProfileId);
248 | const lootResult = [];
249 |
250 | for (itemGrant of catalogEntryToPurchase.itemGrants) {
251 | lootResult.push({
252 | "itemType": itemGrant.templateId,
253 | "itemGuid": itemGrant.templateId,
254 | "itemProfile": grantToProfileId,
255 | "quantity": itemGrant.quantity
256 | });
257 | }
258 |
259 | commoncore.stats.attributes["mtx_purchase_history"] = {
260 | "refundsUsed" : 0,
261 | "refundCredits" : 3,
262 | "tokenRefreshReferenceTime" : "2023-10-12T00:00:00.000Z",
263 | "purchases" : [ {
264 | "purchaseId" : "cc8442a6-77b0-45c7-9c14-6dca6d5cfefe",
265 | "offerId" : "v2:/b0ddecc601a1d316ed24a6fbce4297d931599dfcb16fc9c4bd9ef646f0a3a843",
266 | "purchaseDate" : new Date().toISOString(),
267 | "undoTimeout" : "9999-11-01T17:50:35.861Z",
268 | "freeRefundEligible" : true,
269 | "fulfillments" : [ ],
270 | "lootResult" : [ {
271 | "itemType" : catalogEntryToPurchase.itemGrants.templateId,
272 | "itemGuid" : catalogEntryToPurchase.itemGrants.templateId,
273 | "itemProfile" : catalogEntryToPurchase.itemGrants.itemProfile,
274 | "quantity" : catalogEntryToPurchase.itemGrants.quantity
275 | }
276 | ],
277 | "totalMtxPaid" : req.body["expectedTotalPrice"],
278 | "metadata" : {},
279 | "gameContext" : ""
280 | }]
281 | }
282 | Profile.saveProfile(accountId, "common_core", commoncore)
283 |
284 |
285 | // add creation_time because kyiro had a heartattack when it wasnt their
286 | for (lootResultEntry of lootResult) {
287 | Profile.addItem(grantProfile.profileData, lootResultEntry.itemGuid, {
288 | templateId: lootResultEntry.itemType,
289 | attributes: {
290 | "max_level_bonus": 0,
291 | "level": 1,
292 | "item_seen": false,
293 | "xp": 0,
294 | "variants": [],
295 | "creation_time": new Date().toISOString(),
296 | "favorite": false
297 | },
298 | quantity: lootResultEntry.quantity
299 | }, grantProfile.response.profileChanges);
300 | }
301 |
302 | response.notifications = [
303 | {
304 | "type": "CatalogPurchase",
305 | "primary": true,
306 | "lootResult": {
307 | "items": lootResult
308 | }
309 | }
310 | ];
311 |
312 | if (grantProfile.response.profileChanges.length > 0) {
313 | Profile.bumpRvn(grantProfile.profileData);
314 | response.profileRevision = grantProfile.profileData.rvn || 1;
315 | response.profileCommandRevision = grantProfile.profileData.commandRevision || 1;
316 | Profile.saveProfile(accountId, grantToProfileId, grantProfile.profileData);
317 | }
318 | var athenaProfile = getOrCreateProfile("athena");
319 |
320 | athenaProfile.response.profileChanges = [
321 | {
322 | changeType: "fullProfileUpdate",
323 | profile: athenaProfile.profileData
324 | }
325 | ]
326 |
327 | response.multiUpdate = [athenaProfile.response]
328 | break;
329 | }
330 |
331 | case "BulkEquipBattleRoyaleCustomization":{
332 | break;
333 | }
334 |
335 | case "RefreshExpeditions": {
336 | response.profileChanges = [{
337 | "changeType": "fullProfileUpdate",
338 | "profile": profileData
339 | }];
340 | break;
341 | }
342 |
343 | case "SetItemArchivedStatusBatch": {
344 | req.body.itemIds.forEach(itemId => {
345 | if (typeof itemId === "string" && typeof req.body.archived === "boolean") {
346 | Profile.changeItemAttribute(profileData, itemId, "archived", req.body.archived, profileChanges);
347 | }
348 | });
349 | Profile.bumpRvn(athenprofile)
350 |
351 | break;
352 | }
353 |
354 | case "ClaimMfaEnabled": {
355 | profileData.stats.attributes["mfa_reward_claimed"] = true;
356 | response.profileChanges = [{
357 | "changeType": "fullProfileUpdate",
358 | "profile": profileData
359 | }];
360 | break;
361 | }
362 |
363 | case "SetHardcoreModifier":{
364 | break;
365 | }
366 |
367 | case "QueryProfile":{
368 | getOrCreateProfile(`${profileId}`)
369 | if(profileId == "athena"){
370 | stats(accountId, athenprofile, config, versionGlobal)
371 | if(versionGlobal >= 33){seasonPass(accountId, athenprofile, versionGlobal)}
372 | if(version == 33.11 || version == 23.10 || version == 19.01 || version == 11.31){winterFest(accountId, athenprofile)}
373 | for(const [questId, quest] of Object.entries(miniPassData))
374 | {
375 | Profile.addItem(athenprofile, questId, quest)
376 | }
377 | if(version >= 28.00){MPLockerLoadout(accountId, athenprofile)}
378 | if(version <= 10.40 || VersionFilter.includes(versionGlobal)){CH1Fix(accountId, athenprofile)}
379 | Profile.bumpRvn(profileId)
380 | }
381 | response.profileChanges = [
382 | {
383 | "changeType": "fullProfileUpdate",
384 | "profile": profileData
385 | }
386 | ];
387 | break;
388 | }
389 |
390 | //had to be slighly redone to have a check if there is actually a giftbox or not mainly due to the fact on 11.31(maybe more i didnt test) it will just constantly spam RemoveGiftBox
391 | case "RemoveGiftBox": {
392 | if (req.body.giftBoxItemIds) {
393 | req.body.giftBoxItemIds.forEach(item => {
394 | !profileData.items[item]
395 | ? response.profileChanges = [{
396 | changeType: "fullProfileUpdate",
397 | profile: profileData
398 | }]
399 | : Profile.removeItem(profileData, item, profileChanges);
400 | });
401 | }
402 | if (req.body.giftBoxItemId) {
403 | !profileData.items[req.body.giftBoxItemId]
404 | ? response.profileChanges = [{
405 | changeType: "fullProfileUpdate",
406 | profile: profileData
407 | }]
408 | : Profile.removeItem(profileData, profileData.items[req.body.giftBoxItemId]);
409 | }
410 | Profile.bumpRvn(profileData);
411 | response.profileRevision = profileData.rvn || 1;
412 | response.profileCommandRevision = profileData.commandRevision || 1;
413 | Profile.saveProfile(accountId, profileId, profileData)
414 | break;
415 | }
416 |
417 | case "SetAffiliateName": {
418 | profileData.stats.attributes["mtx_affiliate"] = req.body.affiliateName
419 | profileData.stats.attributes["mtx_affiliate_set_time"] = new Date().toISOString()
420 | response.profileChanges = [
421 | {
422 | "changeType" : "statModified",
423 | "name" : "mtx_affiliate",
424 | "value" : req.body.affiliateName
425 |
426 | },
427 | {
428 | "changeType" : "statModified",
429 | "name" : "mtx_affiliate_set_time",
430 | "value" : new Date().toISOString()
431 | }
432 | ]
433 | Profile.bumpRvn(profileData);
434 | response.profileRevision = profileData.rvn || 1;
435 | response.profileCommandRevision = profileData.commandRevision || 1
436 | Profile.saveProfile(accountId, "common_core", profileData)
437 |
438 | break;
439 | }
440 |
441 | case "SetCosmeticLockerBanner": {
442 | const item = profileData.items[req.body.lockerItem];
443 |
444 | if (!item) {
445 | throw next(new ApiException(errors.com.epicgames.fortnite.item_not_found).withMessage("Locker item {0} not found", req.body.lockerItem));
446 | }
447 |
448 | if (typeof req.body.bannerIconTemplateName === "string" && item.attributes.banner_icon_template != req.body.bannerIconTemplateName) {
449 | Profile.changeItemAttribute(profileData, req.body.lockerItem, "banner_icon_template", req.body.bannerIconTemplateName, profileChanges);
450 | }
451 |
452 | if (typeof req.body.bannerColorTemplateName === "string" && item.attributes.banner_color_template != req.body.bannerColorTemplateName) {
453 | Profile.changeItemAttribute(profileData, req.body.lockerItem, "banner_color_template", req.body.bannerColorTemplateName, profileChanges);
454 | }
455 |
456 | break;
457 | }
458 |
459 | case "RedeemRealMoneyPurchases": {
460 | response.profileChanges = [ {
461 | "changeType" : "statModified",
462 | "name" : "in_app_purchases",
463 | "value" : {
464 | "receipts" : [],
465 | "ignoredReceipts" : [],
466 | "fulfillmentCounts" : {},
467 | "refreshTimers" : {
468 | "MicrosoftStore" : {
469 | "nextEntitlementRefresh" : "9999-12-01T21:10:00.000Z"
470 | },
471 | "SamsungGalaxyAppStore" : {},
472 | "EpicPurchasingService" : {
473 | "nextEntitlementRefresh" : "9999-12-01T21:10:00.000Z"
474 | }
475 | },
476 | "version" : 1
477 | }
478 | },
479 | {
480 | "changeType" : "statModified",
481 | "name" : "subscriptions",
482 | "value" : []
483 | }]
484 | break;
485 | }
486 |
487 | case "SetCosmeticLockerSlot": {
488 | const item = profileData.items[req.body.lockerItem];
489 |
490 | if (!item) {
491 | console.error("[Error] Item not found.");
492 | return;
493 | }
494 |
495 | const locker_slots_data = item.attributes.locker_slots_data ;
496 | let lockerSlot = locker_slots_data.slots[req.body.category];
497 |
498 | const expectedCapacity = {
499 | "Dance": 6,
500 | "ItemWrap": 7,
501 | }[req.body.category] || 1;
502 |
503 | if (!lockerSlot) {
504 | lockerSlot = locker_slots_data.slots[req.body.category] = {
505 | items: new Array(expectedCapacity),
506 | activeVariants: new Array(expectedCapacity)
507 | };
508 | }
509 |
510 | const itemsArray = lockerSlot.items;
511 | let bChanged = false;
512 |
513 | const startIndex = Math.max(0, req.body.slotIndex);
514 | const endIndex = Math.min(expectedCapacity, startIndex + 1);
515 |
516 | for (let index = startIndex; index < endIndex; index++) {
517 | if (index >= itemsArray.length) {
518 | itemsArray.push("");
519 | }
520 | if (itemsArray[index] !== req.body.itemToSlot) {
521 | itemsArray[index] = req.body.itemToSlot;
522 | bChanged = true;
523 | }
524 | }
525 |
526 | if (req.body.variantUpdates.length != 0) {
527 | lockerSlot.activeVariants = [{
528 | "variants": []
529 | }]
530 | req.body.variantUpdates.forEach(variant => {
531 | lockerSlot.activeVariants[0].variants.push(variant)
532 | })
533 | bChanged = true
534 | }
535 |
536 | if (bChanged) {
537 | Profile.bumpRvn(profileData);
538 | response.profileRevision = profileData.rvn || 1;
539 | response.profileCommandRevision = profileData.commandRevision || 1
540 | Profile.saveProfile(accountId, profileId, profileData)
541 | Profile.changeItemAttribute(profileData, req.body.lockerItem, "locker_slots_data", locker_slots_data, profileChanges);
542 |
543 | }
544 | break;
545 | }
546 |
547 | case "SetCosmeticLockerSlots": {
548 | break;
549 | }
550 |
551 | case "PutModularCosmeticLoadout": {
552 | const loadoutData = JSON.parse(req.body["loadoutData"]);
553 | const loadoutType = req.body.loadoutType;
554 | const presetId = req.body.presetId;
555 | const loadoutPresets = profileData.stats.attributes["loadout_presets"][loadoutType];
556 | let loadout = loadoutPresets[presetId];
557 | if (!loadout) {
558 | const newPresetId = uuidv4();
559 | loadoutPresets[presetId] = newPresetId;
560 | Profile.addItem(profileData, newPresetId, {
561 | "templateId": loadoutType,
562 | "attributes": loadoutData,
563 | "quantity": 1
564 | });
565 | Profile.saveProfile(accountId, profileId, profileData);
566 | response.profileChanges = [
567 | {
568 | "changeType": "itemAdded",
569 | "itemId": newPresetId,
570 | "item": {
571 | "templateId": loadoutType,
572 | "attributes": loadoutData,
573 | "quantity": 1
574 | }
575 | },
576 | {
577 | "changeType": "statModified",
578 | "name": "loadout_presets",
579 | "value": profileData.stats.attributes["loadout_presets"]
580 | },
581 | {
582 | "changeType": "statModified",
583 | "name": "locker_two_phase_commit",
584 | "value": "COMMITTED"
585 | }
586 | ]
587 |
588 | }
589 | else if(presetId > 0 && loadout){
590 | Profile.changeItemAttribute(profileData, loadout, "slots", loadoutData.slots);
591 | response.profileChanges =
592 | [{
593 | "changeType": "itemAttrChanged",
594 | "itemId": loadout,
595 | "attributeName": "slots",
596 | "attributeValue": loadoutData.slots
597 | },
598 | {
599 | "changeType": "itemAttrChanged",
600 | "itemId": loadout,
601 | "attributeName": "user_tags",
602 | "attributeValue": []
603 | },
604 | {
605 | "changeType": "itemAttrChanged",
606 | "itemId": loadout,
607 | "attributeName": "display_name",
608 | "attributeValue": loadoutData["display_name"]
609 | },
610 | {
611 | "changeType": "statModified",
612 | "name": "locker_two_phase_commit",
613 | "value": "COMMITTED"
614 | }]
615 | }
616 | else{
617 | Profile.changeItemAttribute(profileData, loadout, "slots", loadoutData.slots);
618 | response.profileChanges.push({
619 | "changeType": "itemAttrChanged",
620 | "itemId": loadout,
621 | "attributeName": "slots",
622 | "attributeValue": profileData.items[loadout].attributes.slots
623 | });
624 | }
625 | Profile.bumpRvn(profileData);
626 | response.profileRevision = profileData.rvn || 1;
627 | response.profileCommandRevision = profileData.commandRevision || 1;
628 | Profile.saveProfile(accountId, profileId, profileData);
629 | break;
630 | }
631 |
632 | case "EquipBattleRoyaleCustomization": {
633 | let statName, itemToSlot
634 | const item = profileData.items[req.body.itemToSlot];
635 | switch (req.body.slotName) {
636 | case "Character":
637 | statName = "favorite_character"
638 | itemToSlot = req.body.itemToSlot
639 | break
640 | case "Backpack":
641 | statName = "favorite_backpack"
642 | itemToSlot = req.body.itemToSlot
643 | break
644 | case "Pickaxe":
645 | statName = "favorite_pickaxe"
646 | itemToSlot = req.body.itemToSlot
647 | break
648 | case "Glider":
649 | statName = "favorite_glider"
650 | itemToSlot = req.body.itemToSlot
651 | break
652 | case "SkyDiveContrail":
653 | statName = "favorite_skydivecontrail"
654 | itemToSlot = req.body.itemToSlot
655 | break
656 | case "MusicPack":
657 | statName = "favorite_musicpack"
658 | itemToSlot = req.body.itemToSlot
659 | break
660 | case "LoadingScreen":
661 | statName = "favorite_loadingscreen"
662 | itemToSlot = req.body.itemToSlot
663 | break
664 | case "Dance":
665 | case "ItemWrap":
666 | var bIsDance = req.body.slotName == "Dance";
667 | statName = bIsDance ? "favorite_dance" : "favorite_itemwraps";
668 | var arr = profileData.stats.attributes[statName] || [];
669 | if (req.body.indexWithinSlot === -1) {
670 | arr = [];
671 |
672 | for (var i = 0; i < (bIsDance ? 6 : 7); ++i) {
673 | arr[i] = req.body.itemToSlot;
674 | }
675 | } else {
676 | arr[req.body.indexWithinSlot || 0] = req.body.itemToSlot;
677 | }
678 |
679 | for (var i = 0; i < arr.length; ++i) {
680 | if (arr[i] == null) {
681 | arr[i] = "";
682 | }
683 | }
684 |
685 | itemToSlot = arr;
686 | break
687 | }
688 | bChanged = false
689 | try{
690 | if (req.body.variantUpdates.length != 0) {
691 | for (var variant in item.attributes.variants) {
692 | if (item.attributes.variants.hasOwnProperty(variant) && req.body.variantUpdates.hasOwnProperty(variant) && item.attributes.variants[variant].channel === req.body.variantUpdates[variant].channel) {
693 | item.attributes.variants[variant].active = req.body.variantUpdates[variant].active;
694 | }
695 | }
696 | response.profileChanges = [{
697 | changeType: "itemAttrChanged",
698 | itemId: req.body.itemToSlot,
699 | attributeName: "variants",
700 | attributeValue: item.attributes.variants
701 | }]
702 | bChanged = true
703 | }
704 | }
705 | catch{}
706 | if (statName != null && itemToSlot != null) {
707 | Profile.modifyStat(profileData, statName, itemToSlot, response.profileChanges);
708 | }
709 |
710 | Profile.bumpRvn(profileData);
711 | response.profileRevision = profileData.rvn || 1;
712 | response.profileCommandRevision = profileData.commandRevision || 1
713 | Profile.saveProfile(accountId, profileId, profileData)
714 | break;
715 | }
716 |
717 | case "SetItemFavoriteStatus": {
718 | if (typeof req.body.bFavorite === "boolean" && profileData.items[req.body.targetItemId].attributes.favorite != req.body.bFavorite) {
719 | Profile.changeItemAttribute(profileData, req.body.targetItemId, "favorite", req.body.bFavorite, profileChanges);
720 | Profile.saveProfile(accountId, "athena", profileData)
721 | }
722 | break;
723 | }
724 |
725 | case "SetItemFavoriteStatusBatch": {
726 | req.body.itemIds.forEach((itemId, index) => {
727 | profileData.items[itemId].attributes.favorite = req.body.itemFavStatus[index];
728 | Profile.saveProfile(accountId, "athena", profileData)
729 | response.profileChanges.push(
730 | {
731 | "changeType" : "itemAttrChanged",
732 | "itemId" : itemId,
733 | "attributeName" : "favorite",
734 | "attributeValue" : req.body.itemFavStatus[index]
735 | }
736 | )
737 |
738 | });
739 | Profile.bumpRvn(profileData);
740 | response.profileRevision = profileData.rvn || 1;
741 | response.profileCommandRevision = profileData.commandRevision || 1;
742 | break;
743 | }
744 |
745 | case "SetMtxPlatform": {
746 |
747 | response.profileChanges[0] = {
748 | changeType: "statModified",
749 | name: "current_mtx_platform",
750 | value: req.body.newPlatform || "EpicPC"
751 | }
752 | break;
753 | }
754 |
755 | case "SetReceiveGiftsEnabled": {
756 |
757 | if (typeof req.body.bReceiveGifts === "boolean") {
758 | Profile.modifyStat(profileData, "allowed_to_receive_gifts", req.body.bReceiveGifts, profileChanges);
759 | }
760 |
761 | break;
762 | }
763 |
764 | case "SetLoadoutShuffleEnabled":{
765 | break;
766 | }
767 |
768 | case "UnlockRewardNode": {
769 | const lootList = [];
770 | const rewards = Array.isArray(winterFestPresents[version][req.body.nodeId])
771 | ? winterFestPresents[version][req.body.nodeId]
772 | : [winterFestPresents[version][req.body.nodeId]]
773 |
774 | rewards.forEach(cosmetic => {
775 | response.profileChanges.push({
776 | changeType: "itemAdded",
777 | itemId: cosmetic,
778 | item: {
779 | templateId: cosmetic,
780 | attributes: {
781 | creation_time: new Date().toISOString(),
782 | level: 1
783 | },
784 | quantity: 1
785 | }
786 | });
787 |
788 | lootList.push({
789 | itemType: cosmetic,
790 | itemGuid: cosmetic,
791 | itemProfile: "athena",
792 | quantity: 1
793 | });
794 | cosmetic.includes("HomebaseBannerIcon")
795 | ? (Profile.addItem(commoncore, cosmetic, {
796 | templateId: cosmetic,
797 | attributes: { item_seen: true },
798 | quantity: 1
799 | }),
800 | Profile.saveProfile(accountId, "common_core", commoncore))
801 |
802 | : Profile.addItem(profileData, cosmetic, {
803 | templateId: cosmetic,
804 | attributes: {
805 | max_level_bonus: 0,
806 | level: 1,
807 | item_seen: false,
808 | xp: 0,
809 | variants: [],
810 | creation_time: new Date().toISOString(),
811 | favorite: false
812 | },
813 | quantity: 1
814 | });
815 | });
816 | response.profileChanges.push({
817 | changeType: "itemAdded",
818 | itemId: uuidv4(),
819 | item: {
820 | templateId: "GiftBox:gb_winterfestreward",
821 | attributes: {
822 | lootList,
823 | level: 1,
824 | giftedOn: new Date().toISOString(),
825 | params: {
826 | SubGame: "Athena",
827 | winterfestGift: "true"
828 | }
829 | },
830 | quantity: 1
831 | }
832 | });
833 | Profile.bumpRvn(profileData);
834 | response.profileRevision = profileData.rvn || 1;
835 | response.profileCommandRevision = profileData.commandRevision || 1;
836 | Profile.saveProfile(accountId, profileId, profileData);
837 | break;
838 | }
839 |
840 |
841 | case "ExchangeGameCurrencyForBattlePassOffer":{
842 | break;
843 | }
844 |
845 | case "ExchangeGameCurrencyForSeasonPassOffer":{
846 | break;
847 | }
848 |
849 | case "RefundMtxPurchase": {
850 | response.profileChanges[0] = {
851 | "changeType": "itemAdded",
852 | "itemId": uuidv4(),
853 | "item": {
854 | "templateId": "Currency:MtxComplimentary",
855 | "attributes": {
856 | "platform": "Shared"
857 | },
858 | "quantity": 1500
859 | }
860 | }
861 | break;
862 | }
863 |
864 | default: {
865 | return next(new ApiException(errors.com.epicgames.fortnite.operation_not_found).with(req.params.command));
866 | }
867 | }
868 | res.json(response)
869 |
870 | }
871 | }
872 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const sails = require('sails');
2 | const NeoLog = require('./structs/NeoLog')
3 | const { default: axios } = require('axios');
4 | const fs = require("fs");
5 | const ini = require("ini");
6 | const config = ini.parse(fs.readFileSync("config.ini", "utf-8"));
7 |
8 | async function compareAndUpdateKeychain() {
9 | const response = await axios.get('https://fortnitecentral.genxgames.gg/api/v1/aes');
10 | if (response.status === 200) {
11 | const data = response.data;
12 | const keychain = JSON.parse(fs.readFileSync('./responses/keychain.json', 'utf8'));
13 | let count = 0;
14 | const keychainArray = [];
15 |
16 | for (const keys of data.dynamicKeys) {
17 | if (!keychain.includes(keys.keychain)) {
18 | count++;
19 | keychainArray.push(keys.keychain);
20 | }
21 | }
22 | keychain.push(...keychainArray);
23 |
24 | fs.writeFileSync('./responses/keychain.json', JSON.stringify(keychain, null, 2));
25 | NeoLog.Debug(`Fetched ${count} New Keychains From Fortnite Central`);
26 | }
27 | else if (response.status === 503 || response.status === 403)
28 | {
29 | NeoLog.Error(`Failed to update Keychain received status: ${response.status}`)
30 | }
31 | }
32 |
33 | async function startbackend(){
34 | sails.lift({
35 | port: 5595,
36 | environment: "production",
37 | hooks: {
38 | session: false,
39 | },
40 | log:{
41 | level: `${config.logLevel}`
42 | },
43 | }, (err) => {
44 | if(err){
45 | console.log(err)
46 | }
47 | });
48 | }
49 |
50 | async function runfunctions(){
51 | await compareAndUpdateKeychain();
52 | await startbackend();
53 | }
54 | runfunctions()
--------------------------------------------------------------------------------
/config.ini:
--------------------------------------------------------------------------------
1 |
2 | ;Log Level(Mainly for debugging issues "warn" log level is fine for most cases)
3 | logLevel="warn"
4 |
5 | ;Log Levels: "silent", "error", "warn", "debug", "info", "verbose", "silly"
6 |
7 |
8 | ;Custom Lobby Background
9 | custom_background = false
10 | image_url="https://cdn2.unrealengine.com/t-background-darkblue-2048x1024-4b5228ccabe2.png"
11 | ;true means you use custom background, false means you dont.
12 |
13 |
14 |
15 | ;Chapter 1 Season 10
16 | bEnableRuinHouseBeacon=false
17 | bEnableGothemCityBeacon=false ;version 10.31 only
18 | bEnableGreasyGroveBeacon=false
19 | bEnableMoistyPalmsBeacon=false
20 | bEnableFrenzyFieldsBeacon=false
21 | bEnableOakBeacon=false
22 | bEnableVoidBeacon=false
23 | bEnableRetailRowBeacon=false
24 | bEnableFloatingCubeIsland=false
25 |
26 | ;-----------------------------------------------------------------------------------------------------------------
27 | ;Chapter 4 Season OG Map Stages
28 | RufusWeek2=false ;map stage 2
29 | RufusWeek3=false ;map stage 3
30 | RufusWeek4=true ;map stage 4(last stage)
31 | ;setting anything other than "true" will disable the stage
32 | ;-----------------------------------------------------------------------------------------------------------------
33 |
34 | ;TMNT Tab Stages
35 | TMNTStage1=true
36 | TMNTStage2=false
37 | TMNTStage3=false
38 | ;setting anything other than "true" will disable the stage
39 | ;-----------------------------------------------------------------------------------------------------------------
40 |
41 | ;Avatar iceberg stages
42 | AtlaIceberg1=false
43 | AtlaIceberg2=false
44 | AtlaIceberg3=true
45 | ;setting anything other than "true" will disable the stage, NOTE: you can activate multiple stages at the same time.
46 | ;-----------------------------------------------------------------------------------------------------------------
47 |
48 | ;Chapter 2 Season 3 Water Level Stages
49 |
50 | WaterLevel_0=true
51 | WaterLevel_1=false
52 | WaterLevel_2=false
53 | WaterLevel_3=false
54 | WaterLevel_4=false
55 | WaterLevel_5=false
56 | WaterLevel_6=false
57 | WaterLevel_7=false
58 | ;setting anything other than "true" will disable the stage
59 | ;-----------------------------------------------------------------------------------------------------------------
60 |
61 | ;Custom Item Shop Billboard Section(disabled by default putting "true" will enable the custom section)
62 | bUseCustomSection=false
63 | sectionId=StartYourEngines
64 | offerSectionId=Behemoth
65 | stackRankValue=99
66 | foregroundUrl=https://cdn3.unrealengine.com/billboard-kelplinencalcium-character-1920x1080-8a2207011c93.png ;image displayed on top of the background
67 | backgroundUrl=https://cdn3.unrealengine.com/billboard-kelplinencalcium-bg-1920x1080-037e44ae3072.png;image url of the background
68 | bodyImage=https://cdn3.unrealengine.com/billboard-kelplinencalcium-itemstack-update-512x96-65438a924b73.png ;image apeaers alongside the text
69 | titleImage=https://cdn3.unrealengine.com/billboard-kelplinencalcium-logo-686x341-93ab8e6a991d.png ;image that appears in the same place as the title text
70 | title=
71 | subtitle=Cool Subtitle ;subtitle, text displayed under the title
72 | buttonText=Cool Button Text ;text of the usually "View Offer" button
73 | SectiondisplayName=Cool Section Name ;Shop section display name
74 | titleColorA=FFFFFF ;color of the title, has to be a hex value
75 | titleColorB=FFFFFF ;color of the subtitle, text below the title, has to be a hex value
76 |
77 | ;Custom BillBoard Section example^ in this example the "LayoutID" would be "StartYourEngines.Behemoth.99", you would need to modify the data in shopv3.json accordingly to utilize your custom section*/
78 | ;Speech Marks("") are not required and your custom item shop billboard section will not work if you have them included.
79 |
80 | ;Change Player Level/Amount of Gold!
81 | Level=1
82 | Gold=0
83 |
84 | ;[EXPERIMENTAL] Fortnite Game Config
85 | FortniteGameConfig=false
86 |
87 | ;Username override mainly to fix when the username is random characters, in the sitation where you are redirecting epics/fortnites endpoints to Neonite
88 | bEnableOverride=false
89 | username=""
--------------------------------------------------------------------------------
/config/apiRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'GET /launcher/api/public/distributionpoints': 'ApiController.distributionpoints',
3 | 'GET /launcher/api/public/assets/:platform/:catalogItemId/:appName': 'ApiController.launcherCatalogItem',
4 | 'GET /Builds/Fortnite/Content/CloudDir/*.manifest':{
5 | action: "manifest",
6 | controller:'ApiController',
7 | skipAssets: false
8 | },
9 | 'GET /Builds/Fortnite/Content/CloudDir/*.ini': 'ApiController.ini',
10 | 'GET /Builds/Fortnite/Content/CloudDir/ChunksV4/:chunknum/*.chunk':{
11 | action: "ChunksV4",
12 | controller:'ApiController',
13 | skipAssets: false
14 | },
15 | 'GET /lightswitch/api/service/bulk/status': 'ApiController.lightSwitchbulk',
16 | 'GET /lightswitch/api/service/:serviceId/status': 'ApiController.lightswitch',
17 | 'ALL /api/v1/events/Fortnite/:event/history/:accountId': 'ApiController.eventHistory',
18 | 'GET /api/v1/games/fortnite/tracks/query*': 'ApiController.habaneroTracks',
19 | 'GET /api/v1/games/fortnite/trackprogress/:accountId': 'ApiController.habaneroProgress',
20 | 'GET /api/v1/games/fortnite/tracks/activeBy/*':{
21 | action: "habaneroActiveBy",
22 | controller:'ApiController',
23 | skipAssets: false
24 | },
25 | 'GET /fortnite/api/v2/versioncheck*': 'ApiController.versionCheck',
26 | 'GET /fortnite/api/versioncheck*': 'ApiController.versionCheck',
27 | 'GET /fortnite/api/version': 'ApiController.versionCheck',
28 | 'GET /fortnite/api/game/v2/privacy/account/:accountId': 'ApiController.privacy',
29 | 'POST /api/v1/assets/Fortnite/:version/:netcl': 'ApiController.FrontendAssets',
30 | 'GET /fortnite/api/storefront/v2/catalog': 'ApiController.catalog',
31 | 'GET /catalog/api/shared/bulk/offers': 'ApiController.catalogBulk',
32 | 'POST /fortnite/api/game/v2/grant_access/:accountId': 'ApiController.grantAccess',
33 | 'GET /fortnite/api/game/v2/enabled_features': 'ApiController.enabledFeatures',
34 | 'POST */datarouter/api/v1/public/*':{
35 | action: "dataRouter",
36 | controller:'ApiController',
37 | skipAssets: false
38 | },
39 | 'GET /presence/api/v1/_/:accountId/settings/subscriptions': 'ApiController.presence',
40 | 'GET /socialban/api/public/v1/:accountId':{
41 | action: "socialban",
42 | controller:'ApiController',
43 | skipAssets: false
44 | },
45 | 'GET /eulatracking/api/public/agreements/fn/account/:accountId':{
46 | action: "eula",
47 | controller:'ApiController',
48 | skipAssets: false
49 | },
50 | 'GET /eulatracking/api/public/agreements/epicgames_privacy_policy_no_table/account/:accountId':'ApiController.eula',
51 | 'GET /eulatracking/api/shared/agreements/fn':'ApiController.eula',
52 | 'GET /fortnite/api/game/v2/creative/*':{
53 | action: "creative",
54 | controller:'ApiController',
55 | skipAssets: false
56 | },
57 | 'GET /affiliate/api/public/affiliates/slug/:affiliateName': 'ApiController.sac',
58 | 'GET /content-controls/:accountId': 'ApiController.contentControls',
59 | 'GET /content-controls/:accountId/rules/namespaces/fn': 'ApiController.contentControlsRules',
60 | 'POST /content-controls/:accountId/verify-pin': 'ApiController.verifyPin',
61 | 'GET /api/v2/interactions/aggregated/Fortnite/:accountId': 'ApiController.interactionsAggregated',
62 | 'ALL /fortnite/api/game/v2/profileToken/verify/:accountId': 'ApiController.profileToken',
63 | 'GET /api/v1/namespace/fn/worlds/accessibleTo/:accountId': 'ApiController.fetchLegoWorlds',
64 | 'POST /api/v1/namespace/fn/worlds/account/:accountId': 'ApiController.makeLegoWorlds',
65 | 'GET /api/v1/namespace/fn/worlds/world/:worldID/session': 'ApiController.legoWorldSession',
66 | 'GET /api/v1/namespace/fn/worlds/world/:worldId/attest/:accountId': 'ApiController.legoMatchMakingToken',
67 | 'GET /fortnite/api/storefront/v2/keychain': 'ApiController.keychain',
68 | 'GET /fortnite/api/game/v2/world/info': 'ApiController.worldInfo',
69 | 'POST /region/check': 'ApiController.regionCheck',
70 | 'PUT /profile/play_region': 'ApiController.playRegion',
71 | 'GET /salesEvent/salesEvent/*':{
72 | action: "salesEvent",
73 | controller:'ApiController',
74 | skipAssets: false
75 | },
76 | 'GET /gameRating/gameRating/*':{
77 | action: "gameRating",
78 | controller:'ApiController',
79 | skipAssets: false
80 | },
81 | 'GET /ias/fortnite/:Hash':{
82 | action: "ias",
83 | controller:'ApiController',
84 | skipAssets: false
85 | },
86 | 'GET /ias/fortnite/chunks/:chunkNum/:chunkFile':{
87 | action: "iasChunks",
88 | controller:'ApiController',
89 | skipAssets: false
90 | },
91 | 'GET /:hash/:trackHash/*.mp4':{
92 | action: "trackSegment",
93 | controller:'ApiController',
94 | skipAssets: false
95 | },
96 | 'GET /:hash/:trackHash/*.m4s':{
97 | action: "trackSegment",
98 | controller:'ApiController',
99 | skipAssets: false
100 | },
101 | 'GET /api/v2/interactions/latest/Fortnite/:accountId': 'ApiController.interactions',
102 | 'POST /fortnite/api/game/v2/tryPlayOnPlatform/account/:accountId': 'ApiController.tryPlayOnPlatform',
103 | 'PUT /profile/privacy_settings': 'ApiController.privacySettings',
104 | 'GET /api/v1/leaderboards/Fortnite/:eventId/:eventWindowId/*':{
105 | action: "leaderboards",
106 | controller:'ApiController',
107 | skipAssets: false
108 | },
109 | 'GET /api/v1/events/Fortnite/download/:accountId':{
110 | action: "eventsDownload",
111 | controller:'ApiController',
112 | skipAssets: false
113 | },
114 | 'GET /region': 'ApiController.region',
115 | 'GET /fortnite/api/game/v2/br-inventory/account/:accountId': 'ApiController.brInventory',
116 | 'GET /fortnite/api/storeaccess/v1/request_access/:accountId': 'ApiController.storeAccess',
117 | 'GET /api/v1/lfg/Fortnite/users/:accountId/settings': 'ApiController.lfgSettings',
118 | 'GET /followers/api/v1/FortniteLive/:accountId/*':{
119 | action: "followers",
120 | controller:'ApiController',
121 | skipAssets: false
122 | },
123 | 'GET /api/content/v2/launch-data': 'ApiController.launchData',
124 | 'POST /api/v1/user/setting': 'ApiController.userSetting',
125 | 'OPTIONS /v1/epic-settings/public/users/:accountId/*':{
126 | action: "epicSettings204",
127 | controller:'ApiController',
128 | skipAssets: false
129 | },
130 | 'PATCH /v1/epic-settings/public/users/:accountId/*':{
131 | action: "epicSettings",
132 | controller:'ApiController',
133 | skipAssets: false
134 | },
135 | 'GET /v1/epic-settings/public/users/:accountId/*':{
136 | action: "epicSettings",
137 | controller:'ApiController',
138 | skipAssets: false
139 | },
140 | 'PUT /profile/languages': 'ApiController.languages',
141 | 'POST /api/content/v2/cooked-content-package': 'ApiController.cookedContent',
142 | 'GET /valkyrie/cooked-content/*':{
143 | action: "valkyrieContent",
144 | controller:'ApiController',
145 | skipAssets: false
146 | },
147 | /*'GET /api/content/v2/search/artifact/:artifactId/*':{
148 | action: "searchArtifact",
149 | controller:'ApiController',
150 | skipAssets: false
151 | },
152 | 'GET /api/content/v2/artifact/:artifactId/*':{
153 | action: "artifact",
154 | controller:'ApiController',
155 | skipAssets: false
156 | },
157 | 'GET /api/v1/redirect/fortnite/valkyrie/cooked-content/*':{
158 | action: "valkyrie",
159 | controller:'ApiController',
160 | skipAssets: false
161 | },*/
162 | 'GET /:resourceId/master.blurl': 'ApiController.blurl',
163 | 'GET /app_installation/status': 'ApiController.postPartyInstallStatus',
164 | 'GET /content/api/pages/fortnite-game/spark-tracks': 'ApiController.sparks',
165 | 'GET /content/api/pages/fortnite-game/eventscreens': 'ApiController.eventScreen',
166 | 'POST /fortnite/api/game/v2/profile/:accountId/client/:command': 'ProfileController.mcp',
167 | 'GET /content/api/pages/fortnite-game': 'FortniteGameController.fortniteGame',
168 | 'GET /content/api/pages/fortnite-game/radio-stations': 'FortniteGameController.stations',
169 | 'GET /:trackdata': 'ApiController.trackData',
170 | 'GET /:sparksTrack.dat': {
171 | action: "sparksTrack",
172 | controller:'ApiController',
173 | skipAssets: false
174 | },
175 | 'GET /:sparksLipSyncData.lad': {
176 | action: "sparksLipSyncData",
177 | controller:'ApiController',
178 | skipAssets: false
179 | },
180 | 'GET /api/v1/Fortnite/get': 'ApiController.interactions',
181 | 'POST /api/v1/fortnite-br/surfaces/:gameMode/target': 'FortniteGameController.motd',
182 | 'POST /api/v1/fortnite-br/channel/motd/target': 'FortniteGameController.motdTarget',
183 | 'POST /api/v1/fortnite-br/interactions/contentHash': 'FortniteGameController.contentHash',
184 | 'GET /content/api/pages/fortnite-game/seasonpasses': 'FortniteGameController.seasonPass',
185 | 'GET /fortnite/api/calendar/v1/timeline': 'TimelineController.timeline',
186 | 'GET /api/locker/v3/:deploymentId/account/:accountId/items': 'LockerController.lockerv3',
187 | 'PUT /api/locker/v3/:deploymentId/loadout/:loadoutType/account/:accountId/:loadout':{
188 | action: "lockerLoadoutV3",
189 | controller:'LockerController',
190 | skipAssets: false
191 | },
192 | 'PUT /api/locker/v3/:deploymentId/loadout/:loadoutType/account/:accountId/loadout-preset/index/:presetIndex': {
193 | action: "lockerPresetV3",
194 | controller:'LockerController',
195 | skipAssets: false
196 | },
197 | 'GET /api/locker/v4/:deploymentId/account/:accountId/items': 'LockerController.lockerv4',
198 | 'PUT /api/locker/v4/:deploymentId/account/:accountId/active-loadout-group':{
199 | action: "lockerLoadoutV4",
200 | controller:'LockerController',
201 | skipAssets: false
202 | },
203 | 'PUT /api/locker/v4/:deploymentId/account/:accountId/loadout-group-preset/index/:presetIndex': {
204 | action: "lockerGroupPresetV4",
205 | controller:'LockerController',
206 | skipAssets: false
207 | },
208 | 'PUT /api/locker/v4/:deploymentId/loadout/:loadoutType/account/:accountId/loadout-preset/index/:presetIndex': {
209 | action: "lockerPresetV4",
210 | controller:'LockerController',
211 | skipAssets: false
212 | },
213 | };
--------------------------------------------------------------------------------
/config/authRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'POST /account/api/oauth/token': 'AuthController.oauthToken',
3 | 'GET /account/api/oauth/verify': 'AuthController.verifyToken',
4 | 'DELETE /account/api/oauth/sessions/kill': 'AuthController.killToken',
5 | 'DELETE /account/api/oauth/sessions/kill/:token': 'AuthController.killToken',
6 | 'GET /account/api/public/account/:accountId':{
7 | action: "accountInfo",
8 | controller:'AuthController',
9 | skipAssets: false ,
10 | },
11 | 'GET /account/api/public/account/displayName/:displayName': 'AuthController.displayName',
12 | 'GET /fortnite/api/discovery/accessToken/*':{
13 | action: "discoveryToken",
14 | controller:'AuthController',
15 | skipAssets: false
16 | },
17 | 'GET /account/api/public/account/': 'AuthController.publicAccount',
18 | 'GET /account/api/public/account/token': 'ApiController.token',
19 | 'GET /account/api/public/account/:accountId/externalAuths': 'AuthController.externalAuths',
20 | 'POST /publickey/v2/publickey': 'AuthController.publicKey',
21 | 'POST /epic/oauth/v2/tokenInfo': 'AuthController.tokenInfo',
22 | 'POST /auth/v1/turn/credentials': 'AuthController.credentials'
23 |
24 |
25 | }
--------------------------------------------------------------------------------
/config/cloudstorageRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'GET /fortnite/api/cloudstorage/system': 'CloudStorageController.cloudstorageSystem',
3 | 'GET /fortnite/api/cloudstorage/system/config': 'CloudStorageController.config',
4 | 'GET /fortnite/api/cloudstorage/system/DefaultGame.ini': 'CloudStorageController.defaultGame',
5 | 'GET /fortnite/api/cloudstorage/system/DefaultEngine.ini': 'CloudStorageController.defaultEngine',
6 | 'GET /fortnite/api/cloudstorage/system/DefaultRuntimeOptions.ini': 'CloudStorageController.defaultRuntimeOptions',
7 | 'GET /fortnite/api/cloudstorage/system/DefaultInput.ini': 'CloudStorageController.defaultInput',
8 | 'GET /fortnite/api/cloudstorage/user/:accountId':{
9 | action: "user",
10 | controller:'CloudStorageController',
11 | skipAssets: false
12 | },
13 | 'GET /fortnite/api/cloudstorage/user/:accountId/:fileName':{
14 | action: "userFile",
15 | controller:'CloudStorageController',
16 | skipAssets: false
17 | },
18 | 'PUT /fortnite/api/cloudstorage/user/:accountId/:fileName': 'CloudStorageController.userPutFile',
19 |
20 | }
--------------------------------------------------------------------------------
/config/discoveryRoutes.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | module.exports.routes = {
4 | 'POST /fortnite/api/game/v2/creative/discovery/surface/*':{
5 | action: "discoveryv1",
6 | controller:'DiscoveryController',
7 | skipAssets: false
8 | },
9 | 'POST /api/v1/discovery/surface/*':{
10 | action: "discoveryv2",
11 | controller:'DiscoveryController',
12 | skipAssets: false
13 | },
14 | 'POST /api/v2/discovery/surface/CreativeDiscoverySurface_Frontend': 'DiscoveryController.discoveryv3',
15 | 'POST /api/v2/discovery/surface/:surface/page':{
16 | action: "page",
17 | controller:'DiscoveryController',
18 | skipAssets: false
19 | },
20 | 'POST /links/api/fn/mnemonic': 'DiscoveryController.mnemonicLinks',
21 | 'GET /links/api/fn/mnemonic/:playlistId/related': 'DiscoveryController.related',
22 | 'POST /api/v1/links/favorites/:accountId/check': 'DiscoveryController.favoritesCheck',
23 | 'POST /api/v1/links/lock-status/:accountId/check': 'DiscoveryController.lockStatus',
24 | 'GET /links/api/fn/mnemonic/:playlistId': 'DiscoveryController.mnemonicPlaylist',
25 |
26 |
27 |
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/config/eosRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'POST /auth/v1/oauth/token': 'EosController.oauthTokenv1',
3 | 'POST /epic/oauth/v2/token': 'EosController.oauthv2',
4 | 'GET /epic/id/v2/sdk/accounts': 'EosController.eossdkv2',
5 | 'GET /sdk/v1/default':{
6 | action: "eossdkv1default",
7 | controller:'EosController',
8 | skipAssets: false
9 | },
10 | 'GET /sdk/v1/product/prod-fn':{
11 | action: "eossdkv1productprod",
12 | controller:'EosController',
13 | skipAssets: false
14 | },
15 | 'GET /sdk/v1/product/*':{
16 | action: "eossdkv1product",
17 | controller:'EosController',
18 | skipAssets: false
19 | },
20 | 'GET /epic/friends/v1/:accountId/blocklist': 'EosController.blocklist',
21 | 'PATCH /epic/presence/v1/:gameNsIg/:accountId/presence/:presenceUuid': 'EosController.presence',
22 | 'GET /v2': 'EosController.wss',
23 | 'POST /user/v9/product-users/search' : 'EosController.productUsersSearch',
24 |
25 |
26 |
27 |
28 | }
--------------------------------------------------------------------------------
/config/http.js:
--------------------------------------------------------------------------------
1 | const NeoLog = require('../structs/NeoLog')
2 |
3 | module.exports.http = {
4 | middleware: {
5 | order: [
6 | 'LogURL',
7 | 'bodyParser',
8 | ],
9 | LogURL: function (req, res, next) {
10 | const startTime = new Date();
11 | res.on('finish', () => {
12 | const endTime = new Date();
13 | const responseTime = endTime - startTime;
14 | if (req.originalUrl === "/fortnite/api/calendar/v1/timeline" || req.originalUrl === "/account/api/public/account/token") {}
15 | else if (res.statusCode == 404) {
16 | NeoLog.warn(`${req.originalUrl}`)
17 | }
18 | else {
19 | NeoLog.URL(`${req.originalUrl} (${responseTime}ms)`);
20 | }
21 | });
22 | next();
23 | },
24 | bodyParser: require('skipper')({ strict: false }),
25 | }
26 | };
--------------------------------------------------------------------------------
/config/matchmakingRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'GET /fortnite/api/game/v2/matchmakingservice/ticket/player/:accountId': 'MatchMakingController.matchmakingTicket',
3 | 'GET /fortnite/api/game/v2/matchmaking/account/:accountId/session/:sessionId': 'MatchMakingController.matchmakingSession',
4 | 'POST /fortnite/api/matchmaking/session/:SessionId/join': 'MatchMakingController.matchmakingSessionJoin',
5 | 'GET /fortnite/api/matchmaking/session/:sessionId': 'MatchMakingController.matchmakingSession2',
6 | 'GET /waitingroom/api/waitingroom': 'MatchMakingController.waitingRoom',
7 | 'GET /fortnite/api/matchmaking/session/findPlayer/:id': 'MatchMakingController.findPlayer',
8 | 'POST /api/verify/match': 'MatchMakingController.verifyMatch',
9 |
10 |
11 | }
--------------------------------------------------------------------------------
/config/playerRoutes.js:
--------------------------------------------------------------------------------
1 | module.exports.routes = {
2 | 'POST /party/api/v1/*/parties':{
3 | action: "parties",
4 | controller:'PlayerController',
5 | skipAssets: false
6 | },
7 | 'GET /party/api/v1/Fortnite/user/:accountId': 'PlayerController.localparty',
8 | 'GET /party/api/v1/*/parties/:partyId/members/:accountId/meta':{
9 | action: "partyMeta",
10 | controller:'PlayerController',
11 | skipAssets: false
12 | },
13 | 'POST /party/api/v1/*/user/:accountId/pings/:pingerId':{
14 | action: "pings",
15 | controller:'PlayerController',
16 | skipAssets: false
17 | },
18 | 'GET /friends/api/v1/*/recent/fortnite':{
19 | action: "recentPlayers",
20 | controller:'PlayerController',
21 | skipAssets: false
22 | },
23 | 'GET /friends/api/v1/:accountId/settings': 'PlayerController.friendsSettings',
24 | 'GET /friends/api/v1/:accountId/summary': 'PlayerController.friendsSummary',
25 | 'GET /friends/api/public/friends/:accountId': 'PlayerController.friends',
26 | 'GET /friends/api/public/list/fortnite/:accountId/recentPlayers/': 'PlayerController.recentPlayers',
27 | 'GET */api/statsv2/account/:accountId':{
28 | action: "stats",
29 | controller:'PlayerController',
30 | skipAssets: false
31 | },
32 | 'ALL /presence/api/v1/*':{
33 | action: "presence",
34 | controller:'PlayerController',
35 | skipAssets: false
36 | },
37 | 'GET /fortnite/api/receipts/v1/account/:accountId/receipts': 'PlayerController.receipts',
38 | 'GET /friends/api/public/blocklist/:accountId': 'PlayerController.blocklist',
39 |
40 |
41 |
42 | }
--------------------------------------------------------------------------------
/discovery/discoveryMenuV1.json:
--------------------------------------------------------------------------------
1 | {
2 | "Panels": [
3 | {
4 | "PanelName": "ByEpicNoBigBattle6Col",
5 | "Pages": [
6 | {
7 | "results": [
8 | {
9 | "linkData": {
10 | "namespace": "fn",
11 | "mnemonic": "playlist_radish",
12 | "linkType": "BR:Playlist",
13 | "active": true,
14 | "disabled": false,
15 | "version": 95,
16 | "moderationStatus": "Unmoderated",
17 | "accountId": "epic",
18 | "creatorName": "Epic",
19 | "descriptionTags": [],
20 | "metadata": {
21 | "image_url": "https://cdn2.unrealengine.com/22br-radish-ingame-playlisttile-1920x1080-d85374a7a6f6.jpg",
22 | "matchmaking": {
23 | "override_playlist": "playlist_radish"
24 | }
25 | }
26 | },
27 | "lastVisited": null,
28 | "linkCode": "playlist_radish",
29 | "isFavorite": false
30 | },
31 | {
32 | "linkData": {
33 | "namespace": "fn",
34 | "mnemonic": "playlist_armadillo",
35 | "linkType": "BR:Playlist",
36 | "active": true,
37 | "disabled": false,
38 | "version": 95,
39 | "moderationStatus": "Unmoderated",
40 | "accountId": "epic",
41 | "creatorName": "Epic",
42 | "descriptionTags": [],
43 | "metadata": {
44 | "image_url": "https://cdn2.unrealengine.com/20br-armadillo-creativetile-tile-1920x1080-1920x1080-f40652491ad0.jpg",
45 | "matchmaking": {
46 | "override_playlist": "playlist_armadillo"
47 | }
48 | }
49 | },
50 | "lastVisited": null,
51 | "linkCode": "playlist_armadillo",
52 | "isFavorite": false
53 | },
54 | {
55 | "linkData": {
56 | "namespace": "fn",
57 | "mnemonic": "playlist_pumpkin",
58 | "linkType": "BR:Playlist",
59 | "active": true,
60 | "disabled": false,
61 | "version": 95,
62 | "moderationStatus": "Unmoderated",
63 | "accountId": "epic",
64 | "creatorName": "Epic",
65 | "descriptionTags": [],
66 | "metadata": {
67 | "image_url": "https://i.imgur.com/IhUmLNI.jpeg",
68 | "matchmaking": {
69 | "override_playlist": "playlist_pumpkin"
70 | }
71 | }
72 | },
73 | "lastVisited": null,
74 | "linkCode": "playlist_pumpkin",
75 | "isFavorite": false
76 | },
77 | {
78 | "linkData": {
79 | "namespace": "fn",
80 | "mnemonic": "playlist_guava",
81 | "linkType": "BR:Playlist",
82 | "active": true,
83 | "disabled": false,
84 | "version": 95,
85 | "moderationStatus": "Unmoderated",
86 | "accountId": "epic",
87 | "creatorName": "Epic",
88 | "descriptionTags": [],
89 | "metadata": {
90 | "image_url": "https://cdn2.unrealengine.com/18br-guava-playlisttile-1920x1080-1920x1080-e66a6a0a08cf.jpg",
91 | "matchmaking": {
92 | "override_playlist": "playlist_guava"
93 | }
94 | }
95 | },
96 | "lastVisited": null,
97 | "linkCode": "playlist_guava",
98 | "isFavorite": false
99 | },
100 | {
101 | "linkData": {
102 | "namespace": "fn",
103 | "mnemonic": "playlist_kiwi",
104 | "linkType": "BR:Playlist",
105 | "active": true,
106 | "disabled": false,
107 | "version": 95,
108 | "moderationStatus": "Unmoderated",
109 | "accountId": "epic",
110 | "creatorName": "Epic",
111 | "descriptionTags": [],
112 | "metadata": {
113 | "image_url": "https://cdn2.unrealengine.com/17br-kiwi-keyart-motd-1920x1080-1920x1080-50ccef17c86f.jpg",
114 | "matchmaking": {
115 | "override_playlist": "playlist_kiwi"
116 | }
117 | }
118 | },
119 | "lastVisited": null,
120 | "linkCode": "playlist_kiwi",
121 | "isFavorite": false
122 | },
123 | {
124 | "linkData": {
125 | "namespace": "fn",
126 | "mnemonic": "playlist_defaultsolo",
127 | "linkType": "BR:Playlist",
128 | "active": true,
129 | "disabled": false,
130 | "version": 95,
131 | "moderationStatus": "Unmoderated",
132 | "accountId": "epic",
133 | "creatorName": "Epic",
134 | "descriptionTags": [],
135 | "metadata": {
136 | "image_url": "https://cdn2.unrealengine.com/25br-solo-1920-1920x1080-f447350aee68.jpg",
137 | "matchmaking": {
138 | "override_playlist": "playlist_defaultsolo"
139 | }
140 | }
141 | },
142 | "lastVisited": null,
143 | "linkCode": "playlist_defaultsolo",
144 | "isFavorite": false
145 | },
146 | {
147 | "linkData": {
148 | "namespace": "fn",
149 | "mnemonic": "playlist_molegame",
150 | "linkType": "BR:Playlist",
151 | "active": true,
152 | "disabled": false,
153 | "version": 95,
154 | "moderationStatus": "Unmoderated",
155 | "accountId": "epic",
156 | "creatorName": "Epic",
157 | "descriptionTags": [],
158 | "metadata": {
159 | "image_url": "https://cdn2.unrealengine.com/17br-mole-motd-1920x1080-1920x1080-0fa22131cdbc.jpg",
160 | "matchmaking": {
161 | "override_playlist": "playlist_molegame"
162 | }
163 | }
164 | },
165 | "lastVisited": null,
166 | "linkCode": "playlist_molegame",
167 | "isFavorite": false
168 | },
169 | {
170 | "linkData": {
171 | "namespace": "fn",
172 | "mnemonic": "playlist_papaya",
173 | "linkType": "BR:Playlist",
174 | "active": true,
175 | "disabled": false,
176 | "version": 95,
177 | "moderationStatus": "Unmoderated",
178 | "accountId": "epic",
179 | "creatorName": "Epic",
180 | "descriptionTags": [],
181 | "metadata": {
182 | "image_url": "https://cdn2.unrealengine.com/partyroyale-1920-1920x1080-7001724bc7ab.jpg",
183 | "matchmaking": {
184 | "override_playlist": "playlist_papaya"
185 | }
186 | }
187 | },
188 | "lastVisited": null,
189 | "linkCode": "playlist_papaya",
190 | "isFavorite": false
191 | }
192 | ],
193 | "hasMore": false
194 | }
195 | ]
196 | }
197 | ],
198 | "TestCohorts": [
199 | "testing"
200 | ],
201 | "ModeSets": {}
202 | }
--------------------------------------------------------------------------------
/hotfixes/DefaultEngine.ini:
--------------------------------------------------------------------------------
1 | ;[OnlineSubsystemMcp.Xmpp Prod]
2 | ;ServerAddr="ws://localhost:5595"
3 | ;Protocol="ws"
4 | ;ServerPort=5595
5 | ;bUseSSL=true
6 |
7 | ;[OnlineSubsystemMcp.Xmpp]
8 | ;ServerAddr="ws://localhost:5595"
9 | ;Protocol="ws"
10 | ;ServerPort=5595
11 | ;bUseSSL=true
12 |
13 | [Core.Log]
14 | LogScheduledEvents=NoLogging
15 | LogSparksSongCatalog=NoLogging
16 | LogPrm=NoLogging
17 | LogIas=NoLogging
18 | LogFortCreativeDiscoverySurfaceManager=NoLogging
19 | LogQos=NoLogging
20 | LogFortUI=NoLogging
21 | LogHttp=Log
22 | LogFortMemory=NoLogging
23 |
24 | [ConsoleVariables]
25 | SupervisedSettings.UseEOSIntegration=false
26 | TopBar.ShowShowdown=0
27 | TopBar.ShowPoblano=1
28 | Fort.CompeteUI.LobbyPanel.ShowPoblano=1
29 | Store.EnableCatabaScreen=1
30 | Store.EnableCatabaHighlights=1
31 | FortPlaylistManager.CachedPlaylistsEnabled=1
32 | Fort.Rollback.UseCosmeticFlowOnlyWhereRequired=1
33 | Athena.Frontend.ShowMPLobbyOnboardingModal=0
34 | ;Fort.Event.bForceOffLoadingScreen=1
35 | Sparks.Catalog.MidiDecryptionKey="KbSsGNCQFmVZJE4VVIvUwRuY0zrVf3sNm//2zrfPYUU="
36 | CMS.DisableFileCache=true
37 |
38 |
39 | [OnlineSubsystemMcp]
40 | bUsePartySystemV2=false ;fixes CH1S8 when the player is forced to sit out in lobby.
41 |
42 | [/Script/Qos.QosRegionManager]
43 | NumTestsPerRegion=1
44 | PingTimeout=0.1
45 |
46 |
47 | [OnlineSubsystemTwitch]
48 | bEnabled=false
--------------------------------------------------------------------------------
/hotfixes/DefaultGame.ini:
--------------------------------------------------------------------------------
1 | [/Script/FortniteGame.FortTextHotfixConfig]
2 |
3 | ; Fracture playlist
4 | +TextReplacements=(Category=Game, Namespace="", bIsMinimalPatch=True, Key="24DB7A5D417D0130F2391DB357DFD11C", NativeString="TBD", LocalizedStrings=(("ar","الانكسار: نهاية الفصل 3"),("en","Fracture: Chapter 3 Finale"),("de","Zersplittert: Finale von Kapitel 3"),("es","Fractura: final del Capítulo 3"),("es-419","Fractura: final del Capítulo 3"),("fr","Fracture : conclusion du Chapitre 3 de Fortnite"),("it","Finale Capitolo 3: Frattura"),("ja","フラクチャー: チャプター3 フィナーレ"),("ko","현실의 붕괴: 챕터 3 피날레"),("pl","Rozpad: finał Rozdziału 3"),("pt-BR","Ruptura: Final do Capítulo 3"),("ru","«Разлом»: финал третьей главы"),("tr","Kırılma: 3. Bölüm Finali")))
5 | +TextReplacements=(Category=Game, Namespace="", bIsMinimalPatch=True, Key="675A186F44757FBE3E35F28865C33C7E", NativeString="TBD", LocalizedStrings=(("ar","حافظوا على وحدتكم"),("en","Don’t Fall To Pieces"),("de","Lass dich nicht zersplittern"),("es","No te desmorones"),("es-419","No te caigas a pedazos"),("fr","Ne vous fracassez pas"),("it","Non cadere a pezzi"),("ja","バラバラになるな"),("ko","조각나지 마세요."),("pl","Nie rozpadnij się na kawałki"),("pt-BR","Não Se Despedace"),("ru","Держитесь крепче"),("tr","Bütünlüğünden Ödün Verme")))
6 |
7 | ; Auth Errors
8 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="OnlineAccount", Key="TokenExpired", NativeString="Login Expired or Logged In Elsewhere", LocalizedStrings=(("en","Server has restarted. Please relaunch your game to continue."),("es","Server se ha reiniciado. Por favor reinicia tu juego para continuar."),("es-419","Server se ha reiniciado. Por favor reinicia tu juego para continuar."),("pl","Server została zrestartowana, Uruchom Fortnite'a ponownie, aby kontynuować.")))
9 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="Fortnite.FortMatchmakingV2", Key="Unauthorized", NativeString="You cannot play this game mode at this time.", LocalizedStrings=(("en","Sorry, but matchmaking is not supported on Server."),("es","Disculpa, pero el emparejamiento no es soportado en Server."),("es-419","Disculpa, pero el emparejamiento no es soportado en Server."),("pl","Przepraszamy, Server nie wspiera dobierania graczy.")))
10 |
11 |
12 | ; Useless strings fix
13 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="71167C024034BDEC47B61ABA2B922F0D", NativeString="Disabled in 15 minutes", LocalizedStrings=("en"," "),("es"," "),("es-419"," "),("pl"," ")))
14 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="5C07BA214E08E82FC49AE78B3A2BA78C", NativeString="Not enough party members.", LocalizedStrings=("en"," "),("es"," "),("es-419"," "),("pl"," ")))
15 |
16 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="FortSocialListItem", Key="ListDisplayName", NativeString="VOICE CHAT MEMBERS", LocalizedStrings=(("en", ""),("es", ""),("es-419", ""),("pl", "")))
17 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="FortChat", Key="SendError", NativeString="You must be in a channel to send a message.", LocalizedStrings=(("en","Chat on Server has been disabled."),("es","El chat en Server ha sido desactivado."),("es-419","El chat en Server ha sido desactivado."),("pl", "Niestety, czat na NeoniteV2 nie działa.")))
18 | ; Loading Screen tips
19 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="BB7A860E4AC0955BAD6E3B99C32E1EEE", NativeString="Wired internet connections provide a faster and more stable connection than wireless.", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
20 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="C934084C42E6B991D40E3E87A8B67A11", NativeString="Epic will never ask for your password", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
21 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="229754C64147301DE0386EA84395E371", NativeString="Double-Clicking the Ping Button will signal to squadmates that you've spotted an enemy.", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
22 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="F011F48E4197F3A810EA4CA38EA7B9DD", NativeString="Protect your Epic Account by enabling two-factor authentication at Fortnite.com", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
23 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="674CEA324A6FEC0921BB59BCA386BA6E", NativeString="When your inventory is full, picking up an item will swap it with what is currently equipped.", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
24 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="", Key="7C217A064DB7F639E3F8298C014E2D91", NativeString="V-Bucks can be purchased directly from the in-game store or with a gift card available at select retailers.", LocalizedStrings=(("en","Thanks for using NeoniteV2!\r\n"),("es","Gracias por usar NeoniteV2!\r\n"),("es-419","Gracias por usar NeoniteV2!\r\n"),("pl","Thanks for using NeoniteV2!\r\n")))
25 |
26 | ;Frontend
27 | +TextReplacements=(Category=Game, Namespace="UAthenaSocialScreen", Key="BuffetHeaderDescription", NativeString="TBD Subtitle text.", LocalizedStrings=(("ar","من 7 إلى 9 أغسطس، ارتحل إلى عوالم سحرية جديدة حيث تتلاقى Fortnite مع نجوم عالميين كسروا الأرقام القياسية. انطلق إلى جولة الصدوع."),("en","From August 6-8, journey to magical new realities where Fortnite and a record-breaking superstar collide. Dive into the Rift Tour."),("de","Begib dich vom 7. bis zum 9. August auf eine Reise zu neuen, magischen Wirklichkeiten, in denen Fortnite und ein erfolgreicher Superstar aufeinandertreffen. Tauche ein in die Rift Tour."),("es","Del 7 al 9 de agosto, viaja a nuevas realidades mágicas donde Fortnite se une con una superestrella que ha batido récords. Adéntrate en el Rift Tour."),("es-419","Del 6 al 8 de agosto, viaja a nuevas y mágicas realidades donde Fortnite y una superestrella que rompe récords se reunirán. Únete al Rift Tour."),("fr","Du 7 au 9 août, explorez des réalités fabuleuses où se croisent Fortnite et une star de tous les records. Découvrez le Rift Tour."),("it","Dal 6 all'9 agosto, viaggia verso nuove magiche realtà in cui Fortnite e una superstar da record si scontrano. Tuffati nel Rift Tour."),("ja","8月7日~9日に、フォートナイトと空前のスーパースターが衝突する、魔法のような新たな現実へと旅立とう。リフトツアーに参加しよう!"),("ko","8월 7일부터 9일(한국 시간)까지, 포트나이트와 기록을 깨뜨리는 슈퍼스타가 함께하는 새로운 현실 속으로 모험을 떠나보세요! 리프트 투어에 함께해요!"),("pl","7-9 sierpnia wyrusz w nowe magiczne światy, w których spotykają się Fortnite i bijąca rekordy supergwiazda. Daj nura w Rift Tour."),("pt-BR","De 6 a 8 de agosto, embarque numa jornada até uma realidade mágica, onde os mundos do Fortnite e de uma superestrela vão colidir: a Turnê da Fenda."),("ru","С 7 по 9 августа вы сможете отправиться в путешествие по волшебным мирам благодаря появлению популярной суперзвезды в Fortnite. Посетите разлом-тур!"),("tr","7-9 Ağustos tarihleri arası, Fortnite ve piyasayı kasıp kavuran yıldızların buluştuğu büyülü yepyeni gerçekliklere doğru yola çık. Girift Turne'ye dal."),("zh-CN","8月7日至9日,开启神奇的新现实之旅,堡垒之夜与破纪录的超级巨星的碰撞。加入穿越之旅。"),("zh-Hant","8月7至9日,到達堡壘之夜和破紀錄超級巨星碰撞出的魔幻新現實。體驗穿越之旅。")))
28 | +TextReplacements=(Category=Game, Namespace="UAthenaSocialScreen", Key="SaveTheDateDisclaimer", NativeString="TBD rsvp text.", LocalizedStrings=(("ar","لا تتطلب هذه الدعوة التأكيد. ننصح الجماهير بالوصول إلى Fortnite قبل العرض بـ60 دقيقة، وقائمة ألعاب جولة الصدوع ستكون متاحة قبل كل عرض بـ30 دقيقة."),("en","This is not an RSVP. We recommend fans arrive in Fortnite 60 minutes before showtime, and the Rift Tour Playlist should be live 30 minutes before each show."),("de","Dies garantiert keine Teilnahme. Wir empfehlen allen Fans, 60 Minuten vor Showbeginn in Fortnite zu sein. Der „Rift Tour“-Modus wird voraussichtlich 30 Minuten vor jeder Show verfügbar gemacht."),("es","Esto no confirma tu asistencia. Te recomendamos entrar a Fortnite 60 min antes de la hora del evento. La cola del Rift Tour estará disponible 30 min antes de cada evento."),("es-419","Esto no confirma tu asistencia. Recomendamos que entres a Fortnite 60 minutos antes del evento. Rift Tour estará disponible 30 minutos antes de cada evento."),("fr","Place non garantie. Il est conseillé d'arriver dans Fortnite une heure avant l'événement. Le mode sera activé environ 30 minutes avant chaque représentation."),("it","Non serve conferma di partecipazione. Consigliamo ai fan di entrare in Fortnite con 60 minuti di anticipo; la playlist del Rift Tour dovrebbe essere disponibile 30 minuti prima di ogni spettacolo."),("ja","出席を迷っている場合ではありませんよ。ショータイムの60分前にはフォートナイト内で待機することをおすすめいたします。リフトツアーのプレイリストは、各ショーの30分前に有効になる予定です。"),("ko","참석 여부에 꼭 체크하지 않으셔도 됩니다. 팬들은 쇼 시간 60분 전에 포트나이트에 도착하는 것을 권장드립니다. 각 쇼가 시작하기 30분 전에 리프트 투어 플레이리스트가 재생됩니다."),("pl","To nie jest potwierdzenie obecności. Zalecamy fanom zalogowanie się w Fortnite 60 minut przed pokazem; tryb Rift Tour powinien być dostępny 30 minut przed każdym przedstawieniem."),("pt-BR","Isso não é uma confirmação. Recomendamos chegar 1 hora antes do evento. A Turnê da Fenda deve estar disponível 30 minutos antes de cada show."),("ru","Отвечать не требуется. Мы рекомендуем всем желающим войти в Fortnite за 1 час до начала представления. Режим разлом-тура будет открываться за 30 минут до начала каждого шоу."),("tr","Bu etkinlik için yer ayırtılamaz. Hayranların gösteriden 60 dakika önce Fortnite'a girmesini öneririz. Girift Turne oyun modu, her gösteriden yaklaşık yarım saat önce açılacak."),("zh-CN","无需回复。我们推荐粉丝在演出前60分钟进入堡垒之夜,穿越之旅的歌单将在演出前30分钟播出。"),("zh-Hant","無需回復。我們推薦粉絲在演出前60分鐘進入堡壘之夜,裂隙之旅的歌單將在演出前30分鐘釋出。")))
29 | +TextReplacements=(Category=Game, Namespace="", Key="C4AA44564CC8DC808546D8BF76F5924C", NativeString="TBD", LocalizedStrings=(("ar","جولة الصدوع"),("en","RIFT TOUR"),("de","RIFT TOUR"),("es","RIFT TOUR"),("es-419","RIFT TOUR"),("fr","RIFT TOUR"),("it","RIFT TOUR"),("ja","リフトツアー"),("ko","리프트 투어"),("pl","RIFT TOUR"),("pt-BR","TURNÊ DA FENDA"),("ru","РАЗЛОМ-ТУР"),("tr","GİRİFT TURNE"),("zh-CN","穿越之旅"),("zh-Hant","裂隙之旅")))
30 | +TextReplacements=(Category=Game, Namespace="", Key="FAB2B69647E72ACA43BCDF83FFEFFB88", NativeString="TBD", LocalizedStrings=(("ar","جولة الصدوع"),("en","RIFT TOUR"),("de","RIFT TOUR"),("es","RIFT TOUR"),("es-419","RIFT TOUR"),("fr","RIFT TOUR"),("it","RIFT TOUR"),("ja","リフトツアー"),("ko","리프트 투어"),("pl","RIFT TOUR"),("pt-BR","TURNÊ DA FENDA"),("ru","РАЗЛОМ-ТУР"),("tr","GİRİFT TURNE"),("zh-CN","穿越之旅"),("zh-Hant","裂隙之旅")))
31 |
32 | +TextReplacements=(Category=Game, Namespace="", Key="A136078340AE60F74647D2B7CFF23AF0", NativeString="World Cup", LocalizedStrings=(("en", "Durrr Burger"), ("ar", "برجر الأخرق"), ("es", "Hamburrrguesa"), ("ja", "ダーバーガー"), ("ko", "병맛 버거"), ("pl", "Durrr Burgerownia"), ("ru", "Фастфуд"), ("zh-CN", "多尔汉堡"), ("zh-Hant", "多爾漢堡")))
33 | +TextReplacements=(Category=Game, Namespace="", Key="3608510344974C317DAF7D8DA3FCFBAE", NativeString="Online Opens", LocalizedStrings=(("en", "Mini Game"), ("ar","لعبة صغيرة") ,("de","Mini-Spiel"),("es","Minijuego"),("es-419","Minijuego"),("fr","Mini jeu"),("it","Mini gioco"),("ja","ミニゲーム"),("ko","미니 게임"),("pl","Mini gra"),("pt-BR","Mini-jogo"),("ru","Мини-игра"),("tr","Mini Game"),("zh-CN","迷你游戏"),("zh-Hant","迷你遊戲")))
34 | +TextReplacements=(Category=Game, Namespace="", Key="9C57B7354606D962FFC7C8A7439FD3CF", NativeString="WATCH LIVE!", LocalizedStrings=(("ar",""),("en",""),("de",""),("es",""),("es-419",""),("fr",""),("it",""),("ja",""),("ko",""),("pl",""),("pt-BR",""),("ru",""),("tr",""),("zh-CN",""),("zh-Hant","")))
35 |
36 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="Fortnite.FortAthenaMatchmakingWidget", Key="Header.MatchmakingError.Number", NativeString="Matchmaking error (#1)", LocalizedStrings=(("en","Lobby Support Only (For Carbon Users)>")))
37 | +TextReplacements=(Category=Game, bIsMinimalPatch=true, Namespace="Fortnite.MatchmakingText", Key="FailedToConnectToMMS", NativeString="We had trouble talking to the matchmaker. Give it another shot, but if the problem continues, check out {CheckStatusURL}.", LocalizedStrings=(("en","Fortnite Versions that are 30.10 and above> only have support for the lobby, there is no in-game support>")))
38 |
39 |
40 | [/Script/FortniteGame.FortGameInstance]
41 | bBattleRoyaleMatchmakingEnabled=true
42 | !FrontEndPlaylistData=ClearArray
43 | +FrontEndPlaylistData=(PlaylistName=Playlist_Buffet, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
44 | +FrontEndPlaylistData=(PlaylistName=Playlist_Kiwi, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
45 | +FrontEndPlaylistData=(PlaylistName=Playlist_Yogurt, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
46 | +FrontEndPlaylistData=(PlaylistName=Playlist_Junior_32, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
47 | +FrontEndPlaylistData=(PlaylistName=Playlist_Guava, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
48 | +FrontEndPlaylistData=(PlaylistName=Playlist_Buffet, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
49 | +FrontEndPlaylistData=(PlaylistName=Playlist_Fritter_64, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
50 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_High, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
51 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Highest, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
52 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Higher, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
53 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Med, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
54 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Low, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
55 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Lowest, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
56 | +FrontEndPlaylistData=(PlaylistName=Playlist_Music_Lower, PlaylistAccess=(bEnabled=false, CategoryIndex=1, DisplayPriority=-999))
57 | +FrontEndPlaylistData=(PlaylistName=Playlist_Pumpkin, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
58 | +FrontEndPlaylistData=(PlaylistName=Playlist_Armadillo, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
59 | +FrontEndPlaylistData=(PlaylistName=Playlist_Radish, PlaylistAccess=(bEnabled=true, CategoryIndex=1, DisplayPriority=-999))
60 | +FrontEndPlaylistData=(PlaylistName=Playlist_DefaultSolo, PlaylistAccess=(bEnabled=true, CategoryIndex=0, DisplayPriority=1, bIsDefaultPlaylist=true))
61 | +FrontEndPlaylistData=(PlaylistName=Playlist_MoleGame, PlaylistAccess=(bEnabled=true, CategoryIndex=0, DisplayPriority=3, bIsDefaultPlaylist=true))
62 | +FrontEndPlaylistData=(PlaylistName=Playlist_Papaya, PlaylistAccess=(bEnabled=true, CategoryIndex=0, DisplayPriority=2, bIsDefaultPlaylist=true))
63 | +FrontEndPlaylistData=(PlaylistName=Playlist_BattleLab, PlaylistAccess=(bEnabled=true, bIsDefaultPlaylist=true, bDisplayAsLimitedTime=false, DisplayPriority=0, CategoryIndex=3))
64 | +FrontEndPlaylistData=(PlaylistName=Playlist_PlaygroundV2, PlaylistAccess=(bEnabled=true, CategoryIndex=0, DisplayPriority=4, bIsDefaultPlaylist=true))
65 | +PlaylistOverrides=(PlaylistName=Playlist_DefaultSolo, bEnabled=true)
66 | +PlaylistOverrides=(PlaylistName=Playlist_DefaultDuo, bEnabled=true)
67 | +PlaylistOverrides=(PlaylistName=Playlist_DefaultSquad, bEnabled=true)
68 | +PlaylistOverrides=(PlaylistName=Playlist_Carmine, bEnabled=true)
69 |
70 | [AssetHotfix]
71 | ;+CurveTable=/TacticalSprintGame/DataTables/TacticalSprintGameData;RowUpdate;Default.TacticalSprint.Sprint.Energy.CostPerSecond;0.0;0.0
72 |
73 |
74 | [VoiceChatManager]
75 | bEnabled=true
76 | bObtainJoinTokenFromPartyService=false
77 | MaxRetries=0
78 | VoiceChatImplementation=
79 |
80 | [/Script/FortniteGame.FortOnlineAccount]
81 | bEnableEulaCheck=false
82 | bPromptUserAndReverifyAuthToken=false
83 |
84 | [/Script/Account.OnlineAccountCommon]
85 | bEnableWaitingRoom=false
86 | bRequireLightswitchAtStartup=false
87 | AccessGrantDelaySeconds=0.0
88 |
89 | [EpicPurchaseFlow]
90 | bUsePaymentWeb=false
91 | bEnabled=true
92 |
93 | [/Script/FortniteGame.FortAnalyticsConfig]
94 | UrlEndpoint=""
95 | !AltDomains="Clear"
96 |
97 | ; Enable 50v50
98 | b50v50ForceEnabled=true
99 |
100 | ; Enable Platoon (50v50-beta)
101 | bPlatoonForceEnabled=true
102 |
103 | ; Enable ShootingTest
104 | bShootingTest3Enabled=true
105 |
106 | ; Enable Event
107 | bEvent1ForceEnabled=true
108 | bEvent2ForceEnabled=true
109 | bEvent3ForceEnabled=true
110 | bEvent4ForceEnabled=true
111 | bEvent5ForceEnabled=true
112 | bEvent6ForceEnabled=true
113 | bEvent7ForceEnabled=true
114 | bEvent8ForceEnabled=true
115 |
116 | [/Script/FortniteUI.AthenaUIStateWidget_Frontend]
117 | !FirstTimeSeasonFlowStepArray=ClearArray
118 | +FirstTimeSeasonFlowStepArray=""
119 |
--------------------------------------------------------------------------------
/hotfixes/DefaultInput.ini:
--------------------------------------------------------------------------------
1 | [/Script/Engine.InputSettings]
2 | +ConsoleKeys=Tilde
3 | +ConsoleKeys=F8
4 |
5 |
--------------------------------------------------------------------------------
/hotfixes/DefaultRuntimeOptions.ini:
--------------------------------------------------------------------------------
1 | [/Script/FortniteGame.FortRuntimeOptions]
2 | !ExperimentalCohortPercent=ClearArray
3 | !AthenaStarterGameMode=ClearArray
4 | !HiddenSettings=ClearArray
5 | !DisabledFrontendNavigationTabs=ClearArray
6 | bEnableBlockedList=false
7 | bShouldSkipAvailabilityCheck=true
8 | bShowStoreBanner=false
9 | bEnableCatabaDynamicBackground=false
10 | bShouldAllowNightNightMode=true
11 | bForceEverybodyToGoNightNight=false
12 | bEnableSocialTab=false
13 | bSkipTrailerMovie=true
14 | bEnableRufusOnePageShop=false
15 | bIsOutOfSeasonMode=false
16 | bHabaneroEnabled=false
17 | bEnableFortniteInterests=false
18 | bEnableLiveStream=true
19 | +HiddenSettings=""
20 | +AthenaStarterGameMode="Playlist_DefaultSolo"
21 | +AthenaStarterGameMode="Playlist_DefaultDuo"
22 | +AthenaStarterGameMode="Playlist_DefaultSquad"
23 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=14)
24 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=15)
25 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=20)
26 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=21)
27 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=22)
28 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=23)
29 | +ExperimentalCohortPercent=(CohortPercent=100,ExperimentNum=24)
30 | ;+ExperimentalBucketPercentList=(ExperimentNum=27,Name="ShowMultiProductItemShop",BucketPercents=(100,0,0),WinningBucketIndex=-1)
31 | +ExperimentalBucketPercentList=(ExperimentNum=35,Name="AllowShopBillboardOfferGroups",BucketPercents=(0,100))
32 | +ExperimentalBucketPercentList=(ExperimentNum=36,Name="AllowShopBillboardOfferGroups_2",BucketPercents=(0,100))
33 | +ExperimentalBucketPercentList=(ExperimentNum=37,Name="AllowShopBillboardOfferGroups_3",BucketPercents=(0,100))
34 | +ExperimentalBucketPercentList=(ExperimentNum=38,Name="AllowShopBillboardOfferGroups_4",BucketPercents=(0,100))
35 | +ExperimentalBucketPercentList=(ExperimentNum=39,Name="AllowShopBillboardOfferGroups_5",BucketPercents=(0,100))
36 | +ExperimentalBucketPercentList=(ExperimentNum=40,Name="AllowShopBillboardOfferGroups_6",BucketPercents=(0,100))
37 | +ExperimentalBucketPercentList=(ExperimentNum=41,Name="AllowShopBillboardOfferGroups_7",BucketPercents=(0,100))
38 | +ExperimentalBucketPercentList=(ExperimentNum=42,Name="AllowShopBillboardOfferGroups_8",BucketPercents=(0,100))
39 | +ExperimentalBucketPercentList=(ExperimentNum=43,Name="AllowShopBillboardOfferGroups_9",BucketPercents=(0,100))
40 | +ExperimentalBucketPercentList=(ExperimentNum=44,Name="AllowShopBillboardOfferGroups_10",BucketPercents=(0,100))
41 | +ExperimentalBucketPercentList=(ExperimentNum=45,Name="AllowShopBillboardOfferGroups_11",BucketPercents=(0,100))
42 | +ExperimentalBucketPercentList=(ExperimentNum=46,Name="AllowShopBillboardOfferGroups_12",BucketPercents=(0,100))
43 | +ExperimentalBucketPercentList=(ExperimentNum=47,Name="AllowShopBillboardOfferGroups_13",BucketPercents=(0,100))
44 | +ExperimentalBucketPercentList=(ExperimentNum=48,Name="AllowShopBillboardOfferGroups_14",BucketPercents=(0,100))
45 | +ExperimentalBucketPercentList=(ExperimentNum=49,Name="AllowShopBillboardOfferGroups_15",BucketPercents=(0,100))
46 | +ExperimentalBucketPercentList=(ExperimentNum=50,Name="AllowShopBillboardOfferGroups_16",BucketPercents=(0,100))
47 | +ExperimentalBucketPercentList=(ExperimentNum=51,Name="AllowShopBillboardOfferGroups_17",BucketPercents=(0,100))
48 | +ExperimentalBucketPercentList=(ExperimentNum=52,Name="AllowShopBillboardOfferGroups_18",BucketPercents=(0,100))
49 | +ExperimentalBucketPercentList=(ExperimentNum=53,Name="AllowShopBillboardOfferGroups_19",BucketPercents=(0,100))
50 | +ExperimentalBucketPercentList=(ExperimentNum=54,Name="AllowShopBillboardOfferGroups_20",BucketPercents=(0,100))
51 | +ExperimentalBucketPercentList=(ExperimentNum=55,Name="AllowShopBillboardOfferGroups_21",BucketPercents=(0,100))
52 | +ExperimentalBucketPercentList=(ExperimentNum=56,Name="AllowShopBillboardOfferGroups_22",BucketPercents=(0,100))
53 | +ExperimentalBucketPercentList=(ExperimentNum=57,Name="AllowShopBillboardOfferGroups_23",BucketPercents=(0,100))
54 | +ExperimentalBucketPercentList=(ExperimentNum=58,Name="AllowShopBillboardOfferGroups_24",BucketPercents=(0,100))
55 | +ExperimentalBucketPercentList=(ExperimentNum=59,Name="AllowShopBillboardOfferGroups_25",BucketPercents=(0,100))
56 | +ExperimentalBucketPercentList=(ExperimentNum=60,Name="AllowShopBillboardOfferGroups_26",BucketPercents=(0,100))
57 | +ExperimentalBucketPercentList=(ExperimentNum=61,Name="AllowShopBillboardOfferGroups_27",BucketPercents=(0,100))
58 | +ExperimentalBucketPercentList=(ExperimentNum=62,Name="AllowShopBillboardOfferGroups_28",BucketPercents=(0,100))
59 | +ExperimentalBucketPercentList=(ExperimentNum=63,Name="AllowShopBillboardOfferGroups_29",BucketPercents=(0,100))
60 | +ExperimentalBucketPercentList=(ExperimentNum=64,Name="AllowShopBillboardOfferGroups_30",BucketPercents=(0,100))
61 | +ExperimentalBucketPercentList=(ExperimentNum=65,Name="AllowShopBillboardOfferGroups_31",BucketPercents=(0,100))
62 | +ExperimentalBucketPercentList=(ExperimentNum=66,Name="AllowShopBillboardOfferGroups_32",BucketPercents=(0,100))
63 | +ExperimentalBucketPercentList=(ExperimentNum=67,Name="AllowShopBillboardOfferGroups_33",BucketPercents=(0,100))
64 | +ExperimentalBucketPercentList=(ExperimentNum=68,Name="AllowShopBillboardOfferGroups_34",BucketPercents=(0,100))
65 | +ExperimentalBucketPercentList=(ExperimentNum=69,Name="AllowShopBillboardOfferGroups_35",BucketPercents=(0,100))
66 | +ExperimentalBucketPercentList=(ExperimentNum=70,Name="AllowShopBillboardOfferGroups_36",BucketPercents=(0,100))
67 | +ExperimentalBucketPercentList=(ExperimentNum=71,Name="AllowShopBillboardOfferGroups_37",BucketPercents=(0,100))
68 | +ExperimentalBucketPercentList=(ExperimentNum=72,Name="AllowShopBillboardOfferGroups_38",BucketPercents=(0,100))
69 | +ExperimentalBucketPercentList=(ExperimentNum=73,Name="AllowShopBillboardOfferGroups_39",BucketPercents=(0,100))
70 | +ExperimentalBucketPercentList=(ExperimentNum=74,Name="AllowShopBillboardOfferGroups_40",BucketPercents=(0,100))
71 | +ExperimentalBucketPercentList=(ExperimentNum=75,Name="AllowShopBillboardOfferGroups_41",BucketPercents=(0,100))
72 | +ExperimentalBucketPercentList=(ExperimentNum=76,Name="AllowShopBillboardOfferGroups_42",BucketPercents=(0,100))
73 | +ExperimentalBucketPercentList=(ExperimentNum=77,Name="AllowShopBillboardOfferGroups_43",BucketPercents=(0,100))
74 | +ExperimentalBucketPercentList=(ExperimentNum=78,Name="AllowShopBillboardOfferGroups_44",BucketPercents=(0,100))
75 | +ExperimentalBucketPercentList=(ExperimentNum=79,Name="AllowShopBillboardOfferGroups_45",BucketPercents=(0,100))
76 | +ExperimentalBucketPercentList=(ExperimentNum=80,Name="AllowShopBillboardOfferGroups_46",BucketPercents=(0,100))
77 | +ExperimentalBucketPercentList=(ExperimentNum=81,Name="AllowShopBillboardOfferGroups_47",BucketPercents=(0,100))
78 | +ExperimentalBucketPercentList=(ExperimentNum=82,Name="AllowShopBillboardOfferGroups_48",BucketPercents=(0,100))
79 | +ExperimentalBucketPercentList=(ExperimentNum=83,Name="AllowShopBillboardOfferGroups_49",BucketPercents=(0,100))
80 | +ExperimentalBucketPercentList=(ExperimentNum=84,Name="AllowShopBillboardOfferGroups_50",BucketPercents=(0,100))
81 | +ExperimentalBucketPercentList=(ExperimentNum=85,Name="AllowShopBillboardOfferGroups_51",BucketPercents=(0,100))
82 | +ExperimentalBucketPercentList=(ExperimentNum=86,Name="AllowShopBillboardOfferGroups_52",BucketPercents=(0,100))
83 | +ExperimentalBucketPercentList=(ExperimentNum=87,Name="AllowShopBillboardOfferGroups_53",BucketPercents=(0,100))
84 | +ExperimentalBucketPercentList=(ExperimentNum=88,Name="AllowShopBillboardOfferGroups_54",BucketPercents=(0,100))
85 | +ExperimentalBucketPercentList=(ExperimentNum=89,Name="AllowShopBillboardOfferGroups_55",BucketPercents=(0,100))
86 | +ExperimentalBucketPercentList=(ExperimentNum=90,Name="AllowShopBillboardOfferGroups_56",BucketPercents=(0,100))
87 | +ExperimentalBucketPercentList=(ExperimentNum=91,Name="AllowShopBillboardOfferGroups_57",BucketPercents=(0,100))
88 | +ExperimentalBucketPercentList=(ExperimentNum=92,Name="AllowShopBillboardOfferGroups_58",BucketPercents=(0,100))
89 | +ExperimentalBucketPercentList=(ExperimentNum=93,Name="AllowShopBillboardOfferGroups_59",BucketPercents=(0,100))
90 | +ExperimentalBucketPercentList=(ExperimentNum=94,Name="AllowShopBillboardOfferGroups_60",BucketPercents=(0,100))
91 | +ExperimentalBucketPercentList=(ExperimentNum=95,Name="AllowShopBillboardOfferGroups_61",BucketPercents=(0,100))
92 | +ExperimentalBucketPercentList=(ExperimentNum=96,Name="AllowShopBillboardOfferGroups_62",BucketPercents=(0,100))
93 | +ExperimentalBucketPercentList=(ExperimentNum=97,Name="AllowShopBillboardOfferGroups_63",BucketPercents=(0,100))
94 | +ExperimentalBucketPercentList=(ExperimentNum=98,Name="AllowShopBillboardOfferGroups_64",BucketPercents=(0,100))
95 | +ExperimentalBucketPercentList=(ExperimentNum=99,Name="AllowShopBillboardOfferGroups_65",BucketPercents=(0,100))
96 | +ExperimentalBucketPercentList=(ExperimentNum=100,Name="AllowShopBillboardOfferGroups_66",BucketPercents=(0,100))
97 | +ExperimentalBucketPercentList=(ExperimentNum=101,Name="AllowShopBillboardOfferGroups_67",BucketPercents=(0,100))
98 | +ExperimentalBucketPercentList=(ExperimentNum=102,Name="AllowShopBillboardOfferGroups_68",BucketPercents=(0,100))
99 | +ExperimentalBucketPercentList=(ExperimentNum=103,Name="AllowShopBillboardOfferGroups_69",BucketPercents=(0,100))
100 | +ExperimentalBucketPercentList=(ExperimentNum=104,Name="AllowShopBillboardOfferGroups_70",BucketPercents=(0,100))
101 | +ExperimentalBucketPercentList=(ExperimentNum=105,Name="AllowShopBillboardOfferGroups_71",BucketPercents=(0,100))
102 | +ExperimentalBucketPercentList=(ExperimentNum=106,Name="AllowShopBillboardOfferGroups_72",BucketPercents=(0,100))
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "neonite",
3 | "version": "1.6.5",
4 | "description": "A Fortnite Private Server",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "NODE_ENV=production node app.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/HybridFNBR/Neonite.git"
12 | },
13 | "author": "kemo",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/HybridFNBR/Neonite/issues"
17 | },
18 | "homepage": "https://github.com/HybridFNBR/Neonite#readme",
19 | "dependencies": {
20 | "axios": "^1.8.4",
21 | "ini": "^5.0.0",
22 | "jsonwebtoken": "^9.0.2",
23 | "path": "^0.12.7",
24 | "sails": "^1.5.14",
25 | "uuid": "^11.1.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/profile.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | // @author armisto#2174
5 | module.exports = {
6 | /**
7 | * Adds an item to the profile JSON.
8 | *
9 | * @param {any} profile the profile object, loaded from a file
10 | * @param {string} itemId the item ID of the item to add
11 | * @param {any} item the item object to add
12 | * @param {any[]} profileChangesArr (optional) if a change is made, adds a profile change entry to the profileChanges array
13 | * @returns {boolean} if the a change is made
14 | */
15 | addItem(profile, itemId, item, profileChangesArr) {
16 | if (profile.items[itemId]) {
17 | return false;
18 | }
19 |
20 | profile.items[itemId] = item;
21 |
22 | if (profileChangesArr) {
23 | profileChangesArr.push({ "changeType": "itemAdded", "itemId": itemId, "item": item });
24 | }
25 |
26 | return true;
27 | },
28 |
29 | /**
30 | * Removes an item from the profile JSON.
31 | *
32 | * @param {any} profile the profile object, loaded from a file
33 | * @param {string} itemId the item ID of the item to remove
34 | * @param {any[]} profileChangesArr (optional) if a change is made, adds a profile change entry to the profileChanges array
35 | * @returns {boolean} if the a change is made
36 | */
37 | removeItem(profile, itemId, profileChangesArr) {
38 | if (!profile.items[itemId]) {
39 | return false;
40 | }
41 | //commented it to prevent removing gift item from the profile
42 | //delete profile.items[itemId];
43 |
44 | delete profile.items[itemId];
45 |
46 | if (profileChangesArr) {
47 | profileChangesArr.push({ "changeType": "itemRemoved", "itemId": itemId });
48 | }
49 |
50 | return true;
51 | },
52 |
53 | /**
54 | * Modifies the quantity of a specified item in the profile JSON.
55 | *
56 | * @param {any} profile the profile object, loaded from a file
57 | * @param {string} itemId the item ID
58 | * @param {number} quantity the new quantity
59 | * @param {any[]} profileChangesArr (optional) if a change is made, adds a profile change entry to the profileChanges array
60 | * @returns {boolean} if the a change is made
61 | */
62 | changeItemQuantity(profile, itemId, newQuantity, profileChangesArr) {
63 | if (!profile.items[itemId] || profile.items[itemId].quantity == newQuantity) {
64 | return false;
65 | }
66 |
67 | profile.items[itemId].quantity = newQuantity;
68 |
69 | if (profileChangesArr != null) {
70 | profileChangesArr.push({ "changeType": "itemQuantityChanged", "itemId": itemId, "quantity": newQuantity });
71 | }
72 |
73 | return true;
74 | },
75 |
76 | /**
77 | * Modifies an attribute value of a specified item in the profile JSON.
78 | *
79 | * @param {any} profile the profile object, loaded from a file
80 | * @param {string} itemId the item ID of the item to have one of its attributes' value changed
81 | * @param {string} attributeName the item attribute property name
82 | * @param {any} attributeValue the new attribute value
83 | * @param {any[]} profileChangesArr (optional) if a change is made, adds a profile change entry to the profileChanges array
84 | * @returns {boolean} if the a change is made
85 | */
86 | changeItemAttribute(profile, itemId, attributeName, attributeValue, profileChangesArr) {
87 | var item = profile.items[itemId];
88 |
89 | if (!item) {
90 | return false;
91 | }
92 |
93 | if (!item.attributes) {
94 | item.attributes = {};
95 | } /*else if (item.attributes[attributeName] == attributeValue) {
96 | return false;
97 | }*/
98 |
99 | item.attributes[attributeName] = attributeValue;
100 |
101 | if (profileChangesArr != null) {
102 | profileChangesArr.push({ "changeType": "itemAttrChanged", "itemId": itemId, "attributeName": attributeName, "attributeValue": attributeValue });
103 | }
104 |
105 | return true;
106 | },
107 |
108 | /**
109 | * Modifies the a stat value of the profile JSON.
110 | *
111 | * @param {any} profile the profile object, loaded from a file
112 | * @param {string} statName the stat property name
113 | * @param {any} statValue the stat value to modify to
114 | * @param {any[]} profileChangesArr (optional) if a change is made, adds a profile change entry to the profileChanges array
115 | * @returns {boolean} if the a change is made
116 | */
117 | modifyStat(profile, statName, statValue, profileChangesArr) {
118 | if (!statName || statValue == null) {
119 | return false;
120 | }
121 | if (!profile.stats) {
122 | profile.stats = {
123 | attributes: {
124 |
125 | }
126 | }
127 | }
128 |
129 | profile.stats.attributes[statName] = statValue;
130 |
131 | if (profileChangesArr != null) {
132 | profileChangesArr.push({ "changeType": "statModified", "name": statName, "value": statValue });
133 | }
134 |
135 | return true;
136 | },
137 |
138 | /**
139 | * Changes the updated date of the profile and its revision so the client can detect if it has been changed.
140 | *
141 | * @param {any} profile the profile object, loaded from a file
142 | * @returns {any} the supplied profile object
143 | */
144 | bumpRvn(profile) {
145 | profile.rvn += 1;
146 | profile.updated = new Date().toISOString();
147 | profile.commandRevision += 1;
148 | return profile;
149 | },
150 |
151 | readProfile(accountId, profileId) {
152 | try {
153 | return JSON.parse(fs.readFileSync(path.join(__dirname, `/profile/${accountId}/profiles/profile_${profileId}.json`), "utf8"));
154 | } catch (e) {
155 | return null;
156 | }
157 | },
158 | readProfileTemplate(profileId) {
159 | try {
160 | return JSON.parse(fs.readFileSync(path.join(__dirname, `/profile_template/profiles/profile_${profileId}.json`), "utf8"));
161 | } catch (e) {
162 | return null;
163 | }
164 | },
165 |
166 | saveProfile(accountId, profileId, data) {
167 | fs.writeFileSync(path.join(__dirname, `/profile/${accountId}/profiles/profile_${profileId}.json`), JSON.stringify(data, null, 2));
168 | },
169 |
170 | readLockerProfile(accountId, version) {
171 | try {
172 | return JSON.parse(fs.readFileSync(path.join(__dirname, `/profile/${accountId}/profiles/lockerv${version}.json`), "utf8"));
173 | } catch (e) {
174 | return null;
175 | }
176 | },
177 |
178 | readLockerTemplate(version) {
179 | try {
180 | return JSON.parse(fs.readFileSync(path.join(__dirname, `/profile_template/profiles/lockerv${version}.json`), "utf8"));
181 | } catch (e) {
182 | return null;
183 | }
184 | },
185 |
186 | saveLocker(accountId, version, data) {
187 | fs.writeFileSync(path.join(__dirname, `/profile/${accountId}/profiles/lockerv${version}.json`), JSON.stringify(data, null, 2));
188 | },
189 | };
190 |
--------------------------------------------------------------------------------
/profile_template/profiles/lockerv3.json:
--------------------------------------------------------------------------------
1 | {
2 | "activeLoadouts": [
3 | {
4 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
5 | "accountId": "",
6 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Character",
7 | "loadoutShuffleType": "DISABLED",
8 | "athenaItemId": "9f8b0158-cd20-45de-89ec-b13ebdbe673e",
9 | "creationTime": "2024-09-28T18:51:50.625635849Z",
10 | "updatedTime": "2024-10-01T18:40:37.919880958Z",
11 | "loadoutSlots": [
12 | {
13 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Character"
14 | },
15 | {
16 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Backpack"
17 | },
18 | {
19 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Pickaxe"
20 | },
21 | {
22 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Glider"
23 | },
24 | {
25 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Contrails"
26 | },
27 | {
28 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Aura"
29 | }
30 | ]
31 | },
32 | {
33 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
34 | "accountId": "",
35 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Emotes",
36 | "loadoutShuffleType": "DISABLED",
37 | "athenaItemId": "445776d5-443d-4c68-b2bc-c954ae8d89bd",
38 | "creationTime": "2024-09-06T01:20:31.897950673Z",
39 | "updatedTime": "2024-09-06T01:20:31.897948266Z",
40 | "loadoutSlots": [
41 | {
42 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_0"
43 | },
44 | {
45 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_1"
46 | },
47 | {
48 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_2"
49 | },
50 | {
51 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_3"
52 | },
53 | {
54 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_4"
55 | },
56 | {
57 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_5"
58 | }
59 | ]
60 | },
61 | {
62 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
63 | "accountId": "",
64 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Jam",
65 | "loadoutShuffleType": "DISABLED",
66 | "athenaItemId": "90d5cb1b-193e-4826-8d86-8dc610e6210b",
67 | "creationTime": "2024-08-25T08:23:22.258337756Z",
68 | "updatedTime": "2024-08-25T08:23:22.258337756Z",
69 | "loadoutSlots": [
70 | {
71 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong0"
72 | },
73 | {
74 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong1"
75 | },
76 | {
77 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong2"
78 | },
79 | {
80 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong3"
81 | },
82 | {
83 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong4"
84 | },
85 | {
86 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong5"
87 | },
88 | {
89 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong6"
90 | },
91 | {
92 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong7"
93 | }
94 | ]
95 | },
96 | {
97 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
98 | "accountId": "",
99 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Platform",
100 | "loadoutShuffleType": "DISABLED",
101 | "athenaItemId": "c91a76e2-d253-4430-967e-c6f85e789504",
102 | "creationTime": "2024-09-20T12:08:48.330010155Z",
103 | "updatedTime": "2024-09-20T12:08:48.330006572Z",
104 | "loadoutSlots": [
105 | {
106 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Banner_Icon"
107 | },
108 | {
109 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Banner_Color",
110 | "equippedItemId": "HomebaseBannerColor:defaultcolor16"
111 | },
112 | {
113 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_LobbyMusic",
114 | "equippedItemId": "AthenaMusicPack:musicpack_217_anglepatch"
115 | },
116 | {
117 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_LoadingScreen"
118 | }
119 | ]
120 | },
121 | {
122 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
123 | "accountId": "",
124 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Sparks",
125 | "loadoutShuffleType": "DISABLED",
126 | "athenaItemId": "1f799b1c-94fb-45a0-b973-3762c5f3d604",
127 | "creationTime": "2024-08-25T08:23:22.258345161Z",
128 | "updatedTime": "2024-08-25T08:23:22.258345161Z",
129 | "loadoutSlots": [
130 | {
131 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Bass"
132 | },
133 | {
134 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Guitar"
135 | },
136 | {
137 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Drum"
138 | },
139 | {
140 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Keyboard"
141 | },
142 | {
143 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Microphone"
144 | }
145 | ]
146 | },
147 | {
148 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
149 | "accountId": "",
150 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Vehicle",
151 | "loadoutShuffleType": "DISABLED",
152 | "athenaItemId": "accff498-a285-4112-b4a0-6301dd93f3fb",
153 | "creationTime": "2024-08-25T08:23:22.258354190Z",
154 | "updatedTime": "2024-08-25T08:23:22.258354190Z",
155 | "loadoutSlots": [
156 | {
157 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Body"
158 |
159 | },
160 | {
161 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Booster"
162 | },
163 | {
164 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_DriftSmoke"
165 | },
166 | {
167 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Wheel"
168 | },
169 | {
170 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Skin"
171 | }
172 | ]
173 | },
174 | {
175 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
176 | "accountId": "",
177 | "loadoutType": "CosmeticLoadout:LoadoutSchema_Wraps",
178 | "loadoutShuffleType": "DISABLED",
179 | "athenaItemId": "162ce736-450d-49c9-8cb7-a8772a84a0ce",
180 | "creationTime": "2024-08-25T08:23:22.258323263Z",
181 | "updatedTime": "2024-08-25T08:23:22.258323263Z",
182 | "loadoutSlots": [
183 | {
184 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_0"
185 | },
186 | {
187 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_1"
188 | },
189 | {
190 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_2"
191 | },
192 | {
193 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_3"
194 | },
195 | {
196 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_4"
197 | },
198 | {
199 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_5"
200 | },
201 | {
202 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_6"
203 | }
204 | ]
205 | }
206 | ],
207 | "loadoutPresets": []
208 | }
--------------------------------------------------------------------------------
/profile_template/profiles/lockerv4.json:
--------------------------------------------------------------------------------
1 | {
2 | "activeLoadoutGroup": {
3 | "accountId": "",
4 | "deploymentId": "62a9473a2dca46b29ccf17577fcf42d7",
5 | "athenaItemId": "0386beaa-f3ff-4a5e-9ba5-1cb005a7b2e1",
6 | "creationTime": "2024-11-02T07:00:34.827224850Z",
7 | "updatedTime": "2024-11-02T07:03:42.790654805Z",
8 | "loadouts": {
9 | "CosmeticLoadout:LoadoutSchema_Emotes": {
10 | "loadoutSlots": [
11 | {
12 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_0",
13 | "equippedItemId":"",
14 | "itemCustomizations": []
15 | },
16 | {
17 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_1",
18 | "equippedItemId":"",
19 | "itemCustomizations": []
20 | },
21 | {
22 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_2",
23 | "equippedItemId":"",
24 | "itemCustomizations": []
25 | },
26 | {
27 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_3",
28 | "equippedItemId":"",
29 | "itemCustomizations": []
30 | },
31 | {
32 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_4",
33 | "equippedItemId":"",
34 | "itemCustomizations": []
35 | },
36 | {
37 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Emote_5",
38 | "equippedItemId":"",
39 | "itemCustomizations": []
40 | }
41 | ],
42 | "shuffleType": "DISABLED"
43 | },
44 | "CosmeticLoadout:LoadoutSchema_Platform": {
45 | "loadoutSlots": [
46 | {
47 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Banner_Icon",
48 | "equippedItemId":"",
49 | "itemCustomizations": []
50 | },
51 | {
52 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Banner_Color",
53 | "equippedItemId":"",
54 | "itemCustomizations": []
55 | },
56 | {
57 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_LobbyMusic",
58 | "equippedItemId":"",
59 | "itemCustomizations": []
60 | },
61 | {
62 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_LoadingScreen",
63 | "equippedItemId":"",
64 | "itemCustomizations": []
65 | }
66 | ],
67 | "shuffleType": "DISABLED"
68 | },
69 | "CosmeticLoadout:LoadoutSchema_Sparks": {
70 | "loadoutSlots": [
71 | {
72 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Bass",
73 | "equippedItemId":"",
74 | "itemCustomizations": []
75 | },
76 | {
77 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Guitar",
78 | "equippedItemId":"",
79 | "itemCustomizations": []
80 | },
81 | {
82 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Drum",
83 | "equippedItemId":"",
84 | "itemCustomizations": []
85 | },
86 | {
87 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Keyboard",
88 | "equippedItemId":"",
89 | "itemCustomizations": []
90 | },
91 | {
92 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Microphone",
93 | "equippedItemId":"",
94 | "itemCustomizations": []
95 | }
96 | ],
97 | "shuffleType": "DISABLED"
98 | },
99 | "CosmeticLoadout:LoadoutSchema_Wraps": {
100 | "loadoutSlots": [
101 | {
102 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_0",
103 | "equippedItemId":"",
104 | "itemCustomizations": []
105 | },
106 | {
107 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_1",
108 | "equippedItemId":"",
109 | "itemCustomizations": []
110 | },
111 | {
112 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_2",
113 | "equippedItemId":"",
114 | "itemCustomizations": []
115 | },
116 | {
117 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_3",
118 | "equippedItemId":"",
119 | "itemCustomizations": []
120 | },
121 | {
122 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_4",
123 | "equippedItemId":"",
124 | "itemCustomizations": []
125 | },
126 | {
127 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_5",
128 | "equippedItemId":"",
129 | "itemCustomizations": []
130 | },
131 | {
132 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Wrap_6",
133 | "equippedItemId":"",
134 | "itemCustomizations": []
135 | }
136 | ],
137 | "shuffleType": "DISABLED"
138 | },
139 | "CosmeticLoadout:LoadoutSchema_Jam": {
140 | "loadoutSlots": [
141 | {
142 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong0",
143 | "equippedItemId":"",
144 | "itemCustomizations": []
145 | },
146 | {
147 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong1",
148 | "equippedItemId":"",
149 | "itemCustomizations": []
150 | },
151 | {
152 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong2",
153 | "equippedItemId":"",
154 | "itemCustomizations": []
155 | },
156 | {
157 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong3",
158 | "equippedItemId":"",
159 | "itemCustomizations": []
160 | },
161 | {
162 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong4",
163 | "equippedItemId":"",
164 | "itemCustomizations": []
165 | },
166 | {
167 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong5",
168 | "equippedItemId":"",
169 | "itemCustomizations": []
170 | },
171 | {
172 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong6",
173 | "equippedItemId":"",
174 | "itemCustomizations": []
175 | },
176 | {
177 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_JamSong7",
178 | "equippedItemId":"",
179 | "itemCustomizations": []
180 | }
181 | ],
182 | "shuffleType": "DISABLED"
183 | },
184 | "CosmeticLoadout:LoadoutSchema_Vehicle": {
185 | "loadoutSlots": [
186 | {
187 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Body",
188 | "equippedItemId":"",
189 | "itemCustomizations": []
190 | },
191 | {
192 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Booster",
193 | "equippedItemId":"",
194 | "itemCustomizations": []
195 | },
196 | {
197 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_DriftSmoke",
198 | "equippedItemId":"",
199 | "itemCustomizations": []
200 | },
201 | {
202 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Wheel",
203 | "equippedItemId":"",
204 | "itemCustomizations": []
205 | },
206 | {
207 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Vehicle_Skin",
208 | "equippedItemId":"",
209 | "itemCustomizations": []
210 | }
211 | ],
212 | "shuffleType": "DISABLED"
213 | },
214 | "CosmeticLoadout:LoadoutSchema_Character": {
215 | "loadoutSlots": [
216 | {
217 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Character",
218 | "equippedItemId":"",
219 | "itemCustomizations": []
220 | },
221 | {
222 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Backpack",
223 | "equippedItemId":"",
224 | "itemCustomizations": []
225 | },
226 | {
227 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Pickaxe",
228 | "equippedItemId":"",
229 | "itemCustomizations": []
230 | },
231 | {
232 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Glider",
233 | "equippedItemId":"",
234 | "itemCustomizations": []
235 | },
236 | {
237 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Contrails",
238 | "equippedItemId":"",
239 | "itemCustomizations": []
240 | },
241 | {
242 | "slotTemplate": "CosmeticLoadoutSlotTemplate:LoadoutSlot_Aura",
243 | "equippedItemId":"",
244 | "itemCustomizations": []
245 | }
246 | ],
247 | "shuffleType": "DISABLED"
248 | }
249 | },
250 | "shuffleType": "DISABLED"
251 | },
252 | "loadoutGroupPresets": [],
253 | "loadoutPresets": []
254 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_athena.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "athena",
10 | "version": "neonite_2",
11 | "items": {
12 | "sandbox_loadout": {
13 | "templateId": "CosmeticLocker:cosmeticlocker_athena",
14 | "attributes": {
15 | "locker_slots_data": {
16 | "slots": {
17 | "Pickaxe": {
18 | "items": [
19 | "AthenaPickaxe:DefaultPickaxe"
20 | ],
21 | "activeVariants": []
22 | },
23 | "Dance": {
24 | "items": [
25 | "AthenaDance:EID_BoogieDown",
26 | "AthenaDance:EID_DanceMoves",
27 | "",
28 | "",
29 | "",
30 | ""
31 | ]
32 | },
33 | "Glider": {
34 | "items": [
35 | "AthenaGlider:DefaultGlider"
36 | ]
37 | },
38 | "Character": {
39 | "items": [
40 | "AthenaCharacter:CID_001_Athena_Commando_F_Default"
41 | ],
42 | "activeVariants": [
43 | {
44 | "variants": []
45 | }
46 | ]
47 | },
48 | "Backpack": {
49 | "items": [
50 | ""
51 | ],
52 | "activeVariants": [
53 | {
54 | "variants": []
55 | }
56 | ]
57 | },
58 | "ItemWrap": {
59 | "items": [
60 | "",
61 | "",
62 | "",
63 | "",
64 | "",
65 | "",
66 | ""
67 | ],
68 | "activeVariants": [
69 | null,
70 | null,
71 | null,
72 | null,
73 | null,
74 | null,
75 | null
76 | ]
77 | },
78 | "LoadingScreen": {
79 | "items": [
80 | ""
81 | ],
82 | "activeVariants": [
83 | null
84 | ]
85 | },
86 | "MusicPack": {
87 | "items": [
88 | ""
89 | ],
90 | "activeVariants": [
91 | null
92 | ]
93 | },
94 | "SkyDiveContrail": {
95 | "items": [
96 | ""
97 | ],
98 | "activeVariants": [
99 | null
100 | ]
101 | }
102 | }
103 | },
104 | "use_count": 1,
105 | "banner_icon_template": "",
106 | "locker_name": "",
107 | "banner_color_template": "",
108 | "item_seen": false,
109 | "favorite": false
110 | },
111 | "quantity": 1
112 | },
113 | "neoset0_loadout": {
114 | "templateId": "CosmeticLocker:cosmeticlocker_athena",
115 | "attributes": {
116 | "locker_slots_data": {
117 | "slots": {
118 | "Pickaxe": {
119 | "items": [
120 | ""
121 | ],
122 | "activeVariants": []
123 | },
124 | "Dance": {
125 | "items": [
126 | "",
127 | "",
128 | "",
129 | "",
130 | "",
131 | ""
132 | ]
133 | },
134 | "Glider": {
135 | "items": [
136 | ""
137 | ]
138 | },
139 | "Character": {
140 | "items": [
141 | ""
142 | ],
143 | "activeVariants": [
144 | {
145 | "variants": []
146 | }
147 | ]
148 | },
149 | "Backpack": {
150 | "items": [
151 | ""
152 | ],
153 | "activeVariants": [
154 | {
155 | "variants": []
156 | }
157 | ]
158 | },
159 | "ItemWrap": {
160 | "items": [
161 | "",
162 | "",
163 | "",
164 | "",
165 | "",
166 | "",
167 | ""
168 | ],
169 | "activeVariants": [
170 | null,
171 | null,
172 | null,
173 | null,
174 | null,
175 | null,
176 | null
177 | ]
178 | },
179 | "LoadingScreen": {
180 | "items": [
181 | ""
182 | ],
183 | "activeVariants": [
184 | null
185 | ]
186 | },
187 | "MusicPack": {
188 | "items": [
189 | ""
190 | ],
191 | "activeVariants": [
192 | null
193 | ]
194 | },
195 | "SkyDiveContrail": {
196 | "items": [
197 | ""
198 | ],
199 | "activeVariants": [
200 | null
201 | ]
202 | }
203 | }
204 | },
205 | "use_count": 1,
206 | "banner_icon_template": "",
207 | "locker_name": "NEOSET",
208 | "banner_color_template": "",
209 | "item_seen": false,
210 | "favorite": false
211 | },
212 | "quantity": 1
213 | },
214 | "AthenaCharacter:CID_001_Athena_Commando_F_Default": {
215 | "attributes": {
216 | "favorite": false,
217 | "item_seen": true,
218 | "level": 0,
219 | "max_level_bonus": 0,
220 | "rnd_sel_cnt": 0,
221 | "variants": [],
222 | "xp": 0
223 | },
224 | "templateId": "AthenaCharacter:CID_001_Athena_Commando_F_Default"
225 | },
226 | "AthenaCharacter:CID_002_Athena_Commando_F_Default": {
227 | "attributes": {
228 | "favorite": false,
229 | "item_seen": true,
230 | "level": 0,
231 | "max_level_bonus": 0,
232 | "rnd_sel_cnt": 0,
233 | "variants": [],
234 | "xp": 0
235 | },
236 | "templateId": "AthenaCharacter:CID_002_Athena_Commando_F_Default"
237 | },
238 | "AthenaCharacter:CID_003_Athena_Commando_F_Default": {
239 | "attributes": {
240 | "favorite": false,
241 | "item_seen": true,
242 | "level": 0,
243 | "max_level_bonus": 0,
244 | "rnd_sel_cnt": 0,
245 | "variants": [],
246 | "xp": 0
247 | },
248 | "templateId": "AthenaCharacter:CID_003_Athena_Commando_F_Default"
249 | },
250 | "AthenaCharacter:CID_004_Athena_Commando_F_Default": {
251 | "attributes": {
252 | "favorite": false,
253 | "item_seen": true,
254 | "level": 0,
255 | "max_level_bonus": 0,
256 | "rnd_sel_cnt": 0,
257 | "variants": [],
258 | "xp": 0
259 | },
260 | "templateId": "AthenaCharacter:CID_004_Athena_Commando_F_Default"
261 | },
262 | "AthenaCharacter:CID_005_Athena_Commando_M_Default": {
263 | "attributes": {
264 | "favorite": false,
265 | "item_seen": true,
266 | "level": 0,
267 | "max_level_bonus": 0,
268 | "rnd_sel_cnt": 0,
269 | "variants": [],
270 | "xp": 0
271 | },
272 | "templateId": "AthenaCharacter:CID_005_Athena_Commando_M_Default"
273 | },
274 | "AthenaCharacter:CID_006_Athena_Commando_M_Default": {
275 | "attributes": {
276 | "favorite": false,
277 | "item_seen": true,
278 | "level": 0,
279 | "max_level_bonus": 0,
280 | "rnd_sel_cnt": 0,
281 | "variants": [],
282 | "xp": 0
283 | },
284 | "templateId": "AthenaCharacter:CID_006_Athena_Commando_M_Default"
285 | },
286 | "AthenaCharacter:CID_007_Athena_Commando_M_Default": {
287 | "attributes": {
288 | "favorite": false,
289 | "item_seen": true,
290 | "level": 0,
291 | "max_level_bonus": 0,
292 | "rnd_sel_cnt": 0,
293 | "variants": [],
294 | "xp": 0
295 | },
296 | "templateId": "AthenaCharacter:CID_007_Athena_Commando_M_Default"
297 | },
298 | "AthenaCharacter:CID_008_Athena_Commando_M_Default": {
299 | "attributes": {
300 | "favorite": false,
301 | "item_seen": true,
302 | "level": 0,
303 | "max_level_bonus": 0,
304 | "rnd_sel_cnt": 0,
305 | "variants": [],
306 | "xp": 0
307 | },
308 | "templateId": "AthenaCharacter:CID_008_Athena_Commando_M_Default"
309 | },
310 | "AthenaCharacter:CID_556_Athena_Commando_F_RebirthDefaultA": {
311 | "attributes": {
312 | "favorite": false,
313 | "item_seen": true,
314 | "level": 0,
315 | "max_level_bonus": 0,
316 | "rnd_sel_cnt": 0,
317 | "variants": [],
318 | "xp": 0
319 | },
320 | "templateId": "AthenaCharacter:CID_556_Athena_Commando_F_RebirthDefaultA"
321 | },
322 | "AthenaCharacter:CID_557_Athena_Commando_F_RebirthDefaultB": {
323 | "attributes": {
324 | "favorite": false,
325 | "item_seen": true,
326 | "level": 0,
327 | "max_level_bonus": 0,
328 | "rnd_sel_cnt": 0,
329 | "variants": [],
330 | "xp": 0
331 | },
332 | "templateId": "AthenaCharacter:CID_557_Athena_Commando_F_RebirthDefaultB"
333 | },
334 | "AthenaCharacter:CID_558_Athena_Commando_F_RebirthDefaultC": {
335 | "attributes": {
336 | "favorite": false,
337 | "item_seen": true,
338 | "level": 0,
339 | "max_level_bonus": 0,
340 | "rnd_sel_cnt": 0,
341 | "variants": [],
342 | "xp": 0
343 | },
344 | "templateId": "AthenaCharacter:CID_558_Athena_Commando_F_RebirthDefaultC"
345 | },
346 | "AthenaCharacter:CID_559_Athena_Commando_F_RebirthDefaultD": {
347 | "attributes": {
348 | "favorite": false,
349 | "item_seen": true,
350 | "level": 0,
351 | "max_level_bonus": 0,
352 | "rnd_sel_cnt": 0,
353 | "variants": [],
354 | "xp": 0
355 | },
356 | "templateId": "AthenaCharacter:CID_559_Athena_Commando_F_RebirthDefaultD"
357 | },
358 | "AthenaCharacter:CID_560_Athena_Commando_M_RebirthDefaultA": {
359 | "attributes": {
360 | "favorite": false,
361 | "item_seen": true,
362 | "level": 0,
363 | "max_level_bonus": 0,
364 | "rnd_sel_cnt": 0,
365 | "variants": [],
366 | "xp": 0
367 | },
368 | "templateId": "AthenaCharacter:CID_560_Athena_Commando_M_RebirthDefaultA"
369 | },
370 | "AthenaCharacter:CID_561_Athena_Commando_M_RebirthDefaultB": {
371 | "attributes": {
372 | "favorite": false,
373 | "item_seen": true,
374 | "level": 0,
375 | "max_level_bonus": 0,
376 | "rnd_sel_cnt": 0,
377 | "variants": [],
378 | "xp": 0
379 | },
380 | "templateId": "AthenaCharacter:CID_561_Athena_Commando_M_RebirthDefaultB"
381 | },
382 | "AthenaCharacter:CID_562_Athena_Commando_M_RebirthDefaultC": {
383 | "attributes": {
384 | "favorite": false,
385 | "item_seen": true,
386 | "level": 0,
387 | "max_level_bonus": 0,
388 | "rnd_sel_cnt": 0,
389 | "variants": [],
390 | "xp": 0
391 | },
392 | "templateId": "AthenaCharacter:CID_562_Athena_Commando_M_RebirthDefaultC"
393 | },
394 | "AthenaCharacter:CID_563_Athena_Commando_M_RebirthDefaultD": {
395 | "attributes": {
396 | "favorite": false,
397 | "item_seen": true,
398 | "level": 0,
399 | "max_level_bonus": 0,
400 | "rnd_sel_cnt": 0,
401 | "variants": [],
402 | "xp": 0
403 | },
404 | "templateId": "AthenaCharacter:CID_563_Athena_Commando_M_RebirthDefaultD"
405 | },
406 | "AthenaCharacter:cid_a_272_athena_commando_f_prime": {
407 | "attributes": {
408 | "favorite": false,
409 | "item_seen": true,
410 | "level": 0,
411 | "max_level_bonus": 0,
412 | "rnd_sel_cnt": 0,
413 | "variants": [],
414 | "xp": 0
415 | },
416 | "templateId": "AthenaCharacter:cid_a_272_athena_commando_f_prime"
417 | },
418 | "AthenaPickaxe:DefaultPickaxe": {
419 | "attributes": {
420 | "favorite": false,
421 | "item_seen": true,
422 | "level": 0,
423 | "max_level_bonus": 0,
424 | "rnd_sel_cnt": 0,
425 | "variants": [],
426 | "xp": 0
427 | },
428 | "templateId": "AthenaPickaxe:DefaultPickaxe"
429 | },
430 | "AthenaGlider:DefaultGlider": {
431 | "attributes": {
432 | "favorite": false,
433 | "item_seen": true,
434 | "level": 0,
435 | "max_level_bonus": 0,
436 | "rnd_sel_cnt": 0,
437 | "variants": [],
438 | "xp": 0
439 | },
440 | "templateId": "AthenaGlider:DefaultGlider"
441 | },
442 | "AthenaDance:EID_DanceMoves": {
443 | "attributes": {
444 | "favorite": false,
445 | "item_seen": true,
446 | "level": 0,
447 | "max_level_bonus": 0,
448 | "rnd_sel_cnt": 0,
449 | "variants": [],
450 | "xp": 0
451 | },
452 | "templateId": "AthenaDance:EID_DanceMoves"
453 | },
454 | "AthenaDance:EID_BoogieDown": {
455 | "attributes": {
456 | "favorite": false,
457 | "item_seen": true,
458 | "level": 0,
459 | "max_level_bonus": 0,
460 | "rnd_sel_cnt": 0,
461 | "variants": [],
462 | "xp": 0
463 | },
464 | "templateId": "AthenaDance:EID_BoogieDown"
465 | }
466 | },
467 | "stats": {
468 | "attributes": {
469 | "season_match_boost": 0,
470 | "loadouts": [
471 | "sandbox_loadout",
472 | "neoset0_loadout"
473 | ],
474 | "rested_xp_overflow": 0,
475 | "mfa_reward_claimed": true,
476 | "quest_manager": {},
477 | "book_level": 0,
478 | "season_num": 16,
479 | "season_update": 1,
480 | "book_xp": 1,
481 | "permissions": [],
482 | "book_purchased": false,
483 | "lifetime_wins": 0,
484 | "party_assist_quest": "",
485 | "purchased_battle_pass_tier_offers": [],
486 | "rested_xp_exchange": 0.333,
487 | "level": 0,
488 | "xp_overflow": 0,
489 | "rested_xp": 0,
490 | "rested_xp_mult": 0,
491 | "accountLevel": 0,
492 | "competitive_identity": {},
493 | "inventory_limit_bonus": 0,
494 | "last_applied_loadout": "sandbox_loadout",
495 | "daily_rewards": {},
496 | "xp": 0,
497 | "season_friend_match_boost": 1,
498 | "active_loadout_index": 1,
499 | "favorite_musicpack": "",
500 | "favorite_glider": "",
501 | "favorite_pickaxe": "",
502 | "favorite_skydivecontrail": "",
503 | "favorite_backpack": "",
504 | "favorite_dance": [
505 | "",
506 | "",
507 | "",
508 | "",
509 | "",
510 | ""
511 | ],
512 | "favorite_itemwraps": [],
513 | "favorite_character": "",
514 | "favorite_loadingscreen": ""
515 | }
516 | },
517 | "commandRevision": 5
518 | }
519 |
--------------------------------------------------------------------------------
/profile_template/profiles/profile_campaign.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "campaign",
10 | "version": "no_version",
11 | "items": {},
12 | "stats": {},
13 | "commandRevision": 1
14 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_collection_book_people0.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "collection_book_people0",
10 | "version": "no_version",
11 | "items": {},
12 | "stats": {
13 | "attributes": {
14 | "inventory_limit_bonus": 0
15 | }
16 | },
17 | "commandRevision": 0
18 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_collection_book_schematics0.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "collection_book_schematics0",
10 | "version": "no_version",
11 | "items": {},
12 | "stats": {
13 | "attributes": {
14 | "inventory_limit_bonus": 0
15 | }
16 | },
17 | "commandRevision": 0
18 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_collections.json:
--------------------------------------------------------------------------------
1 | {
2 | "created": "",
3 | "updated": "",
4 | "rvn": 1,
5 | "wipeNumber": 1,
6 | "accountId": "",
7 | "profileId": "collections",
8 | "version": "fortnite_start",
9 | "items": {},
10 | "stats": {
11 | "attributes": {}
12 | }
13 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_common_core.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "created": "",
4 | "updated": "",
5 | "rvn": 1,
6 | "wipeNumber": 1,
7 | "accountId": "",
8 | "profileId": "common_core",
9 | "version": "neonite_2",
10 | "items": {
11 | "Currency:MtxPurchased": {
12 | "attributes": {
13 | "platform": "EpicPC"
14 | },
15 | "quantity": 0,
16 | "templateId": "Currency:MtxPurchased"
17 | }
18 | },
19 | "stats": {
20 | "attributes": {
21 | "mtx_affiliate": "Neonite",
22 | "current_mtx_platform": "EpicPC",
23 | "mtx_purchase_history": {}
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/profile_template/profiles/profile_common_public.json:
--------------------------------------------------------------------------------
1 | {
2 | "created": "",
3 | "updated": "",
4 | "rvn": 1,
5 | "wipeNumber": 1,
6 | "accountId": "",
7 | "profileId": "common_public",
8 | "version": "fortnite_start",
9 | "items": {},
10 | "stats": {
11 | "attributes": {
12 | "banner_color": "",
13 | "homebase_name": "",
14 | "banner_icon": ""
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_creative.json:
--------------------------------------------------------------------------------
1 | {
2 | "created": "",
3 | "updated": "",
4 | "rvn": 1,
5 | "wipeNumber": 1,
6 | "accountId": "",
7 | "profileId": "creative",
8 | "version": "fix_empty_users_again_july_2019",
9 | "items": {},
10 | "stats": {
11 | "attributes": {
12 | "last_used_battlelab_file": "",
13 | "max_island_plots": 50,
14 | "publish_allowed": true,
15 | "support_code": "",
16 | "last_used_plot": "",
17 | "permissions": [],
18 | "creator_name": "",
19 | "publish_banned": false,
20 | "inventory_limit_bonus": 0
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "metadata",
10 | "version": "no_version",
11 | "items": {},
12 | "stats": {
13 | "attributes": {
14 | "inventory_limit_bonus": 0
15 | }
16 | },
17 | "commandRevision": 0
18 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_outpost0.json:
--------------------------------------------------------------------------------
1 | {
2 | "created": "",
3 | "updated": "",
4 | "rvn": 1,
5 | "wipeNumber": 1,
6 | "accountId": "",
7 | "profileId": "outpost0",
8 | "version": "no_version",
9 | "items": {},
10 | "stats": {
11 | "attributes": {
12 | "inventory_limit_bonus": 0
13 | }
14 | },
15 | "commandRevision": 0
16 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_profile0.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "Update": "profile by tim",
4 | "created": "2022-01-08T22:58:06.983Z",
5 | "updated": "2022-01-08T22:58:57.261Z",
6 | "rvn": 24,
7 | "wipeNumber": 1,
8 | "accountId": "",
9 | "profileId": "profile0",
10 | "version": "neonite_2",
11 | "items": {},
12 | "stats": {
13 | "templateId": "",
14 | "attributes": {
15 | "node_costs": {},
16 | "mission_alert_redemption_record": {
17 | "lastClaimTimesMap": {
18 | "General": {
19 | "missionAlertGUIDs": [],
20 | "lastClaimedTimes": []
21 | },
22 | "StormLow": {
23 | "missionAlertGUIDs": [],
24 | "lastClaimedTimes": []
25 | },
26 | "Halloween": {
27 | "missionAlertGUIDs": [],
28 | "lastClaimedTimes": []
29 | },
30 | "Horde": {
31 | "missionAlertGUIDs": [],
32 | "lastClaimedTimes": []
33 | },
34 | "Storm": {
35 | "missionAlertGUIDs": [],
36 | "lastClaimedTimes": []
37 | }
38 | },
39 | "oldestClaimIndexForCategory": []
40 | },
41 | "twitch": {},
42 | "client_settings": {
43 | "pinnedQuestInstances": []
44 | },
45 | "level": 1,
46 | "named_counters": {
47 | "SubGameSelectCount_Campaign": {
48 | "current_count": 1,
49 | "last_incremented_time": ""
50 | },
51 | "SubGameSelectCount_Athena": {
52 | "current_count": 0,
53 | "last_incremented_time": "2022-01-08T22:58:06.983Z"
54 | }
55 | },
56 | "default_hero_squad_id": "",
57 | "collection_book": {
58 | "pages": [],
59 | "maxBookXpLevelAchieved": 0
60 | },
61 | "quest_manager": {
62 | "dailyLoginInterval": "",
63 | "dailyQuestRerolls": 0
64 | },
65 | "bans": {},
66 | "gameplay_stats": [],
67 | "inventory_limit_bonus": 1,
68 | "current_mtx_platform": "Epic",
69 | "weekly_purchases": {},
70 | "daily_purchases": {},
71 | "mode_loadouts": [],
72 | "in_app_purchases": {},
73 | "daily_rewards": {
74 | "nextDefaultReward": 0,
75 | "totalDaysLoggedIn": 0,
76 | "lastClaimDate": "2022-01-08T22:58:06.983Z",
77 | "additionalSchedules": {
78 | "founderspackdailyrewardtoken": {
79 | "rewardsClaimed": 0,
80 | "claimedToday": true
81 | }
82 | }
83 | },
84 | "monthly_purchases": {},
85 | "xp": 0,
86 | "homebase": {
87 | "townName": "",
88 | "bannerIconId": "",
89 | "bannerColorId": "",
90 | "flagPattern": 0,
91 | "flagColor": 0
92 | },
93 | "packs_granted": 0
94 | }
95 | },
96 | "commandRevision": 3
97 | }
--------------------------------------------------------------------------------
/profile_template/profiles/profile_theater0.json:
--------------------------------------------------------------------------------
1 | {
2 | "_id": "",
3 | "created": "2022-01-08T22:58:06.983Z",
4 | "updated": "2022-01-08T22:58:57.261Z",
5 | "rvn": 24,
6 | "wipeNumber": 1,
7 | "accountId": "",
8 | "profileId": "theater0",
9 | "version": "no_version",
10 | "items": {},
11 | "stats": {
12 | "attributes": {}
13 | },
14 | "profileLockExpiration": "0001-01-01T00:00:00.000Z",
15 | "commandRevision": 0
16 | }
--------------------------------------------------------------------------------
/responses/FortniteAssets.json:
--------------------------------------------------------------------------------
1 | {
2 | "FortCreativeDiscoverySurface": {
3 | "meta": {
4 | "promotion": 26
5 | },
6 | "assets": {
7 | "CreativeDiscoverySurface_Frontend": {
8 | "meta": {
9 | "revision": 32,
10 | "headRevision": 32,
11 | "revisedAt": "2023-04-25T19:30:52.489Z",
12 | "promotion": 26,
13 | "promotedAt": "2023-04-25T19:31:12.618Z"
14 | },
15 | "assetData": {
16 | "AnalyticsId": "v538",
17 | "TestCohorts": [
18 | {
19 | "AnalyticsId": "c-1v2_v2_c727",
20 | "CohortSelector": "PlayerDeterministic",
21 | "PlatformBlacklist": [],
22 | "CountryCodeBlocklist": [],
23 | "ContentPanels": [
24 | {
25 | "NumPages": 1,
26 | "AnalyticsId": "p1114",
27 | "PanelType": "AnalyticsList",
28 | "AnalyticsListName": "ByEpicNoBigBattle",
29 | "CuratedListOfLinkCodes": [],
30 | "ModelName": "",
31 | "PageSize": 7,
32 | "PlatformBlacklist": [],
33 | "PanelName": "ByEpicNoBigBattle6Col",
34 | "MetricInterval": "",
35 | "CountryCodeBlocklist": [],
36 | "SkippedEntriesCount": 0,
37 | "SkippedEntriesPercent": 0,
38 | "SplicedEntries": [],
39 | "PlatformWhitelist": [],
40 | "MMRegionBlocklist": [],
41 | "EntrySkippingMethod": "None",
42 | "PanelDisplayName": {
43 | "Category": "Game",
44 | "NativeCulture": "",
45 | "Namespace": "CreativeDiscoverySurface_Frontend",
46 | "LocalizedStrings": [],
47 | "bIsMinimalPatch": false,
48 | "NativeString": "LTMS",
49 | "Key": "ByEpicNoBigBattle6Col"
50 | },
51 | "PlayHistoryType": "RecentlyPlayed",
52 | "bLowestToHighest": false,
53 | "PanelLinkCodeBlacklist": [],
54 | "CountryCodeAllowlist": [],
55 | "PanelLinkCodeWhitelist": [],
56 | "FeatureTags": [
57 | "col:5"
58 | ],
59 | "MMRegionAllowlist": [],
60 | "MetricName": ""
61 | },
62 | {
63 | "NumPages": 2,
64 | "AnalyticsId": "p969|88dba0c4e2af76447df43d1e31331a3d",
65 | "PanelType": "AnalyticsList",
66 | "AnalyticsListName": "EventPanel",
67 | "CuratedListOfLinkCodes": [],
68 | "ModelName": "",
69 | "PageSize": 25,
70 | "PlatformBlacklist": [],
71 | "PanelName": "EventPanel",
72 | "MetricInterval": "",
73 | "CountryCodeBlocklist": [],
74 | "SkippedEntriesCount": 0,
75 | "SkippedEntriesPercent": 0,
76 | "SplicedEntries": [],
77 | "PlatformWhitelist": [],
78 | "MMRegionBlocklist": [],
79 | "EntrySkippingMethod": "None",
80 | "PanelDisplayName": {
81 | "Category": "Game",
82 | "NativeCulture": "",
83 | "Namespace": "CreativeDiscoverySurface_Frontend",
84 | "LocalizedStrings": [],
85 | "bIsMinimalPatch": false,
86 | "NativeString": "Event LTMS",
87 | "Key": "EventPanel"
88 | },
89 | "PlayHistoryType": "RecentlyPlayed",
90 | "bLowestToHighest": false,
91 | "PanelLinkCodeBlacklist": [],
92 | "CountryCodeAllowlist": [],
93 | "PanelLinkCodeWhitelist": [],
94 | "FeatureTags": [
95 | "col:6"
96 | ],
97 | "MMRegionAllowlist": [],
98 | "MetricName": ""
99 | }
100 | ],
101 | "PlatformWhitelist": [],
102 | "SelectionChance": 0.1,
103 | "TestName": "testing"
104 | }
105 | ],
106 | "GlobalLinkCodeBlacklist": [],
107 | "SurfaceName": "CreativeDiscoverySurface_Frontend",
108 | "TestName": "20.10_4/11/2022_hero_combat_popularConsole",
109 | "primaryAssetId": "FortCreativeDiscoverySurface:CreativeDiscoverySurface_Frontend",
110 | "GlobalLinkCodeWhitelist": []
111 | }
112 | }
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/structs/NeoLog.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | Log(data) {
3 | console.log('[\x1b[35;1mNeonite\x1b[0m]', data, '\x1b[0m')
4 | },
5 | Error(data) {
6 | console.error('[\x1b[31mERROR\x1b[0m]\x1b[31m', data, '\x1b[0m')
7 | },
8 | warn(data, showtype = true) {
9 | console.warn('[\x1b[33mWARNING\x1b[0m]\x1b[33m', data, '\x1b[0m')
10 | },
11 | Debug(data) {
12 | console.log(`[\x1b[32mDEBUG\x1b[0m]: ${data}\x1b[0m`)
13 | },
14 |
15 | URL(data) {
16 | console.log(`[\x1b[32mLogURL\x1b[0m]: ${data}\x1b[0m`)
17 | },
18 | }
--------------------------------------------------------------------------------
/structs/errors.js:
--------------------------------------------------------------------------------
1 | const ORIGINATING_SERVICE = "fortnite";
2 | const INTENT = "prod-live";
3 | String.prototype.format = function () {
4 | const args = arguments[0] instanceof Array ? arguments[0] : arguments;
5 | return this.replace(/{(\d+)}/g, function (match, number) {
6 | return typeof args[number] != "undefined" ? args[number] : match;
7 | });
8 | };
9 |
10 | class ErrDef {
11 | constructor(errorCode, errorMessage, numericErrorCode, httpStatusCode) {
12 | this.errorCode = errorCode;
13 | this.errorMessage = errorMessage;
14 | this.numericErrorCode = numericErrorCode;
15 | this.httpStatusCode = httpStatusCode;
16 | }
17 |
18 | apply(res, ...messageVars) {
19 | return this.applyCustomMessage(res, this.errorMessage, ...messageVars);
20 | }
21 |
22 | applyCustomMessage(res, message, ...messageVars) {
23 | return res
24 | .status(this.httpStatusCode)
25 | .set("X-Epic-Error-Code", this.numericErrorCode)
26 | .set("X-Epic-Error-Name", this.errorCode)
27 | .json({
28 | "errorCode": this.errorCode,
29 | "errorMessage": message.format(messageVars),
30 | "messageVars": messageVars,
31 | "numericErrorCode": this.numericErrorCode,
32 | "originatingService": ORIGINATING_SERVICE,
33 | "intent": INTENT
34 | });
35 | }
36 | }
37 |
38 | class ApiException extends Error {
39 | constructor(errDef) {
40 | super(errDef.errorMessage);
41 | this.errDef = errDef;
42 | this.errorMessage = errDef.errorMessage;
43 | }
44 |
45 | with(...messageVars) {
46 | this.messageVars = messageVars;
47 | messageVars.forEach(msgvar => {
48 | this.errorMessage.replace(`{${messageVars.indexOf(msgvar)}}`, msgvar)
49 | })
50 |
51 | return this;
52 | }
53 |
54 | withMessage(errorMessage, ...messageVars) {
55 | this.errorMessage = errorMessage;
56 | this.messageVars = messageVars;
57 | messageVars.forEach(msgvar => {
58 | this.errorMessage.replace(`{${messageVars.indexOf(msgvar)}}`, msgvar)
59 | })
60 | return this;
61 | }
62 |
63 | apply(res) {
64 | return this.messageVars ? this.errDef.applyCustomMessage(res, this.errorMessage, ...this.messageVars) : this.errDef.applyCustomMessage(res, this.errorMessage);
65 | }
66 | }
67 |
68 | module.exports = {
69 | com: {
70 | epicgames: {
71 | common: {
72 | not_found: new ErrDef("errors.com.epicgames.common.not_found", "Sorry the resource you were trying to find could not be found", 1004, 404),
73 | server_error: new ErrDef("errors.com.epicgames.common.server_error", "Sorry an error occurred and we were unable to resolve it (tracking id: [{0}])", 1000, 500),
74 | throttled: new ErrDef("errors.com.epicgames.common.throttled", "Operation access is limited by throttling policy, please try again in {0} second(s).", 1041, 429),
75 | oauth : {
76 | invalid_request: new ErrDef("errors.com.epicgames.common.oauth.invalid_request", "{0} is required.", 1013, 400),
77 | unsupported_grant_type: new ErrDef("errors.com.epicgames.common.oauth.unsupported_grant_type", "Unsupported grant type: {0}", 1016, 400)
78 | },
79 | authentication: {
80 | authentication_failed: new ErrDef("errors.com.epicgames.common.authentication.authentication_failed", "Authentication failed for {0}]")
81 | }
82 | },
83 | fortnite: {
84 | item_not_found: new ErrDef("errors.com.epicgames.fortnite.item_not_found", "Locker item {0} not found", 16006, 404),
85 | operation_not_found: new ErrDef("errors.com.epicgames.fortnite.operation_not_found", "Operation {0} not valid", 16035, 404),
86 | invalid_bucket_id: new ErrDef("errors.com.epicgames.fortnite.invalid_bucket_id", "blank bucketId", 16102, 400),
87 | invalid_party_player_ids: new ErrDef("errors.com.epicgames.fortnite.invalid_party_player_ids", "blank partyPlayerIds", 16103, 400),
88 | invalid_platform: new ErrDef("errors.com.epicgames.fortnite.invalid_platform", "Invalid platform: '{0}'", 16104, 400)
89 | },
90 | cloudstorage: {
91 | file_not_found: new ErrDef("errors.com.epicgames.cloudstorage.file_not_found", "Sorry, we couldn't find a file {0} for account {1}", 12007, 404)
92 | },
93 | modules: {
94 | gameplayutils: {
95 | not_enough_mtx: new ErrDef("errors.com.epicgames.modules.gameplayutils.not_enough_mtx", "Purchase: {0}: Required {1} MTX but account balance is only {2}.", 12720, 400)
96 | },
97 | gamesubcatalog: {
98 | catalog_out_of_date: new ErrDef("errors.com.epicgames.modules.gamesubcatalog.catalog_out_of_date", "Could not find catalog item {0}", 28001, 400)
99 | },
100 | profiles: {
101 | invalid_command: new ErrDef("errors.com.epicgames.modules.profiles.invalid_command", "{0} is not valid on {1} profiles ({2})", 12801, 400),
102 | operation_forbidden: new ErrDef("errors.com.epicgames.modules.profiles.operation_forbidden", "Unable to find template configuration for profile {0}", 12813, 200)
103 | }
104 | }
105 | }
106 | },
107 | ErrDef,
108 | ApiException
109 | };
110 |
--------------------------------------------------------------------------------