Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/nuget/SixLabors.ImageSharp-3.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
jvyden committed Jan 1, 2024
2 parents c87c780 + 3dc09d0 commit 44f0521
Show file tree
Hide file tree
Showing 39 changed files with 54,968 additions and 134 deletions.
2 changes: 1 addition & 1 deletion Refresh.GameServer/Database/GameDatabaseContext.Levels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public DatabaseList<GameLevel> GetDeveloperLevels(int count, int skip, LevelFilt
[Pure]
public DatabaseList<GameLevel> GetBusiestLevels(int count, int skip, MatchService service, GameUser? user, LevelFilterSettings levelFilterSettings)
{
IOrderedEnumerable<IGrouping<GameLevel?,GameRoom>> rooms = service.Rooms
IOrderedEnumerable<IGrouping<GameLevel?,GameRoom>> rooms = service.RoomAccessor.GetAllRooms()
.Where(r => r.LevelType == RoomSlotType.Online && r.HostId.Id != null) // if playing online level and host exists on server
.GroupBy(r => this.GetLevelById(r.LevelId))
.OrderBy(r => r.Sum(room => room.PlayerIds.Count));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class ApiGameLevelResponse : IApiResponse, IDataConvertableFrom<ApiGameLe
Title = level.Title,
Publisher = ApiGameUserResponse.FromOld(level.Publisher),
LevelId = level.LevelId,
IconHash = level.IconHash,
IconHash = level.GameVersion == TokenGame.LittleBigPlanetPSP ? "psp/" + level.IconHash : level.IconHash,
Description = level.Description,
Location = ApiGameLocationResponse.FromGameLocation(level.Location)!,
PublishDate = DateTimeOffset.FromUnixTimeMilliseconds(level.PublishDate),
Expand Down
7 changes: 5 additions & 2 deletions Refresh.GameServer/Endpoints/ApiV3/InstanceApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Refresh.GameServer.Endpoints.ApiV3.ApiTypes;
using Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response;
using Refresh.GameServer.Services;
using Refresh.GameServer.Types.Matching;
using Refresh.GameServer.Types.RichPresence;

namespace Refresh.GameServer.Endpoints.ApiV3;
Expand All @@ -31,6 +32,8 @@ public ApiResponse<ApiStatisticsResponse> GetStatistics(RequestContext context,
{
requestStatistics = ApiRequestStatisticsResponse.FromOld(database.GetRequestStatistics())!;
}

RoomStatistics statistics = match.RoomAccessor.GetStatistics();

return new ApiStatisticsResponse
{
Expand All @@ -39,8 +42,8 @@ public ApiResponse<ApiStatisticsResponse> GetStatistics(RequestContext context,
ActiveUsers = database.GetActiveUserCount(),
TotalPhotos = database.GetTotalPhotoCount(),
TotalEvents = database.GetTotalEventCount(),
CurrentRoomCount = match.Rooms.Count(),
CurrentIngamePlayersCount = match.TotalPlayers,
CurrentRoomCount = statistics.RoomCount,
CurrentIngamePlayersCount = statistics.PlayerCount,
RequestStatistics = requestStatistics,
};
}
Expand Down
8 changes: 4 additions & 4 deletions Refresh.GameServer/Endpoints/ApiV3/MatchingApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public ApiResponse<ApiGameRoomResponse> GetRoomByUsername(RequestContext context
GameUser? user = database.GetUserByUsername(username);
if (user == null) return ApiNotFoundError.UserMissingError;

GameRoom? room = service.GetRoomByPlayer(user);
GameRoom? room = service.RoomAccessor.GetRoomByUser(user);
if(room == null) return ApiNotFoundError.Instance;

return ApiGameRoomResponse.FromOld(room);
Expand All @@ -42,7 +42,7 @@ public ApiResponse<ApiGameRoomResponse> GetRoomByUserUuid(RequestContext context
GameUser? user = database.GetUserByUuid(uuid);
if (user == null) return ApiNotFoundError.UserMissingError;

GameRoom? room = service.GetRoomByPlayer(user);
GameRoom? room = service.RoomAccessor.GetRoomByUser(user);
if(room == null) return ApiNotFoundError.Instance;

return ApiGameRoomResponse.FromOld(room);
Expand All @@ -58,7 +58,7 @@ public ApiResponse<ApiGameRoomResponse> GetRoomByUuid(RequestContext context, Ma
bool parsed = ObjectId.TryParse(uuid, out ObjectId objectId);
if (!parsed) return ApiValidationError.ObjectIdParseError;

GameRoom? room = service.Rooms.FirstOrDefault(r => r.RoomId == objectId);
GameRoom? room = service.RoomAccessor.GetRoomByUuid(objectId);
if(room == null) return ApiNotFoundError.Instance;

return ApiGameRoomResponse.FromOld(room);
Expand All @@ -69,6 +69,6 @@ public ApiResponse<ApiGameRoomResponse> GetRoomByUuid(RequestContext context, Ma
public ApiListResponse<ApiGameRoomResponse> GetRooms(RequestContext context, MatchService service)
{
(int skip, int count) = context.GetPageData(true);
return new DatabaseList<ApiGameRoomResponse>(ApiGameRoomResponse.FromOldList(service.Rooms), skip, count);
return new DatabaseList<ApiGameRoomResponse>(ApiGameRoomResponse.FromOldList(service.RoomAccessor.GetAllRooms()), skip, count);
}
}
9 changes: 4 additions & 5 deletions Refresh.GameServer/Endpoints/ApiV3/ResourceApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Refresh.GameServer.Endpoints.ApiV3.ApiTypes.Errors;
using Refresh.GameServer.Endpoints.ApiV3.DataTypes.Response;
using Refresh.GameServer.Importing;
using Refresh.GameServer.Time;
using Refresh.GameServer.Types.Assets;
using Refresh.GameServer.Types.UserData;
using Refresh.GameServer.Verification;
Expand Down Expand Up @@ -59,7 +58,7 @@ public Response DownloadPspGameAsset(RequestContext context, IDataStore dataStor
[DocError(typeof(ApiInternalError), ApiInternalError.CouldNotGetAssetErrorWhen)]
[DocError(typeof(ApiValidationError), ApiValidationError.HashMissingErrorWhen)]
public Response DownloadGameAssetAsImage(RequestContext context, IDataStore dataStore, GameDatabaseContext database,
[DocSummary("The SHA1 hash of the asset")] string hash)
[DocSummary("The SHA1 hash of the asset")] string hash, ImageImporter importer)
{
bool isPspAsset = hash.StartsWith("psp/");

Expand All @@ -73,8 +72,8 @@ public Response DownloadGameAssetAsImage(RequestContext context, IDataStore data
{
GameAsset? asset = database.GetAssetFromHash(realHash);
if (asset == null) return ApiInternalError.CouldNotGetAssetDatabaseError;
ImageImporter.ImportAsset(asset, dataStore);

importer.ImportAsset(asset, dataStore);
}

bool gotData = dataStore.TryGetDataFromStore("png/" + realHash, out byte[]? data);
Expand All @@ -90,7 +89,7 @@ public Response DownloadGameAssetAsImage(RequestContext context, IDataStore data
[DocError(typeof(ApiInternalError), ApiInternalError.CouldNotGetAssetErrorWhen)]
[DocError(typeof(ApiValidationError), ApiValidationError.HashMissingErrorWhen)]
public Response DownloadPspGameAssetAsImage(RequestContext context, IDataStore dataStore, GameDatabaseContext database,
[DocSummary("The SHA1 hash of the asset")] string hash) => this.DownloadGameAssetAsImage(context, dataStore, database, $"psp/{hash}");
[DocSummary("The SHA1 hash of the asset")] string hash, ImageImporter importer) => this.DownloadGameAssetAsImage(context, dataStore, database, $"psp/{hash}", importer);

[ApiV3Endpoint("assets/{hash}"), Authentication(false)]
[DocSummary("Gets information from the database about a particular hash. Includes user who uploaded, dependencies, timestamps, etc.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public LevelFilterSettings(RequestContext context, TokenGame game) : this(game)
"true" => MoveFilterType.True,
"false" => MoveFilterType.False,
"only" => MoveFilterType.Only,
"dontCare" => MoveFilterType.True,
_ => throw new ArgumentOutOfRangeException(),
};

Expand Down
21 changes: 15 additions & 6 deletions Refresh.GameServer/Endpoints/Game/Levels/PublishEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ public class PublishEndpoints : EndpointGroup
/// <param name="body">The level to verify</param>
/// <param name="user">The user that is attempting to upload</param>
/// <param name="logger">A logger instance</param>
/// <param name="guidChecker">The associated GuidCheckerService with the request</param>
/// <param name="game">The game the level is being submitted from</param>
/// <returns>Whether or not validation succeeded</returns>
private static bool VerifyLevel(GameLevelRequest body, GameUser user, Logger logger)
private static bool VerifyLevel(GameLevelRequest body, GameUser user, Logger logger, GuidCheckerService guidChecker, TokenGame game)
{
if (body.Title.Length > 256)
{
Expand All @@ -43,15 +45,22 @@ private static bool VerifyLevel(GameLevelRequest body, GameUser user, Logger log
return false;
}

//If the icon hash is a GUID hash, verify its a valid texture GUID
if (body.IconHash.StartsWith('g'))
{
if (!guidChecker.IsTextureGuid(game, long.Parse(body.IconHash.AsSpan()[1..])))
return false;
}

return true;
}

[GameEndpoint("startPublish", ContentType.Xml, HttpMethods.Post)]
[NullStatusCode(BadRequest)]
public SerializedLevelResources? StartPublish(RequestContext context, GameUser user, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore)
public SerializedLevelResources? StartPublish(RequestContext context, GameUser user, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore, GuidCheckerService guidChecker, Token token)
{
//If verifying the request fails, return null
if (!VerifyLevel(body, user, context.Logger)) return null;
if (!VerifyLevel(body, user, context.Logger, guidChecker, token.TokenGame)) return null;

List<string> hashes = new();
hashes.AddRange(body.XmlResources);
Expand All @@ -74,11 +83,11 @@ private static bool VerifyLevel(GameLevelRequest body, GameUser user, Logger log
}

[GameEndpoint("publish", ContentType.Xml, HttpMethods.Post)]
public Response PublishLevel(RequestContext context, GameUser user, Token token, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore)
public Response PublishLevel(RequestContext context, GameUser user, Token token, GameDatabaseContext database, GameLevelRequest body, CommandService command, IDataStore dataStore, GuidCheckerService guidChecker)
{
//If verifying the request fails, return null
if (!VerifyLevel(body, user, context.Logger)) return BadRequest;

if (!VerifyLevel(body, user, context.Logger, guidChecker, token.TokenGame)) return BadRequest;
GameLevel level = body.ToGameLevel(user);
level.GameVersion = token.TokenGame;

Expand Down
26 changes: 15 additions & 11 deletions Refresh.GameServer/Endpoints/Game/ModerationEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,24 @@ public string Filter(RequestContext context, CommandService commandService, stri
{
context.Logger.LogInfo(BunkumCategory.Filter, $"<{user}>: {body}");

try
//If the text starts with a `/`, its a command
if (body.StartsWith('/'))
{
CommandInvocation command = commandService.ParseCommand(body);

context.Logger.LogInfo(BunkumCategory.Commands, $"User used command '{command.Name.ToString()}' with args '{command.Arguments.ToString()}'");
try
{
CommandInvocation command = commandService.ParseCommand(body);

commandService.HandleCommand(command, database, user, token);
return "(Command)";
context.Logger.LogInfo(BunkumCategory.Commands, $"User used command '{command.Name.ToString()}' with args '{command.Arguments.ToString()}'");

commandService.HandleCommand(command, database, user, token);
return "(Command)";
}
catch(Exception ex)
{
context.Logger.LogWarning(BunkumCategory.Commands, $"Error running command {body}. ex {ex}");
//do nothing
}
}
catch(Exception ex)
{
context.Logger.LogWarning(BunkumCategory.Commands, $"Error running command {body}. ex {ex}");
//do nothing
}
}

return body;
Expand Down
4 changes: 2 additions & 2 deletions Refresh.GameServer/Endpoints/Game/PresenceEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ public class PresenceEndpoints : EndpointGroup
{
[GameEndpoint("playersInPodCount")]
[MinimumRole(GameUserRole.Restricted)]
public int TotalPlayersInPod(RequestContext context, MatchService match) => match.TotalPlayersInPod;
public int TotalPlayersInPod(RequestContext context, MatchService match) => match.RoomAccessor.GetStatistics().PlayersInPodCount;

[GameEndpoint("totalPlayerCount")]
[MinimumRole(GameUserRole.Restricted)]
public int TotalPlayers(RequestContext context, MatchService match) => match.TotalPlayers;
public int TotalPlayers(RequestContext context, MatchService match) => match.RoomAccessor.GetStatistics().PlayerCount;

[GameEndpoint("planetStats", HttpMethods.Get, ContentType.Xml)]
[MinimumRole(GameUserRole.Restricted)]
Expand Down
28 changes: 24 additions & 4 deletions Refresh.GameServer/Endpoints/Game/UserEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public SerializedUserList GetMultipleUsers(RequestContext context, GameDatabaseC

[GameEndpoint("updateUser", HttpMethods.Post, ContentType.Xml)]
[NullStatusCode(BadRequest)]
public string? UpdateUser(RequestContext context, GameDatabaseContext database, GameUser user, string body, IDataStore dataStore, Token token)
public string? UpdateUser(RequestContext context, GameDatabaseContext database, GameUser user, string body, IDataStore dataStore, Token token, GuidCheckerService guidChecker)
{
SerializedUpdateData? data = null;

Expand Down Expand Up @@ -96,10 +96,30 @@ public SerializedUserList GetMultipleUsers(RequestContext context, GameDatabaseC
return null;
}

if (data.IconHash != null && !data.IconHash.StartsWith("g") && !dataStore.ExistsInStore(data.IconHash))
if (data.IconHash != null)
{
database.AddErrorNotification("Profile update failed", "Your avatar failed to update because the asset was missing on the server.", user);
return null;
//If the icon is a GUID
if (data.IconHash.StartsWith('g'))
{
//Parse out the GUID
long guid = long.Parse(data.IconHash.AsSpan()[1..]);

//If its not a valid GUID, block the request
if(data.IconHash.StartsWith('g') && !guidChecker.IsTextureGuid(token.TokenGame, guid))
{
database.AddErrorNotification("Profile update failed", "Your avatar failed to update because the asset was an invalid GUID.", user);
return null;
}
}
else
{
//If the asset does not exist on the server, block the request
if (!dataStore.ExistsInStore(data.IconHash))
{
database.AddErrorNotification("Profile update failed", "Your avatar failed to update because the asset was missing on the server.", user);
return null;
}
}
}

if (data.PlanetsHash != null && !dataStore.ExistsInStore(data.PlanetsHash))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public RichPresenceConfiguration GetRichPresenceConfiguration(RequestContext con
GameUser? user = database.GetUserByLegacyId(id);
if (user == null) return null;

GameRoom? room = match.GetRoomByPlayer(user);
GameRoom? room = match.RoomAccessor.GetRoomByUser(user);
if (room == null) return null;

List<int> playerIds = new();
Expand Down
18 changes: 12 additions & 6 deletions Refresh.GameServer/Importing/AssetImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@ public void ImportFromDataStore(GameDatabaseContext database, IDataStore dataSto
int newAssets = 0;
this.Stopwatch.Start();

IEnumerable<string> assetHashes = dataStore.GetKeysFromStore()
.Where(key => !key.Contains('/'));
IEnumerable<string> assetHashes = dataStore.GetKeysFromStore();

List<GameAsset> assets = new();
foreach (string hash in assetHashes)
List<GameAsset> assets = new(assetHashes.Count());
foreach (string path in assetHashes)
{
byte[] data = dataStore.GetDataFromStore(hash);
bool isPsp = path.StartsWith("psp/");
//If the hash has a `/` and it doesnt start with `psp/`, then its an invalid asset
if (path.Contains('/') && !isPsp) continue;

string hash = isPsp ? path[4..] : path;

byte[] data = dataStore.GetDataFromStore(path);

GameAsset? newAsset = this.ReadAndVerifyAsset(hash, data, null);
GameAsset? newAsset = this.ReadAndVerifyAsset(hash, data, isPsp ? TokenPlatform.PSP : null);
if (newAsset == null) continue;

GameAsset? oldAsset = database.GetAssetFromHash(hash);
Expand Down Expand Up @@ -132,6 +137,7 @@ or GameAssetType.Png
or GameAssetType.Tga
or GameAssetType.Texture
or GameAssetType.GameDataTexture
or GameAssetType.Mip
or GameAssetType.Unknown)
{
return false;
Expand Down
7 changes: 7 additions & 0 deletions Refresh.GameServer/Importing/ImageImporter.Conversions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using ICSharpCode.SharpZipLib.Zip.Compression;
using Pfim;
using Refresh.GameServer.Importing.Gtf;
using Refresh.GameServer.Importing.Mip;
using SixLabors.ImageSharp.Formats;

namespace Refresh.GameServer.Importing;
Expand Down Expand Up @@ -33,6 +34,12 @@ private static void GtfToPng(Stream stream, Stream writeStream)
using Image image = new GtfDecoder().Decode(new DecoderOptions(), stream);
image.SaveAsPng(writeStream);
}

private static void MipToPng(Stream stream, Stream writeStream)
{
using Image image = new MipDecoder().Decode(new DecoderOptions(), stream);
image.SaveAsPng(writeStream);
}

private static readonly PfimConfig Config = new();

Expand Down
13 changes: 12 additions & 1 deletion Refresh.GameServer/Importing/ImageImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using NotEnoughLogs;
using Refresh.GameServer.Database;
using Refresh.GameServer.Extensions;
using Refresh.GameServer.Resources;
using Refresh.GameServer.Types.Assets;

namespace Refresh.GameServer.Importing;
Expand All @@ -22,6 +23,7 @@ public void ImportFromDataStore(GameDatabaseContext context, IDataStore dataStor
assets.AddRange(context.GetAssetsByType(GameAssetType.GameDataTexture));
assets.AddRange(context.GetAssetsByType(GameAssetType.Jpeg));
assets.AddRange(context.GetAssetsByType(GameAssetType.Png));
assets.AddRange(context.GetAssetsByType(GameAssetType.Mip));

this.Info("Acquired all other assets");

Expand Down Expand Up @@ -66,7 +68,7 @@ private void ThreadTask(ConcurrentQueue<GameAsset> assetQueue, IDataStore dataSt
this._runningCount--;
}

public static void ImportAsset(GameAsset asset, IDataStore dataStore)
public void ImportAsset(GameAsset asset, IDataStore dataStore)
{
using Stream stream = dataStore.GetStreamFromStore(asset.IsPSP ? "psp/" + asset.AssetHash : asset.AssetHash);
using Stream writeStream = dataStore.OpenWriteStream("png/" + asset.AssetHash);
Expand All @@ -76,6 +78,15 @@ public static void ImportAsset(GameAsset asset, IDataStore dataStore)
case GameAssetType.GameDataTexture:
GtfToPng(stream, writeStream);
break;
case GameAssetType.Mip: {
byte[] rawData = dataStore.GetDataFromStore(asset.IsPSP ? "psp/" + asset.AssetHash : asset.AssetHash);
byte[] data = ResourceHelper.PspDecrypt(rawData, this.PSPKey.Value);

using MemoryStream dataStream = new(data);

MipToPng(dataStream, writeStream);
break;
}
case GameAssetType.Texture:
TextureToPng(stream, writeStream);
break;
Expand Down
Loading

0 comments on commit 44f0521

Please sign in to comment.