Skip to content

Commit

Permalink
Add support for adventures (#598)
Browse files Browse the repository at this point in the history
Pretty straight-forward. Includes a category that only shows adventures
levels as a side-bonus.

This also checks against the `GameAsset` type to see if the RootLevel is
the expected type, returning an error if so. This only happens if the
`GameAsset` is available; the only case where this would happen would be
when re-uploading an adventure/level from the dry archive.
  • Loading branch information
jvyden authored Jul 30, 2024
2 parents a9d6e33 + 2d4d24e commit db0ef76
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 18 deletions.
4 changes: 2 additions & 2 deletions Refresh.Common/Refresh.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bunkum" Version="4.7.1" />
<PackageReference Include="Bunkum.Protocols.Http" Version="4.7.1" />
<PackageReference Include="Bunkum" Version="4.8.1" />
<PackageReference Include="Bunkum.Protocols.Http" Version="4.8.1" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions Refresh.GameServer/Database/GameDatabaseContext.Levels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ public DatabaseList<GameLevel> GetCoolLevels(int count, int skip, GameUser? user
.Where(l => l.Score > 0)
.OrderByDescending(l => l.Score), skip, count);

[Pure]
public DatabaseList<GameLevel> GetAdventureLevels(int count, int skip, GameUser? user, LevelFilterSettings levelFilterSettings) =>
new(this.GetLevelsByGameVersion(levelFilterSettings.GameVersion)
.FilterByLevelFilterSettings(user, levelFilterSettings)
.Where(l => l.IsAdventure), skip, count);

[Pure]
public DatabaseList<GameLevel> SearchForLevels(int count, int skip, GameUser? user, LevelFilterSettings levelFilterSettings, string query)
{
Expand Down
8 changes: 7 additions & 1 deletion Refresh.GameServer/Database/GameDatabaseProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected GameDatabaseProvider(IDateTimeProvider time)
this._time = time;
}

protected override ulong SchemaVersion => 137;
protected override ulong SchemaVersion => 138;

protected override string Filename => "refreshGameServer.realm";

Expand Down Expand Up @@ -310,6 +310,12 @@ protected override void Migrate(Migration migration, ulong oldVersion)
else
newLevel.DateTeamPicked = null;
}

// In version 138 we added support for Adventures in LBP3. Set their status to false by default.
if (oldVersion < 138)
{
newLevel.IsAdventure = false;
}
}

// In version 22, tokens added expiry and types so just wipe them all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
public required bool IsReUpload { get; set; }
public required string? OriginalPublisher { get; set; }

public required bool IsAdventure { get; set; }
public required string Title { get; set; }
public required string IconHash { get; set; }
public required string Description { get; set; }
Expand Down Expand Up @@ -58,6 +59,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe

return new ApiGameLevelResponse
{
IsAdventure = level.IsAdventure,
Title = level.Title,
Publisher = ApiGameUserResponse.FromOld(level.Publisher, dataContext),
OriginalPublisher = level.OriginalPublisher,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Request;
public class GameLevelRequest
{
[XmlElement("id")] public required int LevelId { get; set; }

[XmlElement("isAdventurePlanet")] public required bool IsAdventure { get; set; }

[XmlElement("name")] public required string Title { get; set; }
[XmlElement("icon")] public required string IconHash { get; set; }
Expand Down Expand Up @@ -46,10 +48,13 @@ public class GameLevelRequest

[XmlElement("backgroundGUID")] public string? BackgroundGuid { get; set; }

[XmlArray("slots")] public GameLevelRequest[]? Slots { get; set; }

public GameLevel ToGameLevel(GameUser publisher) =>
new()
{
LevelId = this.LevelId,
IsAdventure = this.IsAdventure,
Title = this.Title,
IconHash = this.IconHash,
Description = this.Description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ namespace Refresh.GameServer.Endpoints.Game.DataTypes.Response;
public class GameLevelResponse : IDataConvertableFrom<GameLevelResponse, GameLevel>
{
[XmlElement("id")] public required int LevelId { get; set; }

[XmlElement("isAdventurePlanet")] public required bool IsAdventure { get; set; }

[XmlElement("name")] public required string Title { get; set; }
[XmlElement("icon")] public required string IconHash { get; set; }
Expand Down Expand Up @@ -96,6 +98,7 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext)
return new GameLevelResponse
{
LevelId = dataContext.Game == TokenGame.LittleBigPlanet3 ? LevelIdFromHash(hash) : int.MaxValue,
IsAdventure = false,
Title = $"Hashed Level - {hash}",
IconHash = "0",
GameVersion = 0,
Expand Down Expand Up @@ -143,6 +146,7 @@ public static GameLevelResponse FromHash(string hash, DataContext dataContext)
GameLevelResponse response = new()
{
LevelId = old.LevelId,
IsAdventure = old.IsAdventure,
Title = old.Title,
IconHash = old.IconHash,
Description = old.Description,
Expand Down
50 changes: 43 additions & 7 deletions Refresh.GameServer/Endpoints/Game/Levels/PublishEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Bunkum.Core;
using Bunkum.Core.Endpoints;
using Bunkum.Core.Endpoints.Debugging;
using Bunkum.Core.Responses;
using Bunkum.Listener.Protocol;
using Bunkum.Protocols.Http;
using Refresh.Common.Constants;
using Refresh.GameServer.Authentication;
using Refresh.GameServer.Database;
using Refresh.GameServer.Endpoints.Game.DataTypes.Request;
using Refresh.GameServer.Endpoints.Game.DataTypes.Response;
using Refresh.GameServer.Extensions;
using Refresh.GameServer.Services;
using Refresh.GameServer.Types.Assets;
using Refresh.GameServer.Types.Data;
using Refresh.GameServer.Types.Levels;
using Refresh.GameServer.Types.UserData;
Expand All @@ -33,14 +36,15 @@ private static bool VerifyLevel(GameLevelRequest body, DataContext dataContext)
body.Description = body.Description[..UgcConstantLimits.DescriptionLimit];

if (body.MaxPlayers is > 4 or < 0 || body.MinPlayers is > 4 or < 0)
{
return false;
}

//If the icon hash is a GUID hash, verify its a valid texture GUID
//If the icon hash is a GUID hash, verify that its a valid texture GUID
if (body.IconHash.StartsWith('g') && !dataContext.GuidChecker.IsTextureGuid(dataContext.Game, long.Parse(body.IconHash.AsSpan()[1..])))
return false;

if (body.IsAdventure && dataContext.Game != TokenGame.LittleBigPlanet3)
return false;

GameLevel? existingLevel = dataContext.Database.GetLevelByRootResource(body.RootResource);
// If there is an existing level with this root hash, and this isn't an update request, block the upload
if (existingLevel != null && body.LevelId != existingLevel.LevelId)
Expand All @@ -62,13 +66,28 @@ private static bool VerifyLevel(GameLevelRequest body, DataContext dataContext)
DataContext dataContext)
{
//If verifying the request fails, return null
if (!VerifyLevel(body, dataContext)) return null;
if (!VerifyLevel(body, dataContext))
{
context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify root level");
return null;
}

if (body.Slots != null)
{
foreach (GameLevelRequest innerLevel in body.Slots)
{
if (VerifyLevel(innerLevel, dataContext)) continue;

context.Logger.LogInfo(RefreshContext.Publishing, "Failed to verify inner level {0}", innerLevel.LevelId);
return null;
}
}

List<string> hashes =
[
.. body.XmlResources,
..body.XmlResources,
body.RootResource,
body.IconHash
body.IconHash,
];

//Remove all invalid or GUID assets
Expand Down Expand Up @@ -111,6 +130,23 @@ public Response PublishLevel(RequestContext context,
//Make sure the root resource exists in the data store
if (!dataContext.DataStore.ExistsInStore(rootResourcePath)) return NotFound;

GameAsset? asset = dataContext.Database.GetAssetFromHash(level.RootResource);
if (asset != null)
{
// ReSharper disable once ConvertIfStatementToSwitchStatement
if (level.IsAdventure && asset.AssetType != GameAssetType.AdventureCreateProfile)
{
dataContext.Database.AddPublishFailNotification("The uploaded adventure data was corrupted.", level, dataContext.User!);
return BadRequest;
}

if (!level.IsAdventure && asset.AssetType != GameAssetType.Level)
{
dataContext.Database.AddPublishFailNotification("The uploaded level data was corrupted.", level, dataContext.User!);
return BadRequest;
}
}

if (level.LevelId != default) // Republish requests contain the id of the old level
{
context.Logger.LogInfo(BunkumCategory.UserContent, "Republishing level id {0}", level.LevelId);
Expand All @@ -122,7 +158,7 @@ public Response PublishLevel(RequestContext context,
return new Response(GameLevelResponse.FromOld(newBody, dataContext)!, ContentType.Xml);
}

dataContext.Database.AddPublishFailNotification("You may not republish another user's level.", level, dataContext.User);
dataContext.Database.AddPublishFailNotification("You may not republish another user's level.", level, dataContext.User!);
return BadRequest;
}

Expand Down
12 changes: 6 additions & 6 deletions Refresh.GameServer/Refresh.GameServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'!='DebugLocalBunkum'">
<PackageReference Include="Bunkum" Version="4.7.1" />
<PackageReference Include="Bunkum.RealmDatabase" Version="4.7.1" />
<PackageReference Include="Bunkum.AutoDiscover" Version="4.7.1" />
<PackageReference Include="Bunkum.HealthChecks" Version="4.7.1" />
<PackageReference Include="Bunkum.HealthChecks.RealmDatabase" Version="4.7.1" />
<PackageReference Include="Bunkum.Protocols.Http" Version="4.7.1" />
<PackageReference Include="Bunkum" Version="4.8.1" />
<PackageReference Include="Bunkum.RealmDatabase" Version="4.8.1" />
<PackageReference Include="Bunkum.AutoDiscover" Version="4.8.1" />
<PackageReference Include="Bunkum.HealthChecks" Version="4.8.1" />
<PackageReference Include="Bunkum.HealthChecks.RealmDatabase" Version="4.8.1" />
<PackageReference Include="Bunkum.Protocols.Http" Version="4.8.1" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions Refresh.GameServer/RefreshContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public enum RefreshContext
PasswordReset,
LevelListOverride,
CoolLevels,
Publishing,
}
24 changes: 24 additions & 0 deletions Refresh.GameServer/Types/Levels/Categories/AdventureCategory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Bunkum.Core;
using Refresh.GameServer.Database;
using Refresh.GameServer.Endpoints.Game.Levels.FilterSettings;
using Refresh.GameServer.Types.Data;
using Refresh.GameServer.Types.UserData;

namespace Refresh.GameServer.Types.Levels.Categories;

public class AdventureCategory : LevelCategory
{
public AdventureCategory() : base("adventure", Array.Empty<string>(), false)
{
this.Name = "Adventures";
this.Description = "Storylines and other big projects by the community.";
this.FontAwesomeIcon = "book-bookmark";
this.IconHash = "g820625";
}

public override DatabaseList<GameLevel>? Fetch(RequestContext context, int skip, int count, DataContext dataContext,
LevelFilterSettings levelFilterSettings, GameUser? _)
{
return dataContext.Database.GetAdventureLevels(count, skip, dataContext.User, levelFilterSettings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class CategoryService : EndpointService
new ByTagCategory(),
new DeveloperLevelsCategory(),
new ContestCategory(),
new AdventureCategory(),
];

internal CategoryService(Logger logger) : base(logger)
Expand Down
2 changes: 2 additions & 0 deletions Refresh.GameServer/Types/Levels/GameLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace Refresh.GameServer.Types.Levels;
public partial class GameLevel : IRealmObject, ISequentialId
{
[PrimaryKey] public int LevelId { get; set; }

public bool IsAdventure { get; set; }

[Indexed(IndexType.FullText)]
public string Title { get; set; } = "";
Expand Down
2 changes: 2 additions & 0 deletions Refresh.GameServer/Types/Levels/GameMinimalLevelResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class GameMinimalLevelResponse : IDataConvertableFrom<GameMinimalLevelRes
//NOTE: THIS MUST BE AT THE TOP OF THE XML RESPONSE OR ELSE LBP PSP WILL CRASH
[XmlElement("id")] public required int LevelId { get; set; }

[XmlElement("isAdventurePlanet")] public required bool IsAdventure { get; set; }
[XmlElement("name")] public required string Title { get; set; } = string.Empty;
[XmlElement("icon")] public required string IconHash { get; set; } = string.Empty;
[XmlElement("game")] public required int GameVersion { get; set; }
Expand Down Expand Up @@ -76,6 +77,7 @@ public static GameMinimalLevelResponse FromHash(string hash, DataContext dataCon
return new GameMinimalLevelResponse
{
Title = level.Title,
IsAdventure = level.IsAdventure,
IconHash = dataContext.Database.GetAssetFromHash(level.IconHash)?.GetAsIcon(dataContext.Game, dataContext) ?? level.IconHash,
GameVersion = level.GameVersion,
RootResource = level.RootResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Refresh.GameServer.Types.Levels;

#nullable disable

[XmlRoot("slot")]
[XmlRoot("slot"), XmlType("slot")]
public class SerializedLevelResources
{
[XmlElement("resource")] public string[] Resources { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion Refresh.HttpsProxy/Refresh.HttpsProxy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bunkum.Protocols.Https" Version="4.7.1" />
<PackageReference Include="Bunkum.Protocols.Https" Version="4.8.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit db0ef76

Please sign in to comment.