├── .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 | --------------------------------------------------------------------------------