Allow for overriding a product's filter.

Useful for when FDev makes a copy/paste error for a new product (i.e. when they released Odyssey with an "edh" filter instead of "edo")

fixes #25
This commit is contained in:
Chris
2021-06-03 23:00:00 -06:00
parent eccfb3292b
commit 0b7f23439e
9 changed files with 99 additions and 45 deletions

View File

@ -4,10 +4,16 @@
### Breaking Changes
- Restart option has moved to the `/restart delay` argument and is no longer specified in the config file.
This allows for creating separate shortcuts, one for normal gameplay and one for restarting.
Instead of specifying `restart: { enabled: true, shutdownTimeout: 3 }`, modify your launch options to include `/restart 3`.
### New Features
- Add ability to override a product's filter via the config file
Useful for when FDev makes a copy/paste error for a new product (i.e. when they released Odyssey with an "edh" filter instead of "edo")
## [0.4.0] - 2021-05-11
### New Features

View File

@ -160,31 +160,35 @@ Linux: `$XDG_CONFIG_HOME/min-ed-launcher/settings.json` (`~/.config` if `$XDG_CO
| language | Sets the game's language. Supported values are _en_ and the names of the language folders in Elite's install directory |
| autoUpdate | Automatically update games that are out of date |
| maxConcurrentDownloads | Maximum number of simultaneous downloads when downloading updates |
| forceUpdate | Be default, Steam and Epic updates are handled by their respective platform. In cases like the Odyssey alpha, FDev doesn't provide updates through Steam or Epic. This allows the launcher to force updates to be done via FDev servers by providing a comma delimited list of SKUs |
| forceUpdate | By default, Steam and Epic updates are handled by their respective platform. In cases like the Odyssey alpha, FDev doesn't provide updates through Steam or Epic. This allows the launcher to force updates to be done via FDev servers by providing a comma delimited list of SKUs |
| processes | Additional applications to launch before launching the game |
| filterOverrides | Manually override a product's filter for use with launch options filter flag (e.g. /edo, /edh, etc...) |
When specifying a path for either `gameLocation` or `processes.fileName` on Windows, it's required to escape backslashes. Make sure to use a
double backslash (`\\`) instead of a single backslash (`\`).
```json
{
"apiUri": "https://api.zaonce.net",
"watchForCrashes": false,
"gameLocation": null,
"language": "en",
"autoUpdate": true,
"maxConcurrentDownloads": 4,
"forceUpdate": "PUBLIC_TEST_SERVER_OD",
"processes": [
{
"fileName": "C:\\path\\to\\app.exe",
"arguments": "--arg1 --arg2"
},
{
"fileName": "C:\\path\\to\\app2.exe",
"arguments": "--arg1 --arg2"
}
]
"apiUri": "https://api.zaonce.net",
"watchForCrashes": false,
"gameLocation": null,
"language": "en",
"autoUpdate": true,
"maxConcurrentDownloads": 4,
"forceUpdate": "PUBLIC_TEST_SERVER_OD",
"processes": [
{
"fileName": "C:\\path\\to\\app.exe",
"arguments": "--arg1 --arg2"
},
{
"fileName": "C:\\path\\to\\app2.exe",
"arguments": "--arg1 --arg2"
}
],
"filterOverrides": [
{ "sku": "FORC-FDEV-DO-1000", "filter": "edo" }
]
}
```

View File

@ -327,7 +327,9 @@ let run settings launcherVersion cancellationToken = task {
Log.debug "Getting authorized products"
match! Api.getAuthorizedProducts settings.Platform None connection with
| Ok authorizedProducts ->
let authorizedProducts = authorizedProducts |> List.map (AuthorizedProduct.fixDirectoryPath productsDir settings.Platform Directory.Exists)
let applyFixes = AuthorizedProduct.fixDirectoryPath productsDir settings.Platform Directory.Exists
>> AuthorizedProduct.fixFilters settings.FilterOverrides
let authorizedProducts = authorizedProducts |> List.map applyFixes
let names = authorizedProducts |> List.map (fun p -> p.Name)
Log.debug $"Authorized Products: %s{String.Join(',', names)}"
Log.info "Checking for updates"
@ -448,7 +450,7 @@ let run settings launcherVersion cancellationToken = task {
if settings.AutoRun then
playableProducts
|> Array.filter (fun p -> settings.ProductWhitelist.Count = 0
|| p.Filters |> Set.union settings.ProductWhitelist |> Set.count > 0)
|| p.Filters |> Set.intersect settings.ProductWhitelist |> Set.count > 0)
|> Array.tryHead
else if playableProducts.Length > 0 then
promptForProductToPlay playableProducts cancellationToken

View File

@ -19,4 +19,12 @@ let fixDirectoryPath productsDir platform directoryExists (product: AuthorizedPr
if dirExists product.Directory then product.Directory else product.Sku
| Frontier _ | Oculus _ | Dev ->
if dirExists product.Sku then product.Sku else product.Directory
{ product with Directory = dir }
{ product with Directory = dir }
// Allows for overriding a product's filter. Useful for when FDev makes a copy/paste error
// for a new product (i.e. when they released Odyssey with an "edh" filter instead of "edo")
let fixFilters overrides (product: AuthorizedProduct) =
overrides
|> Map.tryFind product.Sku
|> Option.map (fun filter -> { product with Filter = filter })
|> Option.defaultValue product

View File

@ -24,7 +24,8 @@ let defaults =
AutoUpdate = true
MaxConcurrentDownloads = 4
ForceUpdate = Set.empty
Processes = List.empty }
Processes = List.empty
FilterOverrides = Map.empty }
[<RequireQualifiedAccess>]
type FrontierCredResult = Found of string * string * string option | NotFound of string | UnexpectedFormat of string | Error of string
@ -135,10 +136,8 @@ let parseArgs defaults (findCbLaunchDir: Platform -> Result<string,string>) (arg
| "/autorun", _ -> { s with AutoRun = true }
| "/autoquit", _ -> { s with AutoQuit = true }
| "/forcelocal", _ -> { s with ForceLocal = true }
| "/ed", _ -> { s with ProductWhitelist = s.ProductWhitelist.Add "ed" }
| "/edh", _ -> { s with ProductWhitelist = s.ProductWhitelist.Add "edh" }
| "/eda", _ -> { s with ProductWhitelist = s.ProductWhitelist.Add "eda" }
| "/edo", _ -> { s with ProductWhitelist = s.ProductWhitelist.Add "edo" }
| arg, _ when arg.StartsWith('/')
&& arg.Length > 1 -> { s with ProductWhitelist = s.ProductWhitelist.Add (arg.TrimStart('/')) }
| _ -> s) defaults
match cbLaunchDir
@ -148,10 +147,9 @@ let parseArgs defaults (findCbLaunchDir: Platform -> Result<string,string>) (arg
applyDeviceAuth settings
| Result.Error msg -> Result.Error msg |> Task.fromResult
[<CLIMutable>]
type ProcessConfig =
{ FileName: string
Arguments: string option }
[<CLIMutable>] type ProcessConfig = { FileName: string; Arguments: string option }
[<CLIMutable>] type FilterConfig = { Sku: string; Filter: string }
[<CLIMutable>]
type Config =
{ ApiUri: string
@ -163,26 +161,33 @@ type Config =
[<DefaultValue("4")>]
MaxConcurrentDownloads: int
ForceUpdate: string list
Processes: ProcessConfig list }
Processes: ProcessConfig list
FilterOverrides: FilterConfig list }
let parseConfig fileName =
let configRoot = ConfigurationBuilder()
.AddJsonFile(fileName, false)
.Build()
let parseKvps section keyName valueName map =
configRoot.GetSection(section).GetChildren()
|> Seq.choose (fun section ->
let key = section.GetValue<string>(keyName)
let value = section.GetValue<string>(valueName)
if String.IsNullOrWhiteSpace(key) then
None
else
map key value)
|> Seq.toList
match AppConfig(configRoot).Get<Config>() with
| Ok config ->
// FsConfig doesn't support list of records so handle it manually
let processes =
configRoot.GetSection("processes").GetChildren()
|> Seq.map (fun section ->
let fileName = section.GetValue<string>("fileName")
let arguments = section.GetValue<string>("arguments")
if fileName = null then
None
else
Some { FileName = fileName; Arguments = if arguments = null then None else Some arguments })
|> Seq.choose id
|> Seq.toList
{ config with Processes = processes } |> ConfigParseResult.Ok
parseKvps "processes" "fileName" "arguments" (fun key value ->
Some { FileName = key; Arguments = if value = null then None else Some value })
let filterOverrides =
parseKvps "filterOverrides" "sku" "filter" (fun key value ->
if String.IsNullOrWhiteSpace(value) then None
else Some { Sku = key; Filter = value })
{ config with Processes = processes; FilterOverrides = filterOverrides } |> ConfigParseResult.Ok
| Error error -> Error error
let getSettings args appDir fileConfig = task {
@ -208,7 +213,7 @@ let getSettings args appDir fileConfig = task {
pInfo.RedirectStandardError <- true
pInfo.WindowStyle <- ProcessWindowStyle.Minimized
pInfo)
let filterOverrides = fileConfig.FilterOverrides |> Seq.map (fun o -> o.Sku, o.Filter) |> Map.ofSeq
let fallbackDirs platform =
match platform with
| Epic d -> Epic.potentialInstallPaths d.AppId |> findCbLaunchDir
@ -224,4 +229,5 @@ let getSettings args appDir fileConfig = task {
MaxConcurrentDownloads = fileConfig.MaxConcurrentDownloads
PreferredLanguage = fileConfig.Language
Processes = processes
FilterOverrides = filterOverrides
WatchForCrashes = fileConfig.WatchForCrashes }) }

View File

@ -45,7 +45,8 @@ type LauncherSettings =
AutoUpdate: bool
MaxConcurrentDownloads: int
ForceUpdate: string Set
Processes: ProcessStartInfo list }
Processes: ProcessStartInfo list
FilterOverrides: Map<string, string> }
type ServerStatus = Healthy
type LocalVersion = Version
type LauncherStatus =

View File

@ -5,5 +5,6 @@
"autoUpdate": true,
"maxConcurrentDownloads": 4,
"forceUpdate": "",
"processes": []
"processes": [],
"filterOverrides": []
}

View File

@ -54,4 +54,27 @@ let tests =
Expect.equal actual.Directory product.Directory ""
}
]
testList "fixFilters" [
let product =
{ Name = ""; Filter = "oldFilter"; Directory = ""; GameArgs = ""; ServerArgs = ""; SortKey = 0; Sku = "sku"; TestApi = false }
test "No change when overrides is empty" {
let actual = AuthorizedProduct.fixFilters Map.empty product
Expect.equal actual product ""
}
test "No change when no matching sku" {
let overrides = [ "asdf", "newFilter" ] |> Map.ofList
let actual = AuthorizedProduct.fixFilters overrides product
Expect.equal actual product ""
}
test "Updates filter when matching sku" {
let newFilter = "newFilter"
let overrides = [ product.Sku, newFilter ] |> Map.ofList
let actual = AuthorizedProduct.fixFilters overrides product
Expect.equal actual.Filter newFilter ""
}
]
]

View File

@ -171,8 +171,11 @@ let tests =
let! settings = parseWithFallback (fun _ -> Ok expectedDir) [||]
Expect.equal settings.CbLauncherDir expectedDir ""
}
testProperty "Unknown arg doesn't change any values" <|
fun (args:string[]) ->
// /* args are considered whitelist args and not unknown
let args = args |> Array.filter (fun arg -> arg = null || (arg.StartsWith('/') && arg.Length < 2))
let settings = parse args
settings.Wait()
settings.Result = Settings.defaults