mirror of
https://github.com/ppy/osu.git
synced 2024-11-15 02:57:51 +08:00
5fa586848d
- Actually shows scores rather than playlist aggregates (which are useful... in playlists, where there is more than one item) - Actually allows scores to be shown by clicking on them - Doesn't completely break down visually on smaller window sizes The general appearance is not as polished as the old one in details but I wanted something quick that we can get out by next weekend. Also includes the naive method of refetching scores once a new top 50 score is detected. I can add a stagger if required.
289 lines
12 KiB
C#
289 lines
12 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
#nullable disable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using Newtonsoft.Json;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Online.API;
|
|
using osu.Game.Online.API.Requests;
|
|
using osu.Game.Online.API.Requests.Responses;
|
|
using osu.Game.Online.Rooms;
|
|
using osu.Game.Rulesets;
|
|
using osu.Game.Rulesets.Scoring;
|
|
using osu.Game.Scoring;
|
|
using osu.Game.Screens.OnlinePlay.Components;
|
|
using osu.Game.Tests.Beatmaps;
|
|
|
|
namespace osu.Game.Tests.Visual.OnlinePlay
|
|
{
|
|
/// <summary>
|
|
/// Represents a handler which pretends to be a server, handling room retrieval and manipulation requests
|
|
/// and returning a roughly expected state, without the need for a server to be running.
|
|
/// </summary>
|
|
public class TestRoomRequestsHandler
|
|
{
|
|
public IReadOnlyList<Room> ServerSideRooms => serverSideRooms;
|
|
|
|
private readonly List<Room> serverSideRooms = new List<Room>();
|
|
|
|
private int currentRoomId = 1;
|
|
private int currentPlaylistItemId = 1;
|
|
private int currentScoreId = 1;
|
|
|
|
/// <summary>
|
|
/// Handles an API request, while also updating the local state to match
|
|
/// how the server would eventually respond and update an <see cref="RoomManager"/>.
|
|
/// </summary>
|
|
/// <param name="request">The API request to handle.</param>
|
|
/// <param name="localUser">The local user to store in responses where required.</param>
|
|
/// <param name="beatmapManager">The beatmap manager to attempt to retrieve beatmaps from, prior to returning dummy beatmaps.</param>
|
|
/// <returns>Whether the request was successfully handled.</returns>
|
|
public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager beatmapManager)
|
|
{
|
|
switch (request)
|
|
{
|
|
case CreateRoomRequest createRoomRequest:
|
|
var apiRoom = cloneRoom(createRoomRequest.Room);
|
|
|
|
// Passwords are explicitly not copied between rooms.
|
|
apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value);
|
|
apiRoom.Password.Value = createRoomRequest.Room.Password.Value;
|
|
|
|
AddServerSideRoom(apiRoom, localUser);
|
|
|
|
var responseRoom = new APICreatedRoom();
|
|
responseRoom.CopyFrom(createResponseRoom(apiRoom, false));
|
|
|
|
createRoomRequest.TriggerSuccess(responseRoom);
|
|
return true;
|
|
|
|
case JoinRoomRequest joinRoomRequest:
|
|
{
|
|
var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value);
|
|
|
|
if (joinRoomRequest.Password != room.Password.Value)
|
|
{
|
|
joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password."));
|
|
return true;
|
|
}
|
|
|
|
joinRoomRequest.TriggerSuccess();
|
|
return true;
|
|
}
|
|
|
|
case GetRoomLeaderboardRequest roomLeaderboardRequest:
|
|
roomLeaderboardRequest.TriggerSuccess(new APILeaderboard
|
|
{
|
|
Leaderboard = new List<APIUserScoreAggregate>
|
|
{
|
|
new APIUserScoreAggregate
|
|
{
|
|
TotalScore = 1000000,
|
|
TotalAttempts = 5,
|
|
CompletedBeatmaps = 2,
|
|
User = new APIUser { Username = "best user" }
|
|
},
|
|
new APIUserScoreAggregate
|
|
{
|
|
TotalScore = 50,
|
|
TotalAttempts = 1,
|
|
CompletedBeatmaps = 1,
|
|
User = new APIUser { Username = "worst user" }
|
|
}
|
|
}
|
|
});
|
|
return true;
|
|
|
|
case IndexPlaylistScoresRequest roomLeaderboardRequest:
|
|
roomLeaderboardRequest.TriggerSuccess(new IndexedMultiplayerScores
|
|
{
|
|
Scores =
|
|
{
|
|
new MultiplayerScore
|
|
{
|
|
ID = currentScoreId++,
|
|
Accuracy = 1,
|
|
Position = 1,
|
|
EndedAt = DateTimeOffset.Now,
|
|
Passed = true,
|
|
Rank = ScoreRank.S,
|
|
MaxCombo = 1000,
|
|
TotalScore = 1000000,
|
|
User = new APIUser { Username = "best user" },
|
|
Statistics = new Dictionary<HitResult, int>()
|
|
},
|
|
new MultiplayerScore
|
|
{
|
|
ID = currentScoreId++,
|
|
Accuracy = 0.7,
|
|
Position = 2,
|
|
EndedAt = DateTimeOffset.Now,
|
|
Passed = true,
|
|
Rank = ScoreRank.B,
|
|
MaxCombo = 100,
|
|
TotalScore = 200000,
|
|
User = new APIUser { Username = "worst user" },
|
|
Statistics = new Dictionary<HitResult, int>()
|
|
},
|
|
},
|
|
UserScore = new MultiplayerScore
|
|
{
|
|
ID = currentScoreId++,
|
|
Accuracy = 0.91,
|
|
Position = 4,
|
|
EndedAt = DateTimeOffset.Now,
|
|
Passed = true,
|
|
Rank = ScoreRank.A,
|
|
MaxCombo = 100,
|
|
TotalScore = 800000,
|
|
User = localUser,
|
|
Statistics = new Dictionary<HitResult, int>()
|
|
},
|
|
});
|
|
return true;
|
|
|
|
case PartRoomRequest partRoomRequest:
|
|
partRoomRequest.TriggerSuccess();
|
|
return true;
|
|
|
|
case GetRoomsRequest getRoomsRequest:
|
|
var roomsWithoutParticipants = new List<Room>();
|
|
|
|
foreach (var r in ServerSideRooms)
|
|
roomsWithoutParticipants.Add(createResponseRoom(r, false));
|
|
|
|
getRoomsRequest.TriggerSuccess(roomsWithoutParticipants);
|
|
return true;
|
|
|
|
case GetRoomRequest getRoomRequest:
|
|
getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true));
|
|
return true;
|
|
|
|
case CreateRoomScoreRequest createRoomScoreRequest:
|
|
createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
|
|
return true;
|
|
|
|
case SubmitRoomScoreRequest submitRoomScoreRequest:
|
|
submitRoomScoreRequest.TriggerSuccess(new MultiplayerScore
|
|
{
|
|
ID = currentScoreId++,
|
|
Accuracy = 1,
|
|
EndedAt = DateTimeOffset.Now,
|
|
Passed = true,
|
|
Rank = ScoreRank.S,
|
|
MaxCombo = 1000,
|
|
TotalScore = 1000000,
|
|
User = localUser,
|
|
Statistics = new Dictionary<HitResult, int>()
|
|
});
|
|
return true;
|
|
|
|
case GetBeatmapRequest getBeatmapRequest:
|
|
{
|
|
getBeatmapRequest.TriggerSuccess(createResponseBeatmaps(getBeatmapRequest.BeatmapInfo.OnlineID).Single());
|
|
return true;
|
|
}
|
|
|
|
case GetBeatmapsRequest getBeatmapsRequest:
|
|
{
|
|
getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = createResponseBeatmaps(getBeatmapsRequest.BeatmapIds.ToArray()) });
|
|
return true;
|
|
}
|
|
|
|
case GetBeatmapSetRequest getBeatmapSetRequest:
|
|
{
|
|
var baseBeatmap = getBeatmapSetRequest.Type == BeatmapSetLookupType.BeatmapId
|
|
? beatmapManager.QueryBeatmap(b => b.OnlineID == getBeatmapSetRequest.ID)
|
|
: beatmapManager.QueryBeatmap(b => b.BeatmapSet.OnlineID == getBeatmapSetRequest.ID);
|
|
|
|
if (baseBeatmap == null)
|
|
{
|
|
baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo;
|
|
baseBeatmap.OnlineID = getBeatmapSetRequest.ID;
|
|
baseBeatmap.BeatmapSet!.OnlineID = getBeatmapSetRequest.ID;
|
|
}
|
|
|
|
getBeatmapSetRequest.TriggerSuccess(OsuTestScene.CreateAPIBeatmapSet(baseBeatmap));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
List<APIBeatmap> createResponseBeatmaps(params int[] beatmapIds)
|
|
{
|
|
var result = new List<APIBeatmap>();
|
|
|
|
foreach (int id in beatmapIds)
|
|
{
|
|
var baseBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == id);
|
|
|
|
if (baseBeatmap == null)
|
|
{
|
|
baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo;
|
|
baseBeatmap.OnlineID = id;
|
|
baseBeatmap.BeatmapSet!.OnlineID = id;
|
|
}
|
|
|
|
result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a room to a local "server-side" list that's returned when a <see cref="GetRoomsRequest"/> is fired.
|
|
/// </summary>
|
|
/// <param name="room">The room.</param>
|
|
/// <param name="host">The room host.</param>
|
|
public void AddServerSideRoom(Room room, APIUser host)
|
|
{
|
|
room.RoomID.Value ??= currentRoomId++;
|
|
room.Host.Value = host;
|
|
|
|
for (int i = 0; i < room.Playlist.Count; i++)
|
|
{
|
|
room.Playlist[i].ID = currentPlaylistItemId++;
|
|
room.Playlist[i].OwnerID = room.Host.Value.OnlineID;
|
|
}
|
|
|
|
serverSideRooms.Add(room);
|
|
}
|
|
|
|
private Room createResponseRoom(Room room, bool withParticipants)
|
|
{
|
|
var responseRoom = cloneRoom(room);
|
|
|
|
// Password is hidden from the response, and is only propagated via HasPassword.
|
|
bool hadPassword = responseRoom.HasPassword.Value;
|
|
responseRoom.Password.Value = null;
|
|
responseRoom.HasPassword.Value = hadPassword;
|
|
|
|
if (!withParticipants)
|
|
responseRoom.RecentParticipants.Clear();
|
|
|
|
return responseRoom;
|
|
}
|
|
|
|
private Room cloneRoom(Room source)
|
|
{
|
|
var result = JsonConvert.DeserializeObject<Room>(JsonConvert.SerializeObject(source));
|
|
Debug.Assert(result != null);
|
|
|
|
// Playlist item IDs aren't serialised.
|
|
if (source.CurrentPlaylistItem.Value != null)
|
|
result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID;
|
|
for (int i = 0; i < source.Playlist.Count; i++)
|
|
result.Playlist[i].ID = source.Playlist[i].ID;
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|