Redesigned whole UI

This commit is contained in:
Alessandro Autiero
2023-02-25 01:28:36 +01:00
parent 63c7cc5c5b
commit 760e336bc0
77 changed files with 693 additions and 387816 deletions

View File

@@ -0,0 +1,2 @@
for /f "tokens=5" %%a in ('netstat -aon ^| find ":3551" ^| find "LISTENING"') do taskkill /f /pid %%a
for /f "tokens=5" %%a in ('netstat -aon ^| find ":8080" ^| find "LISTENING"') do taskkill /f /pid %%a

BIN
assets/binaries/server.zip Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,14 +0,0 @@
[ConsoleVariables]
FortMatchmakingV2.EnableContentBeacon=0
FortMatchmakingV2.ContentBeaconFailureCancelsMatchmaking=0
[PatchCheck]
ModuleName=FortnitePatchCheck
bCheckPlatformOSSForUpdate=false
bCheckOSSForUpdate=false
[XMPP]
bEnableWebsockets=false
[/Script/Engine.InputSettings]
ConsoleKey=F8

View File

@@ -1,31 +0,0 @@
[/Script/FortniteGame.FortGlobals]
bAthenaLeaderboardFrontEndEnabled=false
bAthenaStatsFrontendEnabled=false
bGlobalLeaderboardsFrontEndEnabled=false
SubGameAccess=(SubGame=Campaign,AccessStatus=OpenAccess,MatchmakingStatus=Enabled)
+SubGameAccess=(SubGame=Athena,AccessStatus=OpenAccess,MatchmakingStatus=Enabled)
+SubGameAccess=(SubGame=Campaign,AccessStatus=OpenAccess,MatchmakingStatus=Enabled)
bUploadAthenaStats=false
bUploadAthenaStatsV2=false
[/Script/FortniteGame.FortMatchmakingV2]
bCustomKeyEnabled=true
[/Script/FortniteGame.FortChatManager]
bShouldRequestGeneralChatRooms=false
bShouldJoinGlobalChat=false
bShouldJoinFoaunderChat=false
bIsAthenaGlobalChatEnabled=false
[/Script/FortniteGame.FortGameInstance]
!FrontEndPlaylistData=ClearArray
bBattleRoyaleMatchmakingEnabled=true
+FrontEndPlaylistData=(PlaylistName=Playlist_DefaultSolo, PlaylistAccess=(bEnabled=True, bIsDefaultPlaylist=true, bVisibleWhenDisabled=false, bDisplayAsNew=false, CategoryIndex=0, bDisplayAsLimitedTime=false, DisplayPriority=3))
+FrontEndPlaylistData=(PlaylistName=Playlist_DefaultDuo, PlaylistAccess=(bEnabled=True, bIsDefaultPlaylist=true, bVisibleWhenDisabled=false, bDisplayAsNew=false, CategoryIndex=0, bDisplayAsLimitedTime=false, DisplayPriority=4))
+FrontEndPlaylistData=(PlaylistName=Playlist_Trios, PlaylistAccess=(bEnabled=true, bIsDefaultPlaylist=true, bVisibleWhenDisabled=false, bDisplayAsNew=False, bDisplayAsLimitedTime=false, DisplayPriority=5, CategoryIndex=0))
+FrontEndPlaylistData=(PlaylistName=Playlist_DefaultSquad, PlaylistAccess=(bEnabled=true, bIsDefaultPlaylist=true, bVisibleWhenDisabled=false, bDisplayAsNew=false, CategoryIndex=0, bDisplayAsLimitedTime=false, DisplayPriority=6))
+FrontEndPlaylistData=(PlaylistName=Playlist_PlaygroundV2, PlaylistAccess=(bEnabled=true, bIsDefaultPlaylist=false, bVisibleWhenDisabled=false, bDisplayAsNew=false, CategoryIndex=2, bDisplayAsLimitedTime=false, DisplayPriority=16))
+FrontEndPlaylistData=(PlaylistName=Playlist_Campaign, PlaylistAccess=(bEnabled=true, bInvisibleWhenEnabled=true))
[/Script/Engine.InputSettings]
ConsoleKey=F8

View File

@@ -1,21 +0,0 @@
[/Script/FortniteGame.FortRuntimeOptions]
bEnableGlobalChat=false
bEnableMexiCola=false
bLoadDirectlyIntoLobby=true
bEnableSocialTab=false
bMOTDSameNewsForCreative=true
bForceBRMode=true
bEnableSavedLoadouts=false
bIsOutOfSeasonMode=true
!DisabledTabsForOutOfSeason=ClearArray
+DisabledTabsForOutOfSeason=(TabName="Lobby",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="AthenaCompete",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="AthenaCareer",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="AthenaStore",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="CareerScreen",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="AthenaDirectAcquisition",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="BattlePass",TabState=EFortRuntimeOptionTabState::Hidden)
+DisabledTabsForOutOfSeason=(TabName="AthenaCustomize",TabState=EFortRuntimeOptionTabState::Hidden)
[/Script/Engine.InputSettings]
ConsoleKey=F8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "collection_book_people0",
"version": "no_version",
"items": {
"CollectionBookPage:pageHeroes_Commando": {
"templateId": "CollectionBookPage:pageHeroes_Commando",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageHeroes_Constructor": {
"templateId": "CollectionBookPage:pageHeroes_Constructor",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageHeroes_Ninja": {
"templateId": "CollectionBookPage:pageHeroes_Ninja",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageHeroes_Outlander": {
"templateId": "CollectionBookPage:pageHeroes_Outlander",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pagePeople_Defenders": {
"templateId": "CollectionBookPage:pagePeople_Defenders",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pagePeople_Survivors": {
"templateId": "CollectionBookPage:pagePeople_Survivors",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pagePeople_Leads": {
"templateId": "CollectionBookPage:pagePeople_Leads",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pagePeople_UniqueLeads": {
"templateId": "CollectionBookPage:pagePeople_UniqueLeads",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Winter2017_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_Winter2017_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Halloween2017_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_Halloween2017_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Halloween2017_Workers": {
"templateId": "CollectionBookPage:PageSpecial_Halloween2017_Workers",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_ChineseNewYear2018_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_ChineseNewYear2018_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_SpringItOn2018_People": {
"templateId": "CollectionBookPage:PageSpecial_SpringItOn2018_People",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZoneCyber_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_StormZoneCyber_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Blockbuster2018_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_Blockbuster2018_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_ShadowOps_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_ShadowOps_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_RoadTrip2018_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_RoadTrip2018_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_WildWest_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_WildWest_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZone_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_StormZone_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Scavenger_Heroes": {
"templateId": "CollectionBookPage:PageSpecial_Scavenger_Heroes",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
}
},
"stats": {
"attributes": {
"inventory_limit_bonus": 0
}
},
"commandRevision": 0
}

View File

@@ -1,458 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "collection_book_schematics0",
"version": "no_version",
"items": {
"CollectionBookPage:pageMelee_Axes_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Axes_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Axes_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Axes_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Clubs_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Clubs_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Clubs_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Clubs_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Scythes_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Scythes_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Scythes_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Scythes_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Spears_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Spears_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Spears_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Spears_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Swords_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Swords_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Swords_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Swords_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Tools_Weapons": {
"templateId": "CollectionBookPage:pageMelee_Tools_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageMelee_Tools_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageMelee_Tools_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Assault_Weapons": {
"templateId": "CollectionBookPage:pageRanged_Assault_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Assault_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageRanged_Assault_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Shotgun_Weapons": {
"templateId": "CollectionBookPage:pageRanged_Shotgun_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Shotgun_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageRanged_Shotgun_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:page_Ranged_Pistols_Weapons": {
"templateId": "CollectionBookPage:page_Ranged_Pistols_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:page_Ranged_Pistols_Weapons_Crystal": {
"templateId": "CollectionBookPage:page_Ranged_Pistols_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Snipers_Weapons": {
"templateId": "CollectionBookPage:pageRanged_Snipers_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Snipers_Weapons_Crystal": {
"templateId": "CollectionBookPage:pageRanged_Snipers_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageRanged_Explosive_Weapons": {
"templateId": "CollectionBookPage:pageRanged_Explosive_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageTraps_Wall": {
"templateId": "CollectionBookPage:pageTraps_Wall",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageTraps_Ceiling": {
"templateId": "CollectionBookPage:pageTraps_Ceiling",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:pageTraps_Floor": {
"templateId": "CollectionBookPage:pageTraps_Floor",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_Medieval": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_Medieval",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_Medieval_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_Medieval_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_Medieval": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_Medieval",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_Medieval_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_Medieval_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Winter2017_Weapons": {
"templateId": "CollectionBookPage:PageSpecial_Winter2017_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Winter2017_Weapons_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Winter2017_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_RatRod_Weapons": {
"templateId": "CollectionBookPage:PageSpecial_RatRod_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_RatRod_Weapons_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_RatRod_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_Winter2017": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_Winter2017",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_Winter2017_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_Winter2017_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_Winter2017": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_Winter2017",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_Winter2017_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_Winter2017_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_ChineseNewYear2018": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_ChineseNewYear2018",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Crystal_ChineseNewYear2018": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Crystal_ChineseNewYear2018",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZoneCyber_Ranged": {
"templateId": "CollectionBookPage:PageSpecial_StormZoneCyber_Ranged",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZoneCyber_Melee": {
"templateId": "CollectionBookPage:PageSpecial_StormZoneCyber_Melee",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZoneCyber_Ranged_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_StormZoneCyber_Ranged_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_StormZoneCyber_Melee_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_StormZoneCyber_Melee_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Blockbuster2018_Ranged": {
"templateId": "CollectionBookPage:PageSpecial_Blockbuster2018_Ranged",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Blockbuster2018_Ranged_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Blockbuster2018_Ranged_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_RoadTrip2018_Weapons": {
"templateId": "CollectionBookPage:PageSpecial_RoadTrip2018_Weapons",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_RoadTrip2018_Weapons_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_RoadTrip2018_Weapons_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_StormZone2": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_StormZone2",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Ranged_StormZone2_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Ranged_StormZone2_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_StormZone2": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_StormZone2",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Weapons_Melee_StormZone2_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Weapons_Melee_StormZone2_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Hydraulic": {
"templateId": "CollectionBookPage:PageSpecial_Hydraulic",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Hydraulic_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Hydraulic_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Scavenger": {
"templateId": "CollectionBookPage:PageSpecial_Scavenger",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:PageSpecial_Scavenger_Crystal": {
"templateId": "CollectionBookPage:PageSpecial_Scavenger_Crystal",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
},
"CollectionBookPage:test_TestPage": {
"templateId": "CollectionBookPage:test_TestPage",
"attributes": {
"sectionStates": [],
"state": "Active"
},
"quantity": 1
}
},
"stats": {
"attributes": {
"inventory_limit_bonus": 0
}
},
"commandRevision": 0
}

View File

@@ -1,15 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "collections",
"version": "no_version",
"items": {},
"stats": {
"attributes": {}
},
"commandRevision": 0
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
{
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "common_public",
"version": "no_version",
"items": {},
"stats": {
"attributes": {
"banner_color": "DefaultColor15",
"homebase_name": "",
"banner_icon": "SurvivalBannerStonewoodComplete"
}
},
"commandRevision": 0
}

View File

@@ -1,15 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "creative",
"version": "no_version",
"items": {},
"stats": {
"attributes": {}
},
"commandRevision": 0
}

View File

@@ -1,231 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "metadata",
"version": "no_version",
"items": {
"Outpost:outpostcore_pve_03": {
"templateId": "Outpost:outpostcore_pve_03",
"attributes": {
"cloud_save_info": {
"saveCount": 319,
"savedRecords": [
{
"recordIndex": 0,
"archiveNumber": 1,
"recordFilename": "eb192023-7db8-4bc0-b3e4-bf060c7baf87_r0_a1.sav"
}
]
},
"level": 10,
"outpost_core_info": {
"placedBuildings": [
{
"buildingTag": "Outpost.BuildingActor.Building.00",
"placedTag": "Outpost.PlacementActor.Placement.01"
},
{
"buildingTag": "Outpost.BuildingActor.Building.01",
"placedTag": "Outpost.PlacementActor.Placement.00"
},
{
"buildingTag": "Outpost.BuildingActor.Building.02",
"placedTag": "Outpost.PlacementActor.Placement.05"
},
{
"buildingTag": "Outpost.BuildingActor.Building.03",
"placedTag": "Outpost.PlacementActor.Placement.02"
}
],
"accountsWithEditPermission": [],
"highestEnduranceWaveReached": 30
}
},
"quantity": 1
},
"Outpost:outpostcore_pve_02": {
"templateId": "Outpost:outpostcore_pve_02",
"attributes": {
"cloud_save_info": {
"saveCount": 603,
"savedRecords": [
{
"recordIndex": 0,
"archiveNumber": 0,
"recordFilename": "76fe0295-aee2-463a-9229-d9933b4969b8_r0_a0.sav"
}
]
},
"level": 10,
"outpost_core_info": {
"placedBuildings": [
{
"buildingTag": "Outpost.BuildingActor.Building.00",
"placedTag": "Outpost.PlacementActor.Placement.00"
},
{
"buildingTag": "Outpost.BuildingActor.Building.01",
"placedTag": "Outpost.PlacementActor.Placement.01"
},
{
"buildingTag": "Outpost.BuildingActor.Building.02",
"placedTag": "Outpost.PlacementActor.Placement.04"
},
{
"buildingTag": "Outpost.BuildingActor.Building.03",
"placedTag": "Outpost.PlacementActor.Placement.03"
},
{
"buildingTag": "Outpost.BuildingActor.Building.04",
"placedTag": "Outpost.PlacementActor.Placement.02"
}
],
"accountsWithEditPermission": [],
"highestEnduranceWaveReached": 30
}
},
"quantity": 1
},
"Outpost:outpostcore_pve_04": {
"templateId": "Outpost:outpostcore_pve_04",
"attributes": {
"cloud_save_info": {
"saveCount": 77,
"savedRecords": [
{
"recordIndex": 0,
"archiveNumber": 1,
"recordFilename": "940037e4-87d2-499e-8d00-cdb2dfa326b9_r0_a1.sav"
}
]
},
"level": 10,
"outpost_core_info": {
"placedBuildings": [
{
"buildingTag": "Outpost.BuildingActor.Building.00",
"placedTag": "Outpost.PlacementActor.Placement.00"
},
{
"buildingTag": "Outpost.BuildingActor.Building.01",
"placedTag": "Outpost.PlacementActor.Placement.01"
},
{
"buildingTag": "Outpost.BuildingActor.Building.02",
"placedTag": "Outpost.PlacementActor.Placement.03"
},
{
"buildingTag": "Outpost.BuildingActor.Building.03",
"placedTag": "Outpost.PlacementActor.Placement.05"
}
],
"accountsWithEditPermission": [],
"highestEnduranceWaveReached": 30
}
},
"quantity": 1
},
"Outpost:outpostcore_pve_01": {
"templateId": "Outpost:outpostcore_pve_01",
"attributes": {
"cloud_save_info": {
"saveCount": 851,
"savedRecords": [
{
"recordIndex": 0,
"archiveNumber": 0,
"recordFilename": "a1d68ce6-63a5-499a-946f-9e0c825572d7_r0_a0.sav"
}
]
},
"level": 10,
"outpost_core_info": {
"placedBuildings": [
{
"buildingTag": "Outpost.BuildingActor.Building.00",
"placedTag": "Outpost.PlacementActor.Placement.00"
},
{
"buildingTag": "Outpost.BuildingActor.Building.01",
"placedTag": "Outpost.PlacementActor.Placement.02"
},
{
"buildingTag": "Outpost.BuildingActor.Building.02",
"placedTag": "Outpost.PlacementActor.Placement.01"
},
{
"buildingTag": "Outpost.BuildingActor.Building.03",
"placedTag": "Outpost.PlacementActor.Placement.05"
}
],
"accountsWithEditPermission": [],
"highestEnduranceWaveReached": 30
}
},
"quantity": 1
},
"DeployableBaseCloudSave:testdeployablebaseitemdef": {
"templateId": "DeployableBaseCloudSave:testdeployablebaseitemdef",
"attributes": {
"tier_progression": {
"progressionInfo": [
{
"progressionLayoutGuid": "B70B5C69-437E-75C5-CB91-7E913F3B5294",
"highestDefeatedTier": 0
},
{
"progressionLayoutGuid": "04FD086F-4A99-823B-06C3-979A8F408960",
"highestDefeatedTier": 4
},
{
"progressionLayoutGuid": "D3D31F40-45D8-FD77-67E6-5FBAB0550417",
"highestDefeatedTier": 1
},
{
"progressionLayoutGuid": "92A17A43-4EDC-8F69-688F-24BB3A3D8AEF",
"highestDefeatedTier": 3
},
{
"progressionLayoutGuid": "A2D8DB3E-457E-279B-58F5-AA9BA2FDC547",
"highestDefeatedTier": 4
},
{
"progressionLayoutGuid": "5AAB9A15-49F5-0D74-0B22-BB9686396E8F",
"highestDefeatedTier": 1
},
{
"progressionLayoutGuid": "9077163A-4664-1993-5A20-D28170404FD6",
"highestDefeatedTier": 3
},
{
"progressionLayoutGuid": "FB679125-49BC-0025-48F3-22A1B8085189",
"highestDefeatedTier": 4
}
]
},
"cloud_save_info": {
"saveCount": 11,
"savedRecords": [
{
"recordIndex": 0,
"archiveNumber": 1,
"recordFilename": "2FA8CFBB-4973-CCF0-EEA8-BEBC37D99F52_r0_a1.sav"
}
]
},
"level": 0
},
"quantity": 1
}
},
"stats": {
"attributes": {
"inventory_limit_bonus": 0
}
},
"commandRevision": 0
}

View File

@@ -1,17 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "outpost0",
"version": "no_version",
"items": {},
"stats": {
"attributes": {
"inventory_limit_bonus": 0
}
},
"commandRevision": 0
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,694 +0,0 @@
{
"_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z",
"rvn": 0,
"wipeNumber": 1,
"accountId": "LawinServer",
"profileId": "theater0",
"version": "no_version",
"items": {
"3d81f6f3-1290-326e-dfee-e577af2e9fbb": {
"templateId": "Ingredient:ingredient_blastpowder",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"70ff3716-d732-c472-b1d8-0a20d48dd607": {
"templateId": "Ingredient:ingredient_ore_silver",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"48059439-88b0-a779-daae-36d9495f079e": {
"templateId": "Ingredient:ingredient_ore_alloy",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"7dd4a423-0b6f-3abb-757c-88077adcaacc": {
"templateId": "Ingredient:ingredient_crystal_sunbeam",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"694a4c8d-67b6-f903-85bf-f33d4e7a6859": {
"templateId": "Ingredient:ingredient_ore_obsidian",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"97feb4c9-2290-fd4b-c356-7f346ba67e39": {
"templateId": "Ingredient:ingredient_mechanical_parts_t05",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"25ff4168-8eb9-a5cc-4900-d06cdb8004ab": {
"templateId": "Ingredient:ingredient_mechanical_parts_t02",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"a63022b8-6467-a347-7c11-37d483d45d08": {
"templateId": "Ingredient:ingredient_rare_powercell",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"7e0d23d2-24dc-9579-4f32-507758107bd3": {
"templateId": "Ingredient:ingredient_resin",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"d44ad9ed-a5d3-0642-a865-083061aeb4e6": {
"templateId": "Ingredient:ingredient_powder_t05",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"2011030e-dbce-a02b-c086-ec8a99f16aeb": {
"templateId": "Ingredient:ingredient_crystal_quartz",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"7d43d569-e170-bd46-dfb2-92828ff0c98d": {
"templateId": "Ingredient:ingredient_rare_mechanism",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"1db23fbf-1ab5-72d9-2b20-50eacebda6d5": {
"templateId": "Ingredient:ingredient_nuts_bolts",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 0,
"itemSource": ""
},
"quantity": 999
},
"024aa359-e313-168a-3738-31ae4f04cfb2": {
"templateId": "Ingredient:ingredient_ore_brightcore",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"da7680a3-072e-3e3f-3ed6-bf71159f6df0": {
"templateId": "Ingredient:ingredient_planks",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"10aec620-f4b9-aadd-da3e-5d4a8f87225b": {
"templateId": "Ingredient:ingredient_ore_malachite",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"161217ac-5b39-a93e-a1d8-7f7667646624": {
"templateId": "Ingredient:ingredient_crystal_shadowshard",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"f33663f5-bf16-9315-8df2-91800944b3e8": {
"templateId": "Ingredient:ingredient_twine_t05",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"aa4a3cce-9bb5-50ef-4c59-f959e89c3992": {
"templateId": "Ingredient:ingredient_twine_t01",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"e9eca1e1-7665-1315-de97-6583394e0af1": {
"templateId": "Ingredient:ingredient_batteries",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"d1fd9cb3-0d51-6d7c-e937-9b07406ba42e": {
"templateId": "Ingredient:ingredient_twine_t04",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"7d8f824d-cec2-01d5-0efc-c30073402de2": {
"templateId": "Ingredient:ingredient_herbs",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"bdb45648-18f6-fdc6-8252-b717043f0021": {
"templateId": "Ingredient:ingredient_mechanical_parts_t04",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"ddc2382e-faf9-14dd-c721-c659660540a8": {
"templateId": "Ingredient:ingredient_ore_copper",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"75582a66-bc3e-958a-1943-79a56150d0bb": {
"templateId": "Ingredient:ingredient_powder_t03",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"b4616de1-caf4-3652-a613-edb932df71e0": {
"templateId": "Ingredient:ingredient_mechanical_parts_t01",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"bdc338a8-3667-e7e4-280d-5d4e4255b3f1": {
"templateId": "Ingredient:ingredient_twine_t02",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"d17582f7-eb63-a4a6-cd4d-ff3d68e69757": {
"templateId": "Ingredient:ingredient_powder_t01",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"25e43cee-7dc7-348b-40bc-20b8850468ba": {
"templateId": "Ingredient:ingredient_duct_tape",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"720bc675-44e2-ff74-6e5c-eec23b493bd1": {
"templateId": "Ingredient:ingredient_twine_t03",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"0e2094f2-9c35-9e51-58ea-a87ec89fa758": {
"templateId": "Ingredient:ingredient_powder_t02",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"1cf850a0-1797-4fe8-dd94-34152756c80b": {
"templateId": "Ingredient:ingredient_bacon",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 0,
"itemSource": ""
},
"quantity": 999
},
"7fe47331-1cbd-4606-c12e-6df2c1dc13a3": {
"templateId": "Ingredient:ingredient_mechanical_parts_t03",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"42d429fc-a4cf-974d-2bce-17c6b872c96e": {
"templateId": "Ingredient:ingredient_ore_coal",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"124d77cc-8cd4-fdcc-efe1-c18ee63587eb": {
"templateId": "Ingredient:ingredient_flowers",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"ea2a6495-4b9e-59df-0163-5e5e8f52467e": {
"templateId": "Ingredient:ingredient_powder_t04",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"6291ab77-ec9b-1b35-ccb0-063519415f6d": {
"templateId": "WorldItem:wooditemdata",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"2d7953c0-752f-c2a7-ebef-90b45cb30b5b": {
"templateId": "WorldItem:stoneitemdata",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"06f471d5-046b-50f6-3f07-9aa670b6fecb": {
"templateId": "WorldItem:metalitemdata",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"003e7a8c-92eb-13c1-6b0e-aafad8f3d81d": {
"templateId": "Weapon:edittool",
"attributes": {
"clipSizeScale": 0,
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"baseClipSize": 0,
"durability": 1,
"itemSource": ""
},
"quantity": 1
},
"bcb13e35-c030-cf2a-a003-16377320beda": {
"templateId": "Weapon:buildingitemdata_wall",
"attributes": {
"clipSizeScale": 0,
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"baseClipSize": 0,
"durability": 1,
"itemSource": ""
},
"quantity": 1
},
"97ba026b-a36c-6827-e9d7-21bc6a1f9c53": {
"templateId": "Weapon:buildingitemdata_floor",
"attributes": {
"clipSizeScale": 0,
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"baseClipSize": 0,
"durability": 1,
"itemSource": ""
},
"quantity": 1
},
"15c02d16-11f6-ffd1-e8bb-4b5d56bd5bd9": {
"templateId": "Weapon:buildingitemdata_stair_w",
"attributes": {
"clipSizeScale": 0,
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"baseClipSize": 0,
"durability": 1,
"itemSource": ""
},
"quantity": 1
},
"f603c8af-e326-202e-3b12-b5fd2517e5c2": {
"templateId": "Weapon:buildingitemdata_roofs",
"attributes": {
"clipSizeScale": 0,
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"baseClipSize": 0,
"durability": 1,
"itemSource": ""
},
"quantity": 1
},
"baa4f86c-708c-7689-0859-fbfdb1bc623a": {
"templateId": "Ammo:ammodatabulletsmedium",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"240894a2-f99a-214d-ce50-2dac39394699": {
"templateId": "Ammo:ammodatashells",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": "None"
},
"quantity": 999
},
"1a0c69f4-2c23-a5c9-34a0-f48c45637171": {
"templateId": "Ammo:ammodataenergycell",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": ""
},
"quantity": 999
},
"a92b1e7e-5812-0bfd-0107-f7b97ed166fa": {
"templateId": "Ammo:ammodatabulletsheavy",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": "None"
},
"quantity": 999
},
"7516967c-e831-4c3a-1a24-03834756f532": {
"templateId": "Ammo:ammodatabulletslight",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": "None"
},
"quantity": 999
},
"23220ed0-29d4-3da1-6e0e-6df46a19752d": {
"templateId": "Ammo:ammodataexplosive",
"attributes": {
"loadedAmmo": 0,
"inventory_overflow_date": false,
"level": 0,
"alterationDefinitions": [],
"durability": 1,
"itemSource": "None"
},
"quantity": 999
}
},
"stats": {
"attributes": {
"player_loadout": {
"bPlayerIsNew": false,
"pinnedSchematicInstances": [],
"primaryQuickBarRecord": {
"slots": [
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
}
]
},
"secondaryQuickBarRecord": {
"slots": [
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
},
{
"items": []
}
]
},
"zonesCompleted": 0
},
"theater_unique_id": "",
"past_lifetime_zones_completed": 0,
"last_event_instance_key": "",
"last_zones_completed": 0,
"inventory_limit_bonus": 0
}
},
"profileLockExpiration": "0001-01-01T00:00:00.000Z",
"commandRevision": 0
}

View File

@@ -1,459 +0,0 @@
[
"Schematic:SID_Wall_Wood_Spikes_vR_T01",
"Hero:HID_Commando_Sony_R_T01",
"Schematic:SID_Wall_Wood_Spikes_UC_T01",
"Hero:HID_Commando_ShockDamage_VR_T01",
"Schematic:SID_Wall_Wood_Spikes_SR_T01",
"Hero:HID_Commando_ShockDamage_SR_T01",
"Schematic:SID_Wall_Wood_Spikes_R_T01",
"Hero:HID_Commando_ShockDamage_R_T01",
"Schematic:SID_Wall_Wood_Spikes_C_T01",
"Hero:HID_Commando_GunTough_VR_T01",
"Schematic:SID_Wall_Light_VR_T01",
"Hero:HID_Commando_GunTough_UC_T01",
"Schematic:SID_Wall_Light_SR_T01",
"Hero:HID_Commando_GunTough_SR_T01",
"Schematic:SID_Wall_Light_R_T01",
"Hero:HID_Commando_GunTough_R_T01",
"Schematic:SID_Wall_Launcher_VR_T01",
"Hero:HID_Commando_GunHeadshotHW_SR_T01",
"Schematic:SID_Wall_Launcher_UC_T01",
"Hero:HID_Commando_GunHeadshot_VR_T01",
"Schematic:SID_Wall_Launcher_SR_T01",
"Hero:HID_Commando_GunHeadshot_SR_T01",
"Schematic:SID_Wall_Launcher_R_T01",
"Hero:HID_Commando_GrenadeMaster_SR_T01",
"Schematic:SID_Wall_Electric_VR_T01",
"Hero:HID_Commando_GrenadeGun_VR_T01",
"Schematic:SID_Wall_Electric_UC_T01",
"Hero:HID_Commando_GrenadeGun_SR_T01",
"Schematic:SID_Wall_Electric_SR_T01",
"Hero:HID_Commando_GrenadeGun_R_T01",
"Schematic:SID_Wall_Electric_R_T01",
"Hero:HID_Commando_GCGrenade_VR_T01",
"Schematic:SID_Wall_Darts_VR_T01",
"Hero:HID_Commando_GCGrenade_SR_T01",
"Schematic:SID_Wall_Darts_UC_T01",
"Hero:HID_Commando_GCGrenade_R_T01",
"Schematic:SID_Wall_Darts_SR_T01",
"Hero:HID_Commando_010_VR_T01",
"Schematic:SID_Wall_Darts_R_T01",
"Hero:HID_Commando_010_SR_T01",
"Schematic:SID_Floor_Ward_VR_T01",
"Hero:HID_Commando_009_VR_T01",
"Schematic:SID_Floor_Ward_UC_T01",
"Hero:HID_Commando_009_SR_T01",
"Schematic:SID_Floor_Ward_SR_T01",
"Hero:HID_Commando_009_R_T01",
"Schematic:SID_Floor_Ward_R_T01",
"Hero:HID_Commando_008_VR_T01",
"Schematic:SID_Floor_Spikes_Wood_VR_T01",
"Hero:HID_Commando_008_SR_T01",
"Schematic:SID_Floor_Spikes_Wood_UC_T01",
"Hero:HID_Commando_008_R_T01",
"Schematic:SID_Floor_Spikes_Wood_SR_T01",
"Hero:HID_Commando_008_FoundersM_SR_T01",
"Schematic:SID_Floor_Spikes_Wood_R_T01",
"Hero:HID_Commando_008_FoundersF_SR_T01",
"Schematic:SID_Floor_Spikes_Wood_C_T01",
"Hero:HID_Commando_007_VR_T01",
"Schematic:SID_Floor_Spikes_VR_T01",
"Hero:HID_Commando_007_UC_T01",
"Schematic:SID_Floor_Spikes_UC_T01",
"Hero:HID_Commando_007_SR_T01",
"Schematic:SID_Floor_Spikes_SR_T01",
"Hero:HID_Commando_007_R_T01",
"Schematic:SID_Floor_Spikes_R_T01",
"Hero:HID_Commando_GrenadeGun_UC_T01",
"Schematic:SID_Floor_Launcher_VR_T01",
"Hero:HID_Constructor_Sony_R_T01",
"Schematic:SID_Floor_Launcher_UC_T01",
"Hero:HID_Constructor_RushBASE_VR_T01",
"Schematic:SID_Floor_Launcher_SR_T01",
"Hero:HID_Constructor_RushBASE_UC_T01",
"Schematic:SID_Floor_Launcher_R_T01",
"Hero:HID_Constructor_RushBASE_SR_T01",
"Schematic:SID_Floor_Health_VR_T01",
"Hero:HID_Constructor_RushBASE_R_T01",
"Schematic:SID_Floor_Health_UC_T01",
"Hero:HID_Constructor_PlasmaDamage_VR_T01",
"Schematic:SID_Floor_Health_SR_T01",
"Hero:HID_Constructor_PlasmaDamage_SR_T01",
"Schematic:SID_Floor_Health_R_T01",
"Hero:HID_Constructor_PlasmaDamage_R_T01",
"Schematic:SID_Ceiling_Gas_VR_T01",
"Hero:HID_Constructor_HammerTank_VR_T01",
"Schematic:SID_Ceiling_Gas_UC_T01",
"Hero:HID_Constructor_HammerTank_UC_T01",
"Schematic:SID_Ceiling_Gas_SR_T01",
"Hero:HID_Constructor_HammerTank_SR_T01",
"Schematic:SID_Ceiling_Gas_R_T01",
"Hero:HID_Constructor_HammerTank_R_T01",
"Schematic:SID_Ceiling_Electric_Single_VR_T01",
"Hero:HID_Constructor_HammerPlasma_VR_T01",
"Schematic:SID_Ceiling_Electric_Single_UC_T01",
"Hero:HID_Constructor_HammerPlasma_SR_T01",
"Schematic:SID_Ceiling_Electric_Single_SR_T01",
"Hero:HID_Constructor_BaseHyperHW_SR_T01",
"Schematic:SID_Ceiling_Electric_Single_R_T01",
"Hero:HID_Constructor_BaseHyper_VR_T01",
"Schematic:SID_Ceiling_Electric_Single_C_T01",
"Hero:HID_Constructor_BaseHyper_SR_T01",
"Schematic:SID_Ceiling_Electric_AOE_VR_T01",
"Hero:HID_Constructor_BaseHyper_R_T01",
"Schematic:SID_Ceiling_Electric_AOE_SR_T01",
"Hero:HID_Constructor_BASEBig_SR_T01",
"Schematic:SID_Ceiling_Electric_AOE_R_T01",
"Hero:HID_Constructor_010_VR_T01",
"Schematic:SID_Sniper_TripleShot_VR_Ore_T01",
"Hero:HID_Constructor_010_SR_T01",
"Schematic:SID_Sniper_TripleShot_SR_Ore_T01",
"Hero:HID_Constructor_009_VR_T01",
"Schematic:SID_Sniper_Standard_Scope_VR_Ore_T01",
"Hero:HID_Constructor_009_SR_T01",
"Schematic:SID_Sniper_Standard_Scope_SR_Ore_T01",
"Hero:HID_Constructor_009_R_T01",
"Schematic:SID_Sniper_Standard_VR_Ore_T01",
"Hero:HID_Constructor_008_VR_T01",
"Schematic:SID_Sniper_Standard_SR_Ore_T01",
"Hero:HID_Constructor_008_SR_T01",
"Schematic:SID_Sniper_Standard_Founders_VR_Ore_T01",
"Hero:HID_Constructor_008_R_T01",
"Schematic:SID_Sniper_Standard_UC_Ore_T01",
"Hero:HID_Constructor_008_FoundersM_SR_T01",
"Schematic:SID_Sniper_Standard_R_Ore_T01",
"Hero:HID_Constructor_008_FoundersF_SR_T01",
"Schematic:SID_Sniper_Standard_C_Ore_T01",
"Hero:HID_Constructor_007_VR_T01",
"Schematic:SID_Sniper_Shredder_VR_Ore_T01",
"Hero:HID_Constructor_007_UC_T01",
"Schematic:SID_Sniper_Shredder_SR_Ore_T01",
"Hero:HID_Constructor_007_SR_T01",
"Schematic:SID_Sniper_Hydraulic_VR_Ore_T01",
"Hero:HID_Constructor_007_R_T01",
"Schematic:SID_Sniper_Hydraulic_SR_Ore_T01",
"Hero:HID_Ninja_Swordmaster_SR_T01",
"Schematic:SID_Sniper_BoltAction_Scope_VR_Ore_T01",
"Hero:HID_Ninja_StarsRainHW_SR_T01",
"Schematic:SID_Sniper_BoltAction_Scope_SR_Ore_T01",
"Hero:HID_Ninja_StarsRain_VR_T01",
"Schematic:SID_Sniper_BoltAction_Scope_R_Ore_T01",
"Hero:HID_Ninja_StarsRain_SR_T01",
"Schematic:SID_Sniper_BoltAction_UC_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_VR_T01",
"Schematic:SID_Sniper_BoltAction_R_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_UC_T01",
"Schematic:SID_Sniper_BoltAction_C_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_SR_T01",
"Schematic:SID_Sniper_Auto_VR_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_R_T01",
"Schematic:SID_Sniper_Auto_SR_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_FoundersM_SR_T01",
"Schematic:SID_Sniper_Auto_Founders_VR_Ore_T01",
"Hero:HID_Ninja_StarsAssassin_FoundersF_SR_T01",
"Schematic:SID_Sniper_Auto_UC_Ore_T01",
"Hero:HID_Ninja_Sony_R_T01",
"Schematic:SID_Sniper_Auto_R_Ore_T01",
"Hero:HID_Ninja_SmokeDimMak_VR_T01",
"Schematic:SID_Sniper_AMR_VR_Ore_T01",
"Hero:HID_Ninja_SmokeDimMak_SR_T01",
"Schematic:SID_Sniper_AMR_SR_Ore_T01",
"Hero:HID_Ninja_SmokeDimMak_R_T01",
"Schematic:SID_Sniper_AMR_R_Ore_T01",
"Hero:HID_Ninja_SlashTail_VR_T01",
"Schematic:SID_Shotgun_Tactical_Precision_VR_Ore_T01",
"Hero:HID_Ninja_SlashTail_UC_T01",
"Schematic:SID_Shotgun_Tactical_Precision_SR_Ore_T01",
"Hero:HID_Ninja_SlashTail_SR_T01",
"Schematic:SID_Shotgun_Tactical_Precision_R_Ore_T01",
"Hero:HID_Ninja_SlashTail_R_T01",
"Schematic:SID_Shotgun_Tactical_UC_Ore_T01",
"Hero:HID_Ninja_SlashBreath_VR_T01",
"Schematic:SID_Shotgun_Tactical_R_Ore_T01",
"Hero:HID_Ninja_SlashBreath_SR_T01",
"Schematic:SID_Shotgun_Tactical_Founders_VR_Ore_T01",
"Hero:HID_Ninja_SlashBreath_R_T01",
"Schematic:SID_Shotgun_Tactical_Founders_SR_Ore_T01",
"Hero:HID_Ninja_010_VR_T01",
"Schematic:SID_Shotgun_Tactical_Founders_R_Ore_T01",
"Hero:HID_Ninja_010_SR_T01",
"Schematic:SID_Shotgun_Tactical_C_Ore_T01",
"Hero:HID_Ninja_009_VR_T01",
"Schematic:SID_Shotgun_Standard_VR_Ore_T01",
"Hero:HID_Ninja_009_SR_T01",
"Schematic:SID_Shotgun_Standard_SR_Ore_T01",
"Hero:HID_Ninja_009_R_T01",
"Schematic:SID_Shotgun_Standard_UC_Ore_T01",
"Hero:HID_Ninja_008_VR_T01",
"Schematic:SID_Shotgun_Standard_R_Ore_T01",
"Hero:HID_Ninja_008_SR_T01",
"Schematic:SID_Shotgun_Standard_C_Ore_T01",
"Hero:HID_Ninja_008_R_T01",
"Schematic:SID_Shotgun_SemiAuto_VR_Ore_T01",
"Hero:HID_Ninja_007_VR_T01",
"Schematic:SID_Shotgun_SemiAuto_UC_Ore_T01",
"Hero:HID_Ninja_007_UC_T01",
"Schematic:SID_Shotgun_SemiAuto_SR_Ore_T01",
"Hero:HID_Ninja_007_SR_T01",
"Schematic:SID_Shotgun_SemiAuto_R_Ore_T01",
"Hero:HID_Ninja_007_R_T01",
"Schematic:SID_Shotgun_Minigun_SR_Ore_T01",
"Hero:HID_Outlander_ZonePistolHW_SR_T01",
"Schematic:SID_Shotgun_Longarm_VR_Ore_T01",
"Hero:HID_Outlander_ZonePistol_VR_T01",
"Schematic:SID_Shotgun_Longarm_SR_Ore_T01",
"Hero:HID_Outlander_ZonePistol_SR_T01",
"Schematic:SID_Shotgun_Heavy_SR_Ore_T01",
"Hero:HID_Outlander_ZonePistol_R_T01",
"Schematic:SID_Shotgun_Break_OU_VR_Ore_T01",
"Hero:HID_Outlander_ZoneHarvest_VR_T01",
"Schematic:SID_Shotgun_Break_OU_SR_Ore_T01",
"Hero:HID_Outlander_ZoneHarvest_UC_T01",
"Schematic:SID_Shotgun_Break_OU_UC_Ore_T01",
"Hero:HID_Outlander_ZoneHarvest_SR_T01",
"Schematic:SID_Shotgun_Break_OU_R_Ore_T01",
"Hero:HID_Outlander_ZoneHarvest_R_T01",
"Schematic:SID_Shotgun_Break_VR_Ore_T01",
"Hero:HID_Outlander_ZoneFragment_SR_T01",
"Schematic:SID_Shotgun_Break_SR_Ore_T01",
"Hero:HID_Outlander_SphereFragment_VR_T01",
"Schematic:SID_Shotgun_Break_UC_Ore_T01",
"Hero:HID_Outlander_SphereFragment_SR_T01",
"Schematic:SID_Shotgun_Break_R_Ore_T01",
"Hero:HID_Outlander_SphereFragment_R_T01",
"Schematic:SID_Shotgun_Break_C_Ore_T01",
"Hero:HID_Outlander_Sony_R_T01",
"Schematic:SID_Shotgun_Auto_VR_Ore_T01",
"Hero:HID_Outlander_PunchPhase_VR_T01",
"Schematic:SID_Shotgun_Auto_SR_Ore_T01",
"Hero:HID_Outlander_PunchPhase_UC_T01",
"Schematic:SID_Shotgun_Auto_Founders_VR_Ore_T01",
"Hero:HID_Outlander_PunchPhase_SR_T01",
"Schematic:SID_Shotgun_Auto_UC_Ore_T01",
"Hero:HID_Outlander_PunchPhase_R_T01",
"Schematic:SID_Shotgun_Auto_R_Ore_T01",
"Hero:HID_Outlander_PunchDamage_VR_T01",
"Schematic:SID_Pistol_Zapper_VR_Ore_T01",
"Hero:HID_Outlander_PunchDamage_SR_T01",
"Schematic:SID_Pistol_Zapper_SR_Ore_T01",
"Hero:HID_Outlander_010_VR_T01",
"Schematic:SID_Pistol_Space_VR_Ore_T01",
"Hero:HID_Outlander_010_SR_T01",
"Schematic:SID_Pistol_Space_SR_Ore_T01",
"Hero:HID_Outlander_009_VR_T01",
"Schematic:SID_Pistol_SixShooter_UC_Ore_T01",
"Hero:HID_Outlander_009_SR_T01",
"Schematic:SID_Pistol_SixShooter_R_Ore_T01",
"Hero:HID_Outlander_009_R_T01",
"Schematic:SID_Pistol_SixShooter_C_Ore_T01",
"Hero:HID_Outlander_008_VR_T01",
"Schematic:SID_Pistol_SemiAuto_VR_Ore_T01",
"Hero:HID_Outlander_008_SR_T01",
"Schematic:SID_Pistol_SemiAuto_SR_Ore_T01",
"Hero:HID_Outlander_008_R_T01",
"Schematic:SID_Pistol_SemiAuto_Founders_VR_Ore_T01",
"Hero:HID_Outlander_008_FoundersM_SR_T01",
"Schematic:SID_Pistol_SemiAuto_UC_Ore_T01",
"Hero:HID_Outlander_008_FoundersF_SR_T01",
"Schematic:SID_Pistol_SemiAuto_R_Ore_T01",
"Hero:HID_Outlander_007_VR_T01",
"Schematic:SID_Pistol_SemiAuto_C_Ore_T01",
"Hero:HID_Outlander_007_UC_T01",
"Schematic:SID_Pistol_Rocket_SR_Ore_T01",
"Hero:HID_Outlander_007_SR_T01",
"Schematic:SID_Pistol_Rapid_VR_Ore_T01",
"Hero:HID_Outlander_007_R_T01",
"Schematic:SID_Pistol_Rapid_SR_Ore_T01",
"Defender:DID_DefenderSniper_Basic_VR_T01",
"Schematic:SID_Pistol_Rapid_R_Ore_T01",
"Defender:DID_DefenderSniper_Basic_UC_T01",
"Schematic:SID_Pistol_Rapid_Founders_VR_Ore_T01",
"Defender:DID_DefenderSniper_Basic_SR_T01",
"Schematic:SID_Pistol_Hydraulic_VR_Ore_T01",
"Defender:DID_DefenderSniper_Basic_R_T01",
"Schematic:SID_Pistol_Hydraulic_SR_Ore_T01",
"Defender:DID_DefenderSniper_Basic_C_T01",
"Schematic:SID_Pistol_Handcannon_Semi_VR_Ore_T01",
"Defender:DID_DefenderShotgun_Basic_VR_T01",
"Schematic:SID_Pistol_Handcannon_Semi_SR_Ore_T01",
"Defender:DID_DefenderShotgun_Basic_UC_T01",
"Schematic:SID_Pistol_Handcannon_Semi_R_Ore_T01",
"Defender:DID_DefenderShotgun_Basic_SR_T01",
"Schematic:SID_Pistol_Handcannon_VR_Ore_T01",
"Defender:DID_DefenderShotgun_Basic_R_T01",
"Schematic:SID_Pistol_Handcannon_SR_Ore_T01",
"Defender:DID_DefenderShotgun_Basic_C_T01",
"Schematic:SID_Pistol_Handcannon_R_Ore_T01",
"Defender:DID_DefenderPistol_Founders_VR_T01",
"Schematic:SID_Pistol_Handcannon_Founders_VR_Ore_T01",
"Defender:DID_DefenderPistol_Basic_VR_T01",
"Schematic:SID_Pistol_Gatling_VR_Ore_T01",
"Defender:DID_DefenderPistol_Basic_UC_T01",
"Schematic:SID_Pistol_Gatling_SR_Ore_T01",
"Defender:DID_DefenderPistol_Basic_SR_T01",
"Schematic:SID_Pistol_FireCracker_VR_Ore_T01",
"Defender:DID_DefenderPistol_Basic_R_T01",
"Schematic:SID_Pistol_FireCracker_SR_Ore_T01",
"Defender:DID_DefenderPistol_Basic_C_T01",
"Schematic:SID_Pistol_FireCracker_R_Ore_T01",
"Defender:DID_DefenderMelee_Basic_VR_T01",
"Schematic:SID_Pistol_Dragon_VR_Ore_T01",
"Defender:DID_DefenderMelee_Basic_UC_T01",
"Schematic:SID_Pistol_Dragon_SR_Ore_T01",
"Defender:DID_DefenderMelee_Basic_SR_T01",
"Schematic:SID_Pistol_BoltRevolver_UC_Ore_T01",
"Defender:DID_DefenderMelee_Basic_R_T01",
"Schematic:SID_Pistol_BoltRevolver_R_Ore_T01",
"Defender:DID_DefenderMelee_Basic_C_T01",
"Schematic:SID_Pistol_BoltRevolver_C_Ore_T01",
"Defender:DID_DefenderAssault_Founders_VR_T01",
"Schematic:SID_Pistol_Bolt_VR_Ore_T01",
"Defender:DID_DefenderAssault_Basic_VR_T01",
"Schematic:SID_Pistol_Bolt_SR_Ore_T01",
"Defender:DID_DefenderAssault_Basic_UC_T01",
"Schematic:SID_Pistol_AutoHeavy_VR_Ore_T01",
"Defender:DID_DefenderAssault_Basic_SR_T01",
"Schematic:SID_Pistol_AutoHeavy_SR_Ore_T01",
"Defender:DID_DefenderAssault_Basic_R_T01",
"Schematic:SID_Pistol_AutoHeavy_Founders_VR_Ore_T01",
"Defender:DID_DefenderAssault_Basic_C_T01",
"Schematic:SID_Pistol_AutoHeavy_Founders_SR_Ore_T01",
"Schematic:SID_Pistol_AutoHeavy_R_Ore_T01",
"Schematic:SID_Pistol_AutoHeavy_Founders_R_Ore_T01",
"Schematic:SID_Pistol_Auto_VR_Ore_T01",
"Schematic:SID_Pistol_Auto_SR_Ore_T01",
"Schematic:SID_Pistol_Auto_UC_Ore_T01",
"Schematic:SID_Pistol_Auto_R_Ore_T01",
"Schematic:SID_Pistol_Auto_C_Ore_T01",
"Schematic:SID_Launcher_Rocket_VR_Ore_T01",
"Schematic:SID_Launcher_Rocket_SR_Ore_T01",
"Schematic:SID_Launcher_Rocket_R_Ore_T01",
"Schematic:SID_Launcher_Pumpkin_RPG_SR_Ore_T01",
"Schematic:SID_Launcher_Hydraulic_VR_Ore_T01",
"Schematic:SID_Launcher_Hydraulic_SR_Ore_T01",
"Schematic:SID_Launcher_Grenade_VR_Ore_T01",
"Schematic:SID_Launcher_Grenade_SR_Ore_T01",
"Schematic:SID_Launcher_Grenade_R_Ore_T01",
"Schematic:SID_Assault_Surgical_VR_Ore_T01",
"Schematic:SID_Assault_Surgical_SR_Ore_T01",
"Schematic:SID_Assault_SingleShot_VR_Ore_T01",
"Schematic:SID_Assault_SingleShot_SR_Ore_T01",
"Schematic:SID_Assault_SingleShot_R_Ore_T01",
"Schematic:SID_Assault_SemiAuto_VR_Ore_T01",
"Schematic:SID_Assault_SemiAuto_SR_Ore_T01",
"Schematic:SID_Assault_SemiAuto_Founders_VR_Ore_T01",
"Schematic:SID_Assault_SemiAuto_UC_Ore_T01",
"Schematic:SID_Assault_SemiAuto_R_Ore_T01",
"Schematic:SID_Assault_SemiAuto_C_Ore_T01",
"Schematic:SID_Assault_Raygun_VR_Ore_T01",
"Schematic:SID_Assault_Raygun_SR_Ore_T01",
"Schematic:SID_Assault_Surgical_Drum_Founders_R_Ore_T01",
"Schematic:SID_Assault_LMG_VR_Ore_T01",
"Schematic:SID_Assault_LMG_SR_Ore_T01",
"Schematic:SID_Assault_LMG_R_Ore_T01",
"Schematic:SID_Assault_LMG_Drum_Founders_VR_Ore_T01",
"Schematic:SID_Assault_LMG_Drum_Founders_SR_Ore_T01",
"Schematic:SID_Assault_Hydra_SR_Ore_T01",
"Schematic:SID_Assault_Doubleshot_VR_Ore_T01",
"Schematic:SID_Assault_Doubleshot_SR_Ore_T01",
"Schematic:SID_Assault_Burst_VR_Ore_T01",
"Schematic:SID_Assault_Burst_SR_Ore_T01",
"Schematic:SID_Assault_Burst_UC_Ore_T01",
"Schematic:SID_Assault_Burst_R_Ore_T01",
"Schematic:SID_Assault_Burst_C_Ore_T01",
"Schematic:SID_Assault_Auto_VR_Ore_T01",
"Schematic:SID_Assault_Auto_SR_Ore_T01",
"Schematic:SID_Assault_Auto_Halloween_SR_Ore_T01",
"Schematic:SID_Assault_Auto_Founders_SR_Ore_T01",
"Schematic:SID_Assault_Auto_UC_Ore_T01",
"Schematic:SID_Assault_Auto_R_Ore_T01",
"Schematic:SID_Assault_Auto_C_Ore_T01",
"Schematic:SID_Piercing_Spear_Military_VR_Ore_T01",
"Schematic:SID_Piercing_Spear_Military_SR_Ore_T01",
"Schematic:SID_Piercing_Spear_Military_R_Ore_T01",
"Schematic:SID_Piercing_Spear_C_Ore_T01",
"Schematic:SID_Piercing_Spear_Laser_VR_Ore_T01",
"Schematic:SID_Piercing_Spear_Laser_SR_Ore_T01",
"Schematic:SID_Piercing_Spear_VR_Ore_T01",
"Schematic:SID_Piercing_Spear_SR_Ore_T01",
"Schematic:SID_Piercing_Spear_UC_Ore_T01",
"Schematic:SID_Piercing_Spear_R_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_C_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_Laser_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_Laser_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_Laser_Founders_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_Laser_Founders_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_Laser_Founders_R_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_UC_Ore_T01",
"Schematic:SID_Edged_Sword_Medium_R_Ore_T01",
"Schematic:SID_Edged_Sword_Light_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Light_UC_Ore_T01",
"Schematic:SID_Edged_Sword_Light_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Light_R_Ore_T01",
"Schematic:SID_Edged_Sword_Light_Founders_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Light_C_Ore_T01",
"Schematic:SID_Edged_Sword_Hydraulic_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Hydraulic_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_SR_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_R_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_Founders_VR_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_UC_Ore_T01",
"Schematic:SID_Edged_Sword_Heavy_C_Ore_T01",
"Schematic:SID_Edged_Scythe_C_Ore_T01",
"Schematic:SID_Edged_Scythe_Laser_VR_Ore_T01",
"Schematic:SID_Edged_Scythe_Laser_SR_Ore_T01",
"Schematic:SID_Edged_Scythe_VR_Ore_T01",
"Schematic:SID_Edged_Scythe_SR_Ore_T01",
"Schematic:SID_Edged_Scythe_UC_Ore_T01",
"Schematic:SID_Edged_Scythe_R_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_C_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_Laser_VR_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_Laser_SR_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_VR_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_SR_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_Founders_VR_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_UC_Ore_T01",
"Schematic:SID_Edged_Axe_Medium_R_Ore_T01",
"Schematic:SID_Edged_Axe_Light_VR_Ore_T01",
"Schematic:SID_Edged_Axe_Light_SR_Ore_T01",
"Schematic:SID_Edged_Axe_Light_UC_Ore_T01",
"Schematic:SID_Edged_Axe_Light_R_Ore_T01",
"Schematic:SID_Edged_Axe_Light_C_Ore_T01",
"Schematic:SID_Edged_Axe_Heavy_C_Ore_T01",
"Schematic:SID_Edged_Axe_Heavy_VR_Ore_T01",
"Schematic:SID_Edged_Axe_Heavy_SR_Ore_T01",
"Schematic:SID_Edged_Axe_Heavy_UC_Ore_T01",
"Schematic:SID_Edged_Axe_Heavy_R_Ore_T01",
"Schematic:SID_Blunt_Medium_C_Ore_T01",
"Schematic:SID_Blunt_Medium_VR_Ore_T01",
"Schematic:SID_Blunt_Medium_SR_Ore_T01",
"Schematic:SID_Blunt_Medium_UC_Ore_T01",
"Schematic:SID_Blunt_Medium_R_Ore_T01",
"Schematic:SID_Blunt_Light_VR_Ore_T01",
"Schematic:SID_Blunt_Light_SR_Ore_T01",
"Schematic:SID_Blunt_Tool_Light_UC_Ore_T01",
"Schematic:SID_Blunt_Tool_Light_R_Ore_T01",
"Schematic:SID_Blunt_Hammer_Rocket_VR_Ore_T01",
"Schematic:SID_Blunt_Hammer_Rocket_SR_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_C_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_VR_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_SR_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_Founders_VR_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_UC_Ore_T01",
"Schematic:SID_Blunt_Hammer_Heavy_R_Ore_T01",
"Schematic:SID_Blunt_Light_Rocketbat_VR_Ore_T01",
"Schematic:SID_Blunt_Light_Rocketbat_SR_Ore_T01",
"Schematic:SID_Blunt_Light_C_Ore_T01",
"Schematic:SID_Blunt_Light_Bat_UC_Ore_T01",
"Schematic:SID_Blunt_Light_Bat_R_Ore_T01",
"Schematic:SID_Blunt_Club_Light_VR_Ore_T01",
"Schematic:SID_Blunt_Club_Light_SR_Ore_T01",
"Schematic:SID_Blunt_Light_UC_Ore_T01",
"Schematic:SID_Blunt_Light_R_Ore_T01",
"Schematic:SID_Blunt_Heavy_Paddle_UC_Ore_T01",
"Schematic:SID_Blunt_Heavy_Paddle_R_Ore_T01",
"Schematic:SID_Blunt_Heavy_Paddle_C_Ore_T01"
]

View File

@@ -1,7 +0,0 @@
[
"lawin",
"ti93",
"pro100katyt",
"playeereq",
"matteoki"
]

View File

@@ -1,56 +0,0 @@
{
"Season2": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season3": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season4": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season5": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season6": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season7": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season8": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season9": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
},
"Season10": {
"battlePassPurchased": false,
"battlePassTier": 1,
"battlePassXPBoost": 0,
"battlePassXPFriendBoost": 0
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
[]

View File

@@ -1,10 +0,0 @@
{
"friends": [],
"incoming": [],
"outgoing": [],
"suggested": [],
"blocklist": [],
"settings": {
"acceptInvites": "public"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
{
"contentType": "collection",
"contentId": "motd-default-collection",
"tcId": "634e8e85-e2fc-4c68-bb10-93604cf6605f",
"contentItems": [
{
"contentType": "content-item",
"contentId": "46874c56-0973-4cbe-ac98-b580c5b36df5",
"tcId": "61fb3dd8-f23d-45cc-9058-058ab223ba5c",
"contentFields": {
"body": {
"ar": "استمتع بتجربة لعب استثنائية!",
"en": "Have a phenomenal gaming experience!",
"de": "Wünsche allen ein wunderbares Spielerlebnis!",
"es": "¡Que disfrutes de tu experiencia de videojuegos!",
"es-419": "¡Ten una experiencia de juego espectacular!",
"fr": "Un bon jeu à toutes et à tous !",
"it": "Ti auguriamo un'esperienza di gioco fenomenale!",
"ja": "驚きの体験をしよう!",
"ko": "게임에서 환상적인 경험을 해보세요!",
"pl": "Życzymy fenomenalnej gry!",
"pt-BR": "Tenha uma experiência de jogo fenomenal!",
"ru": "Желаю невероятно приятной игры!",
"tr": "Muhteşem bir oyun deneyimi yaşamanı dileriz!"
},
"entryType": "Website",
"image": [
{
"width": 1920,
"height": 1080,
"url": "https://fortnite-public-service-prod11.ol.epicgames.com/images/motd.png"
}
],
"tabTitleOverride": "LawinServer",
"tileImage": [
{
"width": 1024,
"height": 512,
"url": "https://fortnite-public-service-prod11.ol.epicgames.com/images/motd-s.png"
}
],
"title": {
"ar": "مرحبًا بك في LawinServer!",
"en": "Welcome to LawinServer!",
"de": "Willkommen bei LawinServer!",
"es": "¡Bienvenidos a LawinServer!",
"es-419": "¡Bienvenidos a LawinServer!",
"fr": "Bienvenue sur LawinServer !",
"it": "Benvenuto in LawinServer!",
"ja": "LawinServerへようこそ",
"ko": "LawinServer에 오신 것을 환영합니다!",
"pl": "Witaj w LawinServerze!",
"pt-BR": "Bem-vindo ao LawinServer!",
"ru": "Добро пожаловать в LawinServer!",
"tr": "LavinServer'a Hoş Geldiniz!"
},
"videoAutoplay": false,
"videoLoop": false,
"videoMute": false,
"videoStreamingEnabled": false,
"websiteButtonText": "Discord",
"websiteURL": "https://discord.gg/KJ8UaHZ"
},
"contentSchemaName": "MotdWebsiteNewsWithVideo"
}
]
}

View File

@@ -1,4 +0,0 @@
{
"accountId": "",
"optOutOfPublicLeaderboards": false
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
{
"author": "List made by PRO100KatYT",
"Season11": {
"ERG.Node.A.1": "AthenaCharacter:cid_645_athena_commando_f_wolly",
"ERG.Node.B.1": "AthenaGlider:glider_id_188_galileorocket_g7oki",
"ERG.Node.C.1": "AthenaBackpack:bid_430_galileospeedboat_9rxe3",
"ERG.Node.D.1": "AthenaCharacter:cid_643_athena_commando_m_ornamentsoldier",
"ERG.Node.A.2": "AthenaPickaxe:pickaxe_id_329_gingerbreadcookie1h",
"ERG.Node.A.3": "AthenaPickaxe:pickaxe_id_332_mintminer",
"ERG.Node.A.4": "AthenaDance:eid_snowglobe",
"ERG.Node.A.5": "AthenaGlider:glider_id_191_pinetree",
"ERG.Node.A.6": "AthenaItemWrap:wrap_188_wrappingpaper",
"ERG.Node.A.7": "AthenaItemWrap:wrap_183_newyear2020",
"ERG.Node.A.8": "AthenaSkyDiveContrail:trails_id_082_holidaygarland",
"ERG.Node.A.9": "AthenaMusicPack:musicpack_040_xmaschiptunes",
"ERG.Node.A.10": "AthenaLoadingScreen:lsid_208_smpattern",
"ERG.Node.A.11": "AthenaLoadingScreen:lsid_209_akcrackshot"
},
"Season19": {
"ERG.Node.A.1": "Token:14daysoffortnite_small_giftbox",
"ERG.Node.B.1": "AthenaDance:emoji_s19_animwinterfest2021",
"ERG.Node.C.1": "AthenaGlider:glider_id_335_logarithm_40qgl",
"ERG.Node.D.1": "AthenaCharacter:cid_a_323_athena_commando_m_bananawinter",
"ERG.Node.A.2": "HomebaseBannerIcon:brs19_winterfest2021",
"ERG.Node.A.3": "AthenaSkyDiveContrail:trails_id_137_turtleneckcrystal",
"ERG.Node.A.4": "AthenaItemWrap:wrap_429_holidaysweater",
"ERG.Node.A.5": "AthenaLoadingScreen:lsid_393_winterfest2021",
"ERG.Node.A.6": "AthenaMusicPack:musicpack_117_winterfest2021",
"ERG.Node.A.7": "AthenaDance:eid_epicyarn",
"ERG.Node.A.8": "AthenaCharacter:cid_a_310_athena_commando_F_scholarfestive",
"ERG.Node.A.9": "AthenaPickaxe:pickaxe_id_731_scholarfestivefemale1h",
"ERG.Node.A.10": "AthenaItemWrap:wrap_430_winterlights",
"ERG.Node.A.11": "AthenaDance:spid_346_winterfest_2021",
"ERG.Node.A.12": "AthenaPickaxe:pickaxe_ID_732_shovelmale"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@ import 'package:reboot_launcher/src/model/game_type.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/patcher.dart';
import 'package:reboot_launcher/src/util/reboot.dart';
import 'package:reboot_launcher/src/util/server.dart' as server;
late String? username;
late GameType type;
@@ -79,12 +80,13 @@ void main(List<String> args) async {
var serverType = getServerType(result);
var host = result["server-host"] ?? serverJson["${serverType.id}_host"];
var port = result["server-port"] ?? serverJson["${serverType.id}_port"];
var started = await startServer(host, port, serverType, result["matchmaking-address"]);
var started = await startServer(host, port, serverType);
if(!started){
stderr.writeln("Cannot start server!");
return;
}
server.writeMatchmakingIp(result["matchmaking-address"]);
autoRestart = result["auto-restart"];
await startGame();
}

View File

@@ -77,8 +77,8 @@ class _RebootApplicationState extends State<RebootApplication> {
);
}
ThemeData _createTheme(Brightness brightness) {
return ThemeData(
FluentThemeData _createTheme(Brightness brightness) {
return FluentThemeData(
brightness: brightness,
accentColor: SystemTheme.accentColor.accent.toAccentColor(),
visualDensity: VisualDensity.standard,

View File

@@ -38,7 +38,7 @@ Future<void> startGame() async {
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
}
_gameProcess = await Process.start(gamePath, createRebootArgs(username!, type))
_gameProcess = await Process.start(gamePath, createRebootArgs(username!, type, ""))
..exitCode.then((_) => _onClose())
..outLines.forEach((line) => _onGameOutput(line, dll, hosting, verbose));
}

View File

@@ -1,19 +1,17 @@
import 'dart:io';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/embedded/server.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_proxy/shelf_proxy.dart';
import '../model/server_type.dart';
import '../util/server.dart';
import 'game.dart';
import '../util/server.dart' as server;
Future<bool> startServer(String? host, String? port, ServerType type, String? matchmakingIp) async {
Future<bool> startServer(String? host, String? port, ServerType type) async {
stdout.writeln("Starting backend server...");
switch(type){
case ServerType.local:
var result = await ping(host ?? "127.0.0.1", port ?? "3551");
var result = await server.ping(host ?? "127.0.0.1", port ?? "3551");
if(result == null){
throw Exception("Local backend server is not running");
}
@@ -22,11 +20,8 @@ Future<bool> startServer(String? host, String? port, ServerType type, String? ma
return true;
case ServerType.embedded:
stdout.writeln("Starting an embedded server...");
await startEmbeddedServer(
() => matchmakingIp ?? "127.0.0.1"
);
await startEmbeddedMatchmaker();
var result = await ping(host ?? "127.0.0.1", port ?? "3551");
await server.startServer();
var result = await server.ping(host ?? "127.0.0.1", port ?? "3551");
if(result == null){
throw Exception("Cannot start embedded server");
}
@@ -62,7 +57,7 @@ Future<HttpServer?> _changeReverseProxyState(String host, String port) async {
}
try{
var uri = await ping(host, port);
var uri = await server.ping(host, port);
if(uri == null){
return null;
}

View File

@@ -1,9 +1,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'package:async/async.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
@@ -14,12 +12,13 @@ import 'package:reboot_launcher/src/model/game_type.dart';
class GameController extends GetxController {
late final GetStorage _storage;
late final TextEditingController username;
late final TextEditingController version;
late final TextEditingController customLaunchArgs;
late final Rx<List<FortniteVersion>> versions;
late final Rxn<FortniteVersion> _selectedVersion;
late final Rx<GameType> type;
late final HashMap<GameType, GameInstance> gameInstancesMap;
late final RxBool started;
late final RxBool autostartGameServer;
late bool updated;
late bool error;
late bool failing;
@@ -50,10 +49,16 @@ class GameController extends GetxController {
username = TextEditingController(text: _readUsername());
username.addListener(() => _storage.write("${type.value == GameType.client ? 'game' : 'host'}_username", username.text));
customLaunchArgs = TextEditingController(text: _storage.read("custom_launch_args" ?? ""));
customLaunchArgs.addListener(() => _storage.write("custom_launch_args", customLaunchArgs.text));
gameInstancesMap= HashMap();
started = RxBool(false);
autostartGameServer = RxBool(_storage.read("auto_start_game_server") ?? true);
autostartGameServer.listen((value) => _storage.write("auto_start_game_server", value));
updated = false;
error = false;
@@ -95,10 +100,10 @@ class GameController extends GetxController {
bool get hasNoVersions => versions.value.isEmpty;
Rxn<FortniteVersion> get selectedVersionObs => _selectedVersion;
GameInstance? get currentGameInstance => gameInstancesMap[type()];
FortniteVersion? get selectedVersion => _selectedVersion();
set selectedVersion(FortniteVersion? version) {
_selectedVersion(version);
_storage.write("version", version?.name);

View File

@@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart';
import 'package:jaguar/jaguar.dart';
import '../model/server_type.dart';
import '../util/server.dart';
class ServerController extends GetxController {
static const String _serverName = "127.0.0.1";
@@ -17,13 +18,14 @@ class ServerController extends GetxController {
late final Rx<ServerType> type;
late final RxBool warning;
late RxBool started;
Jaguar? embeddedServer;
Jaguar? embeddedMatchmaker;
late RxBool loginAutomatically;
HttpServer? remoteServer;
ServerController() {
_storage = GetStorage("server");
started = RxBool(false);
loginAutomatically = RxBool(_storage.read("login_automatically") ?? false);
loginAutomatically.listen((value) => _storage.write("login_automatically", value));
type = Rx(ServerType.values.elementAt(_storage.read("type") ?? 0));
type.listen((value) {
host.text = _readHost();
@@ -35,17 +37,12 @@ class ServerController extends GetxController {
stop();
});
host = TextEditingController(text: _readHost());
host.addListener(() => _storage.write("${type.value.id}_host", host.text));
port = TextEditingController(text: _readPort());
port.addListener(() => _storage.write("${type.value.id}_port", port.text));
warning = RxBool(_storage.read("lawin_value") ?? true);
warning.listen((value) => _storage.write("lawin_value", value));
started = RxBool(false);
}
String _readHost() {
@@ -63,8 +60,7 @@ class ServerController extends GetxController {
try{
switch(type()){
case ServerType.embedded:
await embeddedServer?.close();
await embeddedMatchmaker?.close();
stopServer();
break;
case ServerType.remote:
await remoteServer?.close(force: true);

View File

@@ -1,8 +1,11 @@
import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:reboot_launcher/src/model/tutorial_page.dart';
import 'package:ini/ini.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'dart:ui';
import '../util/reboot.dart';
@@ -15,12 +18,7 @@ class SettingsController extends GetxController {
late final TextEditingController consoleDll;
late final TextEditingController authDll;
late final TextEditingController matchmakingIp;
late final Rx<PaneDisplayMode> displayType;
late final RxBool automaticallyStartMatchmaker;
late final RxBool doNotAskAgain;
late final RxBool advancedMode;
late final RxBool autoUpdate;
late Rx<TutorialPage> tutorialPage;
late double width;
late double height;
late double? offsetX;
@@ -43,29 +41,18 @@ class SettingsController extends GetxController {
matchmakingIp.addListener(() async {
var text = matchmakingIp.text;
_storage.write("ip", text);
writeMatchmakingIp(text);
});
automaticallyStartMatchmaker = RxBool(_storage.read("start_matchmaker_automatically") ?? false);
automaticallyStartMatchmaker.listen((value) => _storage.write("start_matchmaker_automatically", value));
doNotAskAgain = RxBool(_storage.read("do_not_ask_again") ?? false);
doNotAskAgain.listen((value) => _storage.write("do_not_ask_again", value));
width = _storage.read("width") ?? window.physicalSize.width;
height = _storage.read("height") ?? window.physicalSize.height;
width = _storage.read("width") ?? 912;
height = _storage.read("height") ?? 660;
offsetX = _storage.read("offset_x");
offsetY = _storage.read("offset_y");
advancedMode = RxBool(_storage.read("advanced") ?? false);
advancedMode.listen((value) async => _storage.write("advanced", value));
autoUpdate = RxBool(_storage.read("auto_update") ?? false);
autoUpdate.listen((value) async => _storage.write("auto_update", value));
displayType = Rx(PaneDisplayMode.top);
scrollingDistance = 0.0;
tutorialPage = Rx(TutorialPage.start);
}
TextEditingController _createController(String key, String name) {

View File

@@ -40,7 +40,6 @@ class AddLocalVersion extends StatelessWidget {
),
FileSelector(
label: "Location",
placeholder: "Type the game folder",
windowTitle: "Select game folder",
controller: _gamePathController,

View File

@@ -273,7 +273,6 @@ class _AddServerVersionState extends State<AddServerVersion> {
VersionNameInput(controller: _nameController),
const SizedBox(height: 16.0),
FileSelector(
label: "Destination",
placeholder: "Type the download destination",
windowTitle: "Select download destination",
controller: _pathController,

View File

@@ -6,14 +6,12 @@ import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/dialog/dialog.dart';
import 'package:reboot_launcher/src/dialog/dialog_button.dart';
import 'package:reboot_launcher/src/dialog/snackbar.dart';
import 'package:reboot_launcher/src/embedded/server.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:sync/semaphore.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../main.dart';
import '../page/home_page.dart';
import '../util/server.dart';
extension ServerControllerDialog on ServerController {
@@ -81,10 +79,7 @@ extension ServerControllerDialog on ServerController {
try{
switch(type()){
case ServerType.embedded:
embeddedServer = await startEmbeddedServer(
() => Get.find<SettingsController>().matchmakingIp.text,
);
embeddedMatchmaker = await startEmbeddedMatchmaker();
startServer();
break;
case ServerType.remote:
var uriResult = await _pingRemoteInteractive();

View File

@@ -1,333 +0,0 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'package:jaguar/http/context/context.dart';
import 'package:reboot_launcher/src/embedded/utils.dart';
import '../util/os.dart';
final Directory _profiles = Directory("${Platform.environment["UserProfile"]}\\.reboot_launcher\\backend\\profiles");
const String _token = "reboot_token";
const String _clientId = "reboot_client";
const String _device = "reboot_device";
const String _sessionId = "3c3662bcb661d6de679c636744c66b62";
List<Map<String, Object>> getAccounts(Context context) {
return context.query.getList("accountId").map(getAccount).toList();
}
Map<String, Object> getAccount(String account) {
return {"id": account, "displayName": _parseUsername(account), "externalAuths": {}};
}
Map<String, Object> getAccountInfo(Context context) {
var usernameId = context.pathParams.get("accountId")!;
var accountName = _parseUsername(usernameId);
return {
"id": usernameId,
"displayName": accountName,
"name": "Reboot",
"email": usernameId,
"failedLoginAttempts": 0,
"lastLogin": "2022-11-08T18:55:52.341Z",
"numberOfDisplayNameChanges": 0,
"ageGroup": "UNKNOWN",
"headless": false,
"country": "US",
"lastName": "Server",
"preferredLanguage": "en",
"canUpdateDisplayName": false,
"tfaEnabled": false,
"emailVerified": true,
"minorVerified": false,
"minorExpected": false,
"minorStatus": "UNKNOWN"
};
}
List<Map<String, Object>> getExternalAuths(Context context) => [];
Future<Map<String, Object>> getOAuthToken(Context context) async {
var usernameId = await _getUsername(context);
var accountName = _parseUsername(usernameId);
return {
"access_token": _token,
"expires_in": 28800,
"expires_at": "9999-12-02T01:12:01.100Z",
"token_type": "bearer",
"refresh_token": _token,
"refresh_expires": 86400,
"refresh_expires_at": "9999-12-02T01:12:01.100Z",
"account_id": usernameId,
"client_id": _clientId,
"internal_client": true,
"client_service": "fortnite",
"displayName": accountName,
"app": "fortnite",
"in_app_id": usernameId,
"device_id": _device
};
}
Future<String> _getUsername(Context context) async {
var params = await parseBody(context);
var username = params["username"];
return username ?? "unknown@projectreboot.dev";
}
Map<String, Object> verifyOAuthToken(Context context) {
return {
"token": _token,
"session_id": _sessionId,
"token_type": "bearer",
"client_id": _clientId,
"internal_client": true,
"client_service": "fortnite",
"account_id": "unknown",
"expires_in": 28800,
"expires_at": "9999-12-02T01:12:01.100Z",
"auth_method": "exchange_code",
"display_name": "unknown",
"app": "fortnite",
"in_app_id": "unknown",
"device_id": _device
};
}
List<Map<String, Object>> getExchange(Context context) => [];
List<String> getSsoDomains(Context context) => [
"unrealengine.com",
"unrealtournament.com",
"fortnite.com",
"epicgames.com"
];
String tryPlayOnPlatform(Context context) => "true";
List<Map<String, Object>> getFeatures(Context context) => [];
Map<String, Object?> getProfile(Context context){
var profileId = context.query.get("profileId");
if (profileId == null) {
return {"Error": "Profile not defined."};
}
var profileJson = _getProfileJson(profileId, context);
var profileFile = _getProfileFile(context);
var baseRevision = profileJson["rvn"] ?? 0;
var queryRevision = context.query.getInt("rvn") ?? -1;
var profileChanges = _getFullProfileUpdate(context, profileId, profileJson, queryRevision, baseRevision);
if(profileId == "athena" && !profileFile.existsSync()) {
profileFile.writeAsStringSync(json.encode(profileJson), flush: true);
}
return {
"profileRevision": baseRevision,
"profileId": profileId,
"profileChangesBaseRevision": baseRevision,
"profileChanges": profileChanges,
"profileCommandRevision": profileJson["commandRevision"] ?? 0,
"serverTime": "2022-11-08T18:55:52.341Z",
"responseVersion": 1
};
}
Map<String, dynamic> _getProfileJson(String profileId, Context context) {
if(profileId == "athena"){
var profile = _getProfileFile(context);
if(profile.existsSync()){
return json.decode(profile.readAsStringSync());
}
var body = loadEmbedded("profiles/$profileId.json").readAsStringSync();
return json.decode(body);
}
var profileJson = json.decode(loadEmbedded("profiles/$profileId.json").readAsStringSync());
return profileJson;
}
Future<Map<String, Object>> equipItem(Context context) async {
var profileFile = _getProfileFile(context);
var profileJson = json.decode(profileFile.readAsStringSync());
var baseRevision = profileJson["rvn"] ?? 0;
var queryRevision = context.query.getInt("rvn") ?? -1;
var body = json.decode(utf8.decode(await context.body));
var variant = _getReturnVariant(body, profileJson);
var change = _getStatsChanged(body, profileJson);
var profileChanges = _getProfileChanges(queryRevision, baseRevision, profileJson, change, body, variant);
profileFile.writeAsStringSync(json.encode(profileJson));
return {
"profileRevision": baseRevision,
"profileId": "athena",
"profileChangesBaseRevision": baseRevision,
"profileChanges": profileChanges,
"profileCommandRevision": profileJson["commandRevision"] ?? 0,
"serverTime": "2022-11-08T18:55:52.341Z",
"responseVersion": 1
};
}
List<dynamic> _getProfileChanges(int queryRevision, baseRevision, profileJson, bool change, body, bool variant) {
var changes = [];
if (change) {
var category = ("favorite_${body["slotName"] ?? "character"}")
.toLowerCase();
if (category == "favorite_itemwrap") {
category += "s";
}
profileJson["rvn"] = (profileJson["rvn"] ?? 0) + 1;
profileJson["commandRevision"] = (profileJson["commandRevision"] ?? 0) + 1;
changes.add({
"changeType": "statModified",
"name": category,
"value": profileJson["stats"]["attributes"][category]
});
if (variant) {
changes.add({
"changeType": "itemAttrChanged",
"itemId": body["itemToSlot"],
"attributeName": "variants",
"attributeValue": profileJson["items"][body["itemToSlot"]]["attributes"]["variants"]
});
}
}
if(queryRevision != baseRevision){
return [{
"changeType": "fullProfileUpdate",
"profile": profileJson
}];
}
return changes;
}
bool _getStatsChanged(body, profileJson) {
var slotName = body["slotName"];
if (slotName == null) {
return false;
}
switch (slotName) {
case "Character":
profileJson["stats"]["attributes"]["favorite_character"] =
body["itemToSlot"] ?? "";
return true;
case "Backpack":
profileJson["stats"]["attributes"]["favorite_backpack"] =
body["itemToSlot"] ?? "";
return true;
case "Pickaxe":
profileJson["stats"]["attributes"]["favorite_pickaxe"] =
body["itemToSlot"] ?? "";
return true;
case "Glider":
profileJson["stats"]["attributes"]["favorite_glider"] =
body["itemToSlot"] ?? "";
return true;
case "SkyDiveContrail":
profileJson["stats"]["attributes"]["favorite_skydivecontrail"] =
body["itemToSlot"] ?? "";
return true;
case "MusicPack":
profileJson["stats"]["attributes"]["favorite_musicpack"] =
body["itemToSlot"] ?? "";
return true;
case "LoadingScreen":
profileJson["stats"]["attributes"]["favorite_loadingscreen"] =
body["itemToSlot"] ?? "";
return true;
case "Dance":
var index = body["indexWithinSlot"] ?? 0;
if (index >= 0) {
profileJson["stats"]["attributes"]["favorite_dance"][index] =
body["itemToSlot"] ?? "";
}
return true;
case "ItemWrap":
var index = body["indexWithinSlot"] ?? 0;
if (index < 0) {
for (var i = 0; i < 7; i++) {
profileJson["stats"]["attributes"]["favorite_itemwraps"][i] =
body["itemToSlot"] ?? "";
}
} else {
profileJson["stats"]["attributes"]["favorite_itemwraps"][index] =
body["itemToSlot"] ?? "";
}
return true;
default:
return false;
}
}
bool _getReturnVariant(body, profileJson) {
var variantUpdates = body["variantUpdates"] ?? [];
if(!variantUpdates.toString().contains("active")){
return false;
}
try {
var variantJson = profileJson["items"][body["itemToSlot"]]["attributes"]["variants"] ?? [];
if (variantJson.isEmpty) {
variantJson = variantUpdates;
}
for (var i in variantJson) {
try {
if (variantJson[i]["channel"].toLowerCase() == body["variantUpdates"][i]["channel"].toLowerCase()) {
profileJson["items"][body["itemToSlot"]]["attributes"]["variants"][i]["active"] = body["variantUpdates"][i]["active"] ?? "";
}
} catch (_) {
// Ignored
}
}
return true;
} catch (_) {
// Ignored
}
return false;
}
List<Map<String, Object?>> _getFullProfileUpdate(Context context, String profileName, Map<String, dynamic> profileJson, int queryRevision, int baseRevision) {
if (queryRevision == baseRevision) {
return [];
}
if (profileName == "athena") {
var season = parseSeason(context);
profileJson["stats"]["attributes"]["season_num"] = season;
profileJson["stats"]["attributes"]["book_purchased"] = true;
profileJson["stats"]["attributes"]["book_level"] = 100;
profileJson["stats"]["attributes"]["season_match_boost"] = 100;
profileJson["stats"]["attributes"]["season_friend_match_boost"] = 100;
}
return [{
"changeType": "fullProfileUpdate",
"profile": profileJson
}];
}
String _parseUsername(String username) =>
username.contains("@") ? username.split("@")[0] : username;
File _getProfileFile(Context context) {
if(!_profiles.existsSync()){
_profiles.createSync(recursive: true);
}
return File("${_profiles.path}\\ClientProfile.json");
}

View File

@@ -1,43 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:jaguar/jaguar.dart';
class EmbeddedErrorWriter extends ErrorWriter {
static const String _errorName404 = "errors.com.lawinserver.common.not_found";
static const String _errorName500 = "errors.com.lawinserver.common.error";
static const String _errorCode = "1004";
@override
FutureOr<Response> make404(Context ctx) {
stdout.writeln("Unknown path: ${ctx.uri} with method ${ctx.method}");
ctx.response.headers.set('X-Epic-Error-Name', _errorName404);
ctx.response.headers.set('X-Epic-Error-Code', _errorCode);
return Response.json(
statusCode: 204,
{
"errorCode": _errorName404,
"errorMessage": "Sorry the resource you were trying to find could not be found",
"numericErrorCode": _errorCode,
"originatingService": "any",
"intent": "prod"
}
);
}
@override
FutureOr<Response> make500(Context ctx, Object error, [StackTrace? stack]) {
ctx.response.headers.set('X-Epic-Error-Name', _errorName500);
ctx.response.headers.set('X-Epic-Error-Code', _errorCode);
return Response.json(
statusCode: 500,
{
"errorCode": _errorName500,
"errorMessage": "Sorry the resource you were trying to find threw an error",
"numericErrorCode": _errorCode,
"originatingService": "any",
"intent": "prod"
}
);
}
}

View File

@@ -1,33 +0,0 @@
import 'package:jaguar/http/context/context.dart';
Map<String, Object?> getFortniteStatus(Context context) => {
"serviceInstanceId": "fortnite",
"status": "UP",
"message": "Fortnite is online",
"maintenanceUri": null,
"overrideCatalogIds": ["a7f138b2e51945ffbfdacc1af0541053"],
"allowedActions": [],
"banned": false,
"launcherInfoDTO": {
"appName": "Fortnite",
"catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5",
"namespace": "fn"
}
};
List<Map<String, Object?>> getBulkStatus(Context context) => [
{
"serviceInstanceId": "fortnite",
"status": "UP",
"message": "fortnite is up.",
"maintenanceUri": null,
"overrideCatalogIds": ["a7f138b2e51945ffbfdacc1af0541053"],
"allowedActions": ["PLAY", "DOWNLOAD"],
"banned": false,
"launcherInfoDTO": {
"appName": "Fortnite",
"catalogItemId": "4fe75bbc5a674f4f9b356b5c90567da5",
"namespace": "fn"
}
}
];

View File

@@ -1,144 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:jaguar/http/context/context.dart';
import 'package:uuid/uuid.dart';
import 'package:jaguar/jaguar.dart';
String _build = "0";
String? _customIp;
Map<String, Object> getPlayerTicket(Context context){
var bucketId = context.query.get("bucketId");
if(bucketId == null){
return {"Error": "Missing bucket id"};
}
_build = bucketId.split(":")[0];
_customIp = context.query.get("player.option.customKey");
return {
"serviceUrl": "ws://127.0.0.1:8080",
"ticketType": "mms-player",
"payload": "69=",
"signature": "420="
};
}
Map<String, Object?> getSessionAccount(Context context) => {
"accountId": context.pathParams.get("accountId"),
"sessionId": context.pathParams.get("sessionId"),
"key": "AOJEv8uTFmUh7XM2328kq9rlAzeQ5xzWzPIiyKn2s7s="
};
Future<Map<String, Object?>> getMatch(Context context, String Function() ipQuery) async {
var ipAndPort = _customIp ?? ipQuery().trim();
var ip = ipAndPort.contains(":") ? ipAndPort.split(":")[0] : ipAndPort;
var port = ipAndPort.contains(":") ? int.parse(ipAndPort.split(":")[1]) : 7777;
return {
"id": context.pathParams.get("sessionId"),
"ownerId": _randomUUID(),
"ownerName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968",
"serverName": "[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968",
"serverAddress": ip,
"serverPort": port,
"maxPublicPlayers": 220,
"openPublicPlayers": 175,
"maxPrivatePlayers": 0,
"openPrivatePlayers": 0,
"attributes": {
"REGION_s": "EU",
"GAMEMODE_s": "FORTATHENA",
"ALLOWBROADCASTING_b": true,
"SUBREGION_s": "GB",
"DCID_s": "FORTNITE-LIVEEUGCEC1C2E30UBRCORE0A-14840880",
"tenant_s": "Fortnite",
"MATCHMAKINGPOOL_s": "Any",
"STORMSHIELDDEFENSETYPE_i": 0,
"HOTFIXVERSION_i": 0,
"PLAYLISTNAME_s": "Playlist_DefaultSolo",
"SESSIONKEY_s": _randomUUID(),
"TENANT_s": "Fortnite",
"BEACONPORT_i": 15009
},
"publicPlayers": [],
"privatePlayers": [],
"totalPlayers": 45,
"allowJoinInProgress": false,
"shouldAdvertise": false,
"isDedicated": false,
"usesStats": false,
"allowInvites": false,
"usesPresence": false,
"allowJoinViaPresence": true,
"allowJoinViaPresenceFriendsOnly": false,
"buildUniqueId": _build,
"lastUpdated": "2022-11-08T18:55:52.341Z",
"started": false
};
}
List<Map<String, Object>> getMatchmakingRequests() => [];
void queueMatchmaking(WebSocket ws) {
var now = DateTime.now();
var ticketId = md5.convert(utf8.encode("1$now")).toString();
var matchId = md5.convert(utf8.encode("2$now")).toString();
var sessionId = md5.convert(utf8.encode("3$now")).toString();
ws.addUtf8Text(utf8.encode(
jsonEncode({
"payload": {
"state": "Connecting"
},
"name": "StatusUpdate"
})
));
ws.addUtf8Text(utf8.encode(
jsonEncode({
"payload": {
"totalPlayers": 1,
"connectedPlayers": 1,
"state": "Waiting"
},
"name": "StatusUpdate"
})
));
ws.addUtf8Text(utf8.encode(
jsonEncode({
"payload": {
"ticketId": ticketId,
"queuedPlayers": 0,
"estimatedWaitSec": 0,
"status": {},
"state": "Queued"
},
"name": "StatusUpdate"
})
));
ws.addUtf8Text(utf8.encode(
jsonEncode({
"payload": {
"matchId": matchId,
"state": "SessionAssignment"
},
"name": "StatusUpdate"
})
));
ws.addUtf8Text(utf8.encode(
jsonEncode({
"payload": {
"matchId": matchId,
"sessionId": sessionId,
"joinDelaySec": 1
},
"name": "Play"
})
));
}
String _randomUUID() => const Uuid().v4().replaceAll("-", "").toUpperCase();

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
import 'package:jaguar/http/context/context.dart';
import 'package:reboot_launcher/src/embedded/utils.dart';
Map<String, Object?> getPrivacy(Context context) => {
"accountId": context.pathParams.get("accountId"),
"optOutOfPublicLeaderboards": false
};
Future<Map<String, Object?>> postPrivacy(Context context) async {
var body = await parseBody(context);
return {
"accountId": context.pathParams.get("accountId"),
"optOutOfPublicLeaderboards": body["optOutOfPublicLeaderboards"]
};
}

View File

@@ -1,120 +0,0 @@
import "dart:async";
import "dart:io";
import "package:jaguar/jaguar.dart";
import "package:reboot_launcher/src/embedded/auth.dart";
import 'package:reboot_launcher/src/embedded/misc.dart';
import 'package:reboot_launcher/src/embedded/privacy.dart';
import "package:reboot_launcher/src/embedded/storage.dart";
import 'package:reboot_launcher/src/embedded/storefront.dart';
import "package:reboot_launcher/src/embedded/version.dart";
import '../util/server.dart';
import "error.dart";
import "lightswitch.dart";
import 'matchmaking.dart';
Future<Jaguar> startEmbeddedServer(String Function() ipQuery) async {
var server = Jaguar(port: 3551, errorWriter: EmbeddedErrorWriter());
// Version
server.getJson("unknown", (context) => Response(body: "lawinserver"));
server.getJson("/fortnite/api/version", getVersion);
server.getJson("/fortnite/api/v2/versioncheck/*", hasUpdate);
server.getJson("/fortnite/api/v2/versioncheck*", hasUpdate);
server.getJson("/fortnite/api/versioncheck*", hasUpdate);
// Auth
server.getJson("/account/api/public/account/displayName/:accountId", getAccountInfo);
server.getJson("/account/api/public/account/:accountId", getAccountInfo);
server.getJson("/account/api/public/account/:accountId/externalAuths", getExternalAuths);
server.getJson("/account/api/public/account", getAccounts);
server.delete("/account/api/oauth/sessions/kill/*", (context) => Response(statusCode: 204));
server.getJson("/account/api/oauth/verify", verifyOAuthToken);
server.postJson("/account/api/oauth/token", getOAuthToken);
server.postJson("/account/api/oauth/exchange", getExchange);
server.getJson("/account/api/epicdomains/ssodomains", getSsoDomains);
server.post("/fortnite/api/game/v2/tryPlayOnPlatform/account/*", tryPlayOnPlatform);
server.post("/datarouter/api/v1/public/data/*", (context) => Response(statusCode: 204));
server.getJson("/fortnite/api/game/v2/enabled_features", getFeatures);
server.postJson("/fortnite/api/game/v2/grant_access/*", (context) => Response(statusCode: 204));
server.postJson("/fortnite/api/game/v2/profile/:profileId/client/EquipBattleRoyaleCustomization", equipItem);
server.postJson("/fortnite/api/game/v2/profile/:profileId/client/*", getProfile);
// Storage
server.getJson("/fortnite/api/cloudstorage/system", getStorageSettings);
server.get("/fortnite/api/cloudstorage/system/:file", getStorageSetting);
server.getJson("/fortnite/api/cloudstorage/user/:accountId", getStorageAccount);
server.getJson("/fortnite/api/cloudstorage/user/:accountId/:file", getStorageFile);
server.put("/fortnite/api/cloudstorage/user/:accountId/:file", addStorageFile);
// Status
server.getJson("/lightswitch/api/service/Fortnite/status", getFortniteStatus);
server.getJson("/lightswitch/api/service/bulk/status", getBulkStatus);
// Keychain and catalog
server.get("/fortnite/api/storefront/v2/catalog", getCatalog);
server.get("/fortnite/api/storefront/v2/keychain", getKeyChain);
server.get("/catalog/api/shared/bulk/offers", getOffers);
// Matchmaking
server.get("/fortnite/api/matchmaking/session/findPlayer/*", (context) => Response(statusCode: 200));
server.getJson("/fortnite/api/game/v2/matchmakingservice/ticket/player/*", getPlayerTicket);
server.getJson("/fortnite/api/game/v2/matchmaking/account/:accountId/session/:sessionId", getSessionAccount);
server.getJson("/fortnite/api/matchmaking/session/:sessionId", (context) => getMatch(context, ipQuery));
server.post("/fortnite/api/matchmaking/session/:accountId/join", (context) => Response(statusCode: 204));
server.postJson("/fortnite/api/matchmaking/session/matchMakingRequest", (context) => getMatchmakingRequests);
// Misc
server.getJson("/api/v1/events/Fortnite/download/*", getDownload);
server.getJson("/fortnite/api/receipts/v1/account/:accountId/receipts", getReceipts);
server.getJson("/content/api/pages/*", getContentPages);
server.getJson("/friends/api/v1/:accountId/settings", getFriendsSettings);
server.getJson("/friends/api/v1/:accountId/blocklist", getFriendsBlocklist);
server.getJson("/friends/api/public/blocklist/:accountId", getFriendsBlocklist);
server.getJson("/friends/api/public/friends/:accountId", getFriendsList);
server.getJson("/friends/api/public/list/fortnite/:accountId/recentPlayers", getRecentPlayers);
server.getJson("/fortnite/api/calendar/v1/timeline", getTimeline);
server.getJson("/fortnite/api/game/v2/events/tournamentandhistory/:accountId/EU/WindowsClient", getTournamentHistory);
server.get("/waitingroom/api/waitingroom", (context) => Response(statusCode: 204));
server.postJson("/api/v1/user/setting", (context) => []);
server.getJson("/eulatracking/api/public/agreements/fn/account/*", (context) => Response(statusCode: 204));
server.getJson("/socialban/api/public/v1/:accountId", getSocialBan);
server.getJson("/party/api/v1/Fortnite/user/*", getParty);
server.getJson("/friends/api/v1/*/settings", (context) => {});
server.getJson("/friends/api/v1/*/blocklist", (context) => {});
server.getJson("/friends/api/public/friends", (context) => []);
server.getJson("/friends/api/v1/:accountId/summary", (context) => []);
server.getJson("/friends/api/public/list/fortnite/*/recentPlayers", (context) => []);
server.getJson("/friends/api/public/blocklist/*", getBlockedFriends);
// Privacy
server.getJson("/fortnite/api/game/v2/privacy/account/:accountId", getPrivacy);
server.postJson("/fortnite/api/game/v2/privacy/account/:accountId", postPrivacy);
await server.serve(logRequests: true);
server.log.onRecord.listen((line) {
stdout.writeln(line);
serverLogFile.writeAsString("$line\n", mode: FileMode.append);
});
server.onException.add((ctx, exception, trace) {
stderr.writeln("An error occurred: $exception");
serverLogFile.writeAsString("An error occurred at ${ctx.uri}: \n$exception\n$trace\n", mode: FileMode.append);
});
return server;
}
Future<Jaguar> startEmbeddedMatchmaker() async {
var server = Jaguar(port: 8080);
WebSocket? ws;
server.wsStream(
"/",
(_, input) => ws = input,
after: [(_) => queueMatchmaking(ws!)]
);
await server.serve(logRequests: true);
return server;
}

View File

@@ -1,96 +0,0 @@
import 'package:path/path.dart' as path;
import 'dart:io';
import 'package:jaguar/jaguar.dart';
import 'package:jaguar/http/context/context.dart';
import 'package:crypto/crypto.dart';
import 'package:reboot_launcher/src/embedded/utils.dart';
import '../util/os.dart';
final Directory _settings = Directory("${Platform.environment["UserProfile"]}\\.reboot_launcher\\backend\\settings");
List getStorageSettings(Context context) =>
loadEmbeddedDirectory("config")
.listSync()
.map((e) => File(e.path))
.map(_getStorageSetting)
.toList();
Map<String, Object> _getStorageSetting(File file){
var name = path.basename(file.path);
var bytes = file.readAsBytesSync();
return {
"uniqueFilename": name,
"filename": name,
"hash": sha1.convert(bytes).toString(),
"hash256": sha256.convert(bytes).toString(),
"length": bytes.length,
"contentType": "application/octet-stream",
"uploaded": "2020-02-23T18:35:53.967Z",
"storageType": "S3",
"storageIds": {},
"doNotCache": true
};
}
Response getStorageSetting(Context context) {
var file = loadEmbedded("config\\${context.pathParams.get("file")}");
return Response(body: file.readAsStringSync());
}
Response getStorageFile(Context context) {
if (context.pathParams.get("file")?.toLowerCase() != "clientsettings.sav") {
return Response.json(
{"error": "File not found"},
statusCode: 404
);
}
var file = _getSettingsFile(context);
return Response(
body: file.existsSync() ? file.readAsBytesSync() : null,
headers: {"content-type": "application/octet-stream"}
);
}
List<Map<String, Object?>> getStorageAccount(Context context) {
var file = _getSettingsFile(context);
if (!file.existsSync()) {
return [];
}
var content = file.readAsBytesSync();
return [{
"uniqueFilename": "ClientSettings.Sav",
"filename": "ClientSettings.Sav",
"hash": sha1.convert(content).toString(),
"hash256": sha256.convert(content).toString(),
"length": content.length,
"contentType": "application/octet-stream",
"uploaded": "2020-02-23T18:35:53.967Z",
"storageType": "S3",
"storageIds": {},
"accountId": context.pathParams.get("accountId"),
"doNotCache": true
}];
}
Future<Response> addStorageFile(Context context) async {
if(!_settings.existsSync()){
await _settings.create(recursive: true);
}
var file = _getSettingsFile(context);
await file.writeAsBytes(await context.body);
return Response(statusCode: 204);
}
File _getSettingsFile(Context context) {
if(!_settings.existsSync()){
_settings.createSync(recursive: true);
}
return File("${_settings.path}\\ClientSettings.Sav");
}

View File

@@ -1,19 +0,0 @@
import 'package:jaguar/http/context/context.dart';
import 'package:jaguar/http/response/response.dart';
import 'package:reboot_launcher/src/util/os.dart';
final String _keyChain = loadEmbedded("responses/keychain.json").readAsStringSync();
final String _catalog = loadEmbedded("responses/catalog.json").readAsStringSync();
Response getCatalog(Context context) {
if (context.headers.value("user-agent")?.contains("2870186") == true) {
return Response(statusCode: 404);
}
return Response(body: _catalog, headers: {"content-type": "application/json"});
}
Response getKeyChain(Context context) => Response(body: _keyChain, headers: {"content-type": "application/json"});
Map<String, Object> getOffers(Context context) => {};

View File

@@ -1,42 +0,0 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:math';
import 'package:jaguar/http/context/context.dart';
const String _chars =
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
final Random _random = Random();
String randomString(int length) => String.fromCharCodes(
Iterable.generate(length, (_) => _chars.codeUnitAt(_random.nextInt(_chars.length))));
double parseSeasonBuild(Context context){
String? userAgent = context.headers.value("user-agent");
if (userAgent == null) {
return 1.0;
}
try {
var build = userAgent.split("Release-")[1].split("-")[0];
if (build.split(".").length == 3) {
var value = build.split(".");
return double.parse("${value[0]}.${value[1]}${value[2]}");
}
return double.parse(build);
} catch (_) {
return 2.0;
}
}
int parseSeason(Context context) => int.parse(parseSeasonBuild(context).toString().split(".")[0]);
Future<HashMap<String, String?>> parseBody(Context context) async {
var params = HashMap<String, String?>();
utf8.decode(await context.req.body)
.split("&")
.map((entry) => MapEntry(entry.substring(0, entry.indexOf("=")), entry.substring(entry.indexOf("=") + 1)))
.forEach((element) => params[element.key] = Uri.decodeQueryComponent(element.value));
return params;
}

View File

@@ -1,41 +0,0 @@
import 'package:jaguar/http/context/context.dart';
import 'package:reboot_launcher/src/util/time.dart';
Map<String, Object> getVersion(Context context) => {
"app": "fortnite",
"serverDate": "2022-11-08T18:55:52.341Z",
"overridePropertiesVersion": "unknown",
"cln": "17951730",
"build": "444",
"moduleName": "Fortnite-Core",
"buildDate": "2021-10-27T21:00:51.697Z",
"version": "18.30",
"branch": "Release-18.30",
"modules": {
"Epic-LightSwitch-AccessControlCore": {
"cln": "17237679",
"build": "b2130",
"buildDate": "2021-08-19T18:56:08.144Z",
"version": "1.0.0",
"branch": "trunk"
},
"epic-xmpp-api-v1-base": {
"cln": "5131a23c1470acbd9c94fae695ef7d899c1a41d6",
"build": "b3595",
"buildDate": "2019-07-30T09:11:06.587Z",
"version": "0.0.1",
"branch": "master"
},
"epic-common-core": {
"cln": "17909521",
"build": "3217",
"buildDate": "2021-10-25T18:41:12.486Z",
"version": "3.0",
"branch": "TRUNK"
}
}
};
Map<String, Object> hasUpdate(Context context) => {
"type": "NO_UPDATE"
};

View File

@@ -1,12 +1,15 @@
import 'dart:io';
import 'game_type.dart';
class GameInstance {
final Process gameProcess;
final Process? launcherProcess;
final Process? eacProcess;
bool tokenError;
bool hasChildServer;
GameInstance(this.gameProcess, this.launcherProcess, this.eacProcess)
GameInstance(this.gameProcess, this.launcherProcess, this.eacProcess, this.hasChildServer)
: tokenError = false;
void kill() {

View File

@@ -19,14 +19,8 @@ enum GameType {
}
String get name {
return this == GameType.client ? "Client"
: this == GameType.server ? "Server"
: "Headless Server";
}
String get message {
return this == GameType.client ? "A fortnite client will be launched to play multiplayer games"
: this == GameType.server ? "A fortnite client will be launched to host multiplayer games"
: "A fortnite client will be launched in the background to host multiplayer games";
return this == GameType.client ? "Game client"
: this == GameType.server ? "Game server"
: "Headless game server";
}
}

View File

@@ -19,7 +19,7 @@ enum ServerType {
}
String get name {
return this == ServerType.embedded ? "Embedded"
return this == ServerType.embedded ? "Embedded (Lawin)"
: this == ServerType.remote ? "Remote"
: "Local";
}

View File

@@ -1,5 +0,0 @@
enum TutorialPage {
start,
someoneElse,
yourOwn
}

View File

@@ -1,5 +1,3 @@
import 'dart:ui';
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
@@ -19,7 +17,6 @@ import 'package:window_manager/window_manager.dart';
import '../controller/settings_controller.dart';
import '../model/server_type.dart';
import '../model/tutorial_page.dart';
import 'info_page.dart';
class HomePage extends StatefulWidget {
@@ -30,10 +27,7 @@ class HomePage extends StatefulWidget {
}
class _HomePageState extends State<HomePage> with WindowListener {
static const double _headerSize = 48.0;
static const double _sectionSize = 100.0;
static const double _defaultPadding = 12.0;
static const int _headerButtonCount = 3;
final GameController _gameController = Get.find<GameController>();
final SettingsController _settingsController = Get.find<SettingsController>();
@@ -46,18 +40,12 @@ class _HomePageState extends State<HomePage> with WindowListener {
final RxBool _focused = RxBool(true);
final RxInt _index = RxInt(0);
bool _navigated = false;
bool _shouldMaximize = false;
@override
void initState() {
windowManager.addListener(this);
_searchController.addListener(_onSearch);
_onEasyMode();
_settingsController.advancedMode.listen((advanced) {
_onEasyMode();
_index.value = _index.value + (advanced ? 1 : -1);
});
super.initState();
}
@@ -73,15 +61,6 @@ class _HomePageState extends State<HomePage> with WindowListener {
.cast<NavigationPaneItem>();
}
void _onEasyMode() {
if(_settingsController.advancedMode.value){
return;
}
_gameController.type.value = GameType.client;
_serverController.type.value = ServerType.embedded;
}
@override
void dispose() {
windowManager.removeListener(this);
@@ -100,6 +79,12 @@ class _HomePageState extends State<HomePage> with WindowListener {
_focused.value = false;
}
@override
void onWindowResized() {
_settingsController.saveWindowSize();
super.onWindowResized();
}
@override
void onWindowMoved() {
_settingsController.saveWindowOffset(appWindow.position);
@@ -131,52 +116,52 @@ class _HomePageState extends State<HomePage> with WindowListener {
),
],
);
},
}
);
}
@override
Widget build(BuildContext context) {
return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) {
return _calculateSize();
},
child: SizeChangedLayoutNotifier(
child: Obx(_getViewStack)
)
);
}
Widget _getViewStack() {
var view = _createNavigationView();
return Stack(
Widget build(BuildContext context) => Obx(() => Stack(
children: [
view,
if(_settingsController.displayType() == PaneDisplayMode.top)
Align(
alignment: Alignment.topRight,
child: WindowTitleBar(focused: _focused())
NavigationView(
paneBodyBuilder: (body) => Padding(
padding: const EdgeInsets.all(_defaultPadding),
child: body
),
appBar: NavigationAppBar(
title: _draggableArea,
actions: WindowTitleBar(focused: _focused())
),
pane: NavigationPane(
selected: _selectedIndex,
onChanged: _onIndexChanged,
displayMode: PaneDisplayMode.auto,
items: _items,
footerItems: _footerItems,
autoSuggestBox: _autoSuggestBox,
autoSuggestBoxReplacement: const Icon(FluentIcons.search),
),
onOpenSearch: () => _searchFocusNode.requestFocus(),
transitionBuilder: (child, animation) => child
),
if(_settingsController.displayType() == PaneDisplayMode.top)
_createTopDisplayGestures(view.pane?.items.length ?? 0),
if(_focused() && isWin11)
const WindowBorder()
]
);
));
void _onIndexChanged(int index) {
_index.value = index;
_navigated = true;
}
Padding _createTopDisplayGestures(int size) => Padding(
padding: EdgeInsets.only(
left: _sectionSize * size,
right: _headerSize * _headerButtonCount,
),
child: SizedBox(
height: _headerSize,
child: _createWindowGestures()
)
TextBox get _autoSuggestBox => TextBox(
key: _searchKey,
controller: _searchController,
placeholder: 'Search',
focusNode: _searchFocusNode
);
GestureDetector _createWindowGestures({Widget? child}) => GestureDetector(
GestureDetector get _draggableArea => GestureDetector(
onDoubleTap: () {
if(!_shouldMaximize){
return;
@@ -187,89 +172,9 @@ class _HomePageState extends State<HomePage> with WindowListener {
},
onDoubleTapDown: (details) => _shouldMaximize = true,
onHorizontalDragStart: (event) => appWindow.startDragging(),
onVerticalDragStart: (event) => appWindow.startDragging(),
child: child
onVerticalDragStart: (event) => appWindow.startDragging()
);
NavigationView _createNavigationView() {
return NavigationView(
paneBodyBuilder: (body) => _createPage(body),
pane: NavigationPane(
size: const NavigationPaneSize(
topHeight: _headerSize
),
selected: _selectedIndex,
onChanged: _onIndexChanged,
displayMode: _settingsController.displayType(),
items: _createItems(),
indicator: const EndNavigationIndicator(),
footerItems: _createFooterItems(),
header: _settingsController.displayType() != PaneDisplayMode.open ? null : const SizedBox(height: _defaultPadding),
autoSuggestBox: _createAutoSuggestBox(),
autoSuggestBoxReplacement: _settingsController.displayType() == PaneDisplayMode.top ? null : const Icon(FluentIcons.search),
),
onOpenSearch: () => _searchFocusNode.requestFocus(),
transitionBuilder: _settingsController.displayType() == PaneDisplayMode.top ? null : (child, animation) => child
);
}
void _onIndexChanged(int index) {
_index.value = index;
_navigated = true;
}
TextBox? _createAutoSuggestBox() {
if (_settingsController.displayType() == PaneDisplayMode.top) {
return null;
}
return TextBox(
key: _searchKey,
controller: _searchController,
placeholder: 'Search',
focusNode: _searchFocusNode
);
}
Widget _createPage(Widget? body) {
if(_settingsController.displayType() == PaneDisplayMode.top){
return Padding(
padding: const EdgeInsets.all(_defaultPadding),
child: body
);
}
return Column(
children: [
Row(
children: [
Expanded(
child: _createWindowGestures(
child: Container(
height: _headerSize,
color: Colors.transparent
)
)
),
WindowTitleBar(focused: _focused())
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: _defaultPadding,
right: _defaultPadding,
bottom: _defaultPadding
),
child: body
)
)
],
);
}
int? get _selectedIndex {
var searchItems = _searchItems();
if (searchItems == null) {
@@ -288,10 +193,9 @@ class _HomePageState extends State<HomePage> with WindowListener {
return indexOnScreen;
}
List<NavigationPaneItem> get _allItems => [..._createItems(), ..._createFooterItems()];
List<NavigationPaneItem> get _allItems => [..._items, ..._footerItems];
List<NavigationPaneItem> _createFooterItems() => searchValue.isNotEmpty ? [] : [
if(_settingsController.displayType() != PaneDisplayMode.top)
List<NavigationPaneItem> get _footerItems => searchValue.isNotEmpty ? [] : [
PaneItem(
title: const Text("Settings"),
icon: const Icon(FluentIcons.settings),
@@ -299,14 +203,13 @@ class _HomePageState extends State<HomePage> with WindowListener {
)
];
List<NavigationPaneItem> _createItems() => _searchItems() ?? [
List<NavigationPaneItem> get _items => _searchItems() ?? [
PaneItem(
title: const Text("Home"),
icon: const Icon(FluentIcons.game),
body: const LauncherPage()
),
if(_settingsController.advancedMode.value)
PaneItem(
title: const Text("Backend"),
icon: const Icon(FluentIcons.server_enviroment),
@@ -319,50 +222,15 @@ class _HomePageState extends State<HomePage> with WindowListener {
body: const InfoPage(),
onTap: _onTutorial
),
if(_settingsController.displayType() == PaneDisplayMode.top)
PaneItem(
title: const Text("Settings"),
icon: const Icon(FluentIcons.settings),
body: SettingsPage()
)
];
void _onTutorial() {
if(!_navigated){
setState(() {
_settingsController.tutorialPage.value = TutorialPage.start;
_settingsController.scrollingDistance = 0;
});
setState(() => _settingsController.scrollingDistance = 0);
}
_navigated = false;
}
bool _calculateSize() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_settingsController.saveWindowSize();
var width = window.physicalSize.width;
PaneDisplayMode? newType;
if (width <= 1000) {
newType = PaneDisplayMode.top;
} else if (width >= 1500) {
newType = PaneDisplayMode.open;
} else if (width > 1000) {
newType = PaneDisplayMode.compact;
}
if(newType == null || newType == _settingsController.displayType()){
return;
}
_settingsController.displayType.value = newType;
_searchItems.value = null;
_searchController.text = "";
});
return true;
}
String get searchValue => _searchController.text;
}

View File

@@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import '../controller/settings_controller.dart';
import '../model/tutorial_page.dart';
import '../widget/shared/fluent_card.dart';
class InfoPage extends StatefulWidget {
const InfoPage({Key? key}) : super(key: key);
@@ -36,6 +36,7 @@ class _InfoPageState extends State<InfoPage> {
"Once you are in game, click PLAY to enter in-game\n If this doesn't work open the Fortnite console by clicking the button above tab\n If nothing happens, make sure that your keyboard locale is set to English\n Type 'open TYPE_THE_IP' without the quotes, for example: open 85.182.12.1"
];
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey();
final SettingsController _settingsController = Get.find<SettingsController>();
late final ScrollController _controller;
@@ -55,17 +56,52 @@ class _InfoPageState extends State<InfoPage> {
}
@override
Widget build(BuildContext context) {
switch(_settingsController.tutorialPage()) {
case TutorialPage.start:
return _createHomeScreen();
case TutorialPage.someoneElse:
Widget build(BuildContext context) => Navigator(
key: _navigatorKey,
initialRoute: "home",
onGenerateRoute: (settings) {
var screen = _createScreen(settings.name);
return FluentPageRoute(
builder: (context) => screen,
settings: settings
);
},
);
Widget _createScreen(String? name) {
switch(name){
case "home":
return _homeScreen;
case "else":
return _createInstructions(false);
case TutorialPage.yourOwn:
case "own":
return _createInstructions(true);
default:
throw Exception("Unknown page: $name");
}
}
Widget get _homeScreen => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_createCardWidget(
text: "Play on someone else's server",
description: "If one of your friends is hosting a game server, click here",
onClick: () => _navigatorKey.currentState?.pushNamed("else")
),
const SizedBox(
width: 8.0,
),
_createCardWidget(
text: "Host your own server",
description: "If you want to create your own server to invite your friends or to play around by yourself, click here",
onClick: () => _navigatorKey.currentState?.pushNamed("own")
)
]
);
SizedBox _createInstructions(bool own) {
var titles = own ? _ownTitles : _elseTitles;
var codeName = own ? "own" : "else";
@@ -76,8 +112,7 @@ class _InfoPageState extends State<InfoPage> {
padding: const EdgeInsets.only(
right: 20.0
),
child: Card(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
child: FluentCard(
child: ListTile(
title: SelectableText("${index + 1}. ${titles[index]}"),
subtitle: Padding(
@@ -93,38 +128,14 @@ class _InfoPageState extends State<InfoPage> {
);
}
Widget _createHomeScreen() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_createCardWidget(
text: "Play on someone else's server",
description: "If one of your friends is hosting a game server, click here",
onClick: () => setState(() => _settingsController.tutorialPage.value = TutorialPage.someoneElse)
),
const SizedBox(
width: 8.0,
),
_createCardWidget(
text: "Host your own server",
description: "If you want to create your own server to invite your friends or to play around by yourself, click here",
onClick: () => setState(() => _settingsController.tutorialPage.value = TutorialPage.yourOwn)
)
]
);
}
Widget _createCardWidget({required String text, required String description, required Function() onClick}) {
return Expanded(
Widget _createCardWidget({required String text, required String description, required Function() onClick}) => Expanded(
child: SizedBox(
height: double.infinity,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onClick,
child: Card(
child: FluentCard(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
@@ -155,5 +166,4 @@ class _InfoPageState extends State<InfoPage> {
)
)
);
}
}

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
@@ -9,23 +8,18 @@ import 'package:get_storage/get_storage.dart';
import 'package:reboot_launcher/main.dart';
import 'package:reboot_launcher/src/controller/build_controller.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/controller/server_controller.dart';
import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/dialog/dialog.dart';
import 'package:reboot_launcher/src/model/reboot_download.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/widget/home/game_type_selector.dart';
import 'package:reboot_launcher/src/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/widget/home/username_box.dart';
import 'package:reboot_launcher/src/widget/home/version_selector.dart';
import 'package:reboot_launcher/src/widget/shared/file_selector.dart';
import 'package:reboot_launcher/src/widget/shared/setting_tile.dart';
import '../dialog/dialog_button.dart';
import '../model/server_type.dart';
import '../util/checks.dart';
import '../util/reboot.dart';
import '../widget/shared/smart_input.dart';
import 'home_page.dart';
class LauncherPage extends StatefulWidget {
const LauncherPage(
@@ -38,7 +32,6 @@ class LauncherPage extends StatefulWidget {
class _LauncherPageState extends State<LauncherPage> {
final GameController _gameController = Get.find<GameController>();
final ServerController _serverController = Get.find<ServerController>();
final SettingsController _settingsController = Get.find<SettingsController>();
final BuildController _buildController = Get.find<BuildController>();
@@ -152,28 +145,98 @@ class _LauncherPageState extends State<LauncherPage> {
);
Widget get _homeScreen => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if(_gameController.error)
_updateError,
UsernameBox(),
Tooltip(
message:
"The hostname of the server that hosts the multiplayer matches",
child: Obx(() => SmartInput(
label: "Matchmaking Host",
placeholder:
"Type the hostname of the server that hosts the multiplayer matches",
controller: _settingsController.matchmakingIp,
validatorMode: AutovalidateMode.always,
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: _gameController.error ? _updateError : const SizedBox(),
),
AnimatedSize(
duration: const Duration(milliseconds: 300),
child: SizedBox(height: _gameController.error ? 16.0 : 0.0),
),
SettingTile(
title: "Username",
subtitle: "Enter the name that others will see once you are in-game",
content: TextFormBox(
placeholder: "username",
controller: _gameController.username,
validator: checkMatchmaking,
enabled: _serverController.type() == ServerType.embedded)
autovalidateMode: AutovalidateMode.always
)
),
const VersionSelector(),
if(_settingsController.advancedMode.value)
GameTypeSelector(),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Matchmaking host",
subtitle: "Enter the IP address of the game server hosting the match",
content: TextFormBox(
placeholder: "ip:port",
controller: _settingsController.matchmakingIp,
validator: checkMatchmaking,
autovalidateMode: AutovalidateMode.always
),
expandedContent: [
ListTile(
title: const Text(
"Automatically start a game server",
style: TextStyle(
fontSize: 14
),
),
subtitle: const Text("Choose whether an headless server should be automatically started when matchmaking is on localhost"),
trailing: Obx(() => ToggleSwitch(
checked: _gameController.autostartGameServer(),
onChanged: (value) => _gameController.autostartGameServer.value = value
))
),
],
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Version",
subtitle: "Select the version of Fortnite you want to play with your friends",
content: const VersionSelector(),
expandedContent: [
ListTile(
title: const Text(
"Add a version from this PC's local storage",
style: TextStyle(
fontSize: 14
),
),
trailing: Button(
onPressed: () => VersionSelector.openAddDialog(context),
child: const Text("Add build "),
),
),
ListTile(
title: const Text(
"Download any version from the cloud",
style: TextStyle(
fontSize: 14
),
),
trailing: Button(
onPressed: () => VersionSelector.openDownloadDialog(context),
child: const Text("Download"),
),
),
]
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Instance type",
subtitle: "Select the type of instance you want to launch",
content: GameTypeSelector()
),
const Expanded(child: SizedBox()),
const LaunchButton()
],
);

View File

@@ -6,6 +6,9 @@ import 'package:reboot_launcher/src/widget/server/server_type_selector.dart';
import 'package:reboot_launcher/src/widget/server/port_input.dart';
import 'package:reboot_launcher/src/widget/server/server_button.dart';
import '../model/server_type.dart';
import '../widget/shared/setting_tile.dart';
class ServerPage extends StatelessWidget {
final ServerController _serverController = Get.find<ServerController>();
@@ -14,28 +17,67 @@ class ServerPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Obx(() => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if(_serverController.warning.value)
GestureDetector(
onTap: () => _serverController.warning.value = false,
child: const MouseRegion(
cursor: SystemMouseCursors.click,
child: SizedBox(
const SizedBox(
width: double.infinity,
child: InfoBar(
title: Text("The backend server handles authentication and parties, not game hosting"),
severity: InfoBarSeverity.info
),
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Host",
subtitle: "Enter the host of the backend server",
content: TextFormBox(
placeholder: "host",
controller: _serverController.host,
enabled: _isRemote
)
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Host",
subtitle: "Enter the port of the backend server",
content: TextFormBox(
placeholder: "host",
controller: _serverController.port,
enabled: _isRemote
)
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Type",
subtitle: "Select the type of backend to use",
content: ServerTypeSelector()
),
const SizedBox(
height: 16.0,
),
Align(
alignment: Alignment.bottomCenter,
child: SettingTile(
title: "Login automatically",
subtitle: "Choose whether the game client should login automatically using random credentials",
contentWidth: null,
content: Obx(() => ToggleSwitch(
checked: _serverController.loginAutomatically(),
onChanged: (value) => _serverController.loginAutomatically.value = value
))
),
),
HostInput(),
PortInput(),
ServerTypeSelector(),
const Expanded(child: SizedBox()),
const ServerButton()
]
));
}
bool get _isRemote => _serverController.type.value == ServerType.remote;
}

View File

@@ -1,121 +1,110 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/widget/shared/smart_switch.dart';
import 'package:url_launcher/url_launcher.dart';
import '../util/checks.dart';
import '../widget/setting/url_updater.dart';
import '../widget/shared/file_selector.dart';
import '../widget/shared/setting_tile.dart';
class SettingsPage extends StatelessWidget {
final GameController _gameController = Get.find<GameController>();
final SettingsController _settingsController = Get.find<SettingsController>();
SettingsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) =>
_settingsController.advancedMode.value ? _advancedSettings : _easySettings;
Widget get _advancedSettings => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Widget build(BuildContext context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const RebootUpdaterInput(),
_createFileSelector(),
_createConsoleSelector(),
_createGameSelector(),
_createVersionInfo(),
_createAdvancedSwitch()
]
);
Widget get _easySettings => SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircleAvatar(
radius: 48,
backgroundImage: AssetImage("assets/images/auties.png")),
SettingTile(
title: "File settings",
subtitle: "This section contains all the settings related to files used by Fortnite",
expandedContent: [
_createFileSetting(
title: "Game server",
description: "This file is injected to create a game server to host matches",
controller: _settingsController.rebootDll
),
_createFileSetting(
title: "Unreal engine console",
description: "This file is injected to unlock the Unreal Engine Console in-game",
controller: _settingsController.consoleDll
),
_createFileSetting(
title: "Authentication patcher",
description: "This file is injected to redirect all HTTP requests to the local backend",
controller: _settingsController.authDll
),
],
),
const SizedBox(
height: 16.0,
),
const Text("Made by Auties00"),
const SizedBox(
height: 4.0,
SettingTile(
title: "Automatic updates",
subtitle: "Choose whether the launcher and its files should be automatically updated",
contentWidth: null,
content: Obx(() => ToggleSwitch(
checked: _settingsController.autoUpdate(),
onChanged: (value) => _settingsController.autoUpdate.value = value
))
),
_versionText,
const SizedBox(
height: 8.0,
height: 16.0,
),
Button(
child: const Text("Switch to advanced mode"),
onPressed: () => _settingsController.advancedMode.value = true
SettingTile(
title: "Custom launch arguments",
subtitle: "Enter additional arguments to use when launching the game",
content: TextFormBox(
placeholder: "args",
controller: _gameController.customLaunchArgs,
)
],
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Create a bug report",
subtitle: "Help me fix bugs by reporting them",
content: Button(
onPressed: () => launchUrl(Uri.parse("https://discord.com/channels/998020695223193670/1031262639457828910")),
child: const Text("Report a bug"),
)
),
]
);
Widget _createAdvancedSwitch() => SmartSwitch(
label: "Advanced Mode",
value: _settingsController.advancedMode
);
Widget _createVersionInfo() => Column(
crossAxisAlignment: CrossAxisAlignment.start,
Widget _createFileSetting({required String title, required String description, required TextEditingController controller}) => ListTile(
title: Text(title),
subtitle: Text(description),
trailing: SizedBox(
width: 256,
child: Row(
children: [
const Text("Version Status"),
const SizedBox(height: 6.0),
Button(
child: _versionText,
onPressed: () => launchUrl(safeBinariesDirectory.uri)
Expanded(
child: TextFormBox(
placeholder: "path",
controller: controller,
validator: checkDll,
autovalidateMode: AutovalidateMode.always
),
),
const SizedBox(
width: 8.0,
),
Padding(
padding: const EdgeInsets.only(bottom: 21.0),
child: Button(
onPressed: () { },
child: const Icon(FluentIcons.open_folder_horizontal),
),
)
],
);
Widget _createGameSelector() => Tooltip(
message: "The dll that is injected to make the game work",
child: FileSelector(
label: "Cranium DLL",
placeholder:
"Type the path to the dll used for authentication",
controller: _settingsController.authDll,
windowTitle: "Select a dll",
folder: false,
extension: "dll",
validator: checkDll,
validatorMode: AutovalidateMode.always
)
)
);
Widget _createConsoleSelector() => Tooltip(
message: "The dll that is injected when a client is launched",
child: FileSelector(
label: "Console DLL",
placeholder: "Type the path to the console dll",
controller: _settingsController.consoleDll,
windowTitle: "Select a dll",
folder: false,
extension: "dll",
validator: checkDll,
validatorMode: AutovalidateMode.always),
);
Widget _createFileSelector() => Tooltip(
message: "The dll that is injected when a server is launched",
child: FileSelector(
label: "Reboot DLL",
placeholder: "Type the path to the reboot dll",
controller: _settingsController.rebootDll,
windowTitle: "Select a dll",
folder: false,
extension: "dll",
validator: checkDll,
validatorMode: AutovalidateMode.always),
);
Widget get _versionText => const Text("6.4${kDebugMode ? '-DEBUG' : '-RELEASE'}");
}

View File

@@ -1,19 +1,13 @@
import 'dart:io';
import 'dart:math';
import 'package:html/parser.dart' show parse;
import 'package:http/http.dart' as http;
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/model/fortnite_build.dart';
import 'package:reboot_launcher/src/util/time.dart';
import 'package:reboot_launcher/src/util/version.dart' as parser;
import 'package:version/version.dart';
import 'os.dart';
const _userAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36";
final _manifestSourceUrl = Uri.parse(
"https://github.com/VastBlast/FortniteManifestArchive/blob/main/README.md");
@@ -67,56 +61,3 @@ Future<Process> downloadManifestBuild(
return process;
}
Future<void> downloadArchiveBuild(String archiveUrl, String destination,
Function(double, String) onProgress, Function() onDecompress) async {
var uuid = Random.secure().nextInt(1000000);
var extension = archiveUrl.substring(archiveUrl.lastIndexOf("."));
var tempFile = File(
"$destination\\.temp\\FortniteBuild$uuid$extension"
);
await tempFile.parent.create(recursive: true);
try {
var client = http.Client();
var request = http.Request("GET", Uri.parse(archiveUrl));
request.headers["User-Agent"] = _userAgent;
var response = await client.send(request);
if (response.statusCode != 200) {
throw Exception("Erroneous status code: ${response.statusCode}");
}
var startTime = DateTime.now();
var lastRemaining = -1;
var length = response.contentLength!;
var received = 0;
var sink = tempFile.openWrite();
var lastEta = toETA(0);
await response.stream.map((entry) {
received += entry.length;
var percentage = (received / length) * 100;
var elapsed = DateTime.now().difference(startTime).inMilliseconds;
var newRemaining = (elapsed * length / received - elapsed).round();
if(lastRemaining < 0 || lastRemaining - newRemaining <= -10000 || lastRemaining > newRemaining) {
lastEta = toETA(lastRemaining = newRemaining);
}
onProgress(percentage, lastEta);
return entry;
}).pipe(sink);
onDecompress();
var output = Directory(destination);
await output.create(recursive: true);
await loadBinary("winrar.exe", true);
var shell = Shell(
commandVerbose: false,
commentVerbose: false,
workingDirectory: safeBinariesDirectory.path
);
await shell.run("./winrar.exe x \"${tempFile.path}\" *.* \"${output.path}\"");
} finally {
if (await tempFile.parent.exists()) {
tempFile.parent.delete(recursive: true);
}
}
}

View File

@@ -67,7 +67,7 @@ Directory get safeBinariesDirectory =>
Directory("${Platform.environment["UserProfile"]}\\.reboot_launcher");
Directory get embeddedBackendDirectory =>
Directory("${safeBinariesDirectory.path}\\backend");
Directory("${safeBinariesDirectory.path}\\backend-lawin");
File loadEmbedded(String file) {
var safeBinary = File("${embeddedBackendDirectory.path}\\$file");

View File

@@ -1,6 +1,8 @@
import 'dart:convert';
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:ini/ini.dart';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/model/game_type.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
@@ -12,6 +14,41 @@ import 'package:http/http.dart' as http;
final serverLogFile = File("${Platform.environment["UserProfile"]}\\.reboot_launcher\\server.txt");
Future<void> writeMatchmakingIp(String text) async {
var file = File("${embeddedBackendDirectory.path}\\Config\\config.ini");
if(!file.existsSync()){
return;
}
var splitIndex = text.indexOf(":");
var ip = splitIndex != -1 ? text.substring(0, splitIndex) : text;
var port = splitIndex != -1 ? text.substring(splitIndex + 1) : "7777";
var config = Config.fromString(file.readAsStringSync());
config.set("GameServer", "ip", ip);
config.set("GameServer", "port", port);
file.writeAsStringSync(config.toString());
}
Future<void> startServer() async {
if(!embeddedBackendDirectory.existsSync()){
var serverZip = await loadBinary("server.zip", true);
await extractFileToDisk(serverZip.path, embeddedBackendDirectory.path);
}
var process = await Process.start(
"${embeddedBackendDirectory.path}\\lawinserver-win.exe",
[],
workingDirectory: embeddedBackendDirectory.path
);
process.outLines.forEach((element) => serverLogFile.writeAsStringSync("$element\n", mode: FileMode.append));
process.errLines.forEach((element) => serverLogFile.writeAsStringSync("$element\n", mode: FileMode.append));
}
Future<void> stopServer() async {
var releaseBat = await loadBinary("kill_both_ports.bat", false);
await Process.run(releaseBat.path, []);
}
Future<bool> isLawinPortFree() async {
return http.get(Uri.parse("http://127.0.0.1:3551/unknown"))
.timeout(const Duration(milliseconds: 500))
@@ -44,7 +81,7 @@ Future<void> freeMatchmakerPort() async {
}
}
List<String> createRebootArgs(String username, GameType type) {
List<String> createRebootArgs(String username, GameType type, String additionalArgs) {
var args = [
"-epicapp=Fortnite",
"-epicenv=Prod",
@@ -73,6 +110,10 @@ List<String> createRebootArgs(String username, GameType type) {
]);
}
if(additionalArgs.isNotEmpty){
args.addAll(additionalArgs.split(" "));
}
return args;
}

View File

@@ -12,33 +12,16 @@ class GameTypeSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Tooltip(
message: "The type of Fortnite instance to launch",
child: _createAdvancedSelector(),
);
}
Widget _createAdvancedSelector() => InfoLabel(
label: "Type",
child: SizedBox(
width: double.infinity,
child: Obx(() => DropDownButton(
return Obx(() => DropDownButton(
leading: Text(_gameController.type.value.name),
items: GameType.values
.map((type) => _createItem(type))
.toList())
)
)
);
.toList()
));
}
MenuFlyoutItem _createItem(GameType type) => MenuFlyoutItem(
text: SizedBox(
width: double.infinity,
child: Tooltip(
message: type.message,
child: Text(type.name)
)
),
text: Text(type.name),
onPressed: () {
_gameController.type(type);
_gameController.started.value = _gameController.currentGameInstance != null;

View File

@@ -25,9 +25,7 @@ import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/dialog/snackbar.dart';
import 'package:reboot_launcher/src/model/game_instance.dart';
import '../../page/home_page.dart';
import '../../util/process.dart';
import '../shared/smart_check_box.dart';
class LaunchButton extends StatefulWidget {
const LaunchButton(
@@ -66,21 +64,24 @@ class _LaunchButtonState extends State<LaunchButton> {
}
@override
Widget build(BuildContext context) {
return Align(
Widget build(BuildContext context) => Align(
alignment: AlignmentDirectional.bottomCenter,
child: SizedBox(
width: double.infinity,
child: Obx(() => Tooltip(
message: _gameController.started() ? "Close the running Fortnite instance" : "Launch a new Fortnite instance",
child: Obx(() => SizedBox(
height: 48,
child: Button(
child: Align(
alignment: Alignment.center,
child: Text(
_gameController.started() ? "Close fortnite" : "Launch fortnite"
),
),
onPressed: () => _start(_gameController.type()),
child: Text(_gameController.started() ? "Close" : "Launch")
),
)),
),
);
}
void _start(GameType type) async {
if (_gameController.started()) {
@@ -99,7 +100,7 @@ class _LaunchButtonState extends State<LaunchButton> {
showMessage("No username: expecting self sign in");
}
if (_gameController.selectedVersionObs.value == null) {
if (_gameController.selectedVersion == null) {
showMessage("No version is selected");
_onStop(type);
return;
@@ -115,7 +116,7 @@ class _LaunchButtonState extends State<LaunchButton> {
_fail = false;
await _resetLogFile();
var version = _gameController.selectedVersionObs.value!;
var version = _gameController.selectedVersion!;
var gamePath = version.executable?.path;
if(gamePath == null){
showMissingBuildError(version);
@@ -132,8 +133,8 @@ class _LaunchButtonState extends State<LaunchButton> {
await compute(patchMatchmaking, version.executable!);
await compute(patchHeadless, version.executable!);
await _startMatchMakingServer();
await _startGameProcesses(version, type);
var automaticallyStartedServer = await _startMatchMakingServer();
await _startGameProcesses(version, type, automaticallyStartedServer);
if(type == GameType.headlessServer){
await _showServerLaunchingWarning();
@@ -145,96 +146,40 @@ class _LaunchButtonState extends State<LaunchButton> {
}
}
Future<void> _startGameProcesses(FortniteVersion version, GameType type) async {
Future<void> _startGameProcesses(FortniteVersion version, GameType type, bool hasChildServer) async {
var launcherProcess = await _createLauncherProcess(version);
var eacProcess = await _createEacProcess(version);
var gameProcess = await _createGameProcess(version.executable!.path, type);
_gameController.gameInstancesMap[type] = GameInstance(gameProcess, launcherProcess, eacProcess);
_gameController.gameInstancesMap[type] = GameInstance(gameProcess, launcherProcess, eacProcess, hasChildServer);
_injectOrShowError(Injectable.cranium, type);
}
Future<void> _startMatchMakingServer() async {
Future<bool> _startMatchMakingServer() async {
if(_gameController.type() != GameType.client){
return;
return false;
}
var matchmakingIp = _settingsController.matchmakingIp.text;
if(!matchmakingIp.contains("127.0.0.1") && !matchmakingIp.contains("localhost")) {
return;
return false;
}
var headlessServer = _gameController.gameInstancesMap[GameType.headlessServer] != null;
var server = _gameController.gameInstancesMap[GameType.server] != null;
if(headlessServer || server){
return;
if(!_gameController.autostartGameServer()){
return false;
}
var result = await _askToStartMatchMakingServer();
if(result != true){
return;
}
var version = _gameController.selectedVersionObs.value!;
var version = _gameController.selectedVersion!;
await _startGameProcesses(
version,
GameType.headlessServer
GameType.headlessServer,
false
);
}
Future<bool> _askToStartMatchMakingServer() async {
if(_settingsController.doNotAskAgain()) {
return _settingsController.automaticallyStartMatchmaker();
}
var controller = CheckboxController();
var result = await showDialog<bool>(
context: appKey.currentContext!,
builder: (context) =>
ContentDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
width: double.infinity,
child: Text(
"The matchmaking ip is set to the local machine, but no server is running. "
"If you want to start a match for your friends or just test out Reboot, you need to start a server, either now from this prompt or later manually.",
textAlign: TextAlign.start,
)
),
const SizedBox(height: 12.0),
SmartCheckBox(
controller: controller,
content: const Text("Don't ask again")
)
],
),
actions: [
Button(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Ignore'),
),
FilledButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Start a server'),
)
],
)
);
_settingsController.doNotAskAgain.value = controller.value;
if(result != null){
_settingsController.automaticallyStartMatchmaker.value = result;
}
return result ?? false;
return true;
}
Future<Process> _createGameProcess(String gamePath, GameType type) async {
var gameProcess = await Process.start(gamePath, createRebootArgs(_gameController.username.text, type));
var gameArgs = createRebootArgs(_gameController.username.text, type, _gameController.customLaunchArgs.text);
var gameProcess = await Process.start(gamePath, gameArgs);
gameProcess
..exitCode.then((_) => _onEnd(type))
..outLines.forEach((line) => _onGameOutput(line, type))
@@ -369,9 +314,21 @@ class _LaunchButtonState extends State<LaunchButton> {
_start(type);
}
void _onStop(GameType type) {
_gameController.gameInstancesMap[type]?.kill();
void _onStop(GameType? type) {
if(type == null){
return;
}
var value = _gameController.gameInstancesMap[type];
if(value != null){
if(value.hasChildServer){
_onStop(GameType.headlessServer);
}
value.kill();
_gameController.gameInstancesMap.remove(type);
}
if(type == _gameController.type()) {
_gameController.started.value = false;
}

View File

@@ -1,23 +0,0 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/model/game_type.dart';
import 'package:reboot_launcher/src/widget/shared/smart_input.dart';
class UsernameBox extends StatelessWidget {
final GameController _gameController = Get.find<GameController>();
UsernameBox({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() => Tooltip(
message: _gameController.type.value != GameType.client ? "The username of the game hoster" : "The in-game username of your player",
child: SmartInput(
label: "Username",
placeholder: "Type your ${_gameController.type.value != GameType.client ? 'hosting' : "in-game"} username",
controller: _gameController.username
),
));
}
}

View File

@@ -19,6 +19,19 @@ import '../shared/file_selector.dart';
class VersionSelector extends StatefulWidget {
const VersionSelector({Key? key}) : super(key: key);
static void openDownloadDialog(BuildContext context) async {
await showDialog<bool>(
context: context,
builder: (dialogContext) => const AddServerVersion()
);
}
static void openAddDialog(BuildContext context) async {
await showDialog<bool>(
context: context,
builder: (context) => AddLocalVersion());
}
@override
State<VersionSelector> createState() => _VersionSelectorState();
}
@@ -26,79 +39,44 @@ class VersionSelector extends StatefulWidget {
class _VersionSelectorState extends State<VersionSelector> {
final GameController _gameController = Get.find<GameController>();
final CheckboxController _deleteFilesController = CheckboxController();
final FlyoutController _flyoutController = FlyoutController();
@override
Widget build(BuildContext context) {
return InfoLabel(
label: "Version",
child: Align(
alignment: AlignmentDirectional.centerStart,
child: Row(
children: [
Expanded(child: _createSelector(context)),
const SizedBox(
width: 16,
),
Tooltip(
message: "Add a local fortnite build to the versions list",
child: Button(
child: const Icon(FluentIcons.open_file),
onPressed: () => _openAddLocalVersionDialog(context)),
),
const SizedBox(
width: 16,
),
Tooltip(
message: "Download a fortnite build from the archive",
child: Button(
child: const Icon(FluentIcons.download),
onPressed: () => _openDownloadVersionDialog(context)),
),
],
)));
}
Widget _createSelector(BuildContext context) {
return Tooltip(
message: "The version of Fortnite to launch",
child: Obx(() => _createOptionsMenu(
version: _gameController.selectedVersionObs(),
Widget build(BuildContext context) => Obx(() => _createOptionsMenu(
version: _gameController.selectedVersion,
close: false,
child: FlyoutTarget(
controller: _flyoutController,
child: DropDownButton(
leading: Text(_gameController.selectedVersionObs.value?.name
?? "Select a version"),
leading: Text(_gameController.selectedVersion?.name ?? "Select a version"),
items: _createSelectorItems(context)
),
)
))
);
}
));
List<MenuFlyoutItem> _createSelectorItems(BuildContext context) {
return _gameController.hasNoVersions ? [_createDefaultVersionItem()]
List<MenuFlyoutItem> _createSelectorItems(BuildContext context) => _gameController.hasNoVersions ? [_createDefaultVersionItem()]
: _gameController.versions.value
.map((version) => _createVersionItem(context, version))
.toList();
}
MenuFlyoutItem _createVersionItem(BuildContext context, FortniteVersion version) {
return MenuFlyoutItem(
MenuFlyoutItem _createDefaultVersionItem() => MenuFlyoutItem(
text: const SizedBox(
width: double.infinity, child: Text("No versions available. Add it using the buttons on the right.")),
trailing: const Expanded(child: SizedBox()),
onPressed: () {});
MenuFlyoutItem _createVersionItem(BuildContext context, FortniteVersion version) => MenuFlyoutItem(
text: _createOptionsMenu(
version: version,
close: true,
child: SizedBox(
width: double.infinity,
child: Text(version.name)
),
child: Text(version.name),
),
onPressed: () => _gameController.selectedVersion = version
);
}
Widget _createOptionsMenu({required FortniteVersion? version, required bool close, required Widget child}) {
return Listener(
Widget _createOptionsMenu({required FortniteVersion? version, required bool close, required Widget child}) => Listener(
onPointerDown: (event) async {
if (event.kind != PointerDeviceKind.mouse ||
event.buttons != kSecondaryMouseButton) {
if (event.kind != PointerDeviceKind.mouse || event.buttons != kSecondaryMouseButton) {
return;
}
@@ -106,43 +84,19 @@ class _VersionSelectorState extends State<VersionSelector> {
return;
}
await _openMenu(context, version, event.position, close);
},
child: child
);
}
MenuFlyoutItem _createDefaultVersionItem() {
return MenuFlyoutItem(
text: const SizedBox(
width: double.infinity, child: Text("No versions available. Add it using the buttons on the right.")),
trailing: const Expanded(child: SizedBox()),
onPressed: () {});
}
void _openDownloadVersionDialog(BuildContext context) async {
await showDialog<bool>(
context: context,
builder: (dialogContext) => const AddServerVersion()
);
}
void _openAddLocalVersionDialog(BuildContext context) async {
await showDialog<bool>(
context: context,
builder: (context) => AddLocalVersion());
}
Future<void> _openMenu(
BuildContext context, FortniteVersion version, Offset offset, bool close) async {
var controller = FlyoutController();
var result = await controller.showFlyout(
var result = await _flyoutController.showFlyout<ContextualOption?>(
builder: (context) => MenuFlyout(
items: ContextualOption.values
.map((entry) => _createOption(context, entry))
.toList()
)
);
_handleResult(result, version, close);
},
child: child
);
void _handleResult(ContextualOption? result, FortniteVersion version, bool close) async {
switch (result) {
case ContextualOption.openExplorer:
if(!mounted){
@@ -156,7 +110,6 @@ class _VersionSelectorState extends State<VersionSelector> {
launchUrl(version.location.uri)
.onError((error, stackTrace) => _onExplorerError());
break;
case ContextualOption.modify:
if(!mounted){
return;
@@ -168,7 +121,6 @@ class _VersionSelectorState extends State<VersionSelector> {
await _openRenameDialog(context, version);
break;
case ContextualOption.delete:
if(!mounted){
return;
@@ -184,8 +136,8 @@ class _VersionSelectorState extends State<VersionSelector> {
}
_gameController.removeVersion(version);
if (_gameController.selectedVersionObs.value?.name == version.name || _gameController.hasNoVersions) {
_gameController.selectedVersionObs.value = null;
if (_gameController.selectedVersion?.name == version.name || _gameController.hasNoVersions) {
_gameController.selectedVersion = null;
}
if (_deleteFilesController.value && await version.location.exists()) {
@@ -193,7 +145,6 @@ class _VersionSelectorState extends State<VersionSelector> {
}
break;
default:
break;
}
@@ -276,7 +227,6 @@ class _VersionSelectorState extends State<VersionSelector> {
),
FileSelector(
label: "Location",
placeholder: "Type the new game folder",
windowTitle: "Select game folder",
controller: pathController,

View File

@@ -15,49 +15,32 @@ class _ServerButtonState extends State<ServerButton> {
final ServerController _serverController = Get.find<ServerController>();
@override
Widget build(BuildContext context) {
return Align(
Widget build(BuildContext context) => Align(
alignment: AlignmentDirectional.bottomCenter,
child: SizedBox(
width: double.infinity,
child: Obx(() => Tooltip(
message: _helpMessage,
child: Obx(() => SizedBox(
height: 48,
child: Button(
onPressed: () async => _serverController.toggle(),
child: Text(_buttonText())),
child: Align(
alignment: Alignment.center,
child: Text(_buttonText),
),
onPressed: () => _serverController.toggle()
),
)),
),
);
}
String _buttonText() {
String get _buttonText {
if(_serverController.type.value == ServerType.local){
return "Check";
return "Check backend";
}
if(_serverController.started.value){
return "Stop";
return "Stop backend";
}
return "Start";
}
String get _helpMessage {
switch(_serverController.type.value){
case ServerType.embedded:
if (_serverController.started.value) {
return "Stop the backend server currently running";
}
return "Start a new local backend server";
case ServerType.remote:
if (_serverController.started.value) {
return "Stop the reverse proxy currently running";
}
return "Start a reverse proxy targeting the remote backend server";
case ServerType.local:
return "Check if a local backend server is running";
}
return "Start backend";
}
}

View File

@@ -10,30 +10,19 @@ class ServerTypeSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Tooltip(
message: "Determines the type of backend server to use",
child: InfoLabel(
label: "Type",
child: SizedBox(
width: double.infinity,
child: Obx(() => DropDownButton(
return DropDownButton(
leading: Text(_serverController.type.value.name),
items: ServerType.values
.map((type) => _createItem(type))
.toList()))
),
),
.toList()
);
}
MenuFlyoutItem _createItem(ServerType type) {
return MenuFlyoutItem(
text: SizedBox(
width: double.infinity,
child: Tooltip(
text: Tooltip(
message: type.message,
child: Text(type.name)
)
),
onPressed: () async {
await _serverController.stop();

View File

@@ -51,12 +51,10 @@ class _RebootUpdaterInputState extends State<RebootUpdaterInput> {
const SizedBox(width: 16.0),
Tooltip(
message: _settingsController.autoUpdate.value ? "Disable automatic updates" : "Enable automatic updates",
child: Obx(() => Padding(
padding: _valid() ? EdgeInsets.zero : const EdgeInsets.only(bottom: 21.0),
child: Button(
child: Obx(() => Button(
onPressed: () => _settingsController.autoUpdate.value = !_settingsController.autoUpdate.value,
child: Icon(_settingsController.autoUpdate.value ? FluentIcons.disable_updates : FluentIcons.refresh)
))
)
)
)
],

View File

@@ -9,7 +9,6 @@ import 'package:reboot_launcher/src/dialog/snackbar.dart';
import 'package:reboot_launcher/src/util/selector.dart';
class FileSelector extends StatefulWidget {
final String label;
final String placeholder;
final String windowTitle;
final bool allowNavigator;
@@ -20,8 +19,7 @@ class FileSelector extends StatefulWidget {
final bool folder;
const FileSelector(
{required this.label,
required this.placeholder,
{required this.placeholder,
required this.windowTitle,
required this.controller,
required this.validator,
@@ -38,50 +36,32 @@ class FileSelector extends StatefulWidget {
}
class _FileSelectorState extends State<FileSelector> {
final RxBool _valid = RxBool(true);
late String? Function(String?) validator;
bool _selecting = false;
@override
void initState() {
validator = (value) {
var result = widget.validator(value);
WidgetsBinding.instance.addPostFrameCallback((_) => _valid.value = result == null);
return result;
};
super.initState();
}
@override
Widget build(BuildContext context) {
return InfoLabel(
label: widget.label,
child: Row(
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TextFormBox(
controller: widget.controller,
placeholder: widget.placeholder,
validator: validator,
validator: widget.validator,
autovalidateMode: widget.validatorMode ?? AutovalidateMode.onUserInteraction
)
),
if (widget.allowNavigator) const SizedBox(width: 16.0),
if (widget.allowNavigator)
Tooltip(
message: "Select a ${widget.folder ? 'folder' : 'file'}",
child: Obx(() => Padding(
padding: _valid() ? EdgeInsets.zero : const EdgeInsets.only(bottom: 21.0),
const SizedBox(width: 16.0),
if (widget.allowNavigator)
Padding(
padding: const EdgeInsets.only(bottom: 21.0),
child: Button(
onPressed: _onPressed,
child: const Icon(FluentIcons.open_folder_horizontal)
))
)
)
],
)
);
}

View File

@@ -1,190 +1,16 @@
import 'package:fluent_ui/fluent_ui.dart';
class FluentCard extends StatefulWidget {
const FluentCard({
Key? key,
this.leading,
required this.content,
this.icon,
this.trailing,
this.animationCurve,
this.animationDuration,
this.onPressed,
this.onStateChanged,
this.isButton = false,
this.headerHeight = 68.5,
this.headerBackgroundColor,
this.contentBackgroundColor,
}) : super(key: key);
static Color backgroundColor(ThemeData style, Set<ButtonStates> states, [bool isClickable = true]) {
if (style.brightness == Brightness.light) {
if (!states.isDisabled && isClickable) {
if (states.isPressing) return const ColorConst.withOpacity(0xf9f9f9, 0.2);
if (states.isHovering) return const ColorConst.withOpacity(0xf9f9f9, 0.4);
}
return const ColorConst.withOpacity(0xFFFFFF, 0.7);
} else {
if (!states.isDisabled && isClickable) {
if (states.isPressing) return const ColorConst.withOpacity(0xFFFFFF, 0.03);
if (states.isHovering) return const ColorConst.withOpacity(0xFFFFFF, 0.082);
}
return const ColorConst.withOpacity(0xFFFFFF, 0.05);
}
}
static Color borderColor(ThemeData style, Set<ButtonStates> states, [bool isClickable = true]) {
if (style.brightness == Brightness.light) {
if (isClickable && states.isHovering && !states.isPressing) return const Color(0xFF212121).withOpacity(0.22);
return const Color(0xFF212121).withOpacity(0.17);
} else {
if (isClickable && states.isPressing) return Colors.white.withOpacity(0.062);
if (isClickable && states.isHovering) return Colors.white.withOpacity(0.02);
return Colors.black.withOpacity(0.52);
}
}
/// The leading widget.
///
/// See also:
///
/// * [Icon]
/// * [RadioButton]
/// * [Checkbox]
final Widget? leading;
/// The card content
///
/// Usually a [Text]
final Widget content;
/// The icon of the toggle button.
final Widget? icon;
/// Disable when onPressed is null, always show chevron icon in the right
final bool isButton;
/// The trailing widget. It's positioned at the right of [content]
/// and at the left of [icon].
///
/// See also:
///
/// * [ToggleSwitch]
final Widget? trailing;
/// Makes the card clickable
/// is null by default
final VoidCallback? onPressed;
/// The expand-collapse animation duration. If null, defaults to
/// [FluentTheme.fastAnimationDuration]
final Duration? animationDuration;
/// The expand-collapse animation curve. If null, defaults to
/// [FluentTheme.animationCurve]
final Curve? animationCurve;
/// A callback called when the current state is changed. `true` when
/// open and `false` when closed.
final ValueChanged<bool>? onStateChanged;
/// The height of the header.
///
/// Defaults to 48.0
final double headerHeight;
/// The background color of the header. If null, [ThemeData.scaffoldBackgroundColor]
/// is used
final Color? headerBackgroundColor;
/// The content color of the header. If null, [ThemeData.acrylicBackgroundColor]
/// is used
final Color? contentBackgroundColor;
class FluentCard extends StatelessWidget {
final Widget child;
const FluentCard({Key? key, required this.child}) : super(key: key);
@override
FluentCardState createState() => FluentCardState();
}
class FluentCardState extends State<FluentCard>
with SingleTickerProviderStateMixin {
late ThemeData theme;
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.animationDuration ?? const Duration(milliseconds: 150),
Widget build(BuildContext context) => Mica(
elevation: 1,
child: Card(
backgroundColor: FluentTheme.of(context).menuColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
child: child
)
);
}
static void emptyPressMethod() {}
static const double borderSize = 0.5;
static final Color darkBorderColor = Colors.black.withOpacity(0.8);
static const Duration expanderAnimationDuration = Duration(milliseconds: 70);
/// If this widget acts as a button and is disabled, gray out all text and icons
Widget buttonStyled(Widget child) => !widget.isButton || widget.onPressed != null ? child : IconTheme.merge(
data: IconThemeData(color: theme.disabledColor),
child: DefaultTextStyle.merge(style: TextStyle(color: theme.disabledColor), child: child)
);
@override
Widget build(BuildContext context) {
assert(debugCheckHasFluentTheme(context));
final isLtr = Directionality.of(context) == TextDirection.ltr;
theme = FluentTheme.of(context);
bool isDark = theme.brightness == Brightness.dark;
return buttonStyled(HoverButton(
onPressed: widget.onPressed ?? (widget.isButton ? null : emptyPressMethod),
builder: (context, states) {
return AnimatedContainer(
duration: expanderAnimationDuration,
height: widget.headerHeight,
decoration: BoxDecoration(
color: FluentCard.backgroundColor(theme, states, widget.onPressed != null),
border: Border.all(
width: borderSize,
color: FluentCard.borderColor(theme, states, widget.onPressed != null),
),
borderRadius: const BorderRadius.all(Radius.circular(4.0)),
),
padding: const EdgeInsetsDirectional.only(start: 16.0),
alignment: Alignment.centerLeft,
child: Row(mainAxisSize: MainAxisSize.min, children: [
if (widget.leading != null) Padding(
padding: const EdgeInsetsDirectional.only(end: 17.0),
child: widget.leading!,
),
Expanded(child: widget.content),
if (widget.trailing != null) Padding(
padding: const EdgeInsetsDirectional.only(start: 20.0, end: 13.5),
child: widget.trailing!,
),
if (widget.icon != null || widget.isButton) Container(
margin: EdgeInsetsDirectional.only(
start: widget.trailing != null ? 8.0 : 20.0,
end: 8.0,
top: 8.0,
bottom: 8.0,
),
padding: const EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: widget.icon ?? Icon(isLtr ? isDark ? FluentIcons.chevron_right : FluentIcons.chevron_right_med :
isDark ? FluentIcons.chevron_left : FluentIcons.chevron_left_med, size: 11),
),
]),
);
},
));
}
}
class ColorConst extends Color {
const ColorConst.withOpacity(int value, double opacity) : super(
( (((opacity * 0xff ~/ 1) & 0xff) << 24) | ((0x00ffffff & value)) ) & 0xFFFFFFFF);
}

View File

@@ -0,0 +1,67 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/widget/shared/fluent_card.dart';
class SettingTile extends StatefulWidget {
static const double kDefaultContentWidth = 200.0;
final String title;
final String subtitle;
final Widget? content;
final double? contentWidth;
final List<Widget>? expandedContent;
const SettingTile(
{Key? key,
required this.title,
required this.subtitle,
this.content,
this.contentWidth = kDefaultContentWidth,
this.expandedContent})
: super(key: key);
@override
State<SettingTile> createState() => _SettingTileState();
}
class _SettingTileState extends State<SettingTile> {
@override
Widget build(BuildContext context) {
if(widget.expandedContent == null){
return _contentCard;
}
return Mica(
elevation: 1,
child: Expander(
initiallyExpanded: true,
contentBackgroundColor: FluentTheme.of(context).menuColor,
headerShape: (open) => const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(4.0)),
),
header: ListTile(
title: Text(widget.title),
subtitle: Text(widget.subtitle)
),
headerHeight: 72,
trailing: SizedBox(
width: widget.contentWidth,
child: widget.content
),
content: Column(
children: widget.expandedContent!
)
),
);
}
Widget get _contentCard => FluentCard(
child: ListTile(
title: Text(widget.title),
subtitle: Text(widget.subtitle),
trailing: SizedBox(
width: widget.contentWidth,
child: widget.content
),
),
);
}

View File

@@ -54,9 +54,6 @@ flutter:
- assets/icons/
- assets/binaries/
- assets/images/
- assets/profiles/
- assets/responses/
- assets/config/
msix_config:
display_name: Reboot Launcher