From 5bc62e9862f0bad3cbb580986cec6b388231f77f Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 8 Nov 2023 19:23:51 -0500 Subject: [PATCH 1/7] Allow existing importer to update assets, track size in bytes --- .../Database/GameDatabaseContext.Assets.cs | 10 +-- .../Database/GameDatabaseProvider.cs | 2 +- Refresh.GameServer/Importing/AssetImporter.cs | 63 +++++++++---------- Refresh.GameServer/RefreshGameServer.cs | 9 +-- Refresh.GameServer/Types/Assets/GameAsset.cs | 16 ++--- 5 files changed, 41 insertions(+), 59 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseContext.Assets.cs b/Refresh.GameServer/Database/GameDatabaseContext.Assets.cs index 04bf538d..da85ff91 100644 --- a/Refresh.GameServer/Database/GameDatabaseContext.Assets.cs +++ b/Refresh.GameServer/Database/GameDatabaseContext.Assets.cs @@ -18,15 +18,9 @@ public void AddAssetToDatabase(GameAsset asset) => this._realm.Add(asset); }); - public void AddAssetsToDatabase(IEnumerable assets) => + public void AddOrUpdateAssetsInDatabase(IEnumerable assets) => this._realm.Write(() => { - this._realm.Add(assets); - }); - - public void DeleteAllAssetMetadata() => - this._realm.Write(() => - { - this._realm.RemoveAll(); + this._realm.Add(assets, true); }); } \ No newline at end of file diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index 4ba3d08f..6dd84328 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -32,7 +32,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 97; + protected override ulong SchemaVersion => 98; protected override string Filename => "refreshGameServer.realm"; diff --git a/Refresh.GameServer/Importing/AssetImporter.cs b/Refresh.GameServer/Importing/AssetImporter.cs index 075dc367..ca5fefbd 100644 --- a/Refresh.GameServer/Importing/AssetImporter.cs +++ b/Refresh.GameServer/Importing/AssetImporter.cs @@ -19,54 +19,48 @@ public AssetImporter(Logger? logger = null, IDateTimeProvider? timeProvider = nu this._timeProvider = timeProvider; } - public void ImportFromDataStoreCli(GameDatabaseContext context, IDataStore dataStore) - { - Console.WriteLine("This tool will scan and manually import existing assets into Refresh's database."); - Console.WriteLine("This will wipe all existing asset metadata in the database. Are you sure you want to follow through with this operation?"); - Console.WriteLine(); - Console.Write("Are you sure? [y/N] "); - - char key = char.ToLower(Console.ReadKey().KeyChar); - Console.WriteLine(); - if(key != 'y') - { - if(key != 'n') Console.WriteLine("Unsure what you mean, assuming no."); - Environment.Exit(0); - return; - } - - this.ImportFromDataStore(context, dataStore); - } - - public void ImportFromDataStore(GameDatabaseContext context, IDataStore dataStore) + public void ImportFromDataStore(GameDatabaseContext database, IDataStore dataStore) { + int updatedAssets = 0; + int newAssets = 0; this.Stopwatch.Start(); - context.DeleteAllAssetMetadata(); - this.Info("Deleted all asset metadata"); - - List assetHashes = dataStore.GetKeysFromStore() - .Where(key => !key.Contains('/')) - .ToList(); + IEnumerable assetHashes = dataStore.GetKeysFromStore() + .Where(key => !key.Contains('/')); List assets = new(); foreach (string hash in assetHashes) { byte[] data = dataStore.GetDataFromStore(hash); - GameAsset? asset = this.ReadAndVerifyAsset(hash, data, null); - if (asset == null) continue; + GameAsset? newAsset = this.ReadAndVerifyAsset(hash, data, null); + if (newAsset == null) continue; + + GameAsset? oldAsset = database.GetAssetFromHash(hash); - assets.Add(asset); - this.Info($"Processed {asset.AssetType} asset {hash} ({AssetSafetyLevelExtensions.FromAssetType(asset.AssetType)})"); + if (oldAsset != null) + { + newAsset.OriginalUploader = oldAsset.OriginalUploader; + newAsset.UploadDate = oldAsset.UploadDate; + updatedAssets++; + } + else + { + newAssets++; + } + + assets.Add(newAsset); + this.Info($"Processed {newAsset.AssetType} asset {hash} ({AssetSafetyLevelExtensions.FromAssetType(newAsset.AssetType)})"); } - context.AddAssetsToDatabase(assets); + database.AddOrUpdateAssetsInDatabase(assets); + + int hashCount = assetHashes.Count(); - this.Info($"Successfully imported {assets.Count}/{assetHashes.Count} assets into database"); - if (assets.Count < assetHashes.Count) + this.Info($"Successfully imported {assets.Count}/{hashCount} assets ({newAssets} new, {updatedAssets} updated) into database"); + if (assets.Count < hashCount) { - this.Warn($"{assetHashes.Count - assets.Count} assets were not imported"); + this.Warn($"{hashCount - assets.Count} assets were not imported"); } } @@ -90,6 +84,7 @@ public void ImportFromDataStore(GameDatabaseContext context, IDataStore dataStor AssetHash = hash, AssetType = this.DetermineAssetType(data, platform), IsPSP = platform == TokenPlatform.PSP, + SizeInBytes = data.Length, }; return asset; diff --git a/Refresh.GameServer/RefreshGameServer.cs b/Refresh.GameServer/RefreshGameServer.cs index b5e1d9f1..820adc71 100644 --- a/Refresh.GameServer/RefreshGameServer.cs +++ b/Refresh.GameServer/RefreshGameServer.cs @@ -216,14 +216,7 @@ public void ImportAssets(bool force = false) using GameDatabaseContext context = this.InitializeDatabase(); AssetImporter importer = new(); - if (!force) - { - importer.ImportFromDataStoreCli(context, this._dataStore); - } - else - { - importer.ImportFromDataStore(context, this._dataStore); - } + importer.ImportFromDataStore(context, this._dataStore); } public void ImportImages() diff --git a/Refresh.GameServer/Types/Assets/GameAsset.cs b/Refresh.GameServer/Types/Assets/GameAsset.cs index bcb1daaf..27a32065 100644 --- a/Refresh.GameServer/Types/Assets/GameAsset.cs +++ b/Refresh.GameServer/Types/Assets/GameAsset.cs @@ -3,14 +3,14 @@ namespace Refresh.GameServer.Types.Assets; -[JsonObject(MemberSerialization.OptIn)] public partial class GameAsset : IRealmObject { - [PrimaryKey] [JsonProperty] public string AssetHash { get; set; } = string.Empty; - [JsonProperty] public GameUser? OriginalUploader { get; set; } - [JsonProperty] public DateTimeOffset UploadDate { get; set; } - [JsonProperty] public bool IsPSP { get; set; } - [JsonProperty] [Ignored] public GameAssetType AssetType + [PrimaryKey] public string AssetHash { get; set; } = string.Empty; + public GameUser? OriginalUploader { get; set; } + public DateTimeOffset UploadDate { get; set; } + public bool IsPSP { get; set; } + public int SizeInBytes { get; set; } + [Ignored] public GameAssetType AssetType { get => (GameAssetType)this._AssetType; set => this._AssetType = (int)value; @@ -19,7 +19,7 @@ [JsonProperty] [Ignored] public GameAssetType AssetType // ReSharper disable once InconsistentNaming internal int _AssetType { get; set; } - [JsonProperty] public IList Dependencies { get; } = null!; + public IList Dependencies { get; } = null!; - [JsonProperty] [Ignored] public AssetSafetyLevel SafetyLevel => AssetSafetyLevelExtensions.FromAssetType(this.AssetType); + [Ignored] public AssetSafetyLevel SafetyLevel => AssetSafetyLevelExtensions.FromAssetType(this.AssetType); } \ No newline at end of file From 59ce85e2356f388e23a489c33b424df7cb20f29a Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 8 Nov 2023 19:58:08 -0500 Subject: [PATCH 2/7] Track dependencies --- .../Database/GameDatabaseProvider.cs | 2 +- .../Response/ApiGameAssetResponse.cs | 2 + Refresh.GameServer/Importing/AssetImporter.cs | 67 +++++++++++++++++-- Refresh.GameServer/Importing/Importer.cs | 5 ++ Refresh.GameServer/Types/Assets/GameAsset.cs | 2 +- 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/Refresh.GameServer/Database/GameDatabaseProvider.cs b/Refresh.GameServer/Database/GameDatabaseProvider.cs index 6dd84328..1e48e950 100644 --- a/Refresh.GameServer/Database/GameDatabaseProvider.cs +++ b/Refresh.GameServer/Database/GameDatabaseProvider.cs @@ -32,7 +32,7 @@ protected GameDatabaseProvider(IDateTimeProvider time) this._time = time; } - protected override ulong SchemaVersion => 98; + protected override ulong SchemaVersion => 99; protected override string Filename => "refreshGameServer.realm"; diff --git a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs index 078a214f..fe4e980f 100644 --- a/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs +++ b/Refresh.GameServer/Endpoints/ApiV3/DataTypes/Response/ApiGameAssetResponse.cs @@ -9,6 +9,7 @@ public class ApiGameAssetResponse : IApiResponse, IDataConvertableFrom Dependencies { get; set; } public static ApiGameAssetResponse? FromOld(GameAsset? old) { @@ -20,6 +21,7 @@ public class ApiGameAssetResponse : IApiResponse, IDataConvertableFrom dependencies = new(); + if (AssetTypeHasDependencyTree(asset.AssetType, data)) + { + // Parse dependency table + MemoryStream ms = new(data); + BEBinaryReader reader = new(ms); + + ms.Seek(8, SeekOrigin.Begin); + uint dependencyTableOffset = reader.ReadUInt32(); + + ms.Seek(dependencyTableOffset, SeekOrigin.Begin); + uint dependencyCount = reader.ReadUInt32(); + + this.Debug($"Dependency count offset: {dependencyTableOffset}, count: {dependencyCount}"); + for (int i = 0; i < dependencyCount; i++) + { + byte flags = reader.ReadByte(); + if ((flags & 0x1) != 0) // UGC/SHA1 + { + byte[] hashBytes = reader.ReadBytes(20); + dependencies.Add(GetHashOfShaBytes(hashBytes)); + } + else if ((flags & 0x2) != 0) reader.ReadUInt32(); // Skip GUID + + reader.ReadUInt32(); + } + } + + foreach (string dependency in dependencies) + { + asset.Dependencies.Add(dependency); + } + return asset; } + + [Pure] + private static bool AssetTypeHasDependencyTree(GameAssetType type, byte[] data) + { + if (type is GameAssetType.Jpeg + or GameAssetType.Png + or GameAssetType.Tga + or GameAssetType.Texture + or GameAssetType.GameDataTexture + or GameAssetType.Unknown) + { + return false; + } + + #if DEBUG + char typeChar = (char)data[3]; + if (typeChar != 'b') throw new Exception($"Asset type {type} is not binary (char was '{typeChar}')"); + #endif + + return true; + } } \ No newline at end of file diff --git a/Refresh.GameServer/Importing/Importer.cs b/Refresh.GameServer/Importing/Importer.cs index e325faf4..ad8ee8ba 100644 --- a/Refresh.GameServer/Importing/Importer.cs +++ b/Refresh.GameServer/Importing/Importer.cs @@ -36,6 +36,11 @@ protected void Warn(string message) { this._logger.LogWarning(BunkumCategory.UserContent, $"[{this.Stopwatch.ElapsedMilliseconds}ms] {message}"); } + + protected void Debug(string message) + { + this._logger.LogDebug(BunkumCategory.UserContent, $"[{this.Stopwatch.ElapsedMilliseconds}ms] {message}"); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool MatchesMagic(ReadOnlySpan data, ReadOnlySpan magic) diff --git a/Refresh.GameServer/Types/Assets/GameAsset.cs b/Refresh.GameServer/Types/Assets/GameAsset.cs index 27a32065..29900678 100644 --- a/Refresh.GameServer/Types/Assets/GameAsset.cs +++ b/Refresh.GameServer/Types/Assets/GameAsset.cs @@ -19,7 +19,7 @@ [Ignored] public GameAssetType AssetType // ReSharper disable once InconsistentNaming internal int _AssetType { get; set; } - public IList Dependencies { get; } = null!; + public IList Dependencies { get; } = null!; [Ignored] public AssetSafetyLevel SafetyLevel => AssetSafetyLevelExtensions.FromAssetType(this.AssetType); } \ No newline at end of file From 11b4633be08af9d708a397db2c2f5b0d01361061 Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 8 Nov 2023 20:05:41 -0500 Subject: [PATCH 3/7] Wrap dependency tree reading around try/catch --- Refresh.GameServer/Importing/AssetImporter.cs | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/Refresh.GameServer/Importing/AssetImporter.cs b/Refresh.GameServer/Importing/AssetImporter.cs index 67293603..4335f3ae 100644 --- a/Refresh.GameServer/Importing/AssetImporter.cs +++ b/Refresh.GameServer/Importing/AssetImporter.cs @@ -91,38 +91,21 @@ private static string GetHashOfShaBytes(byte[] data) IsPSP = platform == TokenPlatform.PSP, SizeInBytes = data.Length, }; - - List dependencies = new(); + if (AssetTypeHasDependencyTree(asset.AssetType, data)) { - // Parse dependency table - MemoryStream ms = new(data); - BEBinaryReader reader = new(ms); - - ms.Seek(8, SeekOrigin.Begin); - uint dependencyTableOffset = reader.ReadUInt32(); - - ms.Seek(dependencyTableOffset, SeekOrigin.Begin); - uint dependencyCount = reader.ReadUInt32(); - - this.Debug($"Dependency count offset: {dependencyTableOffset}, count: {dependencyCount}"); - for (int i = 0; i < dependencyCount; i++) + try { - byte flags = reader.ReadByte(); - if ((flags & 0x1) != 0) // UGC/SHA1 + List dependencies = this.ParseDependencyTree(data); + foreach (string dependency in dependencies) { - byte[] hashBytes = reader.ReadBytes(20); - dependencies.Add(GetHashOfShaBytes(hashBytes)); + asset.Dependencies.Add(dependency); } - else if ((flags & 0x2) != 0) reader.ReadUInt32(); // Skip GUID - - reader.ReadUInt32(); } - } - - foreach (string dependency in dependencies) - { - asset.Dependencies.Add(dependency); + catch (Exception e) + { + this.Warn($"Could not parse dependency tree for {hash}: {e}"); + } } return asset; @@ -148,4 +131,35 @@ or GameAssetType.GameDataTexture return true; } + + private List ParseDependencyTree(byte[] data) + { + List dependencies = new(); + + // Parse dependency table + MemoryStream ms = new(data); + BEBinaryReader reader = new(ms); + + ms.Seek(8, SeekOrigin.Begin); + uint dependencyTableOffset = reader.ReadUInt32(); + + ms.Seek(dependencyTableOffset, SeekOrigin.Begin); + uint dependencyCount = reader.ReadUInt32(); + + this.Debug($"Dependency count offset: {dependencyTableOffset}, count: {dependencyCount}"); + for (int i = 0; i < dependencyCount; i++) + { + byte flags = reader.ReadByte(); + if ((flags & 0x1) != 0) // UGC/SHA1 + { + byte[] hashBytes = reader.ReadBytes(20); + dependencies.Add(GetHashOfShaBytes(hashBytes)); + } + else if ((flags & 0x2) != 0) reader.ReadUInt32(); // Skip GUID + + reader.ReadUInt32(); + } + + return dependencies; + } } \ No newline at end of file From 1d2d2792dca383636871e4a766bf2900a1cb0d17 Mon Sep 17 00:00:00 2001 From: jvyden Date: Wed, 8 Nov 2023 20:22:25 -0500 Subject: [PATCH 4/7] Remove 'destructive action' warning in import_assets help text --- Refresh.GameServer/CommandLineManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refresh.GameServer/CommandLineManager.cs b/Refresh.GameServer/CommandLineManager.cs index 00b0cd52..963be16a 100644 --- a/Refresh.GameServer/CommandLineManager.cs +++ b/Refresh.GameServer/CommandLineManager.cs @@ -18,7 +18,7 @@ internal CommandLineManager(RefreshGameServer server) private class Options { [Option('i', "import_assets", Required = false, - HelpText = "Re-import all assets from the datastore into the database. This is a destructive action, only use when upgrading to <=v1.5.0")] + HelpText = "Re-import all assets from the datastore into the database.")] public bool ImportAssets { get; set; } [Option('I', "import_images", Required = false, HelpText = "Convert all images in the database to .PNGs. Otherwise, images will be converted as they are used")] From 2e9c67a967f9d3f2d08a3b28b66866c52521da10 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 9 Nov 2023 17:04:38 -0500 Subject: [PATCH 5/7] Optimize bytes to hex string operation --- Refresh.GameServer/Importing/AssetImporter.cs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Refresh.GameServer/Importing/AssetImporter.cs b/Refresh.GameServer/Importing/AssetImporter.cs index 4335f3ae..fcafc5fc 100644 --- a/Refresh.GameServer/Importing/AssetImporter.cs +++ b/Refresh.GameServer/Importing/AssetImporter.cs @@ -63,18 +63,31 @@ public void ImportFromDataStore(GameDatabaseContext database, IDataStore dataSto this.Warn($"{hashCount - assets.Count} assets were not imported"); } } - - private static string GetHashOfShaBytes(byte[] data) + + private static string BytesToHexString(ReadOnlySpan data) { - return BitConverter.ToString(data) - .Replace("-", "") - .ToLower(); + Span hexChars = stackalloc char[data.Length * 2]; + + for (int i = 0; i < data.Length; i++) + { + byte b = data[i]; + hexChars[i * 2] = GetHexChar(b >> 4); // High bits + hexChars[i * 2 + 1] = GetHexChar(b & 0x0F); // Low bits + } + + return new string(hexChars); + + static char GetHexChar(int value) + { + return (char)(value < 10 ? '0' + value : 'a' + value - 10); + } } + [Pure] public GameAsset? ReadAndVerifyAsset(string hash, byte[] data, TokenPlatform? platform) { - string checkedHash = GetHashOfShaBytes(SHA1.HashData(data)); + string checkedHash = BytesToHexString(SHA1.HashData(data)); if (checkedHash != hash) { @@ -147,13 +160,15 @@ private List ParseDependencyTree(byte[] data) uint dependencyCount = reader.ReadUInt32(); this.Debug($"Dependency count offset: {dependencyTableOffset}, count: {dependencyCount}"); + + Span hashBuffer = stackalloc byte[20]; for (int i = 0; i < dependencyCount; i++) { byte flags = reader.ReadByte(); if ((flags & 0x1) != 0) // UGC/SHA1 { - byte[] hashBytes = reader.ReadBytes(20); - dependencies.Add(GetHashOfShaBytes(hashBytes)); + ms.ReadExactly(hashBuffer); + dependencies.Add(BytesToHexString(hashBuffer)); } else if ((flags & 0x2) != 0) reader.ReadUInt32(); // Skip GUID From 9cfb461f45e9e7762b6ef54b08c93ff344c99103 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 9 Nov 2023 17:22:58 -0500 Subject: [PATCH 6/7] Fill in sizeOfResources for LBP Vita --- .../DataTypes/Response/GameLevelResponse.cs | 13 +++++++++++++ .../Endpoints/Game/Levels/LevelEndpoints.cs | 2 +- .../Extensions/GameAssetExtensions.cs | 19 +++++++++++++++++++ Refresh.GameServer/Types/Assets/GameAsset.cs | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Refresh.GameServer/Extensions/GameAssetExtensions.cs diff --git a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs index 62db3e88..da9c6caf 100644 --- a/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs +++ b/Refresh.GameServer/Endpoints/Game/DataTypes/Response/GameLevelResponse.cs @@ -2,8 +2,10 @@ using Refresh.GameServer.Authentication; using Refresh.GameServer.Database; using Refresh.GameServer.Endpoints.ApiV3.DataTypes; +using Refresh.GameServer.Extensions; using Refresh.GameServer.Services; using Refresh.GameServer.Types; +using Refresh.GameServer.Types.Assets; using Refresh.GameServer.Types.Levels; using Refresh.GameServer.Types.Levels.SkillRewards; using Refresh.GameServer.Types.Matching; @@ -66,6 +68,7 @@ public class GameLevelResponse : IDataConvertableFrom + { + if (asset != null) + this.SizeOfResourcesInBytes += asset.SizeInBytes; + }); + } } } \ No newline at end of file diff --git a/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs b/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs index e04ae875..ea2fa4b4 100644 --- a/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs @@ -69,7 +69,7 @@ public class LevelEndpoints : EndpointGroup return this.GetLevels(context, database, categories, matchService, overrideService, user, token, route); } - [GameEndpoint("s/user/{id}", ContentType.Xml)] + [GameEndpoint("s/user/{id}", ContentType.Xml), Authentication(false)] [NullStatusCode(NotFound)] [MinimumRole(GameUserRole.Restricted)] public GameLevelResponse? LevelById(RequestContext context, GameDatabaseContext database, MatchService matchService, GameUser user, int id) diff --git a/Refresh.GameServer/Extensions/GameAssetExtensions.cs b/Refresh.GameServer/Extensions/GameAssetExtensions.cs new file mode 100644 index 00000000..ac50c770 --- /dev/null +++ b/Refresh.GameServer/Extensions/GameAssetExtensions.cs @@ -0,0 +1,19 @@ +using Refresh.GameServer.Database; +using Refresh.GameServer.Types.Assets; + +namespace Refresh.GameServer.Extensions; + +public static class GameAssetExtensions +{ + public static void TraverseDependenciesRecursively(this GameAsset asset, GameDatabaseContext database, Action callback) + { + callback(asset.AssetHash, asset); + foreach (string internalAssetHash in asset.Dependencies) + { + GameAsset? internalAsset = database.GetAssetFromHash(internalAssetHash); + callback(internalAssetHash, internalAsset); + + internalAsset?.TraverseDependenciesRecursively(database, callback); + } + } +} \ No newline at end of file diff --git a/Refresh.GameServer/Types/Assets/GameAsset.cs b/Refresh.GameServer/Types/Assets/GameAsset.cs index 29900678..73a1e0c6 100644 --- a/Refresh.GameServer/Types/Assets/GameAsset.cs +++ b/Refresh.GameServer/Types/Assets/GameAsset.cs @@ -1,4 +1,5 @@ using Realms; +using Refresh.GameServer.Database; using Refresh.GameServer.Types.UserData; namespace Refresh.GameServer.Types.Assets; From 0bfce30c1ddf772cada71ce819a33621e20a141a Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 9 Nov 2023 17:25:48 -0500 Subject: [PATCH 7/7] Remove debug code --- Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs b/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs index ea2fa4b4..e04ae875 100644 --- a/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs +++ b/Refresh.GameServer/Endpoints/Game/Levels/LevelEndpoints.cs @@ -69,7 +69,7 @@ public class LevelEndpoints : EndpointGroup return this.GetLevels(context, database, categories, matchService, overrideService, user, token, route); } - [GameEndpoint("s/user/{id}", ContentType.Xml), Authentication(false)] + [GameEndpoint("s/user/{id}", ContentType.Xml)] [NullStatusCode(NotFound)] [MinimumRole(GameUserRole.Restricted)] public GameLevelResponse? LevelById(RequestContext context, GameDatabaseContext database, MatchService matchService, GameUser user, int id)