Skip to content

Commit

Permalink
Fixing an issue where Unreal Assets were causing a duplicate key exce…
Browse files Browse the repository at this point in the history
…ption since the dictionary works off of the apps name. For whatever reason unreal assets have two entries with the same appname
  • Loading branch information
tpill90 committed Aug 22, 2024
1 parent 91d11cf commit 8438672
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 131 deletions.
28 changes: 27 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#TODO move this to lancache common
root = true

[*]
end_of_line = lf

[*.js]
indent_style = tab
tab_size = 4
curly_bracket_next_line = true

[*.py]
indent_style = tab
tab_size = 4

# .NET formatting rules. See https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/formatting-rules
# Resharper Rules. See https://www.jetbrains.com/help/resharper/EditorConfig_Index.html
[*.cs]
Expand All @@ -23,12 +33,16 @@ space_between_attribute_sections = false

# CA1002: Do not expose generic lists
dotnet_diagnostic.CA1002.severity = none
# CA1008: Enums should have zero value
# - The enums in this case are modeling Steam enums, so I have to match them exactly.
dotnet_diagnostic.CA1008.severity = none
# CA1014: Mark assemblies with CLSCompliant
dotnet_diagnostic.CA1014.severity = none
# CA1031: Modify (method) to catch a more specific allowed exception type, or rethrow the exception
dotnet_diagnostic.CA1031.severity = none
# CA1034: Nested types should not be visible
dotnet_diagnostic.CA1034.severity = none

# CA1051: Do not declare visible instance fields
dotnet_diagnostic.CA1051.severity = none
# CA1054: URI parameters should not be strings
Expand All @@ -39,6 +53,7 @@ dotnet_diagnostic.CA1056.severity = none
dotnet_diagnostic.CA1062.severity = none
# CA1305: The behavior of 'int.Parse(string)' could vary based on the current user's locale settings.
dotnet_diagnostic.CA1305.severity = none
# CA1307: Specify StringComparison for clarity
dotnet_diagnostic.CA1307.severity = none
dotnet_diagnostic.CA1310.severity = none
# CA1815: Override equals and operator equals on value types
Expand All @@ -64,11 +79,22 @@ dotnet_diagnostic.cs1998.severity = error
# CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
dotnet_diagnostic.cs4014.severity = error

# IDE0028: Simplify collection initialization
# - Not a fan of the syntax, feels a little bit too terse
dotnet_style_prefer_collection_expression = false

# IDE0090 : 'new' expression can be simplified
csharp_style_implicit_object_creation_when_type_is_apparent = false

# IDE0290: Use primary constructor
# - I'm a fan of this syntax in Typescript, but it still feels a bit too jarring seeing it C# now
csharp_style_prefer_primary_constructors = false


# Banned APIs will be bumped up to an error instead of warning
dotnet_diagnostic.rs0030.severity = error

# The method does not need to use async/await - Introduces annoying 'return' statements for async methods, that hurt code readability.
dotnet_diagnostic.AsyncFixer01.severity = none
dotnet_diagnostic.AsyncFixer01.severity = none


122 changes: 6 additions & 116 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,123 +6,13 @@ on:
tags:
- '*'

env:
# Used to parameterize these build scripts between LancachePrefill projects
PROJECT_NAME: EpicPrefill

permissions:
contents: write # Required to create a release

jobs:
# Needed in order to prevent softprops/action-gh-release from creating duplicate releases for each publish target.
# The duplicate releases happen because of a race condition, where each parallel publish job thinks the release has not yet been created.
create-placeholder-release:
runs-on: ubuntu-latest
container: mcr.microsoft.com/dotnet/sdk:8.0
steps:
- uses: actions/checkout@v3
with:
submodules: true
# Gets the executable version that will be used later in the uploaded zip file name
- name: Set Version
id: vars
run: |
version=$(grep -Po '(?<=<VersionPrefix>)(.*?)(?=</VersionPrefix>)' $PROJECT_NAME/$PROJECT_NAME.csproj);
echo "version=$version" >> $GITHUB_OUTPUT;
- name: Create Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
name: "v${{ steps.vars.outputs.version }}"
files: "**/*.zip"
generate_release_notes: true

dotnet-publish:
runs-on: ubuntu-latest
container: mcr.microsoft.com/dotnet/sdk:8.0
needs: create-placeholder-release
strategy:
matrix:
runtime: ['win-x64', 'linux-x64', 'linux-arm64', 'osx-x64']
fail-fast: false
steps:
- uses: actions/checkout@v3
with:
submodules: true
# Gets the executable version that will be used later in the uploaded zip file name
- name: Set Version
id: vars
run: |
version=$(grep -Po '(?<=<VersionPrefix>)(.*?)(?=</VersionPrefix>)' $PROJECT_NAME/$PROJECT_NAME.csproj);
echo "version=$version" >> $GITHUB_OUTPUT;
- run: apt-get update
- run: apt-get install zip -y
- name: Publish
run: >
version=${{ steps.vars.outputs.version }}
dotnet publish $PROJECT_NAME/$PROJECT_NAME.csproj \
--nologo \
-o "publish/$PROJECT_NAME-$version-${{ matrix.runtime }}" \
-c Release \
--runtime "${{ matrix.runtime }}";
cd publish;
zip -r $PROJECT_NAME-$version-${{ matrix.runtime }}.zip $PROJECT_NAME-$version-${{ matrix.runtime }};
cp $PROJECT_NAME-$version-${{ matrix.runtime }}.zip ../;
rm $PROJECT_NAME-$version-${{ matrix.runtime }}.zip;
cd ..;
- name: Upload
uses: actions/upload-artifact@v3
with:
name: EpicPrefill-${{ steps.vars.outputs.version }}-${{ matrix.runtime }}
path: publish/
if-no-files-found: error
- name: Create Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
name: "v${{ steps.vars.outputs.version }}"
files: "**/*.zip"
generate_release_notes: true
docker-publish-x64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 8.0.x
- name: Publish
run: >
dotnet publish $PROJECT_NAME/$PROJECT_NAME.csproj \
--nologo \
-o publish \
-c Release \
--runtime "linux-x64" \
--self-contained true \
/p:PublishSingleFile=true \
/p:PublishReadyToRun=true \
/p:PublishTrimmed=true
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: tpill90/epic-lancache-prefill
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
release-publish:
uses: tpill90/lancache-prefill-common/.github/workflows/release-publish-template.yml@main
secrets: inherit
with:
PROJECT_NAME: EpicPrefill
DOCKERHUB_NAME: epic-lancache-prefill
2 changes: 2 additions & 0 deletions EpicPrefill.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeObjectCreationWhenTypeEvident/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassCanBeSealed_002EGlobal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassCanBeSealed_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToPrimaryConstructor/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForeachStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexedValue">WARNING</s:String>
Expand All @@ -13,6 +14,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MemberCanBePrivate_002ELocal/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterTypeCanBeEnumerable_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterTypeCanBeEnumerable_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithPrimaryConstructorParameter/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FDeconstructionDeclarations/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
Expand Down
8 changes: 1 addition & 7 deletions EpicPrefill/EpicGamesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,10 @@ private async Task DownloadSingleAppAsync(GameAsset app)
}
}

//TODO rename to something like GetAvailableGames?
//TODO should this just be merged with GetOwnedAppsAsync?
public async Task<List<GameAsset>> GetAllAvailableAppsAsync()
{
var ownedApps = await _epicApi.GetOwnedAppsAsync();

// Unreal Engine needs to be filtered out as adding even a single version of it spams select-apps with a huge number of entries
return ownedApps.Where(e => !e.Title.Contains("Unreal Engine") && !e.Title.Contains("Quixel Bridge"))
.OrderBy(e => e.Title, StringComparer.OrdinalIgnoreCase)
.ToList();
return await _epicApi.GetOwnedAppsAsync();
}

#region Select Apps
Expand Down
22 changes: 16 additions & 6 deletions EpicPrefill/Handlers/EpicGamesApi.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace EpicPrefill.Handlers
{
// TODO document
public class EpicGamesApi
public sealed class EpicGamesApi
{
private readonly IAnsiConsole _ansiConsole;
private readonly HttpClientFactory _httpClientFactory;
Expand All @@ -19,6 +19,7 @@ public EpicGamesApi(IAnsiConsole ansiConsole, HttpClientFactory httpClientFactor
}

//TODO comment
//TODO this method has a few too many things going on here between the http request then the metadata population
internal async Task<List<GameAsset>> GetOwnedAppsAsync()
{
//TODO this should probably be a status spinner
Expand All @@ -38,14 +39,21 @@ internal async Task<List<GameAsset>> GetOwnedAppsAsync()
using var responseContent = await response.Content.ReadAsStreamAsync();
var ownedApps = await JsonSerializer.DeserializeAsync(responseContent, SerializationContext.Default.ListGameAsset);

var appMetadata = await LoadAppMetadataAsync(ownedApps);
foreach (var app in ownedApps)
// Removing anything related to unreal engine. We're only interested in actual games
var filteredApps = ownedApps.Where(e => e.Namespace != "ue")
// This namespace is related to unreal assets
.Where(e => e.Namespace != "89efe5924d3d467c839449ab6ab52e7f")
.ToList();

var appMetadata = await LoadAppMetadataAsync(filteredApps);
//TODO this part should probably be inside of the load app metadata part
foreach (var app in filteredApps)
{
app.Title = appMetadata[app.AppId].title;
}

_ansiConsole.LogMarkupLine($"Retrieved {Magenta(ownedApps.Count)} owned apps", timer);
return ownedApps;
_ansiConsole.LogMarkupLine($"Retrieved {Magenta(filteredApps.Count)} owned apps", timer);
return filteredApps.OrderBy(e => e.Title, StringComparer.OrdinalIgnoreCase).ToList();
}

//TODO comment
Expand All @@ -61,7 +69,9 @@ private async Task<Dictionary<string, AppMetadataResponse>> LoadAppMetadataAsync
}

// Determine which apps don't already have their metadata loaded
var appsMissingMetadata = apps.Where(e => !metadataDictionary.ContainsKey(e.AppId)).ToList();
List<GameAsset> appsMissingMetadata = apps.Where(e => !metadataDictionary.ContainsKey(e.AppId))
.OrderBy(e => e.AppId)
.ToList();

// If everything is cached, return
if (!appsMissingMetadata.Any())
Expand Down
6 changes: 5 additions & 1 deletion EpicPrefill/Models/ApiResponses/GameAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
//TODO document
//TODO rename
public class GameAsset
public sealed class GameAsset
{
[JsonPropertyName("appName")]
public string AppId { get; set; }
Expand All @@ -21,6 +21,10 @@ public class GameAsset

public override string ToString()
{
if (Title == null)
{
return $"{AppId} - {Namespace} - {CatalogItemId}";
}
return Title;
}
}
Expand Down
4 changes: 4 additions & 0 deletions _publish-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Write-Color "Current version: ", $currentVersion -Color White, Yellow

# Getting new version to use
$newVersion = Read-Host "Enter new version, with no leading 'v'. Ex. '1.2.3'"
if($newVersion.Contains("v"))
{
Write-Color $newVersion, " is not a valid version since it has a leading 'v'." -Color Yellow, Red
}

# Updating csproj version
$currentContent = Get-Content -Path .\EpicPrefill\EpicPrefill.csproj -Raw
Expand Down

0 comments on commit 8438672

Please sign in to comment.