diff --git a/osu.Android.props b/osu.Android.props
index 2c186a52dd..77c29a5d6e 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs
index 5e46498aca..521c10c10c 100644
--- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
@@ -125,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
/// Ensures alternation is reset before the first hitobject after a break.
///
[Test]
- public void TestInputSingularWithBreak() => CreateModTest(new ModTestData
+ public void TestInputSingularWithBreak([Values] bool pressBeforeSecondObject) => CreateModTest(new ModTestData
{
Mod = new OsuModAlternate(),
PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2,
@@ -155,21 +156,26 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
},
}
},
- ReplayFrames = new List
+ ReplayFrames = new ReplayFrame[]
{
// first press to start alternate lock.
- new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
- new OsuReplayFrame(501, new Vector2(100)),
- // press same key after break but before hit object.
- new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.LeftButton),
- new OsuReplayFrame(2251, new Vector2(300, 100)),
+ new OsuReplayFrame(450, new Vector2(100), OsuAction.LeftButton),
+ new OsuReplayFrame(451, new Vector2(100)),
// press same key at second hitobject and ensure it has been hit.
- new OsuReplayFrame(2500, new Vector2(500, 100), OsuAction.LeftButton),
- new OsuReplayFrame(2501, new Vector2(500, 100)),
+ new OsuReplayFrame(2450, new Vector2(500, 100), OsuAction.LeftButton),
+ new OsuReplayFrame(2451, new Vector2(500, 100)),
// press same key at third hitobject and ensure it has been missed.
- new OsuReplayFrame(3000, new Vector2(500, 100), OsuAction.LeftButton),
- new OsuReplayFrame(3001, new Vector2(500, 100)),
- }
+ new OsuReplayFrame(2950, new Vector2(500, 100), OsuAction.LeftButton),
+ new OsuReplayFrame(2951, new Vector2(500, 100)),
+ }.Concat(!pressBeforeSecondObject
+ ? Enumerable.Empty()
+ : new ReplayFrame[]
+ {
+ // press same key after break but before hit object.
+ new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.LeftButton),
+ new OsuReplayFrame(2251, new Vector2(300, 100)),
+ }
+ ).ToList()
});
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
index a7aca8257b..e4e8905722 100644
--- a/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
+++ b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
@@ -18,7 +18,7 @@ using osu.Game.Utils;
namespace osu.Game.Rulesets.Osu.Mods
{
- public abstract class InputBlockingMod : Mod, IApplicableToDrawableRuleset
+ public abstract class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IUpdatableByPlayfield
{
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(OsuModCinema) };
@@ -62,15 +62,18 @@ namespace osu.Game.Rulesets.Osu.Mods
gameplayClock = drawableRuleset.FrameStableClock;
}
+ public void Update(Playfield playfield)
+ {
+ if (LastAcceptedAction != null && nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
+ LastAcceptedAction = null;
+ }
+
protected abstract bool CheckValidNewAction(OsuAction action);
private bool checkCorrectAction(OsuAction action)
{
if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
- {
- LastAcceptedAction = null;
return true;
- }
switch (action)
{
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs
index ce418f33f0..1858aee76b 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs
@@ -3,6 +3,7 @@
#nullable disable
+using System;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -66,6 +67,18 @@ namespace osu.Game.Tests.Visual.Editing
AddUntilStep("Scroll container is loaded", () => scrollContainer.LoadState >= LoadState.Loaded);
}
+ [Test]
+ public void TestInitialZoomOutOfRange()
+ {
+ AddStep("Invalid ZoomableScrollContainer throws ArgumentException", () =>
+ {
+ Assert.Throws(() =>
+ {
+ _ = new ZoomableScrollContainer(1, 60, 0);
+ });
+ });
+ }
+
[Test]
public void TestWidthInitialization()
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
index e978b57ba4..a97096e143 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface
displayedCount = new OsuSpriteText()
};
- notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
+ notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"unread count: {count.NewValue}"; };
});
[Test]
@@ -270,6 +270,8 @@ namespace osu.Game.Tests.Visual.UserInterface
AddUntilStep("wait completion", () => notification.State == ProgressNotificationState.Completed);
AddAssert("Completion toast shown", () => notificationOverlay.ToastCount == 1);
+ AddUntilStep("wait forwarded", () => notificationOverlay.ToastCount == 0);
+ AddAssert("only one unread", () => notificationOverlay.UnreadCount.Value == 1);
}
[Test]
diff --git a/osu.Game/Database/Live.cs b/osu.Game/Database/Live.cs
index 52e1d420f7..3bb11c3a50 100644
--- a/osu.Game/Database/Live.cs
+++ b/osu.Game/Database/Live.cs
@@ -51,7 +51,15 @@ namespace osu.Game.Database
ID = id;
}
- public bool Equals(Live? other) => ID == other?.ID;
+ public bool Equals(Live? other)
+ {
+ if (ReferenceEquals(this, other)) return true;
+ if (other == null) return false;
+
+ return ID == other.ID;
+ }
+
+ public override int GetHashCode() => HashCode.Combine(ID);
public override string ToString() => PerformRead(i => i.ToString());
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
index 30433ab8cd..c870157fec 100644
--- a/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
@@ -10,6 +10,9 @@ using osu.Game.Scoring;
namespace osu.Game.Online.API.Requests.Responses
{
+ ///
+ /// Represents an aggregate score for a user based off all beatmaps that have been played in the playlist.
+ ///
public class APIUserScoreAggregate
{
[JsonProperty("attempts")]
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs
index 2f3ece0e3b..f51f57c031 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs
@@ -15,6 +15,8 @@ using osu.Framework.Localisation;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
+using osu.Framework.Bindables;
+using osu.Game.Configuration;
namespace osu.Game.Online.Leaderboards
{
@@ -24,6 +26,7 @@ namespace osu.Game.Online.Leaderboards
private FillFlowContainer topScoreStatistics = null!;
private FillFlowContainer bottomScoreStatistics = null!;
private FillFlowContainer modStatistics = null!;
+ private readonly Bindable prefer24HourTime = new Bindable();
public LeaderboardScoreTooltip()
{
@@ -36,8 +39,9 @@ namespace osu.Game.Online.Leaderboards
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OsuColour colours, OsuConfigManager configManager)
{
+ configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime);
InternalChildren = new Drawable[]
{
new Box
@@ -92,6 +96,13 @@ namespace osu.Game.Online.Leaderboards
};
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ prefer24HourTime.BindValueChanged(_ => updateTimestampLabel(), true);
+ }
+
private ScoreInfo? displayedScore;
public void SetContent(ScoreInfo score)
@@ -101,7 +112,7 @@ namespace osu.Game.Online.Leaderboards
displayedScore = score;
- timestampLabel.Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}";
+ updateTimestampLabel();
modStatistics.Clear();
topScoreStatistics.Clear();
@@ -121,6 +132,16 @@ namespace osu.Game.Online.Leaderboards
}
}
+ private void updateTimestampLabel()
+ {
+ if (displayedScore != null)
+ {
+ timestampLabel.Text = prefer24HourTime.Value
+ ? $"Played on {displayedScore.Date.ToLocalTime():d MMMM yyyy HH:mm}"
+ : $"Played on {displayedScore.Date.ToLocalTime():d MMMM yyyy h:mm tt}";
+ }
+ }
+
protected override void PopIn() => this.FadeIn(20, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(80, Easing.OutQuint);
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 9e2384322a..0d115f62fe 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -56,14 +56,12 @@ using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
-using osu.Game.Skinning;
using osu.Game.Skinning.Editor;
using osu.Game.Updater;
using osu.Game.Users;
using osu.Game.Utils;
using osuTK.Graphics;
using Sentry;
-using Logger = osu.Framework.Logging.Logger;
namespace osu.Game
{
@@ -294,25 +292,13 @@ namespace osu.Game
Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ShortName;
- // bind config int to database SkinInfo
configSkin = LocalConfig.GetBindable(OsuSetting.Skin);
+
+ // Transfer skin from config to realm instance once on startup.
+ SkinManager.SetSkinFromConfiguration(configSkin.Value);
+
+ // Transfer any runtime changes back to configuration file.
SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString();
- configSkin.ValueChanged += skinId =>
- {
- Live skinInfo = null;
-
- if (Guid.TryParse(skinId.NewValue, out var guid))
- skinInfo = SkinManager.Query(s => s.ID == guid);
-
- if (skinInfo == null)
- {
- if (guid == SkinInfo.CLASSIC_SKIN)
- skinInfo = DefaultLegacySkin.CreateInfo().ToLiveUnmanaged();
- }
-
- SkinManager.CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.CreateInfo().ToLiveUnmanaged();
- };
- configSkin.TriggerChange();
IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 15573f99af..fad8afd371 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -210,14 +210,14 @@ namespace osu.Game.Overlays
mainContent.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
}
- private void notificationClosed()
+ private void notificationClosed() => Schedule(() =>
{
updateCounts();
// this debounce is currently shared between popin/popout sounds, which means one could potentially not play when the user is expecting it.
// popout is constant across all notification types, and should therefore be handled using playback concurrency instead, but seems broken at the moment.
playDebouncedSample("UI/overlay-pop-out");
- }
+ });
private void playDebouncedSample(string sampleName)
{
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index 885bb2fc6c..4e7cebf0ae 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -26,7 +26,8 @@ namespace osu.Game.Overlays.Notifications
public abstract class Notification : Container
{
///
- /// User requested close.
+ /// Notification was closed, either by user or otherwise.
+ /// Importantly, this event may be fired from a non-update thread.
///
public event Action? Closed;
@@ -55,6 +56,8 @@ namespace osu.Game.Overlays.Notifications
protected Container IconContent;
+ public bool WasClosed { get; private set; }
+
private readonly Container content;
protected override Container Content => content;
@@ -245,21 +248,23 @@ namespace osu.Game.Overlays.Notifications
initialFlash.FadeOutFromOne(2000, Easing.OutQuart);
}
- public bool WasClosed;
-
public virtual void Close(bool runFlingAnimation)
{
if (WasClosed) return;
WasClosed = true;
- if (runFlingAnimation && dragContainer.FlingLeft())
- this.FadeOut(600, Easing.In);
- else
- this.FadeOut(100);
-
Closed?.Invoke();
- Expire();
+
+ Schedule(() =>
+ {
+ if (runFlingAnimation && dragContainer.FlingLeft())
+ this.FadeOut(600, Easing.In);
+ else
+ this.FadeOut(100);
+
+ Expire();
+ });
}
private class DragContainer : Container
diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs
index c4d402e5b9..61bb22041e 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -142,7 +142,6 @@ namespace osu.Game.Overlays.Notifications
case ProgressNotificationState.Completed:
loadingSpinner.Hide();
attemptPostCompletion();
- base.Close(false);
break;
}
}
@@ -166,6 +165,8 @@ namespace osu.Game.Overlays.Notifications
CompletionTarget.Invoke(CreateCompletionNotification());
completionSent = true;
+
+ Close(false);
}
private ProgressNotificationState state;
@@ -239,6 +240,7 @@ namespace osu.Game.Overlays.Notifications
{
switch (State)
{
+ case ProgressNotificationState.Completed:
case ProgressNotificationState.Cancelled:
base.Close(runFlingAnimation);
break;
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index d23ef7e3e7..4787b07af8 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -14,7 +14,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Framework.Platform;
-using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
@@ -36,9 +35,6 @@ namespace osu.Game.Overlays.Settings.Sections
Icon = FontAwesome.Solid.PaintBrush
};
- private readonly Bindable> dropdownBindable = new Bindable> { Default = DefaultSkin.CreateInfo().ToLiveUnmanaged() };
- private readonly Bindable configBindable = new Bindable();
-
private static readonly Live random_skin_info = new SkinInfo
{
ID = SkinInfo.RANDOM_SKIN,
@@ -56,13 +52,14 @@ namespace osu.Game.Overlays.Settings.Sections
private IDisposable realmSubscription;
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor)
+ private void load([CanBeNull] SkinEditorOverlay skinEditor)
{
Children = new Drawable[]
{
skinDropdown = new SkinSettingsDropdown
{
LabelText = SkinSettingsStrings.CurrentSkin,
+ Current = skins.CurrentSkinInfo,
Keywords = new[] { @"skins" }
},
new SettingsButton
@@ -73,47 +70,28 @@ namespace osu.Game.Overlays.Settings.Sections
new ExportSkinButton(),
new DeleteSkinButton(),
};
-
- config.BindWith(OsuSetting.Skin, configBindable);
}
protected override void LoadComplete()
{
base.LoadComplete();
- skinDropdown.Current = dropdownBindable;
-
realmSubscription = realm.RegisterForNotifications(_ => realm.Realm.All()
.Where(s => !s.DeletePending)
.OrderByDescending(s => s.Protected) // protected skins should be at the top.
.ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase), skinsChanged);
- configBindable.BindValueChanged(_ => Scheduler.AddOnce(updateSelectedSkinFromConfig));
-
- dropdownBindable.BindValueChanged(dropdownSelectionChanged);
- }
-
- private void dropdownSelectionChanged(ValueChangedEvent> skin)
- {
- // Only handle cases where it's clear the user has intent to change skins.
- if (skin.OldValue == null) return;
-
- if (skin.NewValue.Equals(random_skin_info))
+ skinDropdown.Current.BindValueChanged(skin =>
{
- var skinBefore = skins.CurrentSkinInfo.Value;
-
- skins.SelectRandomSkin();
-
- if (skinBefore == skins.CurrentSkinInfo.Value)
+ if (skin.NewValue == random_skin_info)
{
- // the random selection didn't change the skin, so we should manually update the dropdown to match.
- dropdownBindable.Value = skins.CurrentSkinInfo.Value;
+ // before selecting random, set the skin back to the previous selection.
+ // this is done because at this point it will be random_skin_info, and would
+ // cause SelectRandomSkin to be unable to skip the previous selection.
+ skins.CurrentSkinInfo.Value = skin.OldValue;
+ skins.SelectRandomSkin();
}
-
- return;
- }
-
- configBindable.Value = skin.NewValue.ID.ToString();
+ });
}
private void skinsChanged(IRealmCollection sender, ChangeSet changes, Exception error)
@@ -132,25 +110,7 @@ namespace osu.Game.Overlays.Settings.Sections
dropdownItems.Add(skin.ToLive(realm));
dropdownItems.Insert(protectedCount, random_skin_info);
- Schedule(() =>
- {
- skinDropdown.Items = dropdownItems;
-
- updateSelectedSkinFromConfig();
- });
- }
-
- private void updateSelectedSkinFromConfig()
- {
- if (!skinDropdown.Items.Any())
- return;
-
- Live skin = null;
-
- if (Guid.TryParse(configBindable.Value, out var configId))
- skin = skinDropdown.Items.FirstOrDefault(s => s.ID == configId);
-
- dropdownBindable.Value = skin ?? skinDropdown.Items.First();
+ Schedule(() => skinDropdown.Items = dropdownItems);
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index 558605efc3..6d7706cde2 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -150,9 +150,9 @@ namespace osu.Game.Rulesets.Mods
if (comboBasedSize)
{
- if (combo > 200)
+ if (combo >= 200)
size *= 0.8f;
- else if (combo > 100)
+ else if (combo >= 100)
size *= 0.9f;
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 9e96a7386d..721f0c4e3b 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -196,7 +196,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
defaultTimelineZoom = getZoomLevelForVisibleMilliseconds(6000);
- float initialZoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
+ float initialZoom = (float)(defaultTimelineZoom * (editorBeatmap.BeatmapInfo.TimelineZoom == 0 ? 1 : editorBeatmap.BeatmapInfo.TimelineZoom));
float minimumZoom = getZoomLevelForVisibleMilliseconds(10000);
float maximumZoom = getZoomLevelForVisibleMilliseconds(500);
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
index 7d51284f46..0fb59a8a1f 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
@@ -87,6 +87,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (minimum > maximum)
throw new ArgumentException($"{nameof(minimum)} ({minimum}) must be less than {nameof(maximum)} ({maximum})");
+ if (initial < minimum || initial > maximum)
+ throw new ArgumentException($"{nameof(initial)} ({initial}) must be between {nameof(minimum)} ({minimum}) and {nameof(maximum)} ({maximum})");
+
minZoom = minimum;
maxZoom = maximum;
CurrentZoom = zoomTarget = initial;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
index 1f80c47d13..5a297f18db 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
@@ -110,6 +110,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
if (Client != null)
{
Client.RoomUpdated -= invokeOnRoomUpdated;
+ Client.LoadRequested -= invokeOnRoomLoadRequested;
Client.UserLeft -= invokeUserLeft;
Client.UserKicked -= invokeUserKicked;
Client.UserJoined -= invokeUserJoined;
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index f677cebe51..7ffea3b54f 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -119,7 +119,9 @@ namespace osu.Game.Skinning
Realm.Run(r =>
{
// choose from only user skins, removing the current selection to ensure a new one is chosen.
- var randomChoices = r.All().Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray();
+ var randomChoices = r.All()
+ .Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID)
+ .ToArray();
if (randomChoices.Length == 0)
{
@@ -297,5 +299,21 @@ namespace osu.Game.Skinning
Delete(items.ToList(), silent);
});
}
+
+ public void SetSkinFromConfiguration(string guidString)
+ {
+ Live skinInfo = null;
+
+ if (Guid.TryParse(guidString, out var guid))
+ skinInfo = Query(s => s.ID == guid);
+
+ if (skinInfo == null)
+ {
+ if (guid == SkinInfo.CLASSIC_SKIN)
+ skinInfo = DefaultLegacySkin.SkinInfo;
+ }
+
+ CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.SkinInfo;
+ }
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index fed7c27f07..29e690a024 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -35,7 +35,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 496bfbb85c..83410b08f6 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -61,7 +61,7 @@
-
+
@@ -82,7 +82,7 @@
-
+