diff --git a/global.json b/global.json
deleted file mode 100644
index fe7e453b1cd8..000000000000
--- a/global.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "sdk": {
- "version": "10.0.100",
- "rollForward": "latestFeature",
- "allowPrerelease": false
- }
-}
\ No newline at end of file
diff --git a/osu.Android.props b/osu.Android.props
index e05709d554b0..28d70c8090b9 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -153,4 +153,20 @@
+
+
+ false
+
+
+
+
+ false
+
+ $(AndroidLinkSkip);Microsoft.CSharp
+
+
+
+
+
+
diff --git a/osu.Android/AndroidManifest.xml b/osu.Android/AndroidManifest.xml
index 15ab78b1a3e6..f80017f9890c 100644
--- a/osu.Android/AndroidManifest.xml
+++ b/osu.Android/AndroidManifest.xml
@@ -10,7 +10,7 @@
reaches DispatchKeyEvent and the OS default for root-task activities (moveTaskToBack,
i.e. minimise) takes over instead.
-->
-
+
diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs
index 7b62d64b41c1..b7fa2f8cec64 100644
--- a/osu.Game/Online/Spectator/SpectatorClient.cs
+++ b/osu.Game/Online/Spectator/SpectatorClient.cs
@@ -204,7 +204,10 @@ public void BeginPlaying(long? scoreToken, GameplayState state, Score score)
Schedule(() =>
{
if (isPlaying)
- throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing");
+ {
+ Logger.Log("BeginPlaying called while already playing; ignoring.", LoggingTarget.Network);
+ return;
+ }
// transfer state at point of beginning play
currentState.BeatmapID = score.ScoreInfo.BeatmapInfo!.OnlineID;
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeCarousel.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeCarousel.cs
index 89c4c0255b35..e3373decbd1a 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeCarousel.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeCarousel.cs
@@ -96,7 +96,7 @@ protected override void Update()
if (currentDisplay == lastDisplayed)
return;
- if (lastDisplayed >= 0)
+ if (lastDisplayed >= 0 && lastDisplayed < content.Count)
{
content[lastDisplayed].FadeOutFromOne(250, Easing.OutQuint);
navigationFlow[lastDisplayed].Active.Value = false;
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs
index 25f1c7465ca3..0bf7ba31e144 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs
@@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Configuration;
@@ -18,8 +19,6 @@ public partial class NewDailyChallengeNotification : SimpleNotification
{
private readonly Room room;
- private BeatmapCardNano card = null!;
-
public NewDailyChallengeNotification(Room room)
{
this.room = room;
@@ -30,8 +29,15 @@ private void load(OsuGame? game, SessionStatics statics)
{
Text = DailyChallengeStrings.ChallengeLiveNotification;
var playlistItem = room.Playlist.FirstOrDefault();
+
if (playlistItem != null)
- Content.Add(card = new BeatmapCardNano((APIBeatmapSet)playlistItem.Beatmap.BeatmapSet!));
+ {
+ Content.Add(new BeatmapCardNano((APIBeatmapSet)playlistItem.Beatmap.BeatmapSet!)
+ {
+ RelativeSizeAxes = Axes.X,
+ });
+ }
+
Activated = () =>
{
if (statics.Get(Static.DailyChallengeIntroPlayed))
@@ -42,11 +48,5 @@ private void load(OsuGame? game, SessionStatics statics)
return true;
};
}
-
- protected override void Update()
- {
- base.Update();
- card.Width = Content.DrawWidth;
- }
}
}
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index a4147b3d3e8d..772b102045e5 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -145,7 +145,7 @@ public void RemoveUser(APIUser user)
{
Debug.Assert(ServerRoom != null);
- ServerRoom.Users.Remove(ServerRoom.Users.Single(u => u.UserID == user.Id));
+ ServerRoom.Users.Remove(ServerRoom.Users.First(u => u.UserID == user.Id));
((IMultiplayerClient)this).UserLeft(clone(new MultiplayerRoomUser(user.Id)));
if (ServerRoom.Users.Any())
@@ -165,7 +165,7 @@ public void ChangeUserState(int userId, MultiplayerUserState newState)
{
Debug.Assert(ServerRoom != null);
- var user = ServerRoom.Users.Single(u => u.UserID == userId);
+ var user = ServerRoom.Users.First(u => u.UserID == userId);
user.State = clone(newState);
((IMultiplayerClient)this).UserStateChanged(clone(userId), clone(user.State));
@@ -224,7 +224,7 @@ public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBea
{
Debug.Assert(ServerRoom != null);
- var user = ServerRoom.Users.Single(u => u.UserID == userId);
+ var user = ServerRoom.Users.First(u => u.UserID == userId);
user.BeatmapAvailability = newBeatmapAvailability;
((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(user.BeatmapAvailability));
@@ -238,7 +238,7 @@ protected override async Task JoinRoomInternal(long roomId, str
roomId = clone(roomId);
password = clone(password);
- ServerAPIRoom = ServerSideRooms.Single(r => r.RoomID == roomId);
+ ServerAPIRoom = ServerSideRooms.First(r => r.RoomID == roomId);
if (password != ServerAPIRoom.Password)
throw new InvalidOperationException("Invalid password.");
@@ -301,7 +301,7 @@ public override Task TransferHost(int userId)
Debug.Assert(ServerRoom != null);
- ServerRoom.Host = ServerRoom.Users.Single(u => u.UserID == userId);
+ ServerRoom.Host = ServerRoom.Users.First(u => u.UserID == userId);
return ((IMultiplayerClient)this).HostChanged(clone(userId));
}
@@ -312,7 +312,7 @@ public override Task KickUser(int userId)
Debug.Assert(ServerRoom != null);
- var user = ServerRoom.Users.Single(u => u.UserID == userId);
+ var user = ServerRoom.Users.First(u => u.UserID == userId);
ServerRoom.Users.Remove(user);
return ((IMultiplayerClient)this).UserKicked(clone(user));
@@ -377,7 +377,7 @@ public void ChangeUserStyle(int userId, int? beatmapId, int? rulesetId)
{
Debug.Assert(ServerRoom != null);
- var user = ServerRoom.Users.Single(u => u.UserID == userId);
+ var user = ServerRoom.Users.First(u => u.UserID == userId);
user.BeatmapId = beatmapId;
user.RulesetId = rulesetId;
@@ -391,7 +391,7 @@ public void ChangeUserMods(int userId, IEnumerable newMods)
{
Debug.Assert(ServerRoom != null);
- var user = ServerRoom.Users.Single(u => u.UserID == userId);
+ var user = ServerRoom.Users.First(u => u.UserID == userId);
user.Mods = newMods.ToArray();
((IMultiplayerClient)this).UserModsChanged(clone(userId), clone(user.Mods));
@@ -810,7 +810,7 @@ private async Task updatePlaylistOrder(MultiplayerRoom room)
// Also ensure that the API room's playlist is correct.
foreach (var item in ServerAPIRoom.Playlist)
- item.PlaylistOrder = ServerRoom.Playlist.Single(i => i.ID == item.ID).PlaylistOrder;
+ item.PlaylistOrder = (ServerRoom.Playlist.FirstOrDefault(i => i.ID == item.ID) ?? new MultiplayerPlaylistItem { ID = item.ID }).PlaylistOrder;
}
private T clone(T incoming)
@@ -818,18 +818,20 @@ private T clone(T incoming)
byte[] serialized = MessagePackSerializer.Serialize(typeof(T), incoming, SignalRUnionWorkaroundResolver.OPTIONS);
var result = MessagePackSerializer.Deserialize(serialized, SignalRUnionWorkaroundResolver.OPTIONS);
- if (incoming is MultiplayerRoomUser { User: { } } sourceUser && result is MultiplayerRoomUser targetUser) targetUser.User = sourceUser.User;
-
- if (incoming is MultiplayerRoom sourceRoom && result is MultiplayerRoom targetRoom)
+ if (incoming is MultiplayerRoomUser sourceUser && result is MultiplayerRoomUser targetUser)
{
- foreach (var user in targetRoom.Users)
- user.User = sourceRoom.Users.FirstOrDefault(u => u.UserID == user.UserID)?.User;
-
- targetRoom.Host?.User = sourceRoom.Host?.User;
+ targetUser.User = sourceUser.User;
}
- else if (incoming is MultiplayerRoomUser sourceSingleUser && result is MultiplayerRoomUser targetSingleUser)
+ else if (incoming is MultiplayerRoom sourceRoom && result is MultiplayerRoom targetRoom)
{
- targetSingleUser.User = sourceSingleUser.User;
+ for (int i = 0; i < sourceRoom.Users.Count; i++)
+ {
+ if (i < targetRoom.Users.Count)
+ targetRoom.Users[i].User = sourceRoom.Users[i].User;
+ }
+
+ if (targetRoom.Host != null && sourceRoom.Host != null)
+ targetRoom.Host.User = sourceRoom.Host.User;
}
return result;
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
index 8bbc426c5d2f..4a9cafbf3146 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
@@ -9,9 +9,6 @@
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.Tests.Beatmaps;
-using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Visual.OnlinePlay
{
@@ -34,16 +31,18 @@ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager
{
case CreateRoomRequest createRoomRequest:
{
- var apiRoom = cloneRoom(createRoomRequest.Room);
+ var apiRoom = createRoomRequest.Room;
+ var responseRoom = cloneRoom(apiRoom);
- // Passwords are explicitly not copied between rooms.
- apiRoom.Password = createRoomRequest.Room.Password;
+ responseRoom.RoomID = currentRoomId++;
+ responseRoom.Host = localUser;
- AddServerSideRoom(apiRoom, localUser);
+ foreach (var item in responseRoom.Playlist)
+ {
+ item.ID = currentPlaylistItemId++;
+ }
- var responseRoom = new APICreatedRoom();
- if (createResponseRoom(apiRoom, false) is Room res)
- responseRoom.CopyFrom(res);
+ ServerSideRooms.Add(responseRoom);
// Propagate back to the source room object used by the test.
createRoomRequest.Room.RoomID = apiRoom.RoomID;
@@ -71,27 +70,6 @@ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager
return true;
}
- case GetRoomsRequest getRoomsRequest:
- {
- var roomsWithoutParticipants = new List();
-
- foreach (var r in ServerSideRooms)
- {
- if (createResponseRoom(r, false) is Room roomsRes)
- roomsWithoutParticipants.Add(roomsRes);
- }
-
- getRoomsRequest.TriggerSuccess(roomsWithoutParticipants);
- return true;
- }
-
- case GetRoomRequest getRoomRequest:
- {
- if (createResponseRoom(ServerSideRooms.FirstOrDefault(r => r.RoomID == getRoomRequest.RoomId), true) is Room getRes)
- getRoomRequest.TriggerSuccess(getRes);
- return true;
- }
-
case CreateRoomScoreRequest createRoomScoreRequest:
createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
return true;
@@ -159,6 +137,27 @@ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager
getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = createResponseBeatmaps(beatmapManager, getBeatmapsRequest.BeatmapIds.ToArray()) });
return true;
}
+
+ case GetRoomsRequest getRoomsRequest:
+ {
+ var roomsWithoutParticipants = new List();
+
+ foreach (var r in ServerSideRooms)
+ {
+ if (createResponseRoom(r, false) is Room roomsRes)
+ roomsWithoutParticipants.Add(roomsRes);
+ }
+
+ getRoomsRequest.TriggerSuccess(roomsWithoutParticipants);
+ return true;
+ }
+
+ case GetRoomRequest getRoomRequest:
+ {
+ if (createResponseRoom(ServerSideRooms.FirstOrDefault(r => r.RoomID == getRoomRequest.RoomId), true) is Room getRes)
+ getRoomRequest.TriggerSuccess(getRes);
+ return true;
+ }
}
return false;
@@ -214,12 +213,12 @@ private static List createResponseBeatmaps(BeatmapManager beatmapMan
if (baseBeatmap == null)
{
- baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo;
+ baseBeatmap = new osu.Game.Tests.Beatmaps.TestBeatmap(new osu.Game.Rulesets.RulesetInfo { OnlineID = 0 }).BeatmapInfo;
baseBeatmap.OnlineID = id;
baseBeatmap.BeatmapSet!.OnlineID = id;
}
- result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap));
+ result.Add(osu.Game.Tests.Visual.OsuTestScene.CreateAPIBeatmap(baseBeatmap));
}
return result;