mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
Redesigned whole UI
This commit is contained in:
2
assets/binaries/kill_both_ports.bat
Normal file
2
assets/binaries/kill_both_ports.bat
Normal 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
BIN
assets/binaries/server.zip
Normal file
Binary file not shown.
Binary file not shown.
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
103315
assets/profiles/athena.json
103315
assets/profiles/athena.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
]
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[
|
|
||||||
"lawin",
|
|
||||||
"ti93",
|
|
||||||
"pro100katyt",
|
|
||||||
"playeereq",
|
|
||||||
"matteoki"
|
|
||||||
]
|
|
||||||
@@ -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
@@ -1 +0,0 @@
|
|||||||
[]
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"friends": [],
|
|
||||||
"incoming": [],
|
|
||||||
"outgoing": [],
|
|
||||||
"suggested": [],
|
|
||||||
"blocklist": [],
|
|
||||||
"settings": {
|
|
||||||
"acceptInvites": "public"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"accountId": "",
|
|
||||||
"optOutOfPublicLeaderboards": false
|
|
||||||
}
|
|
||||||
121549
assets/responses/quests.json
121549
assets/responses/quests.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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/os.dart';
|
||||||
import 'package:reboot_launcher/src/util/patcher.dart';
|
import 'package:reboot_launcher/src/util/patcher.dart';
|
||||||
import 'package:reboot_launcher/src/util/reboot.dart';
|
import 'package:reboot_launcher/src/util/reboot.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/server.dart' as server;
|
||||||
|
|
||||||
late String? username;
|
late String? username;
|
||||||
late GameType type;
|
late GameType type;
|
||||||
@@ -79,12 +80,13 @@ void main(List<String> args) async {
|
|||||||
var serverType = getServerType(result);
|
var serverType = getServerType(result);
|
||||||
var host = result["server-host"] ?? serverJson["${serverType.id}_host"];
|
var host = result["server-host"] ?? serverJson["${serverType.id}_host"];
|
||||||
var port = result["server-port"] ?? serverJson["${serverType.id}_port"];
|
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){
|
if(!started){
|
||||||
stderr.writeln("Cannot start server!");
|
stderr.writeln("Cannot start server!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.writeMatchmakingIp(result["matchmaking-address"]);
|
||||||
autoRestart = result["auto-restart"];
|
autoRestart = result["auto-restart"];
|
||||||
await startGame();
|
await startGame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ class _RebootApplicationState extends State<RebootApplication> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeData _createTheme(Brightness brightness) {
|
FluentThemeData _createTheme(Brightness brightness) {
|
||||||
return ThemeData(
|
return FluentThemeData(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
accentColor: SystemTheme.accentColor.accent.toAccentColor(),
|
accentColor: SystemTheme.accentColor.accent.toAccentColor(),
|
||||||
visualDensity: VisualDensity.standard,
|
visualDensity: VisualDensity.standard,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Future<void> startGame() async {
|
|||||||
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
|
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())
|
..exitCode.then((_) => _onClose())
|
||||||
..outLines.forEach((line) => _onGameOutput(line, dll, hosting, verbose));
|
..outLines.forEach((line) => _onGameOutput(line, dll, hosting, verbose));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:process_run/shell.dart';
|
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/shelf_io.dart' as shelf_io;
|
||||||
import 'package:shelf_proxy/shelf_proxy.dart';
|
import 'package:shelf_proxy/shelf_proxy.dart';
|
||||||
|
|
||||||
import '../model/server_type.dart';
|
import '../model/server_type.dart';
|
||||||
import '../util/server.dart';
|
import '../util/server.dart' as server;
|
||||||
import 'game.dart';
|
|
||||||
|
|
||||||
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...");
|
stdout.writeln("Starting backend server...");
|
||||||
switch(type){
|
switch(type){
|
||||||
case ServerType.local:
|
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){
|
if(result == null){
|
||||||
throw Exception("Local backend server is not running");
|
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;
|
return true;
|
||||||
case ServerType.embedded:
|
case ServerType.embedded:
|
||||||
stdout.writeln("Starting an embedded server...");
|
stdout.writeln("Starting an embedded server...");
|
||||||
await startEmbeddedServer(
|
await server.startServer();
|
||||||
() => matchmakingIp ?? "127.0.0.1"
|
var result = await server.ping(host ?? "127.0.0.1", port ?? "3551");
|
||||||
);
|
|
||||||
await startEmbeddedMatchmaker();
|
|
||||||
var result = await ping(host ?? "127.0.0.1", port ?? "3551");
|
|
||||||
if(result == null){
|
if(result == null){
|
||||||
throw Exception("Cannot start embedded server");
|
throw Exception("Cannot start embedded server");
|
||||||
}
|
}
|
||||||
@@ -62,7 +57,7 @@ Future<HttpServer?> _changeReverseProxyState(String host, String port) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
var uri = await ping(host, port);
|
var uri = await server.ping(host, port);
|
||||||
if(uri == null){
|
if(uri == null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:async/async.dart';
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get_storage/get_storage.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 {
|
class GameController extends GetxController {
|
||||||
late final GetStorage _storage;
|
late final GetStorage _storage;
|
||||||
late final TextEditingController username;
|
late final TextEditingController username;
|
||||||
late final TextEditingController version;
|
late final TextEditingController customLaunchArgs;
|
||||||
late final Rx<List<FortniteVersion>> versions;
|
late final Rx<List<FortniteVersion>> versions;
|
||||||
late final Rxn<FortniteVersion> _selectedVersion;
|
late final Rxn<FortniteVersion> _selectedVersion;
|
||||||
late final Rx<GameType> type;
|
late final Rx<GameType> type;
|
||||||
late final HashMap<GameType, GameInstance> gameInstancesMap;
|
late final HashMap<GameType, GameInstance> gameInstancesMap;
|
||||||
late final RxBool started;
|
late final RxBool started;
|
||||||
|
late final RxBool autostartGameServer;
|
||||||
late bool updated;
|
late bool updated;
|
||||||
late bool error;
|
late bool error;
|
||||||
late bool failing;
|
late bool failing;
|
||||||
@@ -50,10 +49,16 @@ class GameController extends GetxController {
|
|||||||
username = TextEditingController(text: _readUsername());
|
username = TextEditingController(text: _readUsername());
|
||||||
username.addListener(() => _storage.write("${type.value == GameType.client ? 'game' : 'host'}_username", username.text));
|
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();
|
gameInstancesMap= HashMap();
|
||||||
|
|
||||||
started = RxBool(false);
|
started = RxBool(false);
|
||||||
|
|
||||||
|
autostartGameServer = RxBool(_storage.read("auto_start_game_server") ?? true);
|
||||||
|
autostartGameServer.listen((value) => _storage.write("auto_start_game_server", value));
|
||||||
|
|
||||||
updated = false;
|
updated = false;
|
||||||
|
|
||||||
error = false;
|
error = false;
|
||||||
@@ -95,10 +100,10 @@ class GameController extends GetxController {
|
|||||||
|
|
||||||
bool get hasNoVersions => versions.value.isEmpty;
|
bool get hasNoVersions => versions.value.isEmpty;
|
||||||
|
|
||||||
Rxn<FortniteVersion> get selectedVersionObs => _selectedVersion;
|
|
||||||
|
|
||||||
GameInstance? get currentGameInstance => gameInstancesMap[type()];
|
GameInstance? get currentGameInstance => gameInstancesMap[type()];
|
||||||
|
|
||||||
|
FortniteVersion? get selectedVersion => _selectedVersion();
|
||||||
|
|
||||||
set selectedVersion(FortniteVersion? version) {
|
set selectedVersion(FortniteVersion? version) {
|
||||||
_selectedVersion(version);
|
_selectedVersion(version);
|
||||||
_storage.write("version", version?.name);
|
_storage.write("version", version?.name);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart';
|
|||||||
import 'package:jaguar/jaguar.dart';
|
import 'package:jaguar/jaguar.dart';
|
||||||
|
|
||||||
import '../model/server_type.dart';
|
import '../model/server_type.dart';
|
||||||
|
import '../util/server.dart';
|
||||||
|
|
||||||
class ServerController extends GetxController {
|
class ServerController extends GetxController {
|
||||||
static const String _serverName = "127.0.0.1";
|
static const String _serverName = "127.0.0.1";
|
||||||
@@ -17,13 +18,14 @@ class ServerController extends GetxController {
|
|||||||
late final Rx<ServerType> type;
|
late final Rx<ServerType> type;
|
||||||
late final RxBool warning;
|
late final RxBool warning;
|
||||||
late RxBool started;
|
late RxBool started;
|
||||||
Jaguar? embeddedServer;
|
late RxBool loginAutomatically;
|
||||||
Jaguar? embeddedMatchmaker;
|
|
||||||
HttpServer? remoteServer;
|
HttpServer? remoteServer;
|
||||||
|
|
||||||
ServerController() {
|
ServerController() {
|
||||||
_storage = GetStorage("server");
|
_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 = Rx(ServerType.values.elementAt(_storage.read("type") ?? 0));
|
||||||
type.listen((value) {
|
type.listen((value) {
|
||||||
host.text = _readHost();
|
host.text = _readHost();
|
||||||
@@ -35,17 +37,12 @@ class ServerController extends GetxController {
|
|||||||
|
|
||||||
stop();
|
stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
host = TextEditingController(text: _readHost());
|
host = TextEditingController(text: _readHost());
|
||||||
host.addListener(() => _storage.write("${type.value.id}_host", host.text));
|
host.addListener(() => _storage.write("${type.value.id}_host", host.text));
|
||||||
|
|
||||||
port = TextEditingController(text: _readPort());
|
port = TextEditingController(text: _readPort());
|
||||||
port.addListener(() => _storage.write("${type.value.id}_port", port.text));
|
port.addListener(() => _storage.write("${type.value.id}_port", port.text));
|
||||||
|
|
||||||
warning = RxBool(_storage.read("lawin_value") ?? true);
|
warning = RxBool(_storage.read("lawin_value") ?? true);
|
||||||
warning.listen((value) => _storage.write("lawin_value", value));
|
warning.listen((value) => _storage.write("lawin_value", value));
|
||||||
|
|
||||||
started = RxBool(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _readHost() {
|
String _readHost() {
|
||||||
@@ -63,8 +60,7 @@ class ServerController extends GetxController {
|
|||||||
try{
|
try{
|
||||||
switch(type()){
|
switch(type()){
|
||||||
case ServerType.embedded:
|
case ServerType.embedded:
|
||||||
await embeddedServer?.close();
|
stopServer();
|
||||||
await embeddedMatchmaker?.close();
|
|
||||||
break;
|
break;
|
||||||
case ServerType.remote:
|
case ServerType.remote:
|
||||||
await remoteServer?.close(force: true);
|
await remoteServer?.close(force: true);
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get_storage/get_storage.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/os.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/server.dart';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import '../util/reboot.dart';
|
import '../util/reboot.dart';
|
||||||
@@ -15,12 +18,7 @@ class SettingsController extends GetxController {
|
|||||||
late final TextEditingController consoleDll;
|
late final TextEditingController consoleDll;
|
||||||
late final TextEditingController authDll;
|
late final TextEditingController authDll;
|
||||||
late final TextEditingController matchmakingIp;
|
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 final RxBool autoUpdate;
|
||||||
late Rx<TutorialPage> tutorialPage;
|
|
||||||
late double width;
|
late double width;
|
||||||
late double height;
|
late double height;
|
||||||
late double? offsetX;
|
late double? offsetX;
|
||||||
@@ -43,29 +41,18 @@ class SettingsController extends GetxController {
|
|||||||
matchmakingIp.addListener(() async {
|
matchmakingIp.addListener(() async {
|
||||||
var text = matchmakingIp.text;
|
var text = matchmakingIp.text;
|
||||||
_storage.write("ip", text);
|
_storage.write("ip", text);
|
||||||
|
writeMatchmakingIp(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
automaticallyStartMatchmaker = RxBool(_storage.read("start_matchmaker_automatically") ?? false);
|
width = _storage.read("width") ?? 912;
|
||||||
automaticallyStartMatchmaker.listen((value) => _storage.write("start_matchmaker_automatically", value));
|
height = _storage.read("height") ?? 660;
|
||||||
|
|
||||||
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;
|
|
||||||
offsetX = _storage.read("offset_x");
|
offsetX = _storage.read("offset_x");
|
||||||
offsetY = _storage.read("offset_y");
|
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 = RxBool(_storage.read("auto_update") ?? false);
|
||||||
autoUpdate.listen((value) async => _storage.write("auto_update", value));
|
autoUpdate.listen((value) async => _storage.write("auto_update", value));
|
||||||
|
|
||||||
displayType = Rx(PaneDisplayMode.top);
|
|
||||||
|
|
||||||
scrollingDistance = 0.0;
|
scrollingDistance = 0.0;
|
||||||
|
|
||||||
tutorialPage = Rx(TutorialPage.start);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditingController _createController(String key, String name) {
|
TextEditingController _createController(String key, String name) {
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class AddLocalVersion extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
FileSelector(
|
FileSelector(
|
||||||
label: "Location",
|
|
||||||
placeholder: "Type the game folder",
|
placeholder: "Type the game folder",
|
||||||
windowTitle: "Select game folder",
|
windowTitle: "Select game folder",
|
||||||
controller: _gamePathController,
|
controller: _gamePathController,
|
||||||
|
|||||||
@@ -273,7 +273,6 @@ class _AddServerVersionState extends State<AddServerVersion> {
|
|||||||
VersionNameInput(controller: _nameController),
|
VersionNameInput(controller: _nameController),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
FileSelector(
|
FileSelector(
|
||||||
label: "Destination",
|
|
||||||
placeholder: "Type the download destination",
|
placeholder: "Type the download destination",
|
||||||
windowTitle: "Select download destination",
|
windowTitle: "Select download destination",
|
||||||
controller: _pathController,
|
controller: _pathController,
|
||||||
|
|||||||
@@ -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.dart';
|
||||||
import 'package:reboot_launcher/src/dialog/dialog_button.dart';
|
import 'package:reboot_launcher/src/dialog/dialog_button.dart';
|
||||||
import 'package:reboot_launcher/src/dialog/snackbar.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/model/server_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
import 'package:sync/semaphore.dart';
|
import 'package:sync/semaphore.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../main.dart';
|
import '../../main.dart';
|
||||||
import '../page/home_page.dart';
|
|
||||||
import '../util/server.dart';
|
import '../util/server.dart';
|
||||||
|
|
||||||
extension ServerControllerDialog on ServerController {
|
extension ServerControllerDialog on ServerController {
|
||||||
@@ -81,10 +79,7 @@ extension ServerControllerDialog on ServerController {
|
|||||||
try{
|
try{
|
||||||
switch(type()){
|
switch(type()){
|
||||||
case ServerType.embedded:
|
case ServerType.embedded:
|
||||||
embeddedServer = await startEmbeddedServer(
|
startServer();
|
||||||
() => Get.find<SettingsController>().matchmakingIp.text,
|
|
||||||
);
|
|
||||||
embeddedMatchmaker = await startEmbeddedMatchmaker();
|
|
||||||
break;
|
break;
|
||||||
case ServerType.remote:
|
case ServerType.remote:
|
||||||
var uriResult = await _pingRemoteInteractive();
|
var uriResult = await _pingRemoteInteractive();
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -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
@@ -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"]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
@@ -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) => {};
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
};
|
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'game_type.dart';
|
||||||
|
|
||||||
class GameInstance {
|
class GameInstance {
|
||||||
final Process gameProcess;
|
final Process gameProcess;
|
||||||
final Process? launcherProcess;
|
final Process? launcherProcess;
|
||||||
final Process? eacProcess;
|
final Process? eacProcess;
|
||||||
bool tokenError;
|
bool tokenError;
|
||||||
|
bool hasChildServer;
|
||||||
|
|
||||||
GameInstance(this.gameProcess, this.launcherProcess, this.eacProcess)
|
GameInstance(this.gameProcess, this.launcherProcess, this.eacProcess, this.hasChildServer)
|
||||||
: tokenError = false;
|
: tokenError = false;
|
||||||
|
|
||||||
void kill() {
|
void kill() {
|
||||||
|
|||||||
@@ -19,14 +19,8 @@ enum GameType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get name {
|
String get name {
|
||||||
return this == GameType.client ? "Client"
|
return this == GameType.client ? "Game client"
|
||||||
: this == GameType.server ? "Server"
|
: this == GameType.server ? "Game server"
|
||||||
: "Headless Server";
|
: "Headless game 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";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ enum ServerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get name {
|
String get name {
|
||||||
return this == ServerType.embedded ? "Embedded"
|
return this == ServerType.embedded ? "Embedded (Lawin)"
|
||||||
: this == ServerType.remote ? "Remote"
|
: this == ServerType.remote ? "Remote"
|
||||||
: "Local";
|
: "Local";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
enum TutorialPage {
|
|
||||||
start,
|
|
||||||
someoneElse,
|
|
||||||
yourOwn
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
|
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -19,7 +17,6 @@ import 'package:window_manager/window_manager.dart';
|
|||||||
|
|
||||||
import '../controller/settings_controller.dart';
|
import '../controller/settings_controller.dart';
|
||||||
import '../model/server_type.dart';
|
import '../model/server_type.dart';
|
||||||
import '../model/tutorial_page.dart';
|
|
||||||
import 'info_page.dart';
|
import 'info_page.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
@@ -30,10 +27,7 @@ class HomePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomePageState extends State<HomePage> with WindowListener {
|
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 double _defaultPadding = 12.0;
|
||||||
static const int _headerButtonCount = 3;
|
|
||||||
|
|
||||||
final GameController _gameController = Get.find<GameController>();
|
final GameController _gameController = Get.find<GameController>();
|
||||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||||
@@ -46,18 +40,12 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
final RxBool _focused = RxBool(true);
|
final RxBool _focused = RxBool(true);
|
||||||
final RxInt _index = RxInt(0);
|
final RxInt _index = RxInt(0);
|
||||||
bool _navigated = false;
|
bool _navigated = false;
|
||||||
|
|
||||||
bool _shouldMaximize = false;
|
bool _shouldMaximize = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
_searchController.addListener(_onSearch);
|
_searchController.addListener(_onSearch);
|
||||||
_onEasyMode();
|
|
||||||
_settingsController.advancedMode.listen((advanced) {
|
|
||||||
_onEasyMode();
|
|
||||||
_index.value = _index.value + (advanced ? 1 : -1);
|
|
||||||
});
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,15 +61,6 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
.cast<NavigationPaneItem>();
|
.cast<NavigationPaneItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onEasyMode() {
|
|
||||||
if(_settingsController.advancedMode.value){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_gameController.type.value = GameType.client;
|
|
||||||
_serverController.type.value = ServerType.embedded;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
@@ -100,6 +79,12 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
_focused.value = false;
|
_focused.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowResized() {
|
||||||
|
_settingsController.saveWindowSize();
|
||||||
|
super.onWindowResized();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onWindowMoved() {
|
void onWindowMoved() {
|
||||||
_settingsController.saveWindowOffset(appWindow.position);
|
_settingsController.saveWindowOffset(appWindow.position);
|
||||||
@@ -131,52 +116,52 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Obx(() => Stack(
|
||||||
return NotificationListener<SizeChangedLayoutNotification>(
|
children: [
|
||||||
onNotification: (notification) {
|
NavigationView(
|
||||||
return _calculateSize();
|
paneBodyBuilder: (body) => Padding(
|
||||||
},
|
padding: const EdgeInsets.all(_defaultPadding),
|
||||||
child: SizeChangedLayoutNotifier(
|
child: body
|
||||||
child: Obx(_getViewStack)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _getViewStack() {
|
|
||||||
var view = _createNavigationView();
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
view,
|
|
||||||
if(_settingsController.displayType() == PaneDisplayMode.top)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
child: WindowTitleBar(focused: _focused())
|
|
||||||
),
|
),
|
||||||
if(_settingsController.displayType() == PaneDisplayMode.top)
|
appBar: NavigationAppBar(
|
||||||
_createTopDisplayGestures(view.pane?.items.length ?? 0),
|
title: _draggableArea,
|
||||||
if(_focused() && isWin11)
|
actions: WindowTitleBar(focused: _focused())
|
||||||
const WindowBorder()
|
),
|
||||||
]
|
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(_focused() && isWin11)
|
||||||
|
const WindowBorder()
|
||||||
|
]
|
||||||
|
));
|
||||||
|
|
||||||
|
void _onIndexChanged(int index) {
|
||||||
|
_index.value = index;
|
||||||
|
_navigated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Padding _createTopDisplayGestures(int size) => Padding(
|
TextBox get _autoSuggestBox => TextBox(
|
||||||
padding: EdgeInsets.only(
|
key: _searchKey,
|
||||||
left: _sectionSize * size,
|
controller: _searchController,
|
||||||
right: _headerSize * _headerButtonCount,
|
placeholder: 'Search',
|
||||||
),
|
focusNode: _searchFocusNode
|
||||||
child: SizedBox(
|
|
||||||
height: _headerSize,
|
|
||||||
child: _createWindowGestures()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
GestureDetector _createWindowGestures({Widget? child}) => GestureDetector(
|
GestureDetector get _draggableArea => GestureDetector(
|
||||||
onDoubleTap: () {
|
onDoubleTap: () {
|
||||||
if(!_shouldMaximize){
|
if(!_shouldMaximize){
|
||||||
return;
|
return;
|
||||||
@@ -187,89 +172,9 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
},
|
},
|
||||||
onDoubleTapDown: (details) => _shouldMaximize = true,
|
onDoubleTapDown: (details) => _shouldMaximize = true,
|
||||||
onHorizontalDragStart: (event) => appWindow.startDragging(),
|
onHorizontalDragStart: (event) => appWindow.startDragging(),
|
||||||
onVerticalDragStart: (event) => appWindow.startDragging(),
|
onVerticalDragStart: (event) => appWindow.startDragging()
|
||||||
child: child
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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 {
|
int? get _selectedIndex {
|
||||||
var searchItems = _searchItems();
|
var searchItems = _searchItems();
|
||||||
if (searchItems == null) {
|
if (searchItems == null) {
|
||||||
@@ -288,10 +193,9 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
return indexOnScreen;
|
return indexOnScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NavigationPaneItem> get _allItems => [..._createItems(), ..._createFooterItems()];
|
List<NavigationPaneItem> get _allItems => [..._items, ..._footerItems];
|
||||||
|
|
||||||
List<NavigationPaneItem> _createFooterItems() => searchValue.isNotEmpty ? [] : [
|
List<NavigationPaneItem> get _footerItems => searchValue.isNotEmpty ? [] : [
|
||||||
if(_settingsController.displayType() != PaneDisplayMode.top)
|
|
||||||
PaneItem(
|
PaneItem(
|
||||||
title: const Text("Settings"),
|
title: const Text("Settings"),
|
||||||
icon: const Icon(FluentIcons.settings),
|
icon: const Icon(FluentIcons.settings),
|
||||||
@@ -299,19 +203,18 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
List<NavigationPaneItem> _createItems() => _searchItems() ?? [
|
List<NavigationPaneItem> get _items => _searchItems() ?? [
|
||||||
PaneItem(
|
PaneItem(
|
||||||
title: const Text("Home"),
|
title: const Text("Home"),
|
||||||
icon: const Icon(FluentIcons.game),
|
icon: const Icon(FluentIcons.game),
|
||||||
body: const LauncherPage()
|
body: const LauncherPage()
|
||||||
),
|
),
|
||||||
|
|
||||||
if(_settingsController.advancedMode.value)
|
PaneItem(
|
||||||
PaneItem(
|
title: const Text("Backend"),
|
||||||
title: const Text("Backend"),
|
icon: const Icon(FluentIcons.server_enviroment),
|
||||||
icon: const Icon(FluentIcons.server_enviroment),
|
body: ServerPage()
|
||||||
body: ServerPage()
|
),
|
||||||
),
|
|
||||||
|
|
||||||
PaneItem(
|
PaneItem(
|
||||||
title: const Text("Tutorial"),
|
title: const Text("Tutorial"),
|
||||||
@@ -319,50 +222,15 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
body: const InfoPage(),
|
body: const InfoPage(),
|
||||||
onTap: _onTutorial
|
onTap: _onTutorial
|
||||||
),
|
),
|
||||||
|
|
||||||
if(_settingsController.displayType() == PaneDisplayMode.top)
|
|
||||||
PaneItem(
|
|
||||||
title: const Text("Settings"),
|
|
||||||
icon: const Icon(FluentIcons.settings),
|
|
||||||
body: SettingsPage()
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
void _onTutorial() {
|
void _onTutorial() {
|
||||||
if(!_navigated){
|
if(!_navigated){
|
||||||
setState(() {
|
setState(() => _settingsController.scrollingDistance = 0);
|
||||||
_settingsController.tutorialPage.value = TutorialPage.start;
|
|
||||||
_settingsController.scrollingDistance = 0;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_navigated = false;
|
_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;
|
String get searchValue => _searchController.text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../controller/settings_controller.dart';
|
import '../controller/settings_controller.dart';
|
||||||
import '../model/tutorial_page.dart';
|
import '../widget/shared/fluent_card.dart';
|
||||||
|
|
||||||
class InfoPage extends StatefulWidget {
|
class InfoPage extends StatefulWidget {
|
||||||
const InfoPage({Key? key}) : super(key: key);
|
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"
|
"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>();
|
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||||
late final ScrollController _controller;
|
late final ScrollController _controller;
|
||||||
|
|
||||||
@@ -55,17 +56,52 @@ class _InfoPageState extends State<InfoPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Navigator(
|
||||||
switch(_settingsController.tutorialPage()) {
|
key: _navigatorKey,
|
||||||
case TutorialPage.start:
|
initialRoute: "home",
|
||||||
return _createHomeScreen();
|
onGenerateRoute: (settings) {
|
||||||
case TutorialPage.someoneElse:
|
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);
|
return _createInstructions(false);
|
||||||
case TutorialPage.yourOwn:
|
case "own":
|
||||||
return _createInstructions(true);
|
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) {
|
SizedBox _createInstructions(bool own) {
|
||||||
var titles = own ? _ownTitles : _elseTitles;
|
var titles = own ? _ownTitles : _elseTitles;
|
||||||
var codeName = own ? "own" : "else";
|
var codeName = own ? "own" : "else";
|
||||||
@@ -76,8 +112,7 @@ class _InfoPageState extends State<InfoPage> {
|
|||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
right: 20.0
|
right: 20.0
|
||||||
),
|
),
|
||||||
child: Card(
|
child: FluentCard(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
|
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: SelectableText("${index + 1}. ${titles[index]}"),
|
title: SelectableText("${index + 1}. ${titles[index]}"),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
@@ -93,67 +128,42 @@ class _InfoPageState extends State<InfoPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _createHomeScreen() {
|
Widget _createCardWidget({required String text, required String description, required Function() onClick}) => Expanded(
|
||||||
return Row(
|
child: SizedBox(
|
||||||
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(
|
|
||||||
child: SizedBox(
|
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: onClick,
|
onTap: onClick,
|
||||||
child: Card(
|
child: FluentCard(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
fontWeight: FontWeight.bold
|
fontWeight: FontWeight.bold
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
description,
|
description,
|
||||||
textAlign: TextAlign.center
|
textAlign: TextAlign.center
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/foundation.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/main.dart';
|
||||||
import 'package:reboot_launcher/src/controller/build_controller.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/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/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/dialog/dialog.dart';
|
import 'package:reboot_launcher/src/dialog/dialog.dart';
|
||||||
import 'package:reboot_launcher/src/model/reboot_download.dart';
|
import 'package:reboot_launcher/src/model/reboot_download.dart';
|
||||||
import 'package:reboot_launcher/src/util/os.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/game_type_selector.dart';
|
||||||
import 'package:reboot_launcher/src/widget/home/launch_button.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/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 '../dialog/dialog_button.dart';
|
||||||
import '../model/server_type.dart';
|
|
||||||
import '../util/checks.dart';
|
import '../util/checks.dart';
|
||||||
import '../util/reboot.dart';
|
import '../util/reboot.dart';
|
||||||
import '../widget/shared/smart_input.dart';
|
|
||||||
import 'home_page.dart';
|
|
||||||
|
|
||||||
class LauncherPage extends StatefulWidget {
|
class LauncherPage extends StatefulWidget {
|
||||||
const LauncherPage(
|
const LauncherPage(
|
||||||
@@ -38,7 +32,6 @@ class LauncherPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _LauncherPageState extends State<LauncherPage> {
|
class _LauncherPageState extends State<LauncherPage> {
|
||||||
final GameController _gameController = Get.find<GameController>();
|
final GameController _gameController = Get.find<GameController>();
|
||||||
final ServerController _serverController = Get.find<ServerController>();
|
|
||||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||||
final BuildController _buildController = Get.find<BuildController>();
|
final BuildController _buildController = Get.find<BuildController>();
|
||||||
|
|
||||||
@@ -152,28 +145,98 @@ class _LauncherPageState extends State<LauncherPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget get _homeScreen => Column(
|
Widget get _homeScreen => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if(_gameController.error)
|
AnimatedSwitcher(
|
||||||
_updateError,
|
duration: const Duration(milliseconds: 300),
|
||||||
UsernameBox(),
|
child: _gameController.error ? _updateError : const SizedBox(),
|
||||||
Tooltip(
|
),
|
||||||
message:
|
AnimatedSize(
|
||||||
"The hostname of the server that hosts the multiplayer matches",
|
duration: const Duration(milliseconds: 300),
|
||||||
child: Obx(() => SmartInput(
|
child: SizedBox(height: _gameController.error ? 16.0 : 0.0),
|
||||||
label: "Matchmaking Host",
|
),
|
||||||
placeholder:
|
SettingTile(
|
||||||
"Type the hostname of the server that hosts the multiplayer matches",
|
title: "Username",
|
||||||
controller: _settingsController.matchmakingIp,
|
subtitle: "Enter the name that others will see once you are in-game",
|
||||||
validatorMode: AutovalidateMode.always,
|
content: TextFormBox(
|
||||||
|
placeholder: "username",
|
||||||
|
controller: _gameController.username,
|
||||||
validator: checkMatchmaking,
|
validator: checkMatchmaking,
|
||||||
enabled: _serverController.type() == ServerType.embedded)
|
autovalidateMode: AutovalidateMode.always
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
const VersionSelector(),
|
const SizedBox(
|
||||||
if(_settingsController.advancedMode.value)
|
height: 16.0,
|
||||||
GameTypeSelector(),
|
),
|
||||||
|
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()
|
const LaunchButton()
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,36 +6,78 @@ 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/port_input.dart';
|
||||||
import 'package:reboot_launcher/src/widget/server/server_button.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 {
|
class ServerPage extends StatelessWidget {
|
||||||
final ServerController _serverController = Get.find<ServerController>();
|
final ServerController _serverController = Get.find<ServerController>();
|
||||||
|
|
||||||
ServerPage({Key? key}) : super(key: key);
|
ServerPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() => Column(
|
return Obx(() => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if(_serverController.warning.value)
|
const SizedBox(
|
||||||
GestureDetector(
|
width: double.infinity,
|
||||||
onTap: () => _serverController.warning.value = false,
|
child: InfoBar(
|
||||||
child: const MouseRegion(
|
title: Text("The backend server handles authentication and parties, not game hosting"),
|
||||||
cursor: SystemMouseCursors.click,
|
severity: InfoBarSeverity.info
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: InfoBar(
|
|
||||||
title: Text("The backend server handles authentication and parties, not game hosting"),
|
|
||||||
severity: InfoBarSeverity.info
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
HostInput(),
|
),
|
||||||
PortInput(),
|
const SizedBox(
|
||||||
ServerTypeSelector(),
|
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
|
||||||
|
))
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Expanded(child: SizedBox()),
|
||||||
const ServerButton()
|
const ServerButton()
|
||||||
]
|
]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _isRemote => _serverController.type.value == ServerType.remote;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +1,110 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get/get.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/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 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../util/checks.dart';
|
import '../util/checks.dart';
|
||||||
import '../widget/setting/url_updater.dart';
|
import '../widget/shared/setting_tile.dart';
|
||||||
import '../widget/shared/file_selector.dart';
|
|
||||||
|
|
||||||
class SettingsPage extends StatelessWidget {
|
class SettingsPage extends StatelessWidget {
|
||||||
|
final GameController _gameController = Get.find<GameController>();
|
||||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||||
|
|
||||||
SettingsPage({Key? key}) : super(key: key);
|
SettingsPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) =>
|
Widget build(BuildContext context) => Column(
|
||||||
_settingsController.advancedMode.value ? _advancedSettings : _easySettings;
|
|
||||||
|
|
||||||
Widget get _advancedSettings => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const RebootUpdaterInput(),
|
SettingTile(
|
||||||
_createFileSelector(),
|
title: "File settings",
|
||||||
_createConsoleSelector(),
|
subtitle: "This section contains all the settings related to files used by Fortnite",
|
||||||
_createGameSelector(),
|
expandedContent: [
|
||||||
_createVersionInfo(),
|
_createFileSetting(
|
||||||
_createAdvancedSwitch()
|
title: "Game server",
|
||||||
]
|
description: "This file is injected to create a game server to host matches",
|
||||||
);
|
controller: _settingsController.rebootDll
|
||||||
|
),
|
||||||
Widget get _easySettings => SizedBox.expand(
|
_createFileSetting(
|
||||||
child: Column(
|
title: "Unreal engine console",
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
description: "This file is injected to unlock the Unreal Engine Console in-game",
|
||||||
children: [
|
controller: _settingsController.consoleDll
|
||||||
const CircleAvatar(
|
),
|
||||||
radius: 48,
|
_createFileSetting(
|
||||||
backgroundImage: AssetImage("assets/images/auties.png")),
|
title: "Authentication patcher",
|
||||||
|
description: "This file is injected to redirect all HTTP requests to the local backend",
|
||||||
|
controller: _settingsController.authDll
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16.0,
|
height: 16.0,
|
||||||
),
|
),
|
||||||
const Text("Made by Auties00"),
|
SettingTile(
|
||||||
const SizedBox(
|
title: "Automatic updates",
|
||||||
height: 4.0,
|
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(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 16.0,
|
||||||
),
|
),
|
||||||
Button(
|
SettingTile(
|
||||||
child: const Text("Switch to advanced mode"),
|
title: "Custom launch arguments",
|
||||||
onPressed: () => _settingsController.advancedMode.value = true
|
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(
|
Widget _createFileSetting({required String title, required String description, required TextEditingController controller}) => ListTile(
|
||||||
label: "Advanced Mode",
|
title: Text(title),
|
||||||
value: _settingsController.advancedMode
|
subtitle: Text(description),
|
||||||
);
|
trailing: SizedBox(
|
||||||
|
width: 256,
|
||||||
Widget _createVersionInfo() => Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Expanded(
|
||||||
const Text("Version Status"),
|
child: TextFormBox(
|
||||||
const SizedBox(height: 6.0),
|
placeholder: "path",
|
||||||
Button(
|
controller: controller,
|
||||||
child: _versionText,
|
validator: checkDll,
|
||||||
onPressed: () => launchUrl(safeBinariesDirectory.uri)
|
autovalidateMode: AutovalidateMode.always
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
);
|
const SizedBox(
|
||||||
|
width: 8.0,
|
||||||
Widget _createGameSelector() => Tooltip(
|
),
|
||||||
message: "The dll that is injected to make the game work",
|
Padding(
|
||||||
child: FileSelector(
|
padding: const EdgeInsets.only(bottom: 21.0),
|
||||||
label: "Cranium DLL",
|
child: Button(
|
||||||
placeholder:
|
onPressed: () { },
|
||||||
"Type the path to the dll used for authentication",
|
child: const Icon(FluentIcons.open_folder_horizontal),
|
||||||
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'}");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:html/parser.dart' show parse;
|
import 'package:html/parser.dart' show parse;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:process_run/shell.dart';
|
import 'package:process_run/shell.dart';
|
||||||
import 'package:reboot_launcher/src/model/fortnite_build.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:reboot_launcher/src/util/version.dart' as parser;
|
||||||
import 'package:version/version.dart';
|
|
||||||
|
|
||||||
import 'os.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(
|
final _manifestSourceUrl = Uri.parse(
|
||||||
"https://github.com/VastBlast/FortniteManifestArchive/blob/main/README.md");
|
"https://github.com/VastBlast/FortniteManifestArchive/blob/main/README.md");
|
||||||
|
|
||||||
@@ -67,56 +61,3 @@ Future<Process> downloadManifestBuild(
|
|||||||
|
|
||||||
return process;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ Directory get safeBinariesDirectory =>
|
|||||||
Directory("${Platform.environment["UserProfile"]}\\.reboot_launcher");
|
Directory("${Platform.environment["UserProfile"]}\\.reboot_launcher");
|
||||||
|
|
||||||
Directory get embeddedBackendDirectory =>
|
Directory get embeddedBackendDirectory =>
|
||||||
Directory("${safeBinariesDirectory.path}\\backend");
|
Directory("${safeBinariesDirectory.path}\\backend-lawin");
|
||||||
|
|
||||||
File loadEmbedded(String file) {
|
File loadEmbedded(String file) {
|
||||||
var safeBinary = File("${embeddedBackendDirectory.path}\\$file");
|
var safeBinary = File("${embeddedBackendDirectory.path}\\$file");
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:archive/archive_io.dart';
|
||||||
|
import 'package:ini/ini.dart';
|
||||||
import 'package:process_run/shell.dart';
|
import 'package:process_run/shell.dart';
|
||||||
import 'package:reboot_launcher/src/model/game_type.dart';
|
import 'package:reboot_launcher/src/model/game_type.dart';
|
||||||
import 'package:reboot_launcher/src/model/server_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");
|
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 {
|
Future<bool> isLawinPortFree() async {
|
||||||
return http.get(Uri.parse("http://127.0.0.1:3551/unknown"))
|
return http.get(Uri.parse("http://127.0.0.1:3551/unknown"))
|
||||||
.timeout(const Duration(milliseconds: 500))
|
.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 = [
|
var args = [
|
||||||
"-epicapp=Fortnite",
|
"-epicapp=Fortnite",
|
||||||
"-epicenv=Prod",
|
"-epicenv=Prod",
|
||||||
@@ -73,6 +110,10 @@ List<String> createRebootArgs(String username, GameType type) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(additionalArgs.isNotEmpty){
|
||||||
|
args.addAll(additionalArgs.split(" "));
|
||||||
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,33 +12,16 @@ class GameTypeSelector extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Tooltip(
|
return Obx(() => DropDownButton(
|
||||||
message: "The type of Fortnite instance to launch",
|
leading: Text(_gameController.type.value.name),
|
||||||
child: _createAdvancedSelector(),
|
items: GameType.values
|
||||||
);
|
.map((type) => _createItem(type))
|
||||||
|
.toList()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _createAdvancedSelector() => InfoLabel(
|
|
||||||
label: "Type",
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Obx(() => DropDownButton(
|
|
||||||
leading: Text(_gameController.type.value.name),
|
|
||||||
items: GameType.values
|
|
||||||
.map((type) => _createItem(type))
|
|
||||||
.toList())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
MenuFlyoutItem _createItem(GameType type) => MenuFlyoutItem(
|
MenuFlyoutItem _createItem(GameType type) => MenuFlyoutItem(
|
||||||
text: SizedBox(
|
text: Text(type.name),
|
||||||
width: double.infinity,
|
|
||||||
child: Tooltip(
|
|
||||||
message: type.message,
|
|
||||||
child: Text(type.name)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_gameController.type(type);
|
_gameController.type(type);
|
||||||
_gameController.started.value = _gameController.currentGameInstance != null;
|
_gameController.started.value = _gameController.currentGameInstance != null;
|
||||||
|
|||||||
@@ -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/dialog/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/model/game_instance.dart';
|
import 'package:reboot_launcher/src/model/game_instance.dart';
|
||||||
|
|
||||||
import '../../page/home_page.dart';
|
|
||||||
import '../../util/process.dart';
|
import '../../util/process.dart';
|
||||||
import '../shared/smart_check_box.dart';
|
|
||||||
|
|
||||||
class LaunchButton extends StatefulWidget {
|
class LaunchButton extends StatefulWidget {
|
||||||
const LaunchButton(
|
const LaunchButton(
|
||||||
@@ -66,21 +64,24 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Align(
|
||||||
return Align(
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
child: SizedBox(
|
||||||
child: SizedBox(
|
width: double.infinity,
|
||||||
width: double.infinity,
|
child: Obx(() => SizedBox(
|
||||||
child: Obx(() => Tooltip(
|
height: 48,
|
||||||
message: _gameController.started() ? "Close the running Fortnite instance" : "Launch a new Fortnite instance",
|
child: Button(
|
||||||
child: Button(
|
child: Align(
|
||||||
onPressed: () => _start(_gameController.type()),
|
alignment: Alignment.center,
|
||||||
child: Text(_gameController.started() ? "Close" : "Launch")
|
child: Text(
|
||||||
|
_gameController.started() ? "Close fortnite" : "Launch fortnite"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)),
|
onPressed: () => _start(_gameController.type()),
|
||||||
),
|
),
|
||||||
);
|
)),
|
||||||
}
|
),
|
||||||
|
);
|
||||||
|
|
||||||
void _start(GameType type) async {
|
void _start(GameType type) async {
|
||||||
if (_gameController.started()) {
|
if (_gameController.started()) {
|
||||||
@@ -99,7 +100,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
showMessage("No username: expecting self sign in");
|
showMessage("No username: expecting self sign in");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_gameController.selectedVersionObs.value == null) {
|
if (_gameController.selectedVersion == null) {
|
||||||
showMessage("No version is selected");
|
showMessage("No version is selected");
|
||||||
_onStop(type);
|
_onStop(type);
|
||||||
return;
|
return;
|
||||||
@@ -115,7 +116,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
_fail = false;
|
_fail = false;
|
||||||
await _resetLogFile();
|
await _resetLogFile();
|
||||||
|
|
||||||
var version = _gameController.selectedVersionObs.value!;
|
var version = _gameController.selectedVersion!;
|
||||||
var gamePath = version.executable?.path;
|
var gamePath = version.executable?.path;
|
||||||
if(gamePath == null){
|
if(gamePath == null){
|
||||||
showMissingBuildError(version);
|
showMissingBuildError(version);
|
||||||
@@ -132,8 +133,8 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
await compute(patchMatchmaking, version.executable!);
|
await compute(patchMatchmaking, version.executable!);
|
||||||
await compute(patchHeadless, version.executable!);
|
await compute(patchHeadless, version.executable!);
|
||||||
|
|
||||||
await _startMatchMakingServer();
|
var automaticallyStartedServer = await _startMatchMakingServer();
|
||||||
await _startGameProcesses(version, type);
|
await _startGameProcesses(version, type, automaticallyStartedServer);
|
||||||
|
|
||||||
if(type == GameType.headlessServer){
|
if(type == GameType.headlessServer){
|
||||||
await _showServerLaunchingWarning();
|
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 launcherProcess = await _createLauncherProcess(version);
|
||||||
var eacProcess = await _createEacProcess(version);
|
var eacProcess = await _createEacProcess(version);
|
||||||
var gameProcess = await _createGameProcess(version.executable!.path, type);
|
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);
|
_injectOrShowError(Injectable.cranium, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startMatchMakingServer() async {
|
Future<bool> _startMatchMakingServer() async {
|
||||||
if(_gameController.type() != GameType.client){
|
if(_gameController.type() != GameType.client){
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchmakingIp = _settingsController.matchmakingIp.text;
|
var matchmakingIp = _settingsController.matchmakingIp.text;
|
||||||
if(!matchmakingIp.contains("127.0.0.1") && !matchmakingIp.contains("localhost")) {
|
if(!matchmakingIp.contains("127.0.0.1") && !matchmakingIp.contains("localhost")) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var headlessServer = _gameController.gameInstancesMap[GameType.headlessServer] != null;
|
if(!_gameController.autostartGameServer()){
|
||||||
var server = _gameController.gameInstancesMap[GameType.server] != null;
|
return false;
|
||||||
if(headlessServer || server){
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _askToStartMatchMakingServer();
|
var version = _gameController.selectedVersion!;
|
||||||
if(result != true){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = _gameController.selectedVersionObs.value!;
|
|
||||||
await _startGameProcesses(
|
await _startGameProcesses(
|
||||||
version,
|
version,
|
||||||
GameType.headlessServer
|
GameType.headlessServer,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
return true;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Process> _createGameProcess(String gamePath, GameType type) async {
|
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
|
gameProcess
|
||||||
..exitCode.then((_) => _onEnd(type))
|
..exitCode.then((_) => _onEnd(type))
|
||||||
..outLines.forEach((line) => _onGameOutput(line, type))
|
..outLines.forEach((line) => _onGameOutput(line, type))
|
||||||
@@ -369,9 +314,21 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
_start(type);
|
_start(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onStop(GameType type) {
|
void _onStop(GameType? type) {
|
||||||
_gameController.gameInstancesMap[type]?.kill();
|
if(type == null){
|
||||||
_gameController.gameInstancesMap.remove(type);
|
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()) {
|
if(type == _gameController.type()) {
|
||||||
_gameController.started.value = false;
|
_gameController.started.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,19 @@ import '../shared/file_selector.dart';
|
|||||||
class VersionSelector extends StatefulWidget {
|
class VersionSelector extends StatefulWidget {
|
||||||
const VersionSelector({Key? key}) : super(key: key);
|
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
|
@override
|
||||||
State<VersionSelector> createState() => _VersionSelectorState();
|
State<VersionSelector> createState() => _VersionSelectorState();
|
||||||
}
|
}
|
||||||
@@ -26,79 +39,44 @@ class VersionSelector extends StatefulWidget {
|
|||||||
class _VersionSelectorState extends State<VersionSelector> {
|
class _VersionSelectorState extends State<VersionSelector> {
|
||||||
final GameController _gameController = Get.find<GameController>();
|
final GameController _gameController = Get.find<GameController>();
|
||||||
final CheckboxController _deleteFilesController = CheckboxController();
|
final CheckboxController _deleteFilesController = CheckboxController();
|
||||||
|
final FlyoutController _flyoutController = FlyoutController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Obx(() => _createOptionsMenu(
|
||||||
return InfoLabel(
|
version: _gameController.selectedVersion,
|
||||||
label: "Version",
|
close: false,
|
||||||
child: Align(
|
child: FlyoutTarget(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
controller: _flyoutController,
|
||||||
child: Row(
|
child: DropDownButton(
|
||||||
children: [
|
leading: Text(_gameController.selectedVersion?.name ?? "Select a version"),
|
||||||
Expanded(child: _createSelector(context)),
|
items: _createSelectorItems(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(),
|
|
||||||
close: false,
|
|
||||||
child: DropDownButton(
|
|
||||||
leading: Text(_gameController.selectedVersionObs.value?.name
|
|
||||||
?? "Select a version"),
|
|
||||||
items: _createSelectorItems(context)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MenuFlyoutItem> _createSelectorItems(BuildContext context) {
|
|
||||||
return _gameController.hasNoVersions ? [_createDefaultVersionItem()]
|
|
||||||
: _gameController.versions.value
|
|
||||||
.map((version) => _createVersionItem(context, version))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuFlyoutItem _createVersionItem(BuildContext context, FortniteVersion version) {
|
|
||||||
return MenuFlyoutItem(
|
|
||||||
text: _createOptionsMenu(
|
|
||||||
version: version,
|
|
||||||
close: true,
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Text(version.name)
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
onPressed: () => _gameController.selectedVersion = version
|
));
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _createOptionsMenu({required FortniteVersion? version, required bool close, required Widget child}) {
|
List<MenuFlyoutItem> _createSelectorItems(BuildContext context) => _gameController.hasNoVersions ? [_createDefaultVersionItem()]
|
||||||
return Listener(
|
: _gameController.versions.value
|
||||||
|
.map((version) => _createVersionItem(context, version))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
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: Text(version.name),
|
||||||
|
),
|
||||||
|
onPressed: () => _gameController.selectedVersion = version
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _createOptionsMenu({required FortniteVersion? version, required bool close, required Widget child}) => Listener(
|
||||||
onPointerDown: (event) async {
|
onPointerDown: (event) async {
|
||||||
if (event.kind != PointerDeviceKind.mouse ||
|
if (event.kind != PointerDeviceKind.mouse || event.buttons != kSecondaryMouseButton) {
|
||||||
event.buttons != kSecondaryMouseButton) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,43 +84,19 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _openMenu(context, version, event.position, close);
|
var result = await _flyoutController.showFlyout<ContextualOption?>(
|
||||||
|
builder: (context) => MenuFlyout(
|
||||||
|
items: ContextualOption.values
|
||||||
|
.map((entry) => _createOption(context, entry))
|
||||||
|
.toList()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_handleResult(result, version, close);
|
||||||
},
|
},
|
||||||
child: child
|
child: child
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
MenuFlyoutItem _createDefaultVersionItem() {
|
void _handleResult(ContextualOption? result, FortniteVersion version, bool close) async {
|
||||||
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(
|
|
||||||
builder: (context) => MenuFlyout(
|
|
||||||
items: ContextualOption.values
|
|
||||||
.map((entry) => _createOption(context, entry))
|
|
||||||
.toList()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case ContextualOption.openExplorer:
|
case ContextualOption.openExplorer:
|
||||||
if(!mounted){
|
if(!mounted){
|
||||||
@@ -156,7 +110,6 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
launchUrl(version.location.uri)
|
launchUrl(version.location.uri)
|
||||||
.onError((error, stackTrace) => _onExplorerError());
|
.onError((error, stackTrace) => _onExplorerError());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ContextualOption.modify:
|
case ContextualOption.modify:
|
||||||
if(!mounted){
|
if(!mounted){
|
||||||
return;
|
return;
|
||||||
@@ -168,7 +121,6 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
|
|
||||||
await _openRenameDialog(context, version);
|
await _openRenameDialog(context, version);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ContextualOption.delete:
|
case ContextualOption.delete:
|
||||||
if(!mounted){
|
if(!mounted){
|
||||||
return;
|
return;
|
||||||
@@ -184,8 +136,8 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gameController.removeVersion(version);
|
_gameController.removeVersion(version);
|
||||||
if (_gameController.selectedVersionObs.value?.name == version.name || _gameController.hasNoVersions) {
|
if (_gameController.selectedVersion?.name == version.name || _gameController.hasNoVersions) {
|
||||||
_gameController.selectedVersionObs.value = null;
|
_gameController.selectedVersion = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_deleteFilesController.value && await version.location.exists()) {
|
if (_deleteFilesController.value && await version.location.exists()) {
|
||||||
@@ -193,7 +145,6 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -276,7 +227,6 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
FileSelector(
|
FileSelector(
|
||||||
label: "Location",
|
|
||||||
placeholder: "Type the new game folder",
|
placeholder: "Type the new game folder",
|
||||||
windowTitle: "Select game folder",
|
windowTitle: "Select game folder",
|
||||||
controller: pathController,
|
controller: pathController,
|
||||||
|
|||||||
@@ -15,49 +15,32 @@ class _ServerButtonState extends State<ServerButton> {
|
|||||||
final ServerController _serverController = Get.find<ServerController>();
|
final ServerController _serverController = Get.find<ServerController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Align(
|
||||||
return Align(
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
child: SizedBox(
|
||||||
child: SizedBox(
|
width: double.infinity,
|
||||||
width: double.infinity,
|
child: Obx(() => SizedBox(
|
||||||
child: Obx(() => Tooltip(
|
height: 48,
|
||||||
message: _helpMessage,
|
child: Button(
|
||||||
child: Button(
|
child: Align(
|
||||||
onPressed: () async => _serverController.toggle(),
|
alignment: Alignment.center,
|
||||||
child: Text(_buttonText())),
|
child: Text(_buttonText),
|
||||||
)),
|
),
|
||||||
),
|
onPressed: () => _serverController.toggle()
|
||||||
);
|
),
|
||||||
}
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
String _buttonText() {
|
String get _buttonText {
|
||||||
if(_serverController.type.value == ServerType.local){
|
if(_serverController.type.value == ServerType.local){
|
||||||
return "Check";
|
return "Check backend";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_serverController.started.value){
|
if(_serverController.started.value){
|
||||||
return "Stop";
|
return "Stop backend";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Start";
|
return "Start backend";
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,30 +10,19 @@ class ServerTypeSelector extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Tooltip(
|
return DropDownButton(
|
||||||
message: "Determines the type of backend server to use",
|
leading: Text(_serverController.type.value.name),
|
||||||
child: InfoLabel(
|
items: ServerType.values
|
||||||
label: "Type",
|
.map((type) => _createItem(type))
|
||||||
child: SizedBox(
|
.toList()
|
||||||
width: double.infinity,
|
|
||||||
child: Obx(() => DropDownButton(
|
|
||||||
leading: Text(_serverController.type.value.name),
|
|
||||||
items: ServerType.values
|
|
||||||
.map((type) => _createItem(type))
|
|
||||||
.toList()))
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuFlyoutItem _createItem(ServerType type) {
|
MenuFlyoutItem _createItem(ServerType type) {
|
||||||
return MenuFlyoutItem(
|
return MenuFlyoutItem(
|
||||||
text: SizedBox(
|
text: Tooltip(
|
||||||
width: double.infinity,
|
message: type.message,
|
||||||
child: Tooltip(
|
child: Text(type.name)
|
||||||
message: type.message,
|
|
||||||
child: Text(type.name)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _serverController.stop();
|
await _serverController.stop();
|
||||||
|
|||||||
@@ -51,12 +51,10 @@ class _RebootUpdaterInputState extends State<RebootUpdaterInput> {
|
|||||||
const SizedBox(width: 16.0),
|
const SizedBox(width: 16.0),
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: _settingsController.autoUpdate.value ? "Disable automatic updates" : "Enable automatic updates",
|
message: _settingsController.autoUpdate.value ? "Disable automatic updates" : "Enable automatic updates",
|
||||||
child: Obx(() => Padding(
|
child: Obx(() => Button(
|
||||||
padding: _valid() ? EdgeInsets.zero : const EdgeInsets.only(bottom: 21.0),
|
onPressed: () => _settingsController.autoUpdate.value = !_settingsController.autoUpdate.value,
|
||||||
child: Button(
|
child: Icon(_settingsController.autoUpdate.value ? FluentIcons.disable_updates : FluentIcons.refresh)
|
||||||
onPressed: () => _settingsController.autoUpdate.value = !_settingsController.autoUpdate.value,
|
)
|
||||||
child: Icon(_settingsController.autoUpdate.value ? FluentIcons.disable_updates : FluentIcons.refresh)
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:reboot_launcher/src/dialog/snackbar.dart';
|
|||||||
import 'package:reboot_launcher/src/util/selector.dart';
|
import 'package:reboot_launcher/src/util/selector.dart';
|
||||||
|
|
||||||
class FileSelector extends StatefulWidget {
|
class FileSelector extends StatefulWidget {
|
||||||
final String label;
|
|
||||||
final String placeholder;
|
final String placeholder;
|
||||||
final String windowTitle;
|
final String windowTitle;
|
||||||
final bool allowNavigator;
|
final bool allowNavigator;
|
||||||
@@ -20,8 +19,7 @@ class FileSelector extends StatefulWidget {
|
|||||||
final bool folder;
|
final bool folder;
|
||||||
|
|
||||||
const FileSelector(
|
const FileSelector(
|
||||||
{required this.label,
|
{required this.placeholder,
|
||||||
required this.placeholder,
|
|
||||||
required this.windowTitle,
|
required this.windowTitle,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.validator,
|
required this.validator,
|
||||||
@@ -38,50 +36,32 @@ class FileSelector extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FileSelectorState extends State<FileSelector> {
|
class _FileSelectorState extends State<FileSelector> {
|
||||||
final RxBool _valid = RxBool(true);
|
|
||||||
late String? Function(String?) validator;
|
|
||||||
bool _selecting = false;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InfoLabel(
|
return Row(
|
||||||
label: widget.label,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: Row(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Expanded(
|
||||||
children: [
|
child: TextFormBox(
|
||||||
Expanded(
|
controller: widget.controller,
|
||||||
child: TextFormBox(
|
placeholder: widget.placeholder,
|
||||||
controller: widget.controller,
|
validator: widget.validator,
|
||||||
placeholder: widget.placeholder,
|
autovalidateMode: widget.validatorMode ?? AutovalidateMode.onUserInteraction
|
||||||
validator: validator,
|
)
|
||||||
autovalidateMode: widget.validatorMode ?? AutovalidateMode.onUserInteraction
|
),
|
||||||
)
|
if (widget.allowNavigator)
|
||||||
),
|
const SizedBox(width: 16.0),
|
||||||
if (widget.allowNavigator) const SizedBox(width: 16.0),
|
if (widget.allowNavigator)
|
||||||
if (widget.allowNavigator)
|
Padding(
|
||||||
Tooltip(
|
padding: const EdgeInsets.only(bottom: 21.0),
|
||||||
message: "Select a ${widget.folder ? 'folder' : 'file'}",
|
child: Button(
|
||||||
child: Obx(() => Padding(
|
onPressed: _onPressed,
|
||||||
padding: _valid() ? EdgeInsets.zero : const EdgeInsets.only(bottom: 21.0),
|
child: const Icon(FluentIcons.open_folder_horizontal)
|
||||||
child: Button(
|
|
||||||
onPressed: _onPressed,
|
|
||||||
child: const Icon(FluentIcons.open_folder_horizontal)
|
|
||||||
))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
],
|
)
|
||||||
)
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,190 +1,16 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
class FluentCard extends StatefulWidget {
|
class FluentCard extends StatelessWidget {
|
||||||
const FluentCard({
|
final Widget child;
|
||||||
Key? key,
|
const FluentCard({Key? key, required this.child}) : super(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;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FluentCardState createState() => FluentCardState();
|
Widget build(BuildContext context) => Mica(
|
||||||
}
|
elevation: 1,
|
||||||
|
child: Card(
|
||||||
class FluentCardState extends State<FluentCard>
|
backgroundColor: FluentTheme.of(context).menuColor,
|
||||||
with SingleTickerProviderStateMixin {
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
|
||||||
late ThemeData theme;
|
child: child
|
||||||
|
)
|
||||||
late AnimationController _controller;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: widget.animationDuration ?? const Duration(milliseconds: 150),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
67
lib/src/widget/shared/setting_tile.dart
Normal file
67
lib/src/widget/shared/setting_tile.dart
Normal 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
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -54,9 +54,6 @@ flutter:
|
|||||||
- assets/icons/
|
- assets/icons/
|
||||||
- assets/binaries/
|
- assets/binaries/
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/profiles/
|
|
||||||
- assets/responses/
|
|
||||||
- assets/config/
|
|
||||||
|
|
||||||
msix_config:
|
msix_config:
|
||||||
display_name: Reboot Launcher
|
display_name: Reboot Launcher
|
||||||
|
|||||||
Reference in New Issue
Block a user