From dc75d55f72af11bdf81b081dad8f5e84be3ed2e2 Mon Sep 17 00:00:00 2001
From: Gabe Livengood <47010459+ggliv@users.noreply.github.com>
Date: Wed, 8 Jun 2022 14:02:15 -0400
Subject: [PATCH 0001/1604] allow modfailcondition to arbitrarily trigger fail
---
osu.Game/Rulesets/Mods/ModFailCondition.cs | 19 +++++++++++++++++++
osu.Game/Rulesets/Scoring/HealthProcessor.cs | 14 ++++++++++----
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs
index 4425ece513..1aab0ab880 100644
--- a/osu.Game/Rulesets/Mods/ModFailCondition.cs
+++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs
@@ -19,12 +19,31 @@ namespace osu.Game.Rulesets.Mods
public virtual bool PerformFail() => true;
public virtual bool RestartOnFail => Restart.Value;
+ private HealthProcessor healthProcessorInternal;
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
{
+ healthProcessorInternal = healthProcessor;
healthProcessor.FailConditions += FailCondition;
}
+ ///
+ /// Immediately triggers a failure on the loaded .
+ ///
+ protected void TriggerArbitraryFailure() => healthProcessorInternal.TriggerFailure();
+
+ ///
+ /// Determines whether should trigger a failure. Called every time a
+ /// judgement is applied to .
+ ///
+ /// The loaded .
+ /// The latest .
+ /// Whether the fail condition has been met.
+ ///
+ /// This method should only be used to trigger failures based on .
+ /// Using outside values to evaluate failure may introduce event ordering discrepancies, use
+ /// an with instead.
+ ///
protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result);
}
}
diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs
index 0f51560476..4f5ff95477 100644
--- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs
@@ -33,6 +33,15 @@ namespace osu.Game.Rulesets.Scoring
///
public bool HasFailed { get; private set; }
+ ///
+ /// Immediately triggers a failure for this HealthProcessor.
+ ///
+ public void TriggerFailure()
+ {
+ if (Failed?.Invoke() != false)
+ HasFailed = true;
+ }
+
protected override void ApplyResultInternal(JudgementResult result)
{
result.HealthAtJudgement = Health.Value;
@@ -44,10 +53,7 @@ namespace osu.Game.Rulesets.Scoring
Health.Value += GetHealthIncreaseFor(result);
if (meetsAnyFailCondition(result))
- {
- if (Failed?.Invoke() != false)
- HasFailed = true;
- }
+ TriggerFailure();
}
protected override void RevertResultInternal(JudgementResult result)
From 21c5499da16eb88f12d6f5bee8d28b1557559b2f Mon Sep 17 00:00:00 2001
From: Gabe Livengood <47010459+ggliv@users.noreply.github.com>
Date: Fri, 10 Jun 2022 13:11:17 -0400
Subject: [PATCH 0002/1604] remove arbitrary from method name
---
osu.Game/Rulesets/Mods/ModFailCondition.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs
index 1aab0ab880..9500734408 100644
--- a/osu.Game/Rulesets/Mods/ModFailCondition.cs
+++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
///
/// Immediately triggers a failure on the loaded .
///
- protected void TriggerArbitraryFailure() => healthProcessorInternal.TriggerFailure();
+ protected void TriggerFailure() => healthProcessorInternal.TriggerFailure();
///
/// Determines whether should trigger a failure. Called every time a
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mods
///
/// This method should only be used to trigger failures based on .
/// Using outside values to evaluate failure may introduce event ordering discrepancies, use
- /// an with instead.
+ /// an with instead.
///
protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result);
}
From 6e64a8f55ef5838a5d58625668138fb3c85e34db Mon Sep 17 00:00:00 2001
From: Gabe Livengood <47010459+ggliv@users.noreply.github.com>
Date: Fri, 10 Jun 2022 13:13:35 -0400
Subject: [PATCH 0003/1604] use event to trigger failure
---
osu.Game/Rulesets/Mods/ModFailCondition.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs
index 9500734408..63cebc9747 100644
--- a/osu.Game/Rulesets/Mods/ModFailCondition.cs
+++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs
@@ -19,18 +19,18 @@ namespace osu.Game.Rulesets.Mods
public virtual bool PerformFail() => true;
public virtual bool RestartOnFail => Restart.Value;
- private HealthProcessor healthProcessorInternal;
+ private event Action failureTriggered;
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
{
- healthProcessorInternal = healthProcessor;
+ failureTriggered = healthProcessor.TriggerFailure;
healthProcessor.FailConditions += FailCondition;
}
///
/// Immediately triggers a failure on the loaded .
///
- protected void TriggerFailure() => healthProcessorInternal.TriggerFailure();
+ protected void TriggerFailure() => failureTriggered?.Invoke();
///
/// Determines whether should trigger a failure. Called every time a
From 579d5b51eb25e90e7bc28f284b28c5edc14fc249 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 20:34:41 +0900
Subject: [PATCH 0004/1604] Add and consume sample bank constants
---
.../TestSceneAutoJuiceStream.cs | 2 +-
.../Editor/TestSceneSliderSplitting.cs | 4 +-
.../Formats/LegacyBeatmapDecoderTest.cs | 14 +++---
...estSceneHitObjectSamplePointAdjustments.cs | 45 ++++++++++---------
.../TestSceneGameplaySampleTriggerSource.cs | 4 +-
osu.Game/Audio/HitSampleInfo.cs | 9 ++++
.../ControlPoints/SampleControlPoint.cs | 4 +-
.../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +-
.../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 +--
9 files changed, 51 insertions(+), 40 deletions(-)
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
index 202228c9e7..e47a687f24 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests
NewCombo = i % 8 == 0,
Samples = new List(new[]
{
- new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100)
+ new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL, volume: 100)
})
});
}
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs
index 015952c59a..d642d6a5ed 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs
@@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
if (slider is null) return;
- slider.SampleControlPoint.SampleBank = "soft";
+ slider.SampleControlPoint.SampleBank = HitSampleInfo.BANK_SOFT;
slider.SampleControlPoint.SampleVolume = 70;
sample = new HitSampleInfo("hitwhistle");
slider.Samples.Add(sample);
@@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("sliders have hitsounds", hasHitsounds);
bool hasHitsounds() => sample is not null &&
- EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == "soft" &&
+ EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == HitSampleInfo.BANK_SOFT &&
o.SampleControlPoint.SampleVolume == 70 &&
o.Samples.Contains(sample));
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index fdd0167ed3..d9817802f3 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -206,17 +206,17 @@ namespace osu.Game.Tests.Beatmaps.Formats
var soundPoint = controlPoints.SamplePointAt(0);
Assert.AreEqual(956, soundPoint.Time);
- Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank);
Assert.AreEqual(60, soundPoint.SampleVolume);
soundPoint = controlPoints.SamplePointAt(53373);
Assert.AreEqual(53373, soundPoint.Time);
- Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank);
Assert.AreEqual(60, soundPoint.SampleVolume);
soundPoint = controlPoints.SamplePointAt(119637);
Assert.AreEqual(119637, soundPoint.Time);
- Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank);
Assert.AreEqual(80, soundPoint.SampleVolume);
var effectPoint = controlPoints.EffectPointAt(0);
@@ -261,10 +261,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(controlPoints.EffectPointAt(2500).KiaiMode, Is.False);
Assert.That(controlPoints.EffectPointAt(3500).KiaiMode, Is.True);
- Assert.That(controlPoints.SamplePointAt(500).SampleBank, Is.EqualTo("drum"));
- Assert.That(controlPoints.SamplePointAt(1500).SampleBank, Is.EqualTo("drum"));
- Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo("normal"));
- Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo("drum"));
+ Assert.That(controlPoints.SamplePointAt(500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM));
+ Assert.That(controlPoints.SamplePointAt(1500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM));
+ Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_NORMAL));
+ Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM));
Assert.That(controlPoints.TimingPointAt(500).BeatLength, Is.EqualTo(500).Within(0.1));
Assert.That(controlPoints.TimingPointAt(1500).BeatLength, Is.EqualTo(500).Within(0.1));
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs
index 6313842dfd..31939f6971 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs
@@ -7,6 +7,7 @@ using System.Linq;
using Humanizer;
using NUnit.Framework;
using osu.Framework.Testing;
+using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterface;
@@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.Editing
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
SampleControlPoint = new SampleControlPoint
{
- SampleBank = "normal",
+ SampleBank = HitSampleInfo.BANK_NORMAL,
SampleVolume = 80
}
});
@@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.Editing
Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2,
SampleControlPoint = new SampleControlPoint
{
- SampleBank = "soft",
+ SampleBank = HitSampleInfo.BANK_SOFT,
SampleVolume = 60
}
});
@@ -70,7 +71,7 @@ namespace osu.Game.Tests.Visual.Editing
public void TestSingleSelection()
{
clickSamplePiece(0);
- samplePopoverHasSingleBank("normal");
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_NORMAL);
samplePopoverHasSingleVolume(80);
dismissPopover();
@@ -80,14 +81,14 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
clickSamplePiece(1);
- samplePopoverHasSingleBank("soft");
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
samplePopoverHasSingleVolume(60);
setVolumeViaPopover(90);
hitObjectHasSampleVolume(1, 90);
- setBankViaPopover("drum");
- hitObjectHasSampleBank(1, "drum");
+ setBankViaPopover(HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
}
[Test]
@@ -136,27 +137,27 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("unify sample bank", () =>
{
foreach (var h in EditorBeatmap.HitObjects)
- h.SampleControlPoint.SampleBank = "soft";
+ h.SampleControlPoint.SampleBank = HitSampleInfo.BANK_SOFT;
});
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickSamplePiece(0);
- samplePopoverHasSingleBank("soft");
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
dismissPopover();
clickSamplePiece(1);
- samplePopoverHasSingleBank("soft");
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
setBankViaPopover(string.Empty);
- hitObjectHasSampleBank(0, "soft");
- hitObjectHasSampleBank(1, "soft");
- samplePopoverHasSingleBank("soft");
+ hitObjectHasSampleBank(0, HitSampleInfo.BANK_SOFT);
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
- setBankViaPopover("drum");
- hitObjectHasSampleBank(0, "drum");
- hitObjectHasSampleBank(1, "drum");
- samplePopoverHasSingleBank("drum");
+ setBankViaPopover(HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_DRUM);
}
[Test]
@@ -172,14 +173,14 @@ namespace osu.Game.Tests.Visual.Editing
samplePopoverHasIndeterminateBank();
setBankViaPopover(string.Empty);
- hitObjectHasSampleBank(0, "normal");
- hitObjectHasSampleBank(1, "soft");
+ hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
samplePopoverHasIndeterminateBank();
- setBankViaPopover("normal");
- hitObjectHasSampleBank(0, "normal");
- hitObjectHasSampleBank(1, "normal");
- samplePopoverHasSingleBank("normal");
+ setBankViaPopover(HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
+ hitObjectHasSampleBank(1, HitSampleInfo.BANK_NORMAL);
+ samplePopoverHasSingleBank(HitSampleInfo.BANK_NORMAL);
}
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () =>
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
index b6da562bd0..6e53302624 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs
@@ -57,13 +57,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{
StartTime = t += spacing,
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) },
- SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
+ SampleControlPoint = new SampleControlPoint { SampleBank = HitSampleInfo.BANK_SOFT },
},
new HitCircle
{
StartTime = t + spacing,
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) },
- SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
+ SampleControlPoint = new SampleControlPoint { SampleBank = HitSampleInfo.BANK_SOFT },
},
});
diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs
index efa5562cb8..81cfed23f5 100644
--- a/osu.Game/Audio/HitSampleInfo.cs
+++ b/osu.Game/Audio/HitSampleInfo.cs
@@ -19,11 +19,20 @@ namespace osu.Game.Audio
public const string HIT_FINISH = @"hitfinish";
public const string HIT_CLAP = @"hitclap";
+ public const string BANK_NORMAL = @"normal";
+ public const string BANK_SOFT = @"soft";
+ public const string BANK_DRUM = @"drum";
+
///
/// All valid sample addition constants.
///
public static IEnumerable AllAdditions => new[] { HIT_WHISTLE, HIT_FINISH, HIT_CLAP };
+ ///
+ /// All valid bank constants.
+ ///
+ public static IEnumerable AllBanks => new[] { BANK_NORMAL, BANK_SOFT, BANK_DRUM };
+
///
/// The name of the sample to load.
///
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index c454439c5c..e7b869cfa7 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
public class SampleControlPoint : ControlPoint, IEquatable
{
- public const string DEFAULT_BANK = "normal";
+ public const string DEFAULT_BANK = HitSampleInfo.BANK_NORMAL;
public static readonly SampleControlPoint DEFAULT = new SampleControlPoint
{
@@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public readonly Bindable SampleBankBindable = new Bindable(DEFAULT_BANK) { Default = DEFAULT_BANK };
///
- /// The speed multiplier at this control point.
+ /// The sample bank at this control point.
///
public string SampleBank
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 75500fbc4e..b3e6f50366 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -10,6 +10,7 @@ using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Logging;
+using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Beatmaps.Timing;
@@ -412,7 +413,7 @@ namespace osu.Game.Beatmaps.Formats
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
- stringSampleSet = @"normal";
+ stringSampleSet = HitSampleInfo.HIT_NORMAL;
if (timingChange)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 03c63ff4f2..52d1ea60a5 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -547,13 +547,13 @@ namespace osu.Game.Beatmaps.Formats
{
switch (sampleBank?.ToLowerInvariant())
{
- case "normal":
+ case HitSampleInfo.BANK_NORMAL:
return LegacySampleBank.Normal;
- case "soft":
+ case HitSampleInfo.BANK_SOFT:
return LegacySampleBank.Soft;
- case "drum":
+ case HitSampleInfo.BANK_DRUM:
return LegacySampleBank.Drum;
default:
From 9222cb379f7c5cdb1ad7a25f73df28242e5b1406 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 20:53:18 +0900
Subject: [PATCH 0005/1604] Add sample bank suport to editor selection handler
---
.../Components/EditorSelectionHandler.cs | 73 ++++++++++++++++++-
1 file changed, 72 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index 0bdfc5b0a0..1670328e58 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -48,11 +48,40 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
public readonly Dictionary> SelectionSampleStates = new Dictionary>();
+ ///
+ /// The state of each sample bank type for all selected hitobjects.
+ ///
+ public readonly Dictionary> SelectionBankStates = new Dictionary>();
+
///
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
///
private void createStateBindables()
{
+ foreach (string bankName in HitSampleInfo.AllBanks)
+ {
+ var bindable = new Bindable
+ {
+ Description = bankName.Titleize()
+ };
+
+ bindable.ValueChanged += state =>
+ {
+ switch (state.NewValue)
+ {
+ case TernaryState.False:
+ RemoveSampleBank(bankName);
+ break;
+
+ case TernaryState.True:
+ AddSampleBank(bankName);
+ break;
+ }
+ };
+
+ SelectionBankStates[bankName] = bindable;
+ }
+
foreach (string sampleName in HitSampleInfo.AllAdditions)
{
var bindable = new Bindable
@@ -104,12 +133,48 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.Any(s => s.Name == sampleName));
}
+
+ foreach ((string bankName, var bindable) in SelectionBankStates)
+ {
+ bindable.Value = GetStateFromSelection(SelectedItems, h => h.SampleControlPoint.SampleBank == bankName);
+ }
}
#endregion
#region Ternary state changes
+ ///
+ /// Adds a sample bank to all selected s.
+ ///
+ /// The name of the sample bank.
+ public void AddSampleBank(string bankName)
+ {
+ EditorBeatmap.PerformOnSelection(h =>
+ {
+ if (h.SampleControlPoint.SampleBank == bankName)
+ return;
+
+ h.SampleControlPoint.SampleBank = bankName;
+ EditorBeatmap.Update(h);
+ });
+ }
+
+ ///
+ /// Removes a sample bank from all selected s.
+ ///
+ /// The name of the sample bank.
+ public void RemoveSampleBank(string bankName)
+ {
+ EditorBeatmap.PerformOnSelection(h =>
+ {
+ if (h.SampleControlPoint.SampleBank == bankName)
+ h.SampleControlPoint.SampleBankBindable.SetDefault();
+
+ EditorBeatmap.Update(h);
+ });
+ }
+
///
/// Adds a hit sample to all selected s.
///
@@ -174,11 +239,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
yield return new TernaryStateToggleMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } };
}
- yield return new OsuMenuItem("Sound")
+ yield return new OsuMenuItem("Sample")
{
Items = SelectionSampleStates.Select(kvp =>
new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
};
+
+ yield return new OsuMenuItem("Bank")
+ {
+ Items = SelectionBankStates.Select(kvp =>
+ new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray()
+ };
}
#endregion
From 50e24ddd872bcd8e6c5c543d8fa6fcecac0f7b3b Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 21:35:08 +0900
Subject: [PATCH 0006/1604] Add icon and radio button logic
---
osu.Game/Rulesets/Edit/HitObjectComposer.cs | 20 ++++++++-
.../Components/ComposeBlueprintContainer.cs | 42 ++++++++++++++++++-
.../Components/EditorSelectionHandler.cs | 35 +++++++++++++++-
3 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 3bed835854..8857bb2dae 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -71,6 +71,8 @@ namespace osu.Game.Rulesets.Edit
private FillFlowContainer togglesCollection;
+ private FillFlowContainer sampleBankTogglesCollection;
+
private IBindable hasTiming;
protected HitObjectComposer(Ruleset ruleset)
@@ -146,6 +148,16 @@ namespace osu.Game.Rulesets.Edit
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
},
+ },
+ new EditorToolboxGroup("bank (Shift-Q~R)")
+ {
+ Child = sampleBankTogglesCollection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 5),
+ },
}
}
},
@@ -161,6 +173,8 @@ namespace osu.Game.Rulesets.Edit
TernaryStates = CreateTernaryButtons().ToArray();
togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b)));
+ sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Select(b => new DrawableTernaryButton(b)));
+
setSelectTool();
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
@@ -213,7 +227,7 @@ namespace osu.Game.Rulesets.Edit
///
/// Create all ternary states required to be displayed to the user.
///
- protected virtual IEnumerable CreateTernaryButtons() => BlueprintContainer.TernaryStates;
+ protected virtual IEnumerable CreateTernaryButtons() => BlueprintContainer.MainTernaryStates;
///
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
@@ -255,7 +269,9 @@ namespace osu.Game.Rulesets.Edit
if (checkRightToggleFromKey(e.Key, out int rightIndex))
{
- var item = togglesCollection.ElementAtOrDefault(rightIndex);
+ var item = e.ShiftPressed
+ ? sampleBankTogglesCollection.ElementAtOrDefault(rightIndex)
+ : togglesCollection.ElementAtOrDefault(rightIndex);
if (item is DrawableTernaryButton button)
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index ec07da43a0..5adc60f6a7 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -14,6 +14,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Audio;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
@@ -55,7 +57,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader]
private void load()
{
- TernaryStates = CreateTernaryButtons().ToArray();
+ MainTernaryStates = CreateTernaryButtons().ToArray();
+ SampleBankTernaryStates = createSampleBankTernaryButtons().ToArray();
AddInternal(placementBlueprintContainer);
}
@@ -172,7 +175,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// A collection of states which will be displayed to the user in the toolbox.
///
- public TernaryButton[] TernaryStates { get; private set; }
+ public TernaryButton[] MainTernaryStates { get; private set; }
+
+ public TernaryButton[] SampleBankTernaryStates { get; private set; }
///
/// Create all ternary states required to be displayed to the user.
@@ -186,6 +191,39 @@ namespace osu.Game.Screens.Edit.Compose.Components
yield return new TernaryButton(kvp.Value, kvp.Key.Replace("hit", string.Empty).Titleize(), () => getIconForSample(kvp.Key));
}
+ private IEnumerable createSampleBankTernaryButtons()
+ {
+ foreach (var kvp in SelectionHandler.SelectionBankStates)
+ yield return new TernaryButton(kvp.Value, kvp.Key.Titleize(), () => getIconForBank(kvp.Key));
+ }
+
+ private Drawable getIconForBank(string sampleName)
+ {
+ return new Container
+ {
+ Size = new Vector2(30, 20),
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Size = new Vector2(8),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Icon = FontAwesome.Solid.VolumeOff
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ X = 10,
+ Y = -1,
+ Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20),
+ Text = $"{char.ToUpper(sampleName.First())}"
+ }
+ }
+ };
+ }
+
private Drawable getIconForSample(string sampleName)
{
switch (sampleName)
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index 1670328e58..750dedac20 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Audio;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
@@ -70,11 +71,38 @@ namespace osu.Game.Screens.Edit.Compose.Components
switch (state.NewValue)
{
case TernaryState.False:
- RemoveSampleBank(bankName);
+ if (SelectedItems.Count == 0)
+ {
+ // Ensure that if this is the last selected bank, it should remain selected.
+ if (SelectionBankStates.Values.All(b => b.Value == TernaryState.False))
+ bindable.Value = TernaryState.True;
+ }
+ else
+ {
+ // Never remove a sample bank.
+ // These are basically radio buttons, not toggles.
+ if (SelectedItems.All(h => h.SampleControlPoint.SampleBank == bankName))
+ bindable.Value = TernaryState.True;
+ }
+
break;
case TernaryState.True:
- AddSampleBank(bankName);
+ if (SelectedItems.Count == 0)
+ {
+ // Ensure the user can't stack multiple bank selections when there's no hitobject selection.
+ // Note that in normal scenarios this is sorted out by the feedback from applying the bank to the selected objects.
+ foreach (var other in SelectionBankStates.Values)
+ {
+ if (other != bindable)
+ other.Value = TernaryState.False;
+ }
+ }
+ else
+ {
+ AddSampleBank(bankName);
+ }
+
break;
}
};
@@ -82,6 +110,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
SelectionBankStates[bankName] = bindable;
}
+ // start with normal selected.
+ SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
+
foreach (string sampleName in HitSampleInfo.AllAdditions)
{
var bindable = new Bindable
From 372a655be1637d8823d89532d24a28f283798906 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 21:39:51 +0900
Subject: [PATCH 0007/1604] Ensure placement uses currently selected bank
---
.../Components/ComposeBlueprintContainer.cs | 20 +++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index 5adc60f6a7..1c9ac83630 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -77,9 +77,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
// we own SelectionHandler so don't need to worry about making bindable copies (for simplicity)
foreach (var kvp in SelectionHandler.SelectionSampleStates)
- {
kvp.Value.BindValueChanged(_ => updatePlacementSamples());
- }
+
+ foreach (var kvp in SelectionHandler.SelectionBankStates)
+ kvp.Value.BindValueChanged(_ => updatePlacementSamples());
}
protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject)
@@ -146,6 +147,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
foreach (var kvp in SelectionHandler.SelectionSampleStates)
sampleChanged(kvp.Key, kvp.Value.Value);
+
+ foreach (var kvp in SelectionHandler.SelectionBankStates)
+ bankChanged(kvp.Key, kvp.Value.Value);
}
private void sampleChanged(string sampleName, TernaryState state)
@@ -170,6 +174,18 @@ namespace osu.Game.Screens.Edit.Compose.Components
}
}
+ private void bankChanged(string bankName, TernaryState state)
+ {
+ if (currentPlacement == null) return;
+
+ switch (state)
+ {
+ case TernaryState.True:
+ currentPlacement.HitObject.SampleControlPoint.SampleBank = bankName;
+ break;
+ }
+ }
+
public readonly Bindable NewCombo = new Bindable { Description = "New Combo" };
///
From b9f41611a7777951b8eebbb83ab5c9a6b27e1e12 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 21:48:18 +0900
Subject: [PATCH 0008/1604] Fix bank potentially being overwritten during
placement
---
osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
index c8196b6865..132214a0bd 100644
--- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
@@ -74,9 +74,13 @@ namespace osu.Game.Rulesets.Edit
/// Whether this call is committing a value for HitObject.StartTime and continuing with further adjustments.
protected void BeginPlacement(bool commitStart = false)
{
+ // Store and copy the bank, since it is managed by the editor UI.
+ string bank = HitObject.SampleControlPoint.SampleBank;
+
var nearestSampleControlPoint = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.SampleControlPoint?.DeepClone() as SampleControlPoint;
HitObject.SampleControlPoint = nearestSampleControlPoint ?? new SampleControlPoint();
+ HitObject.SampleControlPoint.SampleBank = bank;
placementHandler.BeginPlacement(HitObject);
if (commitStart)
From 677b8d09f8773626b36f0e686520fb871b53220d Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 19 Oct 2022 23:54:12 +0900
Subject: [PATCH 0009/1604] Fix huge oversight causing test failures
---
osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index b3e6f50366..ae57ee6f5a 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -413,7 +413,7 @@ namespace osu.Game.Beatmaps.Formats
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
- stringSampleSet = HitSampleInfo.HIT_NORMAL;
+ stringSampleSet = HitSampleInfo.BANK_NORMAL;
if (timingChange)
{
From 849b50a38fe821720944fff0b560f33dceb25286 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Fri, 21 Oct 2022 00:16:33 +0900
Subject: [PATCH 0010/1604] Use `ToUpperInvariant` for added safety
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Bartłomiej Dach
---
.../Edit/Compose/Components/ComposeBlueprintContainer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index 1c9ac83630..eebc4c8e0e 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -234,7 +234,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
X = 10,
Y = -1,
Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20),
- Text = $"{char.ToUpper(sampleName.First())}"
+ Text = $"{char.ToUpperInvariant(sampleName.First())}"
}
}
};
From e1a21e0cf96df3c7deb4b4fbaf8636ef2ad7611d Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 17 Nov 2022 00:01:29 +0900
Subject: [PATCH 0011/1604] create a task to export to avoid block main thread
Code quality and remove some #nullable disable
---
osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 7 +--
osu.Game/Database/LegacyBeatmapExporter.cs | 7 ++-
osu.Game/Database/LegacyExporter.cs | 54 ++++++++++++++++---
osu.Game/Database/LegacyScoreExporter.cs | 11 ++--
osu.Game/Database/LegacyScoreImporter.cs | 2 -
osu.Game/Database/LegacySkinExporter.cs | 7 ++-
osu.Game/Database/LegacySkinImporter.cs | 2 -
osu.Game/Database/RealmAccess.cs | 5 --
.../Online/Leaderboards/LeaderboardScore.cs | 5 +-
.../Overlays/Settings/Sections/SkinSection.cs | 9 +++-
osu.Game/Screens/Edit/Editor.cs | 5 +-
11 files changed, 78 insertions(+), 36 deletions(-)
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 5c20f46787..7a5f6dbd7c 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -15,6 +15,7 @@ using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
+using osu.Game.Overlays;
using osu.Game.Skinning;
using SharpCompress.Archives.Zip;
@@ -122,7 +123,7 @@ namespace osu.Game.Tests.Skins.IO
import1.PerformRead(s =>
{
- new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
});
string exportFilename = import1.GetDisplayString();
@@ -204,7 +205,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(ArgonSkin), s.CreateInstance(skinManager).GetType());
- new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
@@ -239,7 +240,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
- new LegacySkinExporter(osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index d064b9ed58..3e11e898f3 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -1,10 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Platform;
using osu.Game.Beatmaps;
+using osu.Game.Overlays;
namespace osu.Game.Database
{
@@ -12,8 +11,8 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osz";
- public LegacyBeatmapExporter(Storage storage)
- : base(storage)
+ public LegacyBeatmapExporter(Storage storage, INotificationOverlay? notificationOverlay)
+ : base(storage, notificationOverlay)
{
}
}
diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs
index 16d7441dde..ed16e4bc80 100644
--- a/osu.Game/Database/LegacyExporter.cs
+++ b/osu.Game/Database/LegacyExporter.cs
@@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.IO;
+using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Extensions;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
using SharpCompress.Archives.Zip;
namespace osu.Game.Database
@@ -25,10 +26,17 @@ namespace osu.Game.Database
private readonly Storage exportStorage;
- protected LegacyExporter(Storage storage)
+ private readonly INotificationOverlay? notificationOverlay;
+
+ protected ProgressNotification Notification = null!;
+
+ private string filename = null!;
+
+ protected LegacyExporter(Storage storage, INotificationOverlay? notificationOverlay)
{
exportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
+ this.notificationOverlay = notificationOverlay;
}
///
@@ -37,12 +45,25 @@ namespace osu.Game.Database
/// The item to export.
public void Export(TModel item)
{
- string filename = $"{item.GetDisplayString().GetValidFilename()}{FileExtension}";
+ filename = $"{item.GetDisplayString().GetValidFilename()}{FileExtension}";
- using (var stream = exportStorage.CreateFileSafely(filename))
- ExportModelTo(item, stream);
+ Stream stream = exportStorage.CreateFileSafely(filename);
- exportStorage.PresentFileExternally(filename);
+ Notification = new ProgressNotification
+ {
+ State = ProgressNotificationState.Active,
+ Text = "Exporting...",
+ CompletionText = "Export completed"
+ };
+ Notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+ Notification.CancelRequested += () =>
+ {
+ stream.Dispose();
+ return true;
+ };
+
+ ExportModelTo(item, stream);
+ notificationOverlay?.Post(Notification);
}
///
@@ -57,7 +78,24 @@ namespace osu.Game.Database
foreach (var file in model.Files)
archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
- archive.SaveTo(outputStream);
+ Task.Factory.StartNew(() =>
+ {
+ archive.SaveTo(outputStream);
+ }, Notification.CancellationToken).ContinueWith(t =>
+ {
+ if (t.IsCompletedSuccessfully)
+ {
+ outputStream.Dispose();
+ Notification.State = ProgressNotificationState.Completed;
+ }
+ else
+ {
+ if (Notification.State == ProgressNotificationState.Cancelled) return;
+
+ Notification.State = ProgressNotificationState.Cancelled;
+ Notification.Text = "Export Failed";
+ }
+ });
}
}
}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index 6fa02b957d..1564c7b077 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -1,12 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.IO;
using System.Linq;
using osu.Framework.Platform;
using osu.Game.Extensions;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
using osu.Game.Scoring;
namespace osu.Game.Database
@@ -15,8 +15,8 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osr";
- public LegacyScoreExporter(Storage storage)
- : base(storage)
+ public LegacyScoreExporter(Storage storage, INotificationOverlay? notificationOverlay)
+ : base(storage, notificationOverlay)
{
}
@@ -28,6 +28,9 @@ namespace osu.Game.Database
using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
inputStream.CopyTo(outputStream);
+
+ Notification.State = ProgressNotificationState.Completed;
+ outputStream.Dispose();
}
}
}
diff --git a/osu.Game/Database/LegacyScoreImporter.cs b/osu.Game/Database/LegacyScoreImporter.cs
index f61241141e..131b4ffb0e 100644
--- a/osu.Game/Database/LegacyScoreImporter.cs
+++ b/osu.Game/Database/LegacyScoreImporter.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . 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.IO;
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index 1d5364fb8d..a78d69e7b9 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Platform;
+using osu.Game.Overlays;
using osu.Game.Skinning;
namespace osu.Game.Database
@@ -12,8 +11,8 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osk";
- public LegacySkinExporter(Storage storage)
- : base(storage)
+ public LegacySkinExporter(Storage storage, INotificationOverlay? notificationOverlay)
+ : base(storage, notificationOverlay)
{
}
}
diff --git a/osu.Game/Database/LegacySkinImporter.cs b/osu.Game/Database/LegacySkinImporter.cs
index 42b2f2e1d8..2f05ccae45 100644
--- a/osu.Game/Database/LegacySkinImporter.cs
+++ b/osu.Game/Database/LegacySkinImporter.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Game.Skinning;
namespace osu.Game.Database
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index 1a938c12e5..5a7ead1c59 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -173,11 +173,6 @@ namespace osu.Game.Database
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
Filename += realm_extension;
-#if DEBUG
- if (!DebugUtils.IsNUnitRunning)
- applyFilenameSchemaSuffix(ref Filename);
-#endif
-
string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}";
// Attempt to recover a newer database version if available.
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index a7b6bd044d..72f1a94ec8 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -75,6 +75,9 @@ namespace osu.Game.Online.Leaderboards
[Resolved]
private Storage storage { get; set; }
+ [Resolved]
+ private INotificationOverlay notificationOverlay { get; set; }
+
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => Score;
@@ -427,7 +430,7 @@ namespace osu.Game.Online.Leaderboards
if (Score.Files.Count > 0)
{
- items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score)));
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage, notificationOverlay).Export(Score)));
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
}
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index f602b73065..df6f719b1e 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -141,11 +141,16 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private Storage storage { get; set; }
+ [CanBeNull]
+ private INotificationOverlay notificationOverlay;
+
private Bindable currentSkin;
[BackgroundDependencyLoader]
- private void load()
+ private void load(INotificationOverlay notificationOverlay)
{
+ this.notificationOverlay = notificationOverlay;
+
Text = SkinSettingsStrings.ExportSkinButton;
Action = export;
}
@@ -162,7 +167,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage).Export(s));
+ currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage, notificationOverlay).Export(s));
}
catch (Exception e)
{
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index bb390dfbf3..03bdd69f34 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -93,6 +93,9 @@ namespace osu.Game.Screens.Edit
[Resolved]
private Storage storage { get; set; }
+ [Resolved]
+ private INotificationOverlay notificationOverlay { get; set; }
+
[Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
@@ -938,7 +941,7 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
- new LegacyBeatmapExporter(storage).Export(Beatmap.Value.BeatmapSetInfo);
+ new LegacyBeatmapExporter(storage, notificationOverlay).Export(Beatmap.Value.BeatmapSetInfo);
}
///
From 4b29941b4705a38ea26ce4ca617caf75c3be4f1a Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 17 Nov 2022 23:38:24 +0900
Subject: [PATCH 0012/1604] add `LegacyExportManager`
---
osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 4 +-
osu.Game/Database/LegacyBeatmapExporter.cs | 8 +-
osu.Game/Database/LegacyExportManager.cs | 55 +++++++++
osu.Game/Database/LegacyExporter.cs | 102 ----------------
osu.Game/Database/LegacyModelExporter.cs | 113 ++++++++++++++++++
osu.Game/Database/LegacyScoreExporter.cs | 34 +++---
osu.Game/Database/LegacySkinExporter.cs | 8 +-
.../Online/Leaderboards/LeaderboardScore.cs | 5 +-
osu.Game/OsuGame.cs | 4 +
.../Overlays/Settings/Sections/SkinSection.cs | 10 +-
osu.Game/Screens/Edit/Editor.cs | 9 +-
11 files changed, 209 insertions(+), 143 deletions(-)
create mode 100644 osu.Game/Database/LegacyExportManager.cs
delete mode 100644 osu.Game/Database/LegacyExporter.cs
create mode 100644 osu.Game/Database/LegacyModelExporter.cs
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 7a5f6dbd7c..ef68b06476 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -121,9 +121,9 @@ namespace osu.Game.Tests.Skins.IO
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "custom.osk"));
assertCorrectMetadata(import1, "name 1 [custom]", "author 1", osu);
- import1.PerformRead(s =>
+ import1.PerformRead(async s =>
{
- new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ await new LegacyExportManager().ExportAsync(s, exportStream);
});
string exportFilename = import1.GetDisplayString();
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index 3e11e898f3..140ce43fbd 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -3,16 +3,16 @@
using osu.Framework.Platform;
using osu.Game.Beatmaps;
-using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
namespace osu.Game.Database
{
- public class LegacyBeatmapExporter : LegacyExporter
+ public class LegacyBeatmapExporter : LegacyModelExporter
{
protected override string FileExtension => ".osz";
- public LegacyBeatmapExporter(Storage storage, INotificationOverlay? notificationOverlay)
- : base(storage, notificationOverlay)
+ public LegacyBeatmapExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
+ : base(storage, realm, notification)
{
}
}
diff --git a/osu.Game/Database/LegacyExportManager.cs b/osu.Game/Database/LegacyExportManager.cs
new file mode 100644
index 0000000000..18cd93ea35
--- /dev/null
+++ b/osu.Game/Database/LegacyExportManager.cs
@@ -0,0 +1,55 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Threading.Tasks;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Platform;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
+using osu.Game.Scoring;
+using osu.Game.Skinning;
+
+namespace osu.Game.Database
+{
+ [ExcludeFromDynamicCompile]
+ public class LegacyExportManager : Component
+ {
+ [Resolved]
+ private RealmAccess realmAccess { get; set; } = null!;
+
+ [Resolved]
+ private Storage exportStorage { get; set; } = null!;
+
+ [Resolved]
+ private INotificationOverlay? notifications { get; set; }
+
+ public async Task ExportAsync(IHasGuidPrimaryKey item)
+ {
+ var notification = new ProgressNotification
+ {
+ State = ProgressNotificationState.Active,
+ Text = "Exporting...",
+ CompletionText = "Export completed"
+ };
+ notifications?.Post(notification);
+
+ switch (item)
+ {
+ case SkinInfo:
+ await new LegacySkinExporter(exportStorage, realmAccess, notification).ExportASync(item);
+ break;
+
+ case ScoreInfo:
+ await new LegacyScoreExporter(exportStorage, realmAccess, notification).ExportASync(item, false);
+ break;
+
+ case BeatmapSetInfo:
+ await new LegacyBeatmapExporter(exportStorage, realmAccess, notification).ExportASync(item);
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs
deleted file mode 100644
index ed16e4bc80..0000000000
--- a/osu.Game/Database/LegacyExporter.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.IO;
-using System.Threading.Tasks;
-using osu.Framework.Platform;
-using osu.Game.Extensions;
-using osu.Game.Overlays;
-using osu.Game.Overlays.Notifications;
-using SharpCompress.Archives.Zip;
-
-namespace osu.Game.Database
-{
- ///
- /// A class which handles exporting legacy user data of a single type from osu-stable.
- ///
- public abstract class LegacyExporter
- where TModel : class, IHasNamedFiles
- {
- ///
- /// The file extension for exports (including the leading '.').
- ///
- protected abstract string FileExtension { get; }
-
- protected readonly Storage UserFileStorage;
-
- private readonly Storage exportStorage;
-
- private readonly INotificationOverlay? notificationOverlay;
-
- protected ProgressNotification Notification = null!;
-
- private string filename = null!;
-
- protected LegacyExporter(Storage storage, INotificationOverlay? notificationOverlay)
- {
- exportStorage = storage.GetStorageForDirectory(@"exports");
- UserFileStorage = storage.GetStorageForDirectory(@"files");
- this.notificationOverlay = notificationOverlay;
- }
-
- ///
- /// Exports an item to a legacy (.zip based) package.
- ///
- /// The item to export.
- public void Export(TModel item)
- {
- filename = $"{item.GetDisplayString().GetValidFilename()}{FileExtension}";
-
- Stream stream = exportStorage.CreateFileSafely(filename);
-
- Notification = new ProgressNotification
- {
- State = ProgressNotificationState.Active,
- Text = "Exporting...",
- CompletionText = "Export completed"
- };
- Notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
- Notification.CancelRequested += () =>
- {
- stream.Dispose();
- return true;
- };
-
- ExportModelTo(item, stream);
- notificationOverlay?.Post(Notification);
- }
-
- ///
- /// Exports an item to the given output stream.
- ///
- /// The item to export.
- /// The output stream to export to.
- public virtual void ExportModelTo(TModel model, Stream outputStream)
- {
- using (var archive = ZipArchive.Create())
- {
- foreach (var file in model.Files)
- archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
-
- Task.Factory.StartNew(() =>
- {
- archive.SaveTo(outputStream);
- }, Notification.CancellationToken).ContinueWith(t =>
- {
- if (t.IsCompletedSuccessfully)
- {
- outputStream.Dispose();
- Notification.State = ProgressNotificationState.Completed;
- }
- else
- {
- if (Notification.State == ProgressNotificationState.Cancelled) return;
-
- Notification.State = ProgressNotificationState.Cancelled;
- Notification.Text = "Export Failed";
- }
- });
- }
- }
- }
-}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
new file mode 100644
index 0000000000..d181226803
--- /dev/null
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -0,0 +1,113 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using osu.Framework.Platform;
+using osu.Game.Extensions;
+using osu.Game.Overlays.Notifications;
+using Realms;
+using SharpCompress.Archives.Zip;
+
+namespace osu.Game.Database
+{
+ ///
+ /// A class which handles exporting legacy user data of a single type from osu-stable.
+ ///
+ public abstract class LegacyModelExporter
+ where TModel : RealmObject
+ {
+ ///
+ /// The file extension for exports (including the leading '.').
+ ///
+ protected abstract string FileExtension { get; }
+
+ protected readonly Storage UserFileStorage;
+
+ private readonly Storage exportStorage;
+
+ private readonly RealmAccess realmAccess;
+
+ private readonly ProgressNotification notification;
+
+ protected ProgressNotification Notification = null!;
+
+ private string filename = null!;
+
+ protected LegacyModelExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
+ {
+ exportStorage = storage.GetStorageForDirectory(@"exports");
+ UserFileStorage = storage.GetStorageForDirectory(@"files");
+ this.notification = notification;
+ realmAccess = realm;
+ }
+
+ public async Task ExportASync(IHasGuidPrimaryKey uuid, bool needZipArchive = true)
+ {
+ Guid id = uuid.ID;
+ await Task.Run(() =>
+ {
+ realmAccess.Run(r =>
+ {
+ if (r.Find(id) is IHasNamedFiles model)
+ {
+ filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
+ }
+ else
+ {
+ return;
+ }
+
+ using (var outputStream = exportStorage.CreateFileSafely(filename))
+ {
+ if (needZipArchive)
+ {
+ using (var archive = ZipArchive.Create())
+ {
+ float i = 0;
+
+ foreach (var file in model.Files)
+ {
+ if (notification.CancellationToken.IsCancellationRequested) return;
+ archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ }
+
+ notification.Text = "Saving Zip Archive...";
+ archive.SaveTo(outputStream);
+ }
+ }
+ else
+ {
+ var file = model.Files.SingleOrDefault();
+ if (file == null)
+ return;
+
+ using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ inputStream.CopyTo(outputStream);
+ }
+ }
+ });
+ }).ContinueWith(t =>
+ {
+ if (t.IsFaulted)
+ {
+ notification.State = ProgressNotificationState.Cancelled;
+ return;
+ }
+
+ if (notification.CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ notification.CompletionText = "Export Complete, Click to open the folder";
+ notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+ notification.State = ProgressNotificationState.Completed;
+ });
+ }
+ }
+}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index 1564c7b077..ffbec0530b 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -1,36 +1,32 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.IO;
-using System.Linq;
using osu.Framework.Platform;
-using osu.Game.Extensions;
-using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Scoring;
namespace osu.Game.Database
{
- public class LegacyScoreExporter : LegacyExporter
+ public class LegacyScoreExporter : LegacyModelExporter
{
protected override string FileExtension => ".osr";
- public LegacyScoreExporter(Storage storage, INotificationOverlay? notificationOverlay)
- : base(storage, notificationOverlay)
+ public LegacyScoreExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
+ : base(storage, realm, notification)
{
}
- public override void ExportModelTo(ScoreInfo model, Stream outputStream)
- {
- var file = model.Files.SingleOrDefault();
- if (file == null)
- return;
-
- using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- inputStream.CopyTo(outputStream);
-
- Notification.State = ProgressNotificationState.Completed;
- outputStream.Dispose();
- }
+ //public override void ExportModelTo(ScoreInfo model, Stream outputStream)
+ //{
+ // var file = model.Files.SingleOrDefault();
+ // if (file == null)
+ // return;
+ //
+ // using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ // inputStream.CopyTo(outputStream);
+ //
+ // Notification.State = ProgressNotificationState.Completed;
+ // outputStream.Dispose();
+ //}
}
}
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index a78d69e7b9..a35636c248 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -2,17 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Platform;
-using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
using osu.Game.Skinning;
namespace osu.Game.Database
{
- public class LegacySkinExporter : LegacyExporter
+ public class LegacySkinExporter : LegacyModelExporter
{
protected override string FileExtension => ".osk";
- public LegacySkinExporter(Storage storage, INotificationOverlay? notificationOverlay)
- : base(storage, notificationOverlay)
+ public LegacySkinExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
+ : base(storage, realm, notification)
{
}
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 72f1a94ec8..e2f640a44c 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -76,7 +77,7 @@ namespace osu.Game.Online.Leaderboards
private Storage storage { get; set; }
[Resolved]
- private INotificationOverlay notificationOverlay { get; set; }
+ private LegacyExportManager exporter { get; set; }
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => Score;
@@ -430,7 +431,7 @@ namespace osu.Game.Online.Leaderboards
if (Score.Files.Count > 0)
{
- items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage, notificationOverlay).Export(Score)));
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => exporter.ExportAsync(Score))));
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index a93c187e53..09647f9d1e 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -126,6 +126,9 @@ namespace osu.Game
[Cached]
private readonly LegacyImportManager legacyImportManager = new LegacyImportManager();
+ [Cached]
+ private readonly LegacyExportManager legacyExportManager = new LegacyExportManager();
+
[Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
@@ -868,6 +871,7 @@ namespace osu.Game
}), rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(legacyImportManager, Add);
+ loadComponentSingleFile(legacyExportManager, Add);
loadComponentSingleFile(screenshotManager, Add);
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index df6f719b1e..6cd9e591e1 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -141,16 +141,14 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private Storage storage { get; set; }
- [CanBeNull]
- private INotificationOverlay notificationOverlay;
+ [Resolved]
+ private LegacyExportManager exporter { get; set; }
private Bindable currentSkin;
[BackgroundDependencyLoader]
- private void load(INotificationOverlay notificationOverlay)
+ private void load()
{
- this.notificationOverlay = notificationOverlay;
-
Text = SkinSettingsStrings.ExportSkinButton;
Action = export;
}
@@ -167,7 +165,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage, notificationOverlay).Export(s));
+ currentSkin.Value.SkinInfo.PerformRead(s => exporter.ExportAsync(s));
}
catch (Exception e)
{
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 03bdd69f34..e266239f34 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework;
using osu.Framework.Allocation;
@@ -93,9 +94,6 @@ namespace osu.Game.Screens.Edit
[Resolved]
private Storage storage { get; set; }
- [Resolved]
- private INotificationOverlay notificationOverlay { get; set; }
-
[Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
@@ -185,6 +183,9 @@ namespace osu.Game.Screens.Edit
private Bindable editorBackgroundDim;
private Bindable editorHitMarkers;
+ [Resolved]
+ private LegacyExportManager exporter { get; set; }
+
public Editor(EditorLoader loader = null)
{
this.loader = loader;
@@ -941,7 +942,7 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
- new LegacyBeatmapExporter(storage, notificationOverlay).Export(Beatmap.Value.BeatmapSetInfo);
+ Task.Run(() => exporter.ExportAsync(Beatmap.Value.BeatmapSetInfo));
}
///
From fc4a6cb125c50668765d8b43f5bcf88cb242f28c Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 19 Nov 2022 01:02:35 +0900
Subject: [PATCH 0013/1604] working with test
---
osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 31 +++---
osu.Game/Database/LegacyBeatmapExporter.cs | 5 +-
osu.Game/Database/LegacyExportManager.cs | 2 +-
osu.Game/Database/LegacyModelExporter.cs | 106 +++++++++++----------
osu.Game/Database/LegacyScoreExporter.cs | 52 +++++++---
osu.Game/Database/LegacySkinExporter.cs | 5 +-
6 files changed, 113 insertions(+), 88 deletions(-)
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index ef68b06476..0c3c459e87 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -10,12 +10,11 @@ using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
-using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
using osu.Game.Skinning;
using SharpCompress.Archives.Zip;
@@ -121,9 +120,9 @@ namespace osu.Game.Tests.Skins.IO
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "custom.osk"));
assertCorrectMetadata(import1, "name 1 [custom]", "author 1", osu);
- import1.PerformRead(async s =>
+ await import1.PerformRead(async s =>
{
- await new LegacyExportManager().ExportAsync(s, exportStream);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
});
string exportFilename = import1.GetDisplayString();
@@ -190,7 +189,7 @@ namespace osu.Game.Tests.Skins.IO
});
[Test]
- public Task TestExportThenImportDefaultSkin() => runSkinTest(osu =>
+ public Task TestExportThenImportDefaultSkin() => runSkinTest(async osu =>
{
var skinManager = osu.Dependencies.Get();
@@ -200,30 +199,28 @@ namespace osu.Game.Tests.Skins.IO
Guid originalSkinId = skinManager.CurrentSkinInfo.Value.ID;
- skinManager.CurrentSkinInfo.Value.PerformRead(s =>
+ await skinManager.CurrentSkinInfo.Value.PerformRead(async s =>
{
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(ArgonSkin), s.CreateInstance(skinManager).GetType());
- new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
Assert.Greater(exportStream.Length, 0);
});
- var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk"));
+ var imported = await skinManager.Import(new ImportTask(exportStream, "exported.osk"));
- imported.GetResultSafely().PerformRead(s =>
+ imported.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreNotEqual(originalSkinId, s.ID);
Assert.AreEqual(typeof(ArgonSkin), s.CreateInstance(skinManager).GetType());
});
-
- return Task.CompletedTask;
});
[Test]
- public Task TestExportThenImportClassicSkin() => runSkinTest(osu =>
+ public Task TestExportThenImportClassicSkin() => runSkinTest(async osu =>
{
var skinManager = osu.Dependencies.Get();
@@ -235,26 +232,24 @@ namespace osu.Game.Tests.Skins.IO
Guid originalSkinId = skinManager.CurrentSkinInfo.Value.ID;
- skinManager.CurrentSkinInfo.Value.PerformRead(s =>
+ await skinManager.CurrentSkinInfo.Value.PerformRead(async s =>
{
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
- new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportModelTo(s, exportStream);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
Assert.Greater(exportStream.Length, 0);
});
- var imported = skinManager.Import(new ImportTask(exportStream, "exported.osk"));
+ var imported = await skinManager.Import(new ImportTask(exportStream, "exported.osk"));
- imported.GetResultSafely().PerformRead(s =>
+ imported.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreNotEqual(originalSkinId, s.ID);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
});
-
- return Task.CompletedTask;
});
#endregion
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index 140ce43fbd..5505d141ee 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.IO;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Overlays.Notifications;
@@ -11,8 +12,8 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osz";
- public LegacyBeatmapExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
- : base(storage, realm, notification)
+ public LegacyBeatmapExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
+ : base(storage, realm, notification, stream)
{
}
}
diff --git a/osu.Game/Database/LegacyExportManager.cs b/osu.Game/Database/LegacyExportManager.cs
index 18cd93ea35..08594ab020 100644
--- a/osu.Game/Database/LegacyExportManager.cs
+++ b/osu.Game/Database/LegacyExportManager.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Database
break;
case ScoreInfo:
- await new LegacyScoreExporter(exportStorage, realmAccess, notification).ExportASync(item, false);
+ await new LegacyScoreExporter(exportStorage, realmAccess, notification).ExportASync(item);
break;
case BeatmapSetInfo:
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index d181226803..a4b0f7ba9d 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Platform;
@@ -25,89 +26,92 @@ namespace osu.Game.Database
protected readonly Storage UserFileStorage;
- private readonly Storage exportStorage;
+ protected readonly Storage ExportStorage;
- private readonly RealmAccess realmAccess;
+ protected readonly RealmAccess RealmAccess;
- private readonly ProgressNotification notification;
+ protected readonly ProgressNotification Notification;
- protected ProgressNotification Notification = null!;
+ protected string Filename = null!;
- private string filename = null!;
+ protected Stream? OutputStream;
- protected LegacyModelExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
+ protected bool ShouldDisposeStream;
+
+ protected LegacyModelExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
{
- exportStorage = storage.GetStorageForDirectory(@"exports");
+ ExportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
- this.notification = notification;
- realmAccess = realm;
+ Notification = notification;
+ RealmAccess = realm;
+ OutputStream = stream;
+ ShouldDisposeStream = false;
}
- public async Task ExportASync(IHasGuidPrimaryKey uuid, bool needZipArchive = true)
+ public virtual async Task ExportASync(IHasGuidPrimaryKey uuid)
{
Guid id = uuid.ID;
await Task.Run(() =>
{
- realmAccess.Run(r =>
+ RealmAccess.Run(r =>
{
if (r.Find(id) is IHasNamedFiles model)
{
- filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
+ Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
}
else
{
return;
}
- using (var outputStream = exportStorage.CreateFileSafely(filename))
+ if (OutputStream == null)
{
- if (needZipArchive)
+ OutputStream = ExportStorage.CreateFileSafely(Filename);
+ ShouldDisposeStream = true;
+ }
+
+ using (var archive = ZipArchive.Create())
+ {
+ float i = 0;
+
+ foreach (var file in model.Files)
{
- using (var archive = ZipArchive.Create())
- {
- float i = 0;
+ if (Notification.CancellationToken.IsCancellationRequested) return;
- foreach (var file in model.Files)
- {
- if (notification.CancellationToken.IsCancellationRequested) return;
- archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
- }
-
- notification.Text = "Saving Zip Archive...";
- archive.SaveTo(outputStream);
- }
+ archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ i++;
+ Notification.Progress = i / model.Files.Count();
+ Notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
}
- else
- {
- var file = model.Files.SingleOrDefault();
- if (file == null)
- return;
- using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- inputStream.CopyTo(outputStream);
- }
+ Notification.Text = "Saving Zip Archive...";
+ archive.SaveTo(OutputStream);
}
});
- }).ContinueWith(t =>
+ }).ContinueWith(OnComplete);
+ }
+
+ protected void OnComplete(Task t)
+ {
+ if (ShouldDisposeStream)
{
- if (t.IsFaulted)
- {
- notification.State = ProgressNotificationState.Cancelled;
- return;
- }
+ OutputStream?.Dispose();
+ }
- if (notification.CancellationToken.IsCancellationRequested)
- {
- return;
- }
+ if (t.IsFaulted)
+ {
+ Notification.State = ProgressNotificationState.Cancelled;
+ return;
+ }
- notification.CompletionText = "Export Complete, Click to open the folder";
- notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
- notification.State = ProgressNotificationState.Completed;
- });
+ if (Notification.CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ Notification.CompletionText = "Export Complete, Click to open the folder";
+ Notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(Filename);
+ Notification.State = ProgressNotificationState.Completed;
}
}
}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index ffbec0530b..3004c02978 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -1,7 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
using osu.Framework.Platform;
+using osu.Game.Extensions;
using osu.Game.Overlays.Notifications;
using osu.Game.Scoring;
@@ -11,22 +15,42 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osr";
- public LegacyScoreExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
- : base(storage, realm, notification)
+ public LegacyScoreExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
+ : base(storage, realm, notification, stream)
{
}
- //public override void ExportModelTo(ScoreInfo model, Stream outputStream)
- //{
- // var file = model.Files.SingleOrDefault();
- // if (file == null)
- // return;
- //
- // using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- // inputStream.CopyTo(outputStream);
- //
- // Notification.State = ProgressNotificationState.Completed;
- // outputStream.Dispose();
- //}
+ public override async Task ExportASync(IHasGuidPrimaryKey uuid)
+ {
+ await Task.Run(() =>
+ {
+ RealmAccess.Run(r =>
+ {
+ if (r.Find(uuid.ID) is IHasNamedFiles model)
+ {
+ Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
+ }
+ else
+ {
+ return;
+ }
+
+ var file = model.Files.SingleOrDefault();
+ if (file == null)
+ return;
+
+ if (Notification.CancellationToken.IsCancellationRequested) return;
+
+ if (OutputStream == null)
+ {
+ OutputStream = ExportStorage.CreateFileSafely(Filename);
+ ShouldDisposeStream = true;
+ }
+
+ using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ inputStream.CopyTo(OutputStream);
+ });
+ }).ContinueWith(OnComplete);
+ }
}
}
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index a35636c248..4168763324 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.IO;
using osu.Framework.Platform;
using osu.Game.Overlays.Notifications;
using osu.Game.Skinning;
@@ -11,8 +12,8 @@ namespace osu.Game.Database
{
protected override string FileExtension => ".osk";
- public LegacySkinExporter(Storage storage, RealmAccess realm, ProgressNotification notification)
- : base(storage, realm, notification)
+ public LegacySkinExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
+ : base(storage, realm, notification, stream)
{
}
}
From 4e457871f3a518b05d6d95e7637c74b7ddc978fb Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 19 Nov 2022 01:03:09 +0900
Subject: [PATCH 0014/1604] impossible null and remove storage
---
osu.Game/Online/Leaderboards/LeaderboardScore.cs | 12 ++++--------
osu.Game/Overlays/Settings/Sections/SkinSection.cs | 8 ++------
osu.Game/Screens/Edit/Editor.cs | 8 ++------
3 files changed, 8 insertions(+), 20 deletions(-)
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index e2f640a44c..b374736648 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -18,7 +18,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
-using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Graphics;
@@ -67,16 +66,13 @@ namespace osu.Game.Online.Leaderboards
private List statisticsLabels;
- [Resolved(CanBeNull = true)]
+ [Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
- [Resolved(CanBeNull = true)]
+ [Resolved(canBeNull: true)]
private SongSelect songSelect { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
- [Resolved]
+ [Resolved(canBeNull: true)]
private LegacyExportManager exporter { get; set; }
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
@@ -431,7 +427,7 @@ namespace osu.Game.Online.Leaderboards
if (Score.Files.Count > 0)
{
- items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => exporter.ExportAsync(Score))));
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => exporter?.ExportAsync(Score))));
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
}
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 6cd9e591e1..e5a26c19fc 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -13,7 +13,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
-using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
@@ -138,10 +137,7 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private SkinManager skins { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
- [Resolved]
+ [Resolved(canBeNull: true)]
private LegacyExportManager exporter { get; set; }
private Bindable currentSkin;
@@ -165,7 +161,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- currentSkin.Value.SkinInfo.PerformRead(s => exporter.ExportAsync(s));
+ currentSkin.Value.SkinInfo.PerformRead(s => exporter?.ExportAsync(s));
}
catch (Exception e)
{
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index e266239f34..77f14f689a 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -21,7 +21,6 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Logging;
-using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Threading;
@@ -91,9 +90,6 @@ namespace osu.Game.Screens.Edit
[Resolved]
private RulesetStore rulesets { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
[Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
@@ -183,7 +179,7 @@ namespace osu.Game.Screens.Edit
private Bindable editorBackgroundDim;
private Bindable editorHitMarkers;
- [Resolved]
+ [Resolved(canBeNull: true)]
private LegacyExportManager exporter { get; set; }
public Editor(EditorLoader loader = null)
@@ -942,7 +938,7 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
- Task.Run(() => exporter.ExportAsync(Beatmap.Value.BeatmapSetInfo));
+ Task.Run(() => exporter?.ExportAsync(Beatmap.Value.BeatmapSetInfo));
}
///
From 28867fbbb1750640a30113d576500d0a82502db7 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 19 Nov 2022 12:34:35 +0900
Subject: [PATCH 0015/1604] Add comment
---
osu.Game/Database/LegacyExportManager.cs | 18 ++++++++++++++----
osu.Game/Database/LegacyModelExporter.cs | 6 ++++++
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Database/LegacyExportManager.cs b/osu.Game/Database/LegacyExportManager.cs
index 08594ab020..19fca623c5 100644
--- a/osu.Game/Database/LegacyExportManager.cs
+++ b/osu.Game/Database/LegacyExportManager.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.IO;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -14,6 +15,9 @@ using osu.Game.Skinning;
namespace osu.Game.Database
{
+ ///
+ /// A class which centrally manage legacy file exports.
+ ///
[ExcludeFromDynamicCompile]
public class LegacyExportManager : Component
{
@@ -26,7 +30,13 @@ namespace osu.Game.Database
[Resolved]
private INotificationOverlay? notifications { get; set; }
- public async Task ExportAsync(IHasGuidPrimaryKey item)
+ ///
+ /// Identify the model type and and automatically assigned to the corresponding exporter.
+ ///
+ /// The model should export.
+ /// The stream if requires a specific output-stream
+ ///
+ public async Task ExportAsync(IHasGuidPrimaryKey item, Stream? stream = null)
{
var notification = new ProgressNotification
{
@@ -39,15 +49,15 @@ namespace osu.Game.Database
switch (item)
{
case SkinInfo:
- await new LegacySkinExporter(exportStorage, realmAccess, notification).ExportASync(item);
+ await new LegacySkinExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
break;
case ScoreInfo:
- await new LegacyScoreExporter(exportStorage, realmAccess, notification).ExportASync(item);
+ await new LegacyScoreExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
break;
case BeatmapSetInfo:
- await new LegacyBeatmapExporter(exportStorage, realmAccess, notification).ExportASync(item);
+ await new LegacyBeatmapExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
break;
}
}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index a4b0f7ba9d..d734d3341c 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -48,6 +48,12 @@ namespace osu.Game.Database
ShouldDisposeStream = false;
}
+ ///
+ /// Export model to
+ /// if is null, model will export to default folder.
+ ///
+ /// The model which have Guid.
+ ///
public virtual async Task ExportASync(IHasGuidPrimaryKey uuid)
{
Guid id = uuid.ID;
From 2653bd2f997c305d1497d74da66e33be9630cc84 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 19 Nov 2022 12:57:56 +0900
Subject: [PATCH 0016/1604] Make notification cannot cancel when Saving Zip
Archive
This operation cannot be stopped(if not dispose stream).
so make it cannot cancel
---
osu.Game/Database/LegacyModelExporter.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index d734d3341c..9fa6f64ee4 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -56,6 +56,9 @@ namespace osu.Game.Database
///
public virtual async Task ExportASync(IHasGuidPrimaryKey uuid)
{
+ bool canCancel = true;
+ Notification.CancelRequested += () => canCancel;
+
Guid id = uuid.ID;
await Task.Run(() =>
{
@@ -91,6 +94,7 @@ namespace osu.Game.Database
}
Notification.Text = "Saving Zip Archive...";
+ canCancel = false;
archive.SaveTo(OutputStream);
}
});
From 60ef88844c31459dfa94a5ffdb1fdf41530f2c96 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 17:09:56 +0900
Subject: [PATCH 0017/1604] Recover accidentally deleted codes
---
osu.Game/Database/RealmAccess.cs | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index 5a7ead1c59..1a938c12e5 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -173,6 +173,11 @@ namespace osu.Game.Database
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
Filename += realm_extension;
+#if DEBUG
+ if (!DebugUtils.IsNUnitRunning)
+ applyFilenameSchemaSuffix(ref Filename);
+#endif
+
string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}";
// Attempt to recover a newer database version if available.
From ed53168267485ba0ca2bdc97057fd43566770cd4 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 17:42:11 +0900
Subject: [PATCH 0018/1604] typo fix
---
osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 6 +++---
osu.Game/Database/LegacyExportManager.cs | 4 ++--
osu.Game/Database/LegacyModelExporter.cs | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 0c3c459e87..c4dde59e3f 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -122,7 +122,7 @@ namespace osu.Game.Tests.Skins.IO
await import1.PerformRead(async s =>
{
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
});
string exportFilename = import1.GetDisplayString();
@@ -204,7 +204,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(ArgonSkin), s.CreateInstance(skinManager).GetType());
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
Assert.Greater(exportStream.Length, 0);
});
@@ -237,7 +237,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportASync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
Assert.Greater(exportStream.Length, 0);
});
diff --git a/osu.Game/Database/LegacyExportManager.cs b/osu.Game/Database/LegacyExportManager.cs
index 19fca623c5..a694a0705e 100644
--- a/osu.Game/Database/LegacyExportManager.cs
+++ b/osu.Game/Database/LegacyExportManager.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Database
switch (item)
{
case SkinInfo:
- await new LegacySkinExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
+ await new LegacySkinExporter(exportStorage, realmAccess, notification, stream).ExportAsync(item);
break;
case ScoreInfo:
@@ -57,7 +57,7 @@ namespace osu.Game.Database
break;
case BeatmapSetInfo:
- await new LegacyBeatmapExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
+ await new LegacyBeatmapExporter(exportStorage, realmAccess, notification, stream).ExportAsync(item);
break;
}
}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 9fa6f64ee4..01ebbdcaff 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -54,11 +54,11 @@ namespace osu.Game.Database
///
/// The model which have Guid.
///
- public virtual async Task ExportASync(IHasGuidPrimaryKey uuid)
{
bool canCancel = true;
Notification.CancelRequested += () => canCancel;
+ public virtual async Task ExportAsync(IHasGuidPrimaryKey uuid)
Guid id = uuid.ID;
await Task.Run(() =>
{
From e37d30a3733871eb39622d6f5ea95d6cfe22a5a6 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 18:58:01 +0900
Subject: [PATCH 0019/1604] refactor based on reviews
removed LegacyExportManager
Separated the method of CreateZip method and the default export method
---
osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 7 +-
osu.Game/Database/LegacyBeatmapExporter.cs | 11 +-
osu.Game/Database/LegacyExportManager.cs | 65 ---------
osu.Game/Database/LegacyModelExporter.cs | 128 +++++++++---------
osu.Game/Database/LegacyScoreExporter.cs | 33 ++---
osu.Game/Database/LegacySkinExporter.cs | 11 +-
.../Online/Leaderboards/LeaderboardScore.cs | 13 +-
osu.Game/OsuGame.cs | 4 -
.../Overlays/Settings/Sections/SkinSection.cs | 13 +-
osu.Game/Screens/Edit/Editor.cs | 12 +-
10 files changed, 113 insertions(+), 184 deletions(-)
delete mode 100644 osu.Game/Database/LegacyExportManager.cs
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index c4dde59e3f..703c63b91a 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -14,7 +14,6 @@ using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
-using osu.Game.Overlays.Notifications;
using osu.Game.Skinning;
using SharpCompress.Archives.Zip;
@@ -122,7 +121,7 @@ namespace osu.Game.Tests.Skins.IO
await import1.PerformRead(async s =>
{
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportToStreamAsync(s, exportStream);
});
string exportFilename = import1.GetDisplayString();
@@ -204,7 +203,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(ArgonSkin), s.CreateInstance(skinManager).GetType());
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportToStreamAsync(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
@@ -237,7 +236,7 @@ namespace osu.Game.Tests.Skins.IO
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultLegacySkin), s.CreateInstance(skinManager).GetType());
- await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get(), new ProgressNotification(), exportStream).ExportAsync(s);
+ await new LegacySkinExporter(osu.Dependencies.Get(), osu.Dependencies.Get()).ExportToStreamAsync(s, exportStream);
Assert.Greater(exportStream.Length, 0);
});
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index 5505d141ee..22bccbcda6 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -1,20 +1,19 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.IO;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
-using osu.Game.Overlays.Notifications;
+using osu.Game.Overlays;
namespace osu.Game.Database
{
public class LegacyBeatmapExporter : LegacyModelExporter
{
- protected override string FileExtension => ".osz";
-
- public LegacyBeatmapExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
- : base(storage, realm, notification, stream)
+ public LegacyBeatmapExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
+ : base(storage, realm, notifications)
{
}
+
+ protected override string FileExtension => ".osz";
}
}
diff --git a/osu.Game/Database/LegacyExportManager.cs b/osu.Game/Database/LegacyExportManager.cs
deleted file mode 100644
index a694a0705e..0000000000
--- a/osu.Game/Database/LegacyExportManager.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.IO;
-using System.Threading.Tasks;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Platform;
-using osu.Framework.Testing;
-using osu.Game.Beatmaps;
-using osu.Game.Overlays;
-using osu.Game.Overlays.Notifications;
-using osu.Game.Scoring;
-using osu.Game.Skinning;
-
-namespace osu.Game.Database
-{
- ///
- /// A class which centrally manage legacy file exports.
- ///
- [ExcludeFromDynamicCompile]
- public class LegacyExportManager : Component
- {
- [Resolved]
- private RealmAccess realmAccess { get; set; } = null!;
-
- [Resolved]
- private Storage exportStorage { get; set; } = null!;
-
- [Resolved]
- private INotificationOverlay? notifications { get; set; }
-
- ///
- /// Identify the model type and and automatically assigned to the corresponding exporter.
- ///
- /// The model should export.
- /// The stream if requires a specific output-stream
- ///
- public async Task ExportAsync(IHasGuidPrimaryKey item, Stream? stream = null)
- {
- var notification = new ProgressNotification
- {
- State = ProgressNotificationState.Active,
- Text = "Exporting...",
- CompletionText = "Export completed"
- };
- notifications?.Post(notification);
-
- switch (item)
- {
- case SkinInfo:
- await new LegacySkinExporter(exportStorage, realmAccess, notification, stream).ExportAsync(item);
- break;
-
- case ScoreInfo:
- await new LegacyScoreExporter(exportStorage, realmAccess, notification, stream).ExportASync(item);
- break;
-
- case BeatmapSetInfo:
- await new LegacyBeatmapExporter(exportStorage, realmAccess, notification, stream).ExportAsync(item);
- break;
- }
- }
- }
-}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 01ebbdcaff..83167e7319 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -5,8 +5,10 @@ using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Game.Extensions;
+using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using Realms;
using SharpCompress.Archives.Zip;
@@ -16,112 +18,106 @@ namespace osu.Game.Database
///
/// A class which handles exporting legacy user data of a single type from osu-stable.
///
- public abstract class LegacyModelExporter
- where TModel : RealmObject
+ public abstract class LegacyModelExporter : Component
+ where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
{
///
/// The file extension for exports (including the leading '.').
///
protected abstract string FileExtension { get; }
- protected readonly Storage UserFileStorage;
+ protected Storage UserFileStorage;
+ protected Storage ExportStorage;
- protected readonly Storage ExportStorage;
+ protected RealmAccess RealmAccess;
- protected readonly RealmAccess RealmAccess;
-
- protected readonly ProgressNotification Notification;
+ private readonly ProgressNotification notification;
protected string Filename = null!;
- protected Stream? OutputStream;
+ private bool canCancel = true;
- protected bool ShouldDisposeStream;
-
- protected LegacyModelExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
+ protected LegacyModelExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
{
ExportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
- Notification = notification;
RealmAccess = realm;
- OutputStream = stream;
- ShouldDisposeStream = false;
+
+ notification = new ProgressNotification
+ {
+ State = ProgressNotificationState.Active,
+ Text = "Exporting...",
+ CompletionText = "Export completed"
+ };
+ notification.CancelRequested += () => canCancel;
+
+ notifications?.Post(notification);
}
- ///
- /// Export model to
- /// if is null, model will export to default folder.
- ///
- /// The model which have Guid.
- ///
+ public async Task ExportAsync(RealmObject item)
{
- bool canCancel = true;
- Notification.CancelRequested += () => canCancel;
+ if (item is TModel model)
+ {
+ Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
- public virtual async Task ExportAsync(IHasGuidPrimaryKey uuid)
+ using (var stream = ExportStorage.CreateFileSafely(Filename))
+ {
+ await ExportToStreamAsync(model, stream);
+ }
+ }
+ }
+
+ public virtual async Task ExportToStreamAsync(TModel uuid, Stream stream)
+ {
Guid id = uuid.ID;
await Task.Run(() =>
{
RealmAccess.Run(r =>
{
- if (r.Find(id) is IHasNamedFiles model)
- {
- Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
- }
- else
- {
- return;
- }
-
- if (OutputStream == null)
- {
- OutputStream = ExportStorage.CreateFileSafely(Filename);
- ShouldDisposeStream = true;
- }
-
- using (var archive = ZipArchive.Create())
- {
- float i = 0;
-
- foreach (var file in model.Files)
- {
- if (Notification.CancellationToken.IsCancellationRequested) return;
-
- archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
- i++;
- Notification.Progress = i / model.Files.Count();
- Notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
- }
-
- Notification.Text = "Saving Zip Archive...";
- canCancel = false;
- archive.SaveTo(OutputStream);
- }
+ TModel model = r.Find(id);
+ createZipArchive(model, stream);
});
}).ContinueWith(OnComplete);
}
+ private void createZipArchive(TModel model, Stream outputStream)
+ {
+ using (var archive = ZipArchive.Create())
+ {
+ float i = 0;
+
+ foreach (var file in model.Files)
+ {
+ if (notification.CancellationToken.IsCancellationRequested) return;
+
+ archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ }
+
+ notification.Text = "Saving Zip Archive...";
+ canCancel = false;
+ archive.SaveTo(outputStream);
+ }
+ }
+
protected void OnComplete(Task t)
{
- if (ShouldDisposeStream)
- {
- OutputStream?.Dispose();
- }
-
if (t.IsFaulted)
{
- Notification.State = ProgressNotificationState.Cancelled;
+ notification.State = ProgressNotificationState.Cancelled;
return;
}
- if (Notification.CancellationToken.IsCancellationRequested)
+ if (notification.CancellationToken.IsCancellationRequested)
{
return;
}
- Notification.CompletionText = "Export Complete, Click to open the folder";
- Notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(Filename);
- Notification.State = ProgressNotificationState.Completed;
+ notification.CompletionText = "Export Complete, Click to open the folder";
+ notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(Filename);
+ notification.State = ProgressNotificationState.Completed;
}
}
}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index 3004c02978..e00bc2a0ca 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -6,49 +6,36 @@ using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Extensions;
-using osu.Game.Overlays.Notifications;
+using osu.Game.Overlays;
using osu.Game.Scoring;
namespace osu.Game.Database
{
public class LegacyScoreExporter : LegacyModelExporter
{
- protected override string FileExtension => ".osr";
-
- public LegacyScoreExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
- : base(storage, realm, notification, stream)
+ public LegacyScoreExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
+ : base(storage, realm, notifications)
{
}
- public override async Task ExportASync(IHasGuidPrimaryKey uuid)
+ protected override string FileExtension => ".osr";
+
+ public override async Task ExportToStreamAsync(ScoreInfo uuid, Stream stream)
{
await Task.Run(() =>
{
RealmAccess.Run(r =>
{
- if (r.Find(uuid.ID) is IHasNamedFiles model)
- {
- Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
- }
- else
- {
- return;
- }
+ ScoreInfo model = r.Find(uuid.ID);
+
+ Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
var file = model.Files.SingleOrDefault();
if (file == null)
return;
- if (Notification.CancellationToken.IsCancellationRequested) return;
-
- if (OutputStream == null)
- {
- OutputStream = ExportStorage.CreateFileSafely(Filename);
- ShouldDisposeStream = true;
- }
-
using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- inputStream.CopyTo(OutputStream);
+ inputStream.CopyTo(stream);
});
}).ContinueWith(OnComplete);
}
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index 4168763324..6e65963f29 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -1,20 +1,19 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.IO;
using osu.Framework.Platform;
-using osu.Game.Overlays.Notifications;
+using osu.Game.Overlays;
using osu.Game.Skinning;
namespace osu.Game.Database
{
public class LegacySkinExporter : LegacyModelExporter
{
- protected override string FileExtension => ".osk";
-
- public LegacySkinExporter(Storage storage, RealmAccess realm, ProgressNotification notification, Stream? stream = null)
- : base(storage, realm, notification, stream)
+ public LegacySkinExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
+ : base(storage, realm, notifications)
{
}
+
+ protected override string FileExtension => ".osk";
}
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index b374736648..0a9f2a81bd 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -34,6 +34,7 @@ using osuTK.Graphics;
using osu.Game.Online.API;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Utils;
+using osu.Framework.Platform;
namespace osu.Game.Online.Leaderboards
{
@@ -72,8 +73,14 @@ namespace osu.Game.Online.Leaderboards
[Resolved(canBeNull: true)]
private SongSelect songSelect { get; set; }
- [Resolved(canBeNull: true)]
- private LegacyExportManager exporter { get; set; }
+ [Resolved]
+ private Storage storage { get; set; }
+
+ [Resolved]
+ private RealmAccess realm { get; set; }
+
+ [Resolved]
+ private INotificationOverlay notifications { get; set; }
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => Score;
@@ -427,7 +434,7 @@ namespace osu.Game.Online.Leaderboards
if (Score.Files.Count > 0)
{
- items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => exporter?.ExportAsync(Score))));
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => new LegacyScoreExporter(storage, realm, notifications).ExportAsync(Score))));
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 09647f9d1e..a93c187e53 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -126,9 +126,6 @@ namespace osu.Game
[Cached]
private readonly LegacyImportManager legacyImportManager = new LegacyImportManager();
- [Cached]
- private readonly LegacyExportManager legacyExportManager = new LegacyExportManager();
-
[Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
@@ -871,7 +868,6 @@ namespace osu.Game
}), rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(legacyImportManager, Add);
- loadComponentSingleFile(legacyExportManager, Add);
loadComponentSingleFile(screenshotManager, Add);
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index e5a26c19fc..46e760283d 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -13,6 +13,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
+using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
@@ -137,8 +138,14 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private SkinManager skins { get; set; }
- [Resolved(canBeNull: true)]
- private LegacyExportManager exporter { get; set; }
+ [Resolved]
+ private Storage storage { get; set; }
+
+ [Resolved]
+ private RealmAccess realm { get; set; }
+
+ [Resolved]
+ private INotificationOverlay notifications { get; set; }
private Bindable currentSkin;
@@ -161,7 +168,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- currentSkin.Value.SkinInfo.PerformRead(s => exporter?.ExportAsync(s));
+ currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage, realm, notifications).ExportAsync(s));
}
catch (Exception e)
{
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 77f14f689a..e53c550a1a 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -21,6 +21,7 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Logging;
+using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Threading;
@@ -90,6 +91,12 @@ namespace osu.Game.Screens.Edit
[Resolved]
private RulesetStore rulesets { get; set; }
+ [Resolved]
+ private Storage storage { get; set; }
+
+ [Resolved]
+ private RealmAccess realm { get; set; }
+
[Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
@@ -179,9 +186,6 @@ namespace osu.Game.Screens.Edit
private Bindable editorBackgroundDim;
private Bindable editorHitMarkers;
- [Resolved(canBeNull: true)]
- private LegacyExportManager exporter { get; set; }
-
public Editor(EditorLoader loader = null)
{
this.loader = loader;
@@ -938,7 +942,7 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
- Task.Run(() => exporter?.ExportAsync(Beatmap.Value.BeatmapSetInfo));
+ Task.Run(() => new LegacyBeatmapExporter(storage, realm, notifications).ExportAsync(Beatmap.Value.BeatmapSetInfo));
}
///
From 9c6421a462d67e30d9bf9d4b2cdf9171765ae97e Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 19:00:10 +0900
Subject: [PATCH 0020/1604] log the error
---
osu.Game/Database/LegacyModelExporter.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 83167e7319..9769a6d42a 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Graphics;
+using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Extensions;
using osu.Game.Overlays;
@@ -107,6 +108,8 @@ namespace osu.Game.Database
if (t.IsFaulted)
{
notification.State = ProgressNotificationState.Cancelled;
+ Logger.Error(t.Exception, "An error occurred while exporting");
+
return;
}
From 564f136945c0f3fef744014c5762f44145e325ae Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 19:04:05 +0900
Subject: [PATCH 0021/1604] add xmldoc
---
osu.Game/Database/LegacyModelExporter.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 9769a6d42a..47068a8d2b 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -55,6 +55,11 @@ namespace osu.Game.Database
notifications?.Post(notification);
}
+ ///
+ /// Export the model to default folder.
+ ///
+ /// The model should export.
+ ///
public async Task ExportAsync(RealmObject item)
{
if (item is TModel model)
@@ -68,6 +73,12 @@ namespace osu.Game.Database
}
}
+ ///
+ /// Export te model corresponding to uuid to given stream.
+ ///
+ /// The medel which have .
+ /// The stream to export.
+ ///
public virtual async Task ExportToStreamAsync(TModel uuid, Stream stream)
{
Guid id = uuid.ID;
From 162f0bb95e1c525ce926935fafbcf98f3a76bdac Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 19:05:26 +0900
Subject: [PATCH 0022/1604] filename can be private
---
osu.Game/Database/LegacyModelExporter.cs | 8 ++++----
osu.Game/Database/LegacyScoreExporter.cs | 2 --
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 47068a8d2b..3780f2b9cc 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Database
private readonly ProgressNotification notification;
- protected string Filename = null!;
+ private string filename = "";
private bool canCancel = true;
@@ -64,9 +64,9 @@ namespace osu.Game.Database
{
if (item is TModel model)
{
- Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
+ filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
- using (var stream = ExportStorage.CreateFileSafely(Filename))
+ using (var stream = ExportStorage.CreateFileSafely(filename))
{
await ExportToStreamAsync(model, stream);
}
@@ -130,7 +130,7 @@ namespace osu.Game.Database
}
notification.CompletionText = "Export Complete, Click to open the folder";
- notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(Filename);
+ notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(filename);
notification.State = ProgressNotificationState.Completed;
}
}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index e00bc2a0ca..ae0fbf8d19 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -28,8 +28,6 @@ namespace osu.Game.Database
{
ScoreInfo model = r.Find(uuid.ID);
- Filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
-
var file = model.Files.SingleOrDefault();
if (file == null)
return;
From c509c5be40a449b872053e129358dd747b7e72f1 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Mon, 21 Nov 2022 19:45:30 +0900
Subject: [PATCH 0023/1604] impossible null
---
osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +-
osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 0a9f2a81bd..27c3e0ce47 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -79,7 +79,7 @@ namespace osu.Game.Online.Leaderboards
[Resolved]
private RealmAccess realm { get; set; }
- [Resolved]
+ [Resolved(canBeNull: true)]
private INotificationOverlay notifications { get; set; }
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 46e760283d..eb51c18e31 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private RealmAccess realm { get; set; }
- [Resolved]
+ [Resolved(canBeNull: true)]
private INotificationOverlay notifications { get; set; }
private Bindable currentSkin;
From 6adac853e85f7ebf81f61fa90a3d3afc8f556d0a Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 27 Nov 2022 09:58:54 +0900
Subject: [PATCH 0024/1604] Spite sync method `ExportToStream`
`uuid` to `model`
---
osu.Game/Database/LegacyModelExporter.cs | 14 ++++++++------
osu.Game/Database/LegacyScoreExporter.cs | 21 ++++++---------------
2 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 3780f2b9cc..3523d18f29 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -74,24 +74,26 @@ namespace osu.Game.Database
}
///
- /// Export te model corresponding to uuid to given stream.
+ /// Export te model corresponding to model to given stream.
///
- /// The medel which have .
+ /// The medel which have .
/// The stream to export.
///
- public virtual async Task ExportToStreamAsync(TModel uuid, Stream stream)
+ public async Task ExportToStreamAsync(TModel model, Stream stream)
{
- Guid id = uuid.ID;
+ Guid id = model.ID;
await Task.Run(() =>
{
RealmAccess.Run(r =>
{
- TModel model = r.Find(id);
- createZipArchive(model, stream);
+ TModel refetchModel = r.Find(id);
+ ExportToStream(refetchModel, stream);
});
}).ContinueWith(OnComplete);
}
+ protected virtual void ExportToStream(TModel model, Stream outputStream) => createZipArchive(model, outputStream);
+
private void createZipArchive(TModel model, Stream outputStream)
{
using (var archive = ZipArchive.Create())
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index ae0fbf8d19..eb46bc6db2 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -3,7 +3,6 @@
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Extensions;
using osu.Game.Overlays;
@@ -20,22 +19,14 @@ namespace osu.Game.Database
protected override string FileExtension => ".osr";
- public override async Task ExportToStreamAsync(ScoreInfo uuid, Stream stream)
+ protected override void ExportToStream(ScoreInfo model, Stream stream)
{
- await Task.Run(() =>
- {
- RealmAccess.Run(r =>
- {
- ScoreInfo model = r.Find(uuid.ID);
+ var file = model.Files.SingleOrDefault();
+ if (file == null)
+ return;
- var file = model.Files.SingleOrDefault();
- if (file == null)
- return;
-
- using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- inputStream.CopyTo(stream);
- });
- }).ContinueWith(OnComplete);
+ using (var inputStream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ inputStream.CopyTo(stream);
}
}
}
From 19afce67767b95e67aaf06ea1df8c5b291797ddd Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Fri, 9 Dec 2022 23:41:07 +0900
Subject: [PATCH 0025/1604] Fix overwriting existing files
https://github.com/ppy/osu/pull/21468
---
osu.Game/Database/LegacyModelExporter.cs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 3523d18f29..5c4b7a4578 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -11,6 +12,7 @@ using osu.Framework.Platform;
using osu.Game.Extensions;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
+using osu.Game.Utils;
using Realms;
using SharpCompress.Archives.Zip;
@@ -64,7 +66,11 @@ namespace osu.Game.Database
{
if (item is TModel model)
{
- filename = $"{model.GetDisplayString().GetValidFilename()}{FileExtension}";
+ string itemFilename = item.GetDisplayString().GetValidFilename();
+
+ IEnumerable existingExports = ExportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
+
+ string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
using (var stream = ExportStorage.CreateFileSafely(filename))
{
From 405985ec5b09769ca5d9031eadc2c8ad801b399d Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Fri, 9 Dec 2022 23:57:03 +0900
Subject: [PATCH 0026/1604] remove Component
exportStorage to private
notification should post when export instead of class being constructed
---
osu.Game/Database/LegacyModelExporter.cs | 25 ++++++++++++------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 5c4b7a4578..ae092d15e8 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Extensions;
@@ -21,7 +20,7 @@ namespace osu.Game.Database
///
/// A class which handles exporting legacy user data of a single type from osu-stable.
///
- public abstract class LegacyModelExporter : Component
+ public abstract class LegacyModelExporter
where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
{
///
@@ -30,22 +29,23 @@ namespace osu.Game.Database
protected abstract string FileExtension { get; }
protected Storage UserFileStorage;
- protected Storage ExportStorage;
+ private readonly Storage exportStorage;
protected RealmAccess RealmAccess;
private readonly ProgressNotification notification;
- private string filename = "";
-
private bool canCancel = true;
+ private readonly INotificationOverlay? notifications;
+
protected LegacyModelExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
{
- ExportStorage = storage.GetStorageForDirectory(@"exports");
+ exportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
RealmAccess = realm;
+ this.notifications = notifications;
notification = new ProgressNotification
{
State = ProgressNotificationState.Active,
@@ -53,8 +53,6 @@ namespace osu.Game.Database
CompletionText = "Export completed"
};
notification.CancelRequested += () => canCancel;
-
- notifications?.Post(notification);
}
///
@@ -66,13 +64,15 @@ namespace osu.Game.Database
{
if (item is TModel model)
{
+ notifications?.Post(notification);
+
string itemFilename = item.GetDisplayString().GetValidFilename();
-
- IEnumerable existingExports = ExportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
-
+ IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
- using (var stream = ExportStorage.CreateFileSafely(filename))
+ notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+
+ using (var stream = exportStorage.CreateFileSafely(filename))
{
await ExportToStreamAsync(model, stream);
}
@@ -138,7 +138,6 @@ namespace osu.Game.Database
}
notification.CompletionText = "Export Complete, Click to open the folder";
- notification.CompletionClickAction += () => ExportStorage.PresentFileExternally(filename);
notification.State = ProgressNotificationState.Completed;
}
}
From 951302fe61374cb8b4cc1dedf830c6d8fe748ae6 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 10 Dec 2022 00:43:03 +0900
Subject: [PATCH 0027/1604] ExportAsync use TModel
---
osu.Game/Database/LegacyModelExporter.cs | 27 +++++++++++-------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index ae092d15e8..ad61338c8b 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -58,24 +58,21 @@ namespace osu.Game.Database
///
/// Export the model to default folder.
///
- /// The model should export.
+ /// The model should export.
///
- public async Task ExportAsync(RealmObject item)
+ public async Task ExportAsync(TModel model)
{
- if (item is TModel model)
+ notifications?.Post(notification);
+
+ string itemFilename = model.GetDisplayString().GetValidFilename();
+ IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
+ string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
+
+ notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+
+ using (var stream = exportStorage.CreateFileSafely(filename))
{
- notifications?.Post(notification);
-
- string itemFilename = item.GetDisplayString().GetValidFilename();
- IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
- string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
-
- notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
-
- using (var stream = exportStorage.CreateFileSafely(filename))
- {
- await ExportToStreamAsync(model, stream);
- }
+ await ExportToStreamAsync(model, stream);
}
}
From fa30f3348ff11e68c42b9b63082b5409ee479746 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 11 Dec 2022 16:53:11 +0900
Subject: [PATCH 0028/1604] `onComplete` should private
---
osu.Game/Database/LegacyModelExporter.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index ad61338c8b..4ea56c0056 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Database
TModel refetchModel = r.Find(id);
ExportToStream(refetchModel, stream);
});
- }).ContinueWith(OnComplete);
+ }).ContinueWith(onComplete);
}
protected virtual void ExportToStream(TModel model, Stream outputStream) => createZipArchive(model, outputStream);
@@ -119,7 +119,7 @@ namespace osu.Game.Database
}
}
- protected void OnComplete(Task t)
+ private void onComplete(Task t)
{
if (t.IsFaulted)
{
From 2d5763340944ab99fcffb81f3c2915a441befa6e Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 11 Dec 2022 16:54:20 +0900
Subject: [PATCH 0029/1604] rename method name and add xmldoc
---
osu.Game/Database/LegacyModelExporter.cs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 4ea56c0056..cf41ffc780 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -95,9 +95,14 @@ namespace osu.Game.Database
}).ContinueWith(onComplete);
}
- protected virtual void ExportToStream(TModel model, Stream outputStream) => createZipArchive(model, outputStream);
+ protected virtual void ExportToStream(TModel model, Stream outputStream) => exportZipArchive(model, outputStream);
- private void createZipArchive(TModel model, Stream outputStream)
+ ///
+ /// Exports an item to Stream as a legacy (.zip based) package.
+ ///
+ /// The item to export.
+ /// The output stream to export to.
+ private void exportZipArchive(TModel model, Stream outputStream)
{
using (var archive = ZipArchive.Create())
{
From a87bcccc42b2e69d619087b46b324499bfdd058a Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 11 Dec 2022 16:55:44 +0900
Subject: [PATCH 0030/1604] xmldoc
---
osu.Game/Database/LegacyModelExporter.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index cf41ffc780..18e840fedd 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -95,6 +95,12 @@ namespace osu.Game.Database
}).ContinueWith(onComplete);
}
+ ///
+ /// Exports an item to Stream.
+ /// Override if custom export method is required.
+ ///
+ /// The item to export.
+ /// The output stream to export to.
protected virtual void ExportToStream(TModel model, Stream outputStream) => exportZipArchive(model, outputStream);
///
From e02b8cb199ebdc13f18aaae292febbfb9c75ec89 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 11 Dec 2022 18:30:24 +0900
Subject: [PATCH 0031/1604] Group export methods into their respective managers
---
osu.Game/Beatmaps/BeatmapManager.cs | 9 ++
osu.Game/Database/LegacyBeatmapExporter.cs | 5 +-
osu.Game/Database/LegacyModelExporter.cs | 82 +++++++++----------
osu.Game/Database/LegacyScoreExporter.cs | 8 +-
osu.Game/Database/LegacySkinExporter.cs | 5 +-
.../Online/Leaderboards/LeaderboardScore.cs | 18 ++--
.../Overlays/Settings/Sections/SkinSection.cs | 12 +--
osu.Game/Scoring/ScoreManager.cs | 8 ++
osu.Game/Screens/Edit/Editor.cs | 11 +--
osu.Game/Skinning/SkinManager.cs | 9 ++
10 files changed, 80 insertions(+), 87 deletions(-)
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 965cc43815..34780082da 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -42,6 +42,8 @@ namespace osu.Game.Beatmaps
private readonly WorkingBeatmapCache workingBeatmapCache;
+ private readonly LegacyBeatmapExporter beatmapExporter;
+
public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; }
public BeatmapManager(Storage storage, RealmAccess realm, IAPIProvider? api, AudioManager audioManager, IResourceStore gameResources, GameHost? host = null,
@@ -66,6 +68,11 @@ namespace osu.Game.Beatmaps
beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
+
+ beatmapExporter = new LegacyBeatmapExporter(storage, realm)
+ {
+ PostNotification = obj => PostNotification?.Invoke(obj)
+ };
}
protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore resources, IResourceStore storage, WorkingBeatmap? defaultBeatmap,
@@ -446,6 +453,8 @@ namespace osu.Game.Beatmaps
public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original) =>
beatmapImporter.ImportAsUpdate(notification, importTask, original);
+ public void Export(BeatmapSetInfo beatmap) => Task.Run(() => beatmapExporter.ExportAsync(beatmap));
+
private void updateHashAndMarkDirty(BeatmapSetInfo setInfo)
{
setInfo.Hash = beatmapImporter.ComputeHash(setInfo);
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index 22bccbcda6..6349ebde2c 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -3,14 +3,13 @@
using osu.Framework.Platform;
using osu.Game.Beatmaps;
-using osu.Game.Overlays;
namespace osu.Game.Database
{
public class LegacyBeatmapExporter : LegacyModelExporter
{
- public LegacyBeatmapExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
- : base(storage, realm, notifications)
+ public LegacyBeatmapExporter(Storage storage, RealmAccess realm)
+ : base(storage, realm)
{
}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 18e840fedd..5150651b15 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Extensions;
-using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Utils;
using Realms;
@@ -33,26 +32,16 @@ namespace osu.Game.Database
protected RealmAccess RealmAccess;
- private readonly ProgressNotification notification;
-
private bool canCancel = true;
- private readonly INotificationOverlay? notifications;
+ private string filename = string.Empty;
+ public Action? PostNotification { get; set; }
- protected LegacyModelExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
+ protected LegacyModelExporter(Storage storage, RealmAccess realm)
{
exportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
RealmAccess = realm;
-
- this.notifications = notifications;
- notification = new ProgressNotification
- {
- State = ProgressNotificationState.Active,
- Text = "Exporting...",
- CompletionText = "Export completed"
- };
- notification.CancelRequested += () => canCancel;
}
///
@@ -62,13 +51,9 @@ namespace osu.Game.Database
///
public async Task ExportAsync(TModel model)
{
- notifications?.Post(notification);
-
string itemFilename = model.GetDisplayString().GetValidFilename();
IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
- string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
-
- notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+ filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
using (var stream = exportStorage.CreateFileSafely(filename))
{
@@ -77,22 +62,50 @@ namespace osu.Game.Database
}
///
- /// Export te model corresponding to model to given stream.
+ /// Export model to stream.
///
/// The medel which have .
/// The stream to export.
///
public async Task ExportToStreamAsync(TModel model, Stream stream)
{
+ ProgressNotification notification = new ProgressNotification
+ {
+ State = ProgressNotificationState.Active,
+ Text = "Exporting...",
+ CompletionText = "Export completed"
+ };
+ notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+ notification.CancelRequested += () => canCancel;
+ PostNotification?.Invoke(notification);
+ canCancel = true;
+
Guid id = model.ID;
await Task.Run(() =>
{
RealmAccess.Run(r =>
{
TModel refetchModel = r.Find(id);
- ExportToStream(refetchModel, stream);
+ ExportToStream(refetchModel, stream, notification);
});
- }).ContinueWith(onComplete);
+ }).ContinueWith(t =>
+ {
+ if (t.IsFaulted)
+ {
+ notification.State = ProgressNotificationState.Cancelled;
+ Logger.Error(t.Exception, "An error occurred while exporting");
+
+ return;
+ }
+
+ if (notification.CancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ notification.CompletionText = "Export Complete, Click to open the folder";
+ notification.State = ProgressNotificationState.Completed;
+ });
}
///
@@ -101,14 +114,16 @@ namespace osu.Game.Database
///
/// The item to export.
/// The output stream to export to.
- protected virtual void ExportToStream(TModel model, Stream outputStream) => exportZipArchive(model, outputStream);
+ /// The notification will displayed to the user
+ protected virtual void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification) => exportZipArchive(model, outputStream, notification);
///
/// Exports an item to Stream as a legacy (.zip based) package.
///
/// The item to export.
/// The output stream to export to.
- private void exportZipArchive(TModel model, Stream outputStream)
+ /// The notification will displayed to the user
+ private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification)
{
using (var archive = ZipArchive.Create())
{
@@ -129,24 +144,5 @@ namespace osu.Game.Database
archive.SaveTo(outputStream);
}
}
-
- private void onComplete(Task t)
- {
- if (t.IsFaulted)
- {
- notification.State = ProgressNotificationState.Cancelled;
- Logger.Error(t.Exception, "An error occurred while exporting");
-
- return;
- }
-
- if (notification.CancellationToken.IsCancellationRequested)
- {
- return;
- }
-
- notification.CompletionText = "Export Complete, Click to open the folder";
- notification.State = ProgressNotificationState.Completed;
- }
}
}
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index eb46bc6db2..1a30d823e1 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -5,21 +5,21 @@ using System.IO;
using System.Linq;
using osu.Framework.Platform;
using osu.Game.Extensions;
-using osu.Game.Overlays;
+using osu.Game.Overlays.Notifications;
using osu.Game.Scoring;
namespace osu.Game.Database
{
public class LegacyScoreExporter : LegacyModelExporter
{
- public LegacyScoreExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
- : base(storage, realm, notifications)
+ public LegacyScoreExporter(Storage storage, RealmAccess realm)
+ : base(storage, realm)
{
}
protected override string FileExtension => ".osr";
- protected override void ExportToStream(ScoreInfo model, Stream stream)
+ protected override void ExportToStream(ScoreInfo model, Stream stream, ProgressNotification notification)
{
var file = model.Files.SingleOrDefault();
if (file == null)
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index 6e65963f29..52d0e56cbf 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -2,15 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Platform;
-using osu.Game.Overlays;
using osu.Game.Skinning;
namespace osu.Game.Database
{
public class LegacySkinExporter : LegacyModelExporter
{
- public LegacySkinExporter(Storage storage, RealmAccess realm, INotificationOverlay? notifications = null)
- : base(storage, realm, notifications)
+ public LegacySkinExporter(Storage storage, RealmAccess realm)
+ : base(storage, realm)
{
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index f0457de2dc..e5edf90d1a 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -18,7 +17,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
-using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@@ -34,7 +32,6 @@ using osuTK.Graphics;
using osu.Game.Online.API;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Utils;
-using osu.Framework.Platform;
namespace osu.Game.Online.Leaderboards
{
@@ -73,18 +70,11 @@ namespace osu.Game.Online.Leaderboards
[Resolved(canBeNull: true)]
private SongSelect songSelect { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
- [Resolved]
- private RealmAccess realm { get; set; }
-
- [Resolved(canBeNull: true)]
- private INotificationOverlay notifications { get; set; }
-
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => Score;
+ private ScoreManager scoreManager = null!;
+
public LeaderboardScore(ScoreInfo score, int? rank, bool isOnlineScope = true)
{
Score = score;
@@ -103,6 +93,8 @@ namespace osu.Game.Online.Leaderboards
statisticsLabels = GetStatistics(Score).Select(s => new ScoreComponentLabel(s)).ToList();
+ this.scoreManager = scoreManager;
+
ClickableAvatar innerAvatar;
Children = new Drawable[]
@@ -434,7 +426,7 @@ namespace osu.Game.Online.Leaderboards
if (Score.Files.Count > 0)
{
- items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => Task.Run(() => new LegacyScoreExporter(storage, realm, notifications).ExportAsync(Score))));
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score)));
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
}
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 9400e803c8..6d338df2fe 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -13,7 +13,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
-using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
@@ -138,15 +137,6 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private SkinManager skins { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
- [Resolved]
- private RealmAccess realm { get; set; }
-
- [Resolved(canBeNull: true)]
- private INotificationOverlay notifications { get; set; }
-
private Bindable currentSkin;
[BackgroundDependencyLoader]
@@ -168,7 +158,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- currentSkin.Value.SkinInfo.PerformRead(s => new LegacySkinExporter(storage, realm, notifications).ExportAsync(s));
+ skins.ExporCurrenttSkin();
}
catch (Exception e)
{
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index b2944ad219..525ff58778 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Scoring
{
private readonly OsuConfigManager configManager;
private readonly ScoreImporter scoreImporter;
+ private readonly LegacyScoreExporter scoreExporter;
public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, RealmAccess realm, IAPIProvider api,
OsuConfigManager configManager = null)
@@ -38,6 +39,11 @@ namespace osu.Game.Scoring
{
PostNotification = obj => PostNotification?.Invoke(obj)
};
+
+ scoreExporter = new LegacyScoreExporter(storage, realm)
+ {
+ PostNotification = obj => PostNotification?.Invoke(obj)
+ };
}
public Score GetScore(ScoreInfo score) => scoreImporter.GetScore(score);
@@ -177,6 +183,8 @@ namespace osu.Game.Scoring
public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) => scoreImporter.Import(notification, tasks);
+ public void Export(ScoreInfo score) => Task.Run(() => scoreExporter.ExportAsync(score));
+
public Task> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original);
public Live Import(ScoreInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default) =>
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 342ee2e219..69bcda69c1 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework;
using osu.Framework.Allocation;
@@ -21,7 +20,6 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Logging;
-using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Threading;
@@ -30,7 +28,6 @@ using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
-using osu.Game.Database;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
@@ -89,12 +86,6 @@ namespace osu.Game.Screens.Edit
[Resolved]
private RulesetStore rulesets { get; set; }
- [Resolved]
- private Storage storage { get; set; }
-
- [Resolved]
- private RealmAccess realm { get; set; }
-
[Resolved(canBeNull: true)]
private IDialogOverlay dialogOverlay { get; set; }
@@ -958,7 +949,7 @@ namespace osu.Game.Screens.Edit
private void exportBeatmap()
{
Save();
- Task.Run(() => new LegacyBeatmapExporter(storage, realm, notifications).ExportAsync(Beatmap.Value.BeatmapSetInfo));
+ beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
}
///
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index 4a5277f3bf..65a602b764 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -58,6 +58,8 @@ namespace osu.Game.Skinning
private readonly SkinImporter skinImporter;
+ private readonly LegacySkinExporter skinExporter;
+
private readonly IResourceStore userFiles;
private Skin argonSkin { get; }
@@ -109,6 +111,11 @@ namespace osu.Game.Skinning
SourceChanged?.Invoke();
};
+
+ skinExporter = new LegacySkinExporter(storage, realm)
+ {
+ PostNotification = obj => PostNotification?.Invoke(obj)
+ };
}
public void SelectRandomSkin()
@@ -280,6 +287,8 @@ namespace osu.Game.Skinning
public Task> Import(ImportTask task, bool batchImport = false, CancellationToken cancellationToken = default) => skinImporter.Import(task, batchImport, cancellationToken);
+ public void ExporCurrenttSkin() => CurrentSkinInfo.Value.PerformRead(s => skinExporter.ExportAsync(s));
+
#endregion
public void Delete([CanBeNull] Expression> filter = null, bool silent = false)
From 3d6d3b40257c17184311f1442b850747d1b3b287 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 20:59:11 +0900
Subject: [PATCH 0032/1604] fix weird async logic
---
osu.Game/Beatmaps/BeatmapManager.cs | 2 +-
osu.Game/Scoring/ScoreManager.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 34780082da..56b87eaf11 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -453,7 +453,7 @@ namespace osu.Game.Beatmaps
public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original) =>
beatmapImporter.ImportAsUpdate(notification, importTask, original);
- public void Export(BeatmapSetInfo beatmap) => Task.Run(() => beatmapExporter.ExportAsync(beatmap));
+ public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap);
private void updateHashAndMarkDirty(BeatmapSetInfo setInfo)
{
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 525ff58778..f52172a200 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -183,7 +183,7 @@ namespace osu.Game.Scoring
public Task>> Import(ProgressNotification notification, params ImportTask[] tasks) => scoreImporter.Import(notification, tasks);
- public void Export(ScoreInfo score) => Task.Run(() => scoreExporter.ExportAsync(score));
+ public Task Export(ScoreInfo score) => scoreExporter.ExportAsync(score);
public Task> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original);
From c9cffc82484f32c70395509111149d5b0123b539 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 21:01:38 +0900
Subject: [PATCH 0033/1604] use resolved attribute
---
osu.Game/Online/Leaderboards/LeaderboardScore.cs | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index e5edf90d1a..e4ea277756 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -73,7 +73,8 @@ namespace osu.Game.Online.Leaderboards
public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => Score;
- private ScoreManager scoreManager = null!;
+ [Resolved]
+ private ScoreManager scoreManager { get; set; } = null!;
public LeaderboardScore(ScoreInfo score, int? rank, bool isOnlineScope = true)
{
@@ -87,14 +88,12 @@ namespace osu.Game.Online.Leaderboards
}
[BackgroundDependencyLoader]
- private void load(IAPIProvider api, OsuColour colour, ScoreManager scoreManager)
+ private void load(IAPIProvider api, OsuColour colour)
{
var user = Score.User;
statisticsLabels = GetStatistics(Score).Select(s => new ScoreComponentLabel(s)).ToList();
- this.scoreManager = scoreManager;
-
ClickableAvatar innerAvatar;
Children = new Drawable[]
From 6900d0120a70287ef531a4c0facd398508a76b06 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 21:39:48 +0900
Subject: [PATCH 0034/1604] change abstract implement
---
osu.Game/Database/LegacyBeatmapExporter.cs | 2 +-
osu.Game/Database/LegacyModelExporter.cs | 21 ++++++++++++++++-----
osu.Game/Database/LegacySkinExporter.cs | 2 +-
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs
index 6349ebde2c..107b91a234 100644
--- a/osu.Game/Database/LegacyBeatmapExporter.cs
+++ b/osu.Game/Database/LegacyBeatmapExporter.cs
@@ -6,7 +6,7 @@ using osu.Game.Beatmaps;
namespace osu.Game.Database
{
- public class LegacyBeatmapExporter : LegacyModelExporter
+ public class LegacyBeatmapExporter : LegacyArchiveExporter
{
public LegacyBeatmapExporter(Storage storage, RealmAccess realm)
: base(storage, realm)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 5150651b15..6b5f8e7e96 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Database
protected RealmAccess RealmAccess;
- private bool canCancel = true;
+ protected bool CanCancel = true;
private string filename = string.Empty;
public Action? PostNotification { get; set; }
@@ -76,9 +76,9 @@ namespace osu.Game.Database
CompletionText = "Export completed"
};
notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
- notification.CancelRequested += () => canCancel;
+ notification.CancelRequested += () => CanCancel;
PostNotification?.Invoke(notification);
- canCancel = true;
+ CanCancel = true;
Guid id = model.ID;
await Task.Run(() =>
@@ -115,7 +115,18 @@ namespace osu.Game.Database
/// The item to export.
/// The output stream to export to.
/// The notification will displayed to the user
- protected virtual void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification) => exportZipArchive(model, outputStream, notification);
+ protected abstract void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification);
+ }
+
+ public abstract class LegacyArchiveExporter : LegacyModelExporter
+ where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
+ {
+ protected LegacyArchiveExporter(Storage storage, RealmAccess realm)
+ : base(storage, realm)
+ {
+ }
+
+ protected override void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification) => exportZipArchive(model, outputStream, notification);
///
/// Exports an item to Stream as a legacy (.zip based) package.
@@ -140,7 +151,7 @@ namespace osu.Game.Database
}
notification.Text = "Saving Zip Archive...";
- canCancel = false;
+ CanCancel = false;
archive.SaveTo(outputStream);
}
}
diff --git a/osu.Game/Database/LegacySkinExporter.cs b/osu.Game/Database/LegacySkinExporter.cs
index 52d0e56cbf..d3e6a2f0f4 100644
--- a/osu.Game/Database/LegacySkinExporter.cs
+++ b/osu.Game/Database/LegacySkinExporter.cs
@@ -6,7 +6,7 @@ using osu.Game.Skinning;
namespace osu.Game.Database
{
- public class LegacySkinExporter : LegacyModelExporter
+ public class LegacySkinExporter : LegacyArchiveExporter
{
public LegacySkinExporter(Storage storage, RealmAccess realm)
: base(storage, realm)
From 6ef5b2733f01a4ebd2ccd733a4e6c80c6158698f Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 21:41:15 +0900
Subject: [PATCH 0035/1604] Export instead of ExportCurrentSkin
---
osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +-
osu.Game/Skinning/SkinManager.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
index 6d338df2fe..1a64416b3e 100644
--- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs
@@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
try
{
- skins.ExporCurrenttSkin();
+ skins.CurrentSkinInfo.Value.PerformRead(s => skins.ExportSkin(s));
}
catch (Exception e)
{
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index 65a602b764..d4e7d252ac 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -287,7 +287,7 @@ namespace osu.Game.Skinning
public Task> Import(ImportTask task, bool batchImport = false, CancellationToken cancellationToken = default) => skinImporter.Import(task, batchImport, cancellationToken);
- public void ExporCurrenttSkin() => CurrentSkinInfo.Value.PerformRead(s => skinExporter.ExportAsync(s));
+ public Task ExportSkin(SkinInfo skin) => skinExporter.ExportAsync(skin);
#endregion
From ec251664a7c4a2bbb6b022a046cf35960bd2497b Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 22:45:36 +0900
Subject: [PATCH 0036/1604] use ThrowIfCancellationRequested instead of
CancelRequested
---
osu.Game/Database/LegacyModelExporter.cs | 70 ++++++++++++++----------
1 file changed, 42 insertions(+), 28 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 6b5f8e7e96..0ebcfaa07e 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -32,8 +32,6 @@ namespace osu.Game.Database
protected RealmAccess RealmAccess;
- protected bool CanCancel = true;
-
private string filename = string.Empty;
public Action? PostNotification { get; set; }
@@ -54,10 +52,16 @@ namespace osu.Game.Database
string itemFilename = model.GetDisplayString().GetValidFilename();
IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
+ bool success;
using (var stream = exportStorage.CreateFileSafely(filename))
{
- await ExportToStreamAsync(model, stream);
+ success = await ExportToStreamAsync(model, stream);
+ }
+
+ if (!success)
+ {
+ exportStorage.Delete(filename);
}
}
@@ -66,8 +70,8 @@ namespace osu.Game.Database
///
/// The medel which have .
/// The stream to export.
- ///
- public async Task ExportToStreamAsync(TModel model, Stream stream)
+ /// Whether the export was successful
+ public async Task ExportToStreamAsync(TModel model, Stream stream)
{
ProgressNotification notification = new ProgressNotification
{
@@ -76,35 +80,33 @@ namespace osu.Game.Database
CompletionText = "Export completed"
};
notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
- notification.CancelRequested += () => CanCancel;
PostNotification?.Invoke(notification);
- CanCancel = true;
Guid id = model.ID;
- await Task.Run(() =>
+ return await Task.Run(() =>
{
RealmAccess.Run(r =>
{
TModel refetchModel = r.Find(id);
ExportToStream(refetchModel, stream, notification);
});
- }).ContinueWith(t =>
+ }, notification.CancellationToken).ContinueWith(t =>
{
+ if (t.IsCanceled)
+ {
+ return false;
+ }
+
if (t.IsFaulted)
{
notification.State = ProgressNotificationState.Cancelled;
Logger.Error(t.Exception, "An error occurred while exporting");
-
- return;
- }
-
- if (notification.CancellationToken.IsCancellationRequested)
- {
- return;
+ return false;
}
notification.CompletionText = "Export Complete, Click to open the folder";
notification.State = ProgressNotificationState.Completed;
+ return true;
});
}
@@ -121,6 +123,8 @@ namespace osu.Game.Database
public abstract class LegacyArchiveExporter : LegacyModelExporter
where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
{
+ private bool canCancel = true;
+
protected LegacyArchiveExporter(Storage storage, RealmAccess realm)
: base(storage, realm)
{
@@ -136,23 +140,33 @@ namespace osu.Game.Database
/// The notification will displayed to the user
private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification)
{
- using (var archive = ZipArchive.Create())
+ try
{
- float i = 0;
+ notification.CancelRequested += () => canCancel;
- foreach (var file in model.Files)
+ using (var archive = ZipArchive.Create())
{
- if (notification.CancellationToken.IsCancellationRequested) return;
+ float i = 0;
- archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ foreach (var file in model.Files)
+ {
+ notification.CancellationToken.ThrowIfCancellationRequested();
+
+ archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ }
+
+ notification.Text = "Saving Zip Archive...";
+ canCancel = false;
+ archive.SaveTo(outputStream);
}
-
- notification.Text = "Saving Zip Archive...";
- CanCancel = false;
- archive.SaveTo(outputStream);
+ }
+ catch (OperationCanceledException)
+ {
+ Logger.Log("Export operat canceled");
+ throw;
}
}
}
From f5226bd50b02068e42116940e12c1f47bbf158ce Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 23:12:25 +0900
Subject: [PATCH 0037/1604] use ZipWriter
Export directly to stream instead of creating a archive
so we can cancel this anytime
---
osu.Game/Database/LegacyModelExporter.cs | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 0ebcfaa07e..9e0c1e0c6d 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -12,7 +12,9 @@ using osu.Game.Extensions;
using osu.Game.Overlays.Notifications;
using osu.Game.Utils;
using Realms;
-using SharpCompress.Archives.Zip;
+using SharpCompress.Common;
+using SharpCompress.Writers;
+using SharpCompress.Writers.Zip;
namespace osu.Game.Database
{
@@ -123,8 +125,6 @@ namespace osu.Game.Database
public abstract class LegacyArchiveExporter : LegacyModelExporter
where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
{
- private bool canCancel = true;
-
protected LegacyArchiveExporter(Storage storage, RealmAccess realm)
: base(storage, realm)
{
@@ -142,9 +142,7 @@ namespace osu.Game.Database
{
try
{
- notification.CancelRequested += () => canCancel;
-
- using (var archive = ZipArchive.Create())
+ using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate)))
{
float i = 0;
@@ -152,15 +150,11 @@ namespace osu.Game.Database
{
notification.CancellationToken.ThrowIfCancellationRequested();
- archive.AddEntry(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ writer.Write(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
i++;
notification.Progress = i / model.Files.Count();
notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
}
-
- notification.Text = "Saving Zip Archive...";
- canCancel = false;
- archive.SaveTo(outputStream);
}
}
catch (OperationCanceledException)
From dadadaff65057cd16210ae9ba05634e308b5ec53 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 23:20:29 +0900
Subject: [PATCH 0038/1604] remove try catch
---
osu.Game/Database/LegacyModelExporter.cs | 26 ++++++++----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 9e0c1e0c6d..824460cdc9 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -140,28 +140,20 @@ namespace osu.Game.Database
/// The notification will displayed to the user
private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification)
{
- try
+ using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate)))
{
- using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate)))
+ float i = 0;
+
+ foreach (var file in model.Files)
{
- float i = 0;
+ notification.CancellationToken.ThrowIfCancellationRequested();
- foreach (var file in model.Files)
- {
- notification.CancellationToken.ThrowIfCancellationRequested();
-
- writer.Write(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
- }
+ writer.Write(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
}
}
- catch (OperationCanceledException)
- {
- Logger.Log("Export operat canceled");
- throw;
- }
}
}
}
From cd8420bc66c56aa7e686a579ebd9a18634861b45 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 23:34:40 +0900
Subject: [PATCH 0039/1604] Handle the case where the file cannot be found
---
osu.Game/Database/LegacyModelExporter.cs | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 824460cdc9..531a6f48da 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -143,12 +143,33 @@ namespace osu.Game.Database
using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate)))
{
float i = 0;
+ bool fileMissing = false;
foreach (var file in model.Files)
{
notification.CancellationToken.ThrowIfCancellationRequested();
- writer.Write(file.Filename, UserFileStorage.GetStream(file.File.GetStoragePath()));
+ using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ {
+ // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
+ if (stream == null)
+ {
+ // Only pop up once to prevent spam.
+ if (!fileMissing)
+ {
+ PostNotification?.Invoke(new SimpleErrorNotification
+ {
+ Text = "Some of your files are missing, they will not be included in the archive"
+ });
+ fileMissing = true;
+ }
+ }
+ else
+ {
+ writer.Write(file.Filename, stream);
+ }
+ }
+
i++;
notification.Progress = i / model.Files.Count();
notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
From 5912dfd443effad317f5085aba57f4b380868d6e Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 15 Dec 2022 23:42:49 +0900
Subject: [PATCH 0040/1604] using declaration
reshaper
---
osu.Game/Database/LegacyModelExporter.cs | 49 ++++++++++++------------
1 file changed, 24 insertions(+), 25 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 531a6f48da..d896e4bce6 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -140,40 +140,39 @@ namespace osu.Game.Database
/// The notification will displayed to the user
private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification)
{
- using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate)))
+ using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
+
+ float i = 0;
+ bool fileMissing = false;
+
+ foreach (var file in model.Files)
{
- float i = 0;
- bool fileMissing = false;
+ notification.CancellationToken.ThrowIfCancellationRequested();
- foreach (var file in model.Files)
+ using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
{
- notification.CancellationToken.ThrowIfCancellationRequested();
-
- using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
+ if (stream == null)
{
- // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
- if (stream == null)
+ // Only pop up once to prevent spam.
+ if (!fileMissing)
{
- // Only pop up once to prevent spam.
- if (!fileMissing)
+ PostNotification?.Invoke(new SimpleErrorNotification
{
- PostNotification?.Invoke(new SimpleErrorNotification
- {
- Text = "Some of your files are missing, they will not be included in the archive"
- });
- fileMissing = true;
- }
- }
- else
- {
- writer.Write(file.Filename, stream);
+ Text = "Some of your files are missing, they will not be included in the archive"
+ });
+ fileMissing = true;
}
}
-
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ else
+ {
+ writer.Write(file.Filename, stream);
+ }
}
+
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
}
}
}
From b37f1cce3fc4bfd3c5408a8fe32a4fdc796f3b8d Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Tue, 20 Dec 2022 23:46:05 +0900
Subject: [PATCH 0041/1604] Added ability to report chat
---
.../Visual/Online/TestSceneChatOverlay.cs | 38 +++++++
osu.Game/Overlays/Chat/ChatReportReason.cs | 36 ++++++
osu.Game/Overlays/Chat/DrawableUsername.cs | 16 ++-
osu.Game/Overlays/Chat/ReportChatPopover.cs | 106 ++++++++++++++++++
osu.Game/Overlays/ChatOverlay.cs | 3 +-
5 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 osu.Game/Overlays/Chat/ChatReportReason.cs
create mode 100644 osu.Game/Overlays/Chat/ReportChatPopover.cs
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 8cc4eabcd7..44d739a6e3 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -30,6 +30,8 @@ using osu.Game.Overlays.Chat.Listing;
using osu.Game.Overlays.Chat.ChannelList;
using osuTK;
using osuTK.Input;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Graphics.Sprites;
namespace osu.Game.Tests.Visual.Online
{
@@ -530,6 +532,42 @@ namespace osu.Game.Tests.Visual.Online
});
}
+ [Test]
+ public void TestChatReport()
+ {
+ AddStep("Show overlay with channel", () =>
+ {
+ chatOverlay.Show();
+ channelManager.CurrentChannel.Value = channelManager.JoinChannel(testChannel1);
+ });
+
+ AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
+ waitForChannel1Visible();
+
+ AddStep("Right click username", () =>
+ {
+ var username = this.ChildrenOfType().First();
+ InputManager.MoveMouseTo(username);
+ InputManager.Click(MouseButton.Right);
+ });
+
+ AddStep("Click report", () =>
+ {
+ var btn = this.ChildrenOfType().First(x => x.Text == "Report");
+ InputManager.MoveMouseTo(btn);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddStep("Try to report", () =>
+ {
+ var btn = this.ChildrenOfType().Single().ChildrenOfType().Single();
+ InputManager.MoveMouseTo(btn);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddAssert("Report message sended", () => channelManager.CurrentChannel.Value.Messages.Any(x => x.Content.Contains("!report")));
+ }
+
private void joinTestChannel(int i)
{
AddStep($"Join test channel {i}", () => channelManager.JoinChannel(testChannels[i]));
diff --git a/osu.Game/Overlays/Chat/ChatReportReason.cs b/osu.Game/Overlays/Chat/ChatReportReason.cs
new file mode 100644
index 0000000000..55593d29ad
--- /dev/null
+++ b/osu.Game/Overlays/Chat/ChatReportReason.cs
@@ -0,0 +1,36 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
+
+namespace osu.Game.Overlays.Chat
+{
+ public enum ChatReportReason
+ {
+ [Description("Insulting People")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsInsults))]
+ Insults,
+
+ [Description("Spam")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsSpam))]
+ Spam,
+
+ [Description("Cheating")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsCheating))]
+ FoulPlay,
+
+ [Description("Unwanted Content")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsUnwantedContent))]
+ UnwantedContent,
+
+ [Description("Nonsense")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsNonsense))]
+ Nonsense,
+
+ [Description("Other")]
+ [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsOther))]
+ Other
+ }
+}
diff --git a/osu.Game/Overlays/Chat/DrawableUsername.cs b/osu.Game/Overlays/Chat/DrawableUsername.cs
index 6bae498a6c..f70175f081 100644
--- a/osu.Game/Overlays/Chat/DrawableUsername.cs
+++ b/osu.Game/Overlays/Chat/DrawableUsername.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
+using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -25,7 +26,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays.Chat
{
- public partial class DrawableUsername : OsuClickableContainer, IHasContextMenu
+ public partial class DrawableUsername : OsuClickableContainer, IHasContextMenu, IHasPopover
{
public Color4 AccentColour { get; }
@@ -152,12 +153,20 @@ namespace osu.Game.Overlays.Chat
};
if (!user.Equals(api.LocalUser.Value))
+ {
items.Add(new OsuMenuItem("Start Chat", MenuItemType.Standard, openUserChannel));
+ items.Add(new OsuMenuItem("Report", MenuItemType.Destructive, this.ShowPopover));
+ }
return items.ToArray();
}
}
+ private void report(ChatReportReason reason, string comments)
+ {
+ chatManager?.PostMessage($"!report {user.Username} ({reason.GetDescription()}): {comments}");
+ }
+
private void openUserChannel()
{
chatManager?.OpenPrivateChannel(user);
@@ -221,5 +230,10 @@ namespace osu.Game.Overlays.Chat
Color4Extensions.FromHex("812a96"),
Color4Extensions.FromHex("992861"),
};
+
+ public Popover GetPopover() => new ReportChatPopover(user)
+ {
+ Action = report
+ };
}
}
diff --git a/osu.Game/Overlays/Chat/ReportChatPopover.cs b/osu.Game/Overlays/Chat/ReportChatPopover.cs
new file mode 100644
index 0000000000..cf5e456874
--- /dev/null
+++ b/osu.Game/Overlays/Chat/ReportChatPopover.cs
@@ -0,0 +1,106 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Resources.Localisation.Web;
+using osuTK;
+
+namespace osu.Game.Overlays.Chat
+{
+ public partial class ReportChatPopover : OsuPopover
+ {
+ public Action? Action;
+
+ private readonly APIUser? user;
+
+ private OsuEnumDropdown reasonDropdown = null!;
+ private OsuTextBox commentsTextBox = null!;
+
+ public ReportChatPopover(APIUser? user)
+ {
+ this.user = user;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, AudioManager audio)
+ {
+ Child = new ReverseChildIDFillFlowContainer
+ {
+ Direction = FillDirection.Vertical,
+ Width = 500,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(7),
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Icon = FontAwesome.Solid.ExclamationTriangle,
+ Size = new Vector2(36),
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = ReportStrings.UserTitle(user?.Username ?? @"Someone"),
+ Font = OsuFont.Torus.With(size: 25),
+ Margin = new MarginPadding { Bottom = 10 }
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = UsersStrings.ReportReason,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Child = reasonDropdown = new OsuEnumDropdown
+ {
+ RelativeSizeAxes = Axes.X
+ }
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = UsersStrings.ReportComments,
+ },
+ commentsTextBox = new OsuTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ PlaceholderText = UsersStrings.ReportPlaceholder,
+ },
+ new RoundedButton
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Width = 200,
+ BackgroundColour = colours.Red3,
+ Text = UsersStrings.ReportActionsSend,
+ Action = () =>
+ {
+ Action?.Invoke(reasonDropdown.Current.Value, commentsTextBox.Text);
+ this.HidePopover();
+ },
+ Margin = new MarginPadding { Bottom = 5, Top = 10 },
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index c539cdc5ec..c3a6ed0175 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
@@ -122,7 +123,7 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Y,
Width = side_bar_width,
},
- new Container
+ new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
From ffa32307c3ef8c9917a210a31871b9000cf80246 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Wed, 21 Dec 2022 01:37:16 +0900
Subject: [PATCH 0042/1604] abstract ReportPopover
---
.../Graphics/UserInterfaceV2/ReportPopover.cs | 114 ++++++++++++++++++
osu.Game/Overlays/Chat/ReportChatPopover.cs | 94 +--------------
.../Overlays/Comments/ReportCommentPopover.cs | 98 +--------------
3 files changed, 119 insertions(+), 187 deletions(-)
create mode 100644 osu.Game/Graphics/UserInterfaceV2/ReportPopover.cs
diff --git a/osu.Game/Graphics/UserInterfaceV2/ReportPopover.cs b/osu.Game/Graphics/UserInterfaceV2/ReportPopover.cs
new file mode 100644
index 0000000000..44af108ab1
--- /dev/null
+++ b/osu.Game/Graphics/UserInterfaceV2/ReportPopover.cs
@@ -0,0 +1,114 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Localisation;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Resources.Localisation.Web;
+using osuTK;
+
+namespace osu.Game.Graphics.UserInterfaceV2
+{
+ public abstract partial class ReportPopover : OsuPopover
+ where T : struct, Enum
+ {
+ public Action? Action;
+
+ private OsuEnumDropdown reasonDropdown = null!;
+ private OsuTextBox commentsTextBox = null!;
+ private RoundedButton submitButton = null!;
+
+ public bool CanSubmitEmptyReason = false;
+ public LocalisableString Header;
+
+ protected ReportPopover(LocalisableString headerString)
+ {
+ Header = headerString;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Child = new ReverseChildIDFillFlowContainer
+ {
+ Direction = FillDirection.Vertical,
+ Width = 500,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(7),
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Icon = FontAwesome.Solid.ExclamationTriangle,
+ Size = new Vector2(36),
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = Header,
+ Font = OsuFont.Torus.With(size: 25),
+ Margin = new MarginPadding { Bottom = 10 }
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = UsersStrings.ReportReason,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Child = reasonDropdown = new OsuEnumDropdown
+ {
+ RelativeSizeAxes = Axes.X
+ }
+ },
+ new OsuSpriteText
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Text = UsersStrings.ReportComments,
+ },
+ commentsTextBox = new OsuTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ PlaceholderText = UsersStrings.ReportPlaceholder,
+ },
+ submitButton = new RoundedButton
+ {
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Width = 200,
+ BackgroundColour = colours.Red3,
+ Text = UsersStrings.ReportActionsSend,
+ Action = () =>
+ {
+ Action?.Invoke(reasonDropdown.Current.Value, commentsTextBox.Text);
+ this.HidePopover();
+ },
+ Margin = new MarginPadding { Bottom = 5, Top = 10 },
+ }
+ }
+ };
+
+ if (!CanSubmitEmptyReason)
+ {
+ commentsTextBox.Current.BindValueChanged(e =>
+ {
+ submitButton.Enabled.Value = !string.IsNullOrWhiteSpace(e.NewValue);
+ }, true);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Chat/ReportChatPopover.cs b/osu.Game/Overlays/Chat/ReportChatPopover.cs
index cf5e456874..79c3922795 100644
--- a/osu.Game/Overlays/Chat/ReportChatPopover.cs
+++ b/osu.Game/Overlays/Chat/ReportChatPopover.cs
@@ -1,106 +1,18 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
-using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Resources.Localisation.Web;
-using osuTK;
namespace osu.Game.Overlays.Chat
{
- public partial class ReportChatPopover : OsuPopover
+ public partial class ReportChatPopover : ReportPopover
{
- public Action? Action;
-
- private readonly APIUser? user;
-
- private OsuEnumDropdown reasonDropdown = null!;
- private OsuTextBox commentsTextBox = null!;
-
public ReportChatPopover(APIUser? user)
+ : base(ReportStrings.UserTitle(user?.Username ?? @"Someone"))
{
- this.user = user;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, AudioManager audio)
- {
- Child = new ReverseChildIDFillFlowContainer
- {
- Direction = FillDirection.Vertical,
- Width = 500,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(7),
- Children = new Drawable[]
- {
- new SpriteIcon
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Icon = FontAwesome.Solid.ExclamationTriangle,
- Size = new Vector2(36),
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = ReportStrings.UserTitle(user?.Username ?? @"Someone"),
- Font = OsuFont.Torus.With(size: 25),
- Margin = new MarginPadding { Bottom = 10 }
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = UsersStrings.ReportReason,
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = 40,
- Child = reasonDropdown = new OsuEnumDropdown
- {
- RelativeSizeAxes = Axes.X
- }
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = UsersStrings.ReportComments,
- },
- commentsTextBox = new OsuTextBox
- {
- RelativeSizeAxes = Axes.X,
- PlaceholderText = UsersStrings.ReportPlaceholder,
- },
- new RoundedButton
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Width = 200,
- BackgroundColour = colours.Red3,
- Text = UsersStrings.ReportActionsSend,
- Action = () =>
- {
- Action?.Invoke(reasonDropdown.Current.Value, commentsTextBox.Text);
- this.HidePopover();
- },
- Margin = new MarginPadding { Bottom = 5, Top = 10 },
- }
- }
- };
+ CanSubmitEmptyReason = true;
}
}
}
diff --git a/osu.Game/Overlays/Comments/ReportCommentPopover.cs b/osu.Game/Overlays/Comments/ReportCommentPopover.cs
index f3b2a2f97c..e688dad755 100644
--- a/osu.Game/Overlays/Comments/ReportCommentPopover.cs
+++ b/osu.Game/Overlays/Comments/ReportCommentPopover.cs
@@ -1,111 +1,17 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
-using osu.Framework.Allocation;
-using osu.Framework.Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Resources.Localisation.Web;
-using osuTK;
namespace osu.Game.Overlays.Comments
{
- public partial class ReportCommentPopover : OsuPopover
+ public partial class ReportCommentPopover : ReportPopover
{
- public Action? Action;
-
- private readonly Comment? comment;
-
- private OsuEnumDropdown reasonDropdown = null!;
- private OsuTextBox commentsTextBox = null!;
- private RoundedButton submitButton = null!;
-
public ReportCommentPopover(Comment? comment)
+ : base(ReportStrings.CommentTitle(comment?.User?.Username ?? comment?.LegacyName ?? @"Someone"))
{
- this.comment = comment;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Child = new ReverseChildIDFillFlowContainer
- {
- Direction = FillDirection.Vertical,
- Width = 500,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(7),
- Children = new Drawable[]
- {
- new SpriteIcon
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Icon = FontAwesome.Solid.ExclamationTriangle,
- Size = new Vector2(36),
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = ReportStrings.CommentTitle(comment?.User?.Username ?? comment?.LegacyName ?? @"Someone"),
- Font = OsuFont.Torus.With(size: 25),
- Margin = new MarginPadding { Bottom = 10 }
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = UsersStrings.ReportReason,
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- Height = 40,
- Child = reasonDropdown = new OsuEnumDropdown
- {
- RelativeSizeAxes = Axes.X
- }
- },
- new OsuSpriteText
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Text = UsersStrings.ReportComments,
- },
- commentsTextBox = new OsuTextBox
- {
- RelativeSizeAxes = Axes.X,
- PlaceholderText = UsersStrings.ReportPlaceholder,
- },
- submitButton = new RoundedButton
- {
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Width = 200,
- BackgroundColour = colours.Red3,
- Text = UsersStrings.ReportActionsSend,
- Action = () =>
- {
- Action?.Invoke(reasonDropdown.Current.Value, commentsTextBox.Text);
- this.HidePopover();
- },
- Margin = new MarginPadding { Bottom = 5, Top = 10 },
- }
- }
- };
-
- commentsTextBox.Current.BindValueChanged(e =>
- {
- submitButton.Enabled.Value = !string.IsNullOrWhiteSpace(e.NewValue);
- }, true);
}
}
}
From 1b2c821346da690e932b4d74fda4ac9307d99bb3 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Wed, 21 Dec 2022 15:44:02 +0900
Subject: [PATCH 0043/1604] showpopover directly
---
.../Visual/Online/TestSceneChatOverlay.cs | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index 44d739a6e3..3a6cbdfb0f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -12,6 +12,7 @@ using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
@@ -544,19 +545,7 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("Overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
waitForChannel1Visible();
- AddStep("Right click username", () =>
- {
- var username = this.ChildrenOfType().First();
- InputManager.MoveMouseTo(username);
- InputManager.Click(MouseButton.Right);
- });
-
- AddStep("Click report", () =>
- {
- var btn = this.ChildrenOfType().First(x => x.Text == "Report");
- InputManager.MoveMouseTo(btn);
- InputManager.Click(MouseButton.Left);
- });
+ AddStep("Show report popover", () => this.ChildrenOfType().First().ShowPopover());
AddStep("Try to report", () =>
{
From bbb22479a8aefd1b040d236dde395c0220ed4bc4 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sun, 25 Dec 2022 21:32:47 +0100
Subject: [PATCH 0044/1604] Add "ModBubbles" for the osu ruleset.
---
.../Mods/TestSceneOsuModBubbles.cs | 19 ++
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 228 ++++++++++++++++++
osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +-
osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 +
4 files changed, 252 insertions(+), 1 deletion(-)
create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
new file mode 100644
index 0000000000..e72a1f79f5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
@@ -0,0 +1,19 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Rulesets.Osu.Tests.Mods
+{
+ public partial class TestSceneOsuModBubbles : OsuModTestScene
+ {
+ [Test]
+ public void TestOsuModBubbles() => CreateModTest(new ModTestData
+ {
+ Mod = new OsuModBubbles(),
+ Autoplay = true,
+ PassCondition = () => true
+ });
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
new file mode 100644
index 0000000000..c51ebde383
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -0,0 +1,228 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
+using osu.Framework.Graphics.Performance;
+using osu.Framework.Graphics.Pooling;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Localisation;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Pooling;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
+using osu.Game.Scoring;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ public partial class OsuModBubbles : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToScoreProcessor
+ {
+ public override string Name => "Bubbles";
+
+ public override string Acronym => "BB";
+
+ public override LocalisableString Description => "Dont let their popping distract you!";
+
+ public override double ScoreMultiplier => 1;
+
+ public override ModType Type => ModType.Fun;
+
+ // Compatibility with these seems potentially feasible in the future, blocked for now because they dont work as one would expect
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
+
+ private PlayfieldAdjustmentContainer adjustmentContainer = null!;
+ private BubbleContainer bubbleContainer = null!;
+
+ private readonly Bindable currentCombo = new BindableInt();
+
+ private float maxSize;
+ private float bubbleRadius;
+ private double bubbleFade;
+
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
+
+ public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
+ {
+ currentCombo.BindTo(scoreProcessor.Combo);
+ currentCombo.BindValueChanged(combo =>
+ maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
+ }
+
+ public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
+ {
+ // Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen
+ bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f);
+ bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimeFadeIn * 2;
+
+ // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
+ drawableRuleset.Playfield.DisplayJudgements.Value = false;
+
+ adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
+
+ adjustmentContainer.Add(bubbleContainer = new BubbleContainer());
+ drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer);
+ }
+
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
+
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
+
+ private void applyBubbleState(DrawableHitObject drawableObject)
+ {
+ if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return;
+
+ OsuHitObject hitObject = drawableOsuObject.HitObject;
+
+ switch (drawableOsuObject)
+ {
+ //Needs to be done explicitly to avoid being handled by DrawableHitCircle below
+ case DrawableSliderHead:
+ addBubbleContainer(hitObject.Position);
+ break;
+
+ //Stack leniency causes placement issues if this isn't handled as such.
+ case DrawableHitCircle hitCircle:
+ addBubbleContainer(hitCircle.Position);
+ break;
+
+ case DrawableSpinnerTick:
+ case DrawableSlider:
+ return;
+
+ default:
+ addBubbleContainer(hitObject.Position);
+ break;
+ }
+
+ void addBubbleContainer(Vector2 position) => bubbleContainer.Add(new BubbleLifeTimeEntry
+ {
+ LifetimeStart = bubbleContainer.Time.Current,
+ Colour = drawableOsuObject.AccentColour.Value,
+ Position = position,
+ InitialSize = new Vector2(bubbleRadius),
+ MaxSize = maxSize,
+ FadeTime = bubbleFade,
+ IsHit = drawableOsuObject.IsHit
+ }
+ );
+ }
+
+ #region Pooled Bubble drawable
+
+ //LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it
+ //Todo: find solution to bubbles rewinding in "groups"
+ private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer
+ {
+ protected override bool RemoveRewoundEntry => true;
+
+ private readonly DrawablePool pool;
+
+ public BubbleContainer()
+ {
+ RelativeSizeAxes = Axes.Both;
+ AddInternal(pool = new DrawablePool(10, 1000));
+ }
+
+ protected override BubbleObject GetDrawable(BubbleLifeTimeEntry entry) => pool.Get(d => d.Apply(entry));
+ }
+
+ private sealed partial class BubbleObject : PoolableDrawableWithLifetime
+ {
+ private readonly BubbleDrawable bubbleDrawable;
+
+ public BubbleObject()
+ {
+ InternalChild = bubbleDrawable = new BubbleDrawable();
+ }
+
+ protected override void OnApply(BubbleLifeTimeEntry entry)
+ {
+ base.OnApply(entry);
+ if (IsLoaded)
+ apply(entry);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ apply(Entry);
+ }
+
+ private void apply(BubbleLifeTimeEntry? entry)
+ {
+ if (entry == null)
+ return;
+
+ ApplyTransformsAt(float.MinValue, true);
+ ClearTransforms(true);
+
+ Position = entry.Position;
+
+ bubbleDrawable.Animate(entry);
+
+ LifetimeEnd = bubbleDrawable.LatestTransformEndTime;
+ }
+ }
+
+ private partial class BubbleDrawable : CompositeDrawable
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ InternalChild = new Circle
+ {
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Colour4.Black.Opacity(0.05f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 5
+ }
+ };
+ }
+
+ public void Animate(BubbleLifeTimeEntry entry)
+ {
+ Size = entry.InitialSize;
+ this
+ .ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine)
+ .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine)
+ .Delay(entry.FadeTime)
+ .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine)
+ .Then()
+ .ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine)
+ .FadeTo(0, entry.FadeTime, Easing.OutQuint);
+ }
+ }
+
+ private class BubbleLifeTimeEntry : LifetimeEntry
+ {
+ public Vector2 InitialSize { get; set; }
+
+ public float MaxSize { get; set; }
+
+ public Vector2 Position { get; set; }
+
+ public Colour4 Colour { get; set; }
+
+ // FadeTime is based on the approach rate of the beatmap.
+ public double FadeTime { get; set; }
+
+ // Whether the corresponding HitObject was hit
+ public bool IsHit { get; set; }
+ }
+
+ #endregion
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 79a566e33c..0df1e4dfca 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -202,7 +202,8 @@ namespace osu.Game.Rulesets.Osu
new OsuModNoScope(),
new MultiMod(new OsuModMagnetised(), new OsuModRepel()),
new ModAdaptiveSpeed(),
- new OsuModFreezeFrame()
+ new OsuModFreezeFrame(),
+ new OsuModBubbles()
};
case ModType.System:
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index 45fa55c7f2..2c9ef357b5 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -83,6 +83,9 @@ namespace osu.Game.Rulesets.Mods
flashlight.Combo.BindTo(Combo);
drawableRuleset.KeyBindingInputManager.Add(flashlight);
+
+ // Stop flashlight from being drawn underneath other mods that generate HitObjects.
+ drawableRuleset.KeyBindingInputManager.ChangeChildDepth(flashlight, -1);
}
protected abstract Flashlight CreateFlashlight();
From 8a108b143e10bf80162eb0109aad7a68ae9692fc Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sun, 25 Dec 2022 21:33:10 +0100
Subject: [PATCH 0045/1604] Address mod incompatibilities
---
osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 3 +++
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 17 +++++++++++------
osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 2 +-
osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs | 2 +-
4 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
index 9e71f657ce..2394cf92fc 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
@@ -10,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModBarrelRoll : ModBarrelRoll, IApplicableToDrawableHitObject
{
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModBubbles) };
+
public void ApplyToDrawableHitObject(DrawableHitObject d)
{
d.OnUpdate += _ =>
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index c51ebde383..2e4d574148 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -86,12 +86,12 @@ namespace osu.Game.Rulesets.Osu.Mods
{
//Needs to be done explicitly to avoid being handled by DrawableHitCircle below
case DrawableSliderHead:
- addBubbleContainer(hitObject.Position);
+ addBubbleContainer(hitObject.Position, drawableOsuObject);
break;
//Stack leniency causes placement issues if this isn't handled as such.
case DrawableHitCircle hitCircle:
- addBubbleContainer(hitCircle.Position);
+ addBubbleContainer(hitCircle.Position, drawableOsuObject);
break;
case DrawableSpinnerTick:
@@ -99,19 +99,24 @@ namespace osu.Game.Rulesets.Osu.Mods
return;
default:
- addBubbleContainer(hitObject.Position);
+ addBubbleContainer(hitObject.Position, drawableOsuObject);
break;
}
+ }
- void addBubbleContainer(Vector2 position) => bubbleContainer.Add(new BubbleLifeTimeEntry
+ private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject)
+ {
+ bubbleContainer.Add
+ (
+ new BubbleLifeTimeEntry
{
LifetimeStart = bubbleContainer.Time.Current,
- Colour = drawableOsuObject.AccentColour.Value,
+ Colour = hitObject.AccentColour.Value,
Position = position,
InitialSize = new Vector2(bubbleRadius),
MaxSize = maxSize,
FadeTime = bubbleFade,
- IsHit = drawableOsuObject.IsHit
+ IsHit = hitObject.IsHit
}
);
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs
index 38d90eb121..c8c4cd6a14 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Fun;
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
public override double ScoreMultiplier => 0.5;
- public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles) };
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs
index 31a6b69d6b..28d459cedb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Fun;
public override LocalisableString Description => "Hit objects run away!";
public override double ScoreMultiplier => 1;
- public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles) };
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
From ca84b885dcc6695cb7da3549757f7e6338bc37de Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 11 Jan 2023 17:51:41 +0100
Subject: [PATCH 0046/1604] Add more detail to bubbles
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 58 ++++++++++++++++-----
1 file changed, 45 insertions(+), 13 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 2e4d574148..f5e7e035b2 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
+using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Pooling;
@@ -21,6 +22,7 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -178,21 +180,38 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
- private partial class BubbleDrawable : CompositeDrawable
+ private partial class BubbleDrawable : CompositeDrawable, IHasAccentColour
{
+ public Color4 AccentColour { get; set; }
+
+ private Circle outerCircle = null!;
+ private Circle innerCircle = null!;
+
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = new Circle
+ InternalChildren = new Drawable[]
{
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- EdgeEffect = new EdgeEffectParameters
+ outerCircle = new Circle
{
- Colour = Colour4.Black.Opacity(0.05f),
- Type = EdgeEffectType.Shadow,
- Radius = 5
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ MaskingSmoothness = 2,
+ BorderThickness = 0,
+ BorderColour = Colour4.Transparent,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Colour = Colour4.Black.Opacity(0.05f)
+ }
+ },
+ innerCircle = new Circle
+ {
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Scale = new Vector2(0.5f)
}
};
}
@@ -202,12 +221,25 @@ namespace osu.Game.Rulesets.Osu.Mods
Size = entry.InitialSize;
this
.ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine)
- .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine)
- .Delay(entry.FadeTime)
- .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine)
.Then()
.ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine)
- .FadeTo(0, entry.FadeTime, Easing.OutQuint);
+ .FadeTo(0, entry.FadeTime, Easing.OutExpo);
+
+ animateCircles(entry);
+ }
+
+ private void animateCircles(BubbleLifeTimeEntry entry)
+ {
+ innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black)
+ .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, entry.FadeTime * 7);
+
+ innerCircle.FadeTo(0.5f, entry.FadeTime * 7, Easing.InExpo)
+ .ScaleTo(1.1f, entry.FadeTime * 7, Easing.InSine);
+
+ outerCircle
+ .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine)
+ .Delay(entry.FadeTime)
+ .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine);
}
}
From ff5a12fcb4be833fdeb21b406b2ec9e19f88f8ff Mon Sep 17 00:00:00 2001
From: ansel <79257300125@ya.ru>
Date: Mon, 16 Jan 2023 20:39:38 +0300
Subject: [PATCH 0047/1604] Localise login form
---
osu.Game/Localisation/LoginPanelStrings.cs | 54 ++++++++++++++++++++++
osu.Game/Overlays/Login/LoginForm.cs | 13 +++---
osu.Game/Overlays/Login/LoginPanel.cs | 7 +--
osu.Game/Overlays/Login/UserAction.cs | 10 ++--
4 files changed, 69 insertions(+), 15 deletions(-)
create mode 100644 osu.Game/Localisation/LoginPanelStrings.cs
diff --git a/osu.Game/Localisation/LoginPanelStrings.cs b/osu.Game/Localisation/LoginPanelStrings.cs
new file mode 100644
index 0000000000..6dfb48fbdc
--- /dev/null
+++ b/osu.Game/Localisation/LoginPanelStrings.cs
@@ -0,0 +1,54 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Localisation;
+
+namespace osu.Game.Localisation
+{
+ public static class LoginPanelStrings
+ {
+ private const string prefix = @"osu.Game.Resources.Localisation.LoginPanel";
+
+ ///
+ /// "Do not disturb"
+ ///
+ public static LocalisableString DoNotDisturb => new TranslatableString(getKey(@"do_not_disturb"), @"Do not disturb");
+
+ ///
+ /// "Appear offline"
+ ///
+ public static LocalisableString AppearOffline => new TranslatableString(getKey(@"appear_offline"), @"Appear offline");
+
+ ///
+ /// "Sign out"
+ ///
+ public static LocalisableString SignOut => new TranslatableString(getKey(@"sign_out"), @"Sign out");
+
+ ///
+ /// "Signed in"
+ ///
+ public static LocalisableString SignedIn => new TranslatableString(getKey(@"signed_in"), @"Signed in");
+
+ ///
+ /// "ACCOUNT"
+ ///
+ public static LocalisableString Account => new TranslatableString(getKey(@"account"), @"ACCOUNT");
+
+ ///
+ /// "Remember username"
+ ///
+ public static LocalisableString RememberUsername => new TranslatableString(getKey(@"remember_username"), @"Remember username");
+
+ ///
+ /// "Stay signed in"
+ ///
+ public static LocalisableString StaySignedIn => new TranslatableString(getKey(@"stay_signed_in"), @"Stay signed in");
+
+ ///
+ /// "Register"
+ ///
+ public static LocalisableString Register => new TranslatableString(getKey(@"register"), @"Register");
+
+ private static string getKey(string key) => $@"{prefix}:{key}";
+ }
+}
diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs
index af145c418c..9f9b8d9342 100644
--- a/osu.Game/Overlays/Login/LoginForm.cs
+++ b/osu.Game/Overlays/Login/LoginForm.cs
@@ -16,6 +16,7 @@ using osu.Game.Online.API;
using osu.Game.Overlays.Settings;
using osu.Game.Resources.Localisation.Web;
using osuTK;
+using osu.Game.Localisation;
namespace osu.Game.Overlays.Login
{
@@ -47,7 +48,7 @@ namespace osu.Game.Overlays.Login
RelativeSizeAxes = Axes.X;
ErrorTextFlowContainer errorText;
- LinkFlowContainer forgottenPaswordLink;
+ LinkFlowContainer forgottenPasswordLink;
Children = new Drawable[]
{
@@ -71,15 +72,15 @@ namespace osu.Game.Overlays.Login
},
new SettingsCheckbox
{
- LabelText = "Remember username",
+ LabelText = LoginPanelStrings.RememberUsername,
Current = config.GetBindable(OsuSetting.SaveUsername),
},
new SettingsCheckbox
{
- LabelText = "Stay signed in",
+ LabelText = LoginPanelStrings.StaySignedIn,
Current = config.GetBindable(OsuSetting.SavePassword),
},
- forgottenPaswordLink = new LinkFlowContainer
+ forgottenPasswordLink = new LinkFlowContainer
{
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS },
RelativeSizeAxes = Axes.X,
@@ -105,7 +106,7 @@ namespace osu.Game.Overlays.Login
},
new SettingsButton
{
- Text = "Register",
+ Text = LoginPanelStrings.Register,
Action = () =>
{
RequestHide?.Invoke();
@@ -114,7 +115,7 @@ namespace osu.Game.Overlays.Login
}
};
- forgottenPaswordLink.AddLink(LayoutStrings.PopupLoginLoginForgot, $"{api.WebsiteRootUrl}/home/password-reset");
+ forgottenPasswordLink.AddLink(LayoutStrings.PopupLoginLoginForgot, $"{api.WebsiteRootUrl}/home/password-reset");
password.OnCommit += (_, _) => performLogin();
diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs
index 44f2f3273a..fb9987bd82 100644
--- a/osu.Game/Overlays/Login/LoginPanel.cs
+++ b/osu.Game/Overlays/Login/LoginPanel.cs
@@ -6,6 +6,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -81,7 +82,7 @@ namespace osu.Game.Overlays.Login
{
new OsuSpriteText
{
- Text = "ACCOUNT",
+ Text = LoginPanelStrings.Account,
Margin = new MarginPadding { Bottom = 5 },
Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
@@ -115,7 +116,7 @@ namespace osu.Game.Overlays.Login
},
};
- linkFlow.AddLink("cancel", api.Logout, string.Empty);
+ linkFlow.AddLink(Resources.Localisation.Web.CommonStrings.ButtonsCancel.ToLower(), api.Logout, string.Empty);
break;
case APIState.Online:
@@ -140,7 +141,7 @@ namespace osu.Game.Overlays.Login
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = "Signed in",
+ Text = LoginPanelStrings.SignedIn,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold),
Margin = new MarginPadding { Top = 5, Bottom = 5 },
},
diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs
index 7a18e38109..813968a053 100644
--- a/osu.Game/Overlays/Login/UserAction.cs
+++ b/osu.Game/Overlays/Login/UserAction.cs
@@ -1,11 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
-using System.ComponentModel;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
+using osu.Game.Localisation;
namespace osu.Game.Overlays.Login
{
@@ -14,13 +12,13 @@ namespace osu.Game.Overlays.Login
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))]
Online,
- [Description(@"Do not disturb")]
+ [LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.DoNotDisturb))]
DoNotDisturb,
- [Description(@"Appear offline")]
+ [LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.AppearOffline))]
AppearOffline,
- [Description(@"Sign out")]
+ [LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.SignOut))]
SignOut,
}
}
From 4c341db33f12a05a1ec6e4abbf60fd4294b0a70d Mon Sep 17 00:00:00 2001
From: ansel <79257300125@ya.ru>
Date: Mon, 16 Jan 2023 21:31:01 +0300
Subject: [PATCH 0048/1604] Localise registration window
---
.../Localisation/AccountCreationStrings.cs | 77 +++++++++++++++++++
.../Overlays/AccountCreation/ScreenEntry.cs | 19 ++---
.../Overlays/AccountCreation/ScreenWarning.cs | 5 +-
.../Overlays/AccountCreation/ScreenWelcome.cs | 9 +--
4 files changed, 94 insertions(+), 16 deletions(-)
create mode 100644 osu.Game/Localisation/AccountCreationStrings.cs
diff --git a/osu.Game/Localisation/AccountCreationStrings.cs b/osu.Game/Localisation/AccountCreationStrings.cs
new file mode 100644
index 0000000000..4e702ea7cc
--- /dev/null
+++ b/osu.Game/Localisation/AccountCreationStrings.cs
@@ -0,0 +1,77 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Localisation;
+
+namespace osu.Game.Localisation
+{
+ public static class AccountCreationStrings
+ {
+ private const string prefix = @"osu.Game.Resources.Localisation.AccountCreation";
+
+ ///
+ /// "New Player Registration"
+ ///
+ public static LocalisableString NewPlayerRegistration => new TranslatableString(getKey(@"new_player_registration"), @"New Player Registration");
+
+ ///
+ /// "let's get you started"
+ ///
+ public static LocalisableString LetsGetYouStarted => new TranslatableString(getKey(@"lets_get_you_started"), @"let's get you started");
+
+ ///
+ /// "Let's create an account!"
+ ///
+ public static LocalisableString LetsCreateAnAccount => new TranslatableString(getKey(@"lets_create_an_account"), @"Let's create an account!");
+
+ ///
+ /// "Help, I can't access my account!"
+ ///
+ public static LocalisableString HelpICantAccess => new TranslatableString(getKey(@"help_icant_access"), @"Help, I can't access my account!");
+
+ ///
+ /// "I understand. This account isn't for me."
+ ///
+ public static LocalisableString AccountIsntForMe => new TranslatableString(getKey(@"account_isnt_for_me"), @"I understand. This account isn't for me.");
+
+ ///
+ /// "email address"
+ ///
+ public static LocalisableString EmailAddress => new TranslatableString(getKey(@"email_address"), @"email address");
+
+ ///
+ /// "This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!"
+ ///
+ public static LocalisableString ThisWillBeYourPublic => new TranslatableString(getKey(@"this_will_be_your_public"),
+ @"This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!");
+
+ ///
+ /// "Will be used for notifications, account verification and in the case you forget your password. No spam, ever."
+ ///
+ public static LocalisableString EmailUsage =>
+ new TranslatableString(getKey(@"email_usage"), @"Will be used for notifications, account verification and in the case you forget your password. No spam, ever.");
+
+ ///
+ /// " Make sure to get it right!"
+ ///
+ public static LocalisableString MakeSureToGetIt => new TranslatableString(getKey(@"make_sure_to_get_it"), @" Make sure to get it right!");
+
+ ///
+ /// "At least "
+ ///
+ public static LocalisableString BeforeCharactersLong => new TranslatableString(getKey(@"before_characters_long"), @"At least ");
+
+ ///
+ /// "8 characters long"
+ ///
+ public static LocalisableString CharactersLong => new TranslatableString(getKey(@"characters_long"), @"8 characters long");
+
+ ///
+ /// ". Choose something long but also something you will remember, like a line from your favourite song."
+ ///
+ public static LocalisableString AfterCharactersLong =>
+ new TranslatableString(getKey(@"after_characters_long"), @". Choose something long but also something you will remember, like a line from your favourite song.");
+
+ private static string getKey(string key) => $@"{prefix}:{key}";
+ }
+}
diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
index 2e20f83e9e..6718b72805 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
@@ -17,6 +17,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Localisation;
using osu.Game.Online.API;
using osu.Game.Overlays.Settings;
using osu.Game.Resources.Localisation.Web;
@@ -71,7 +72,7 @@ namespace osu.Game.Overlays.AccountCreation
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 20),
- Text = "Let's create an account!",
+ Text = AccountCreationStrings.LetsCreateAnAccount
},
usernameTextBox = new OsuTextBox
{
@@ -86,7 +87,7 @@ namespace osu.Game.Overlays.AccountCreation
},
emailTextBox = new OsuTextBox
{
- PlaceholderText = "email address",
+ PlaceholderText = AccountCreationStrings.EmailAddress,
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this
},
@@ -118,7 +119,7 @@ namespace osu.Game.Overlays.AccountCreation
AutoSizeAxes = Axes.Y,
Child = new SettingsButton
{
- Text = "Register",
+ Text = LoginPanelStrings.Register,
Margin = new MarginPadding { Vertical = 20 },
Action = performRegistration
}
@@ -132,14 +133,14 @@ namespace osu.Game.Overlays.AccountCreation
textboxes = new[] { usernameTextBox, emailTextBox, passwordTextBox };
- usernameDescription.AddText("This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!");
+ usernameDescription.AddText(AccountCreationStrings.ThisWillBeYourPublic);
- emailAddressDescription.AddText("Will be used for notifications, account verification and in the case you forget your password. No spam, ever.");
- emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = cp.Font.With(Typeface.Torus, weight: FontWeight.Bold));
+ emailAddressDescription.AddText(AccountCreationStrings.EmailUsage);
+ emailAddressDescription.AddText(AccountCreationStrings.MakeSureToGetIt, cp => cp.Font = cp.Font.With(Typeface.Torus, weight: FontWeight.Bold));
- passwordDescription.AddText("At least ");
- characterCheckText = passwordDescription.AddText("8 characters long");
- passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song.");
+ passwordDescription.AddText(AccountCreationStrings.BeforeCharactersLong);
+ characterCheckText = passwordDescription.AddText(AccountCreationStrings.CharactersLong);
+ passwordDescription.AddText(AccountCreationStrings.AfterCharactersLong);
passwordTextBox.Current.BindValueChanged(_ => updateCharacterCheckTextColour(), true);
characterCheckText.DrawablePartsRecreated += _ => updateCharacterCheckTextColour();
diff --git a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
index a833a871f9..f5807b49b5 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenWarning.cs
@@ -17,6 +17,7 @@ using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
using osuTK;
using osuTK.Graphics;
+using osu.Game.Localisation;
namespace osu.Game.Overlays.AccountCreation
{
@@ -101,13 +102,13 @@ namespace osu.Game.Overlays.AccountCreation
},
new SettingsButton
{
- Text = "Help, I can't access my account!",
+ Text = AccountCreationStrings.HelpICantAccess,
Margin = new MarginPadding { Top = 50 },
Action = () => game?.OpenUrlExternally(help_centre_url)
},
new DangerousSettingsButton
{
- Text = "I understand. This account isn't for me.",
+ Text = AccountCreationStrings.AccountIsntForMe,
Action = () => this.Push(new ScreenEntry())
},
furtherAssistance = new LinkFlowContainer(cp => cp.Font = cp.Font.With(size: 12))
diff --git a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
index 4becb225f8..a81b1019fe 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenWelcome.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -12,6 +10,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
using osuTK;
+using osu.Game.Localisation;
namespace osu.Game.Overlays.AccountCreation
{
@@ -46,18 +45,18 @@ namespace osu.Game.Overlays.AccountCreation
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light),
- Text = "New Player Registration",
+ Text = AccountCreationStrings.NewPlayerRegistration,
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 12),
- Text = "let's get you started",
+ Text = AccountCreationStrings.LetsGetYouStarted,
},
new SettingsButton
{
- Text = "Let's create an account!",
+ Text = AccountCreationStrings.LetsCreateAnAccount,
Margin = new MarginPadding { Vertical = 120 },
Action = () => this.Push(new ScreenWarning())
}
From bb3668c76901f5d2109697748b91627fdcb54878 Mon Sep 17 00:00:00 2001
From: ansel <79257300125@ya.ru>
Date: Mon, 16 Jan 2023 22:24:03 +0300
Subject: [PATCH 0049/1604] Reuse existing
---
osu.Game/Localisation/AccountCreationStrings.cs | 5 -----
osu.Game/Localisation/LoginPanelStrings.cs | 5 -----
osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 2 +-
osu.Game/Overlays/Login/UserAction.cs | 2 +-
4 files changed, 2 insertions(+), 12 deletions(-)
diff --git a/osu.Game/Localisation/AccountCreationStrings.cs b/osu.Game/Localisation/AccountCreationStrings.cs
index 4e702ea7cc..0b27944a61 100644
--- a/osu.Game/Localisation/AccountCreationStrings.cs
+++ b/osu.Game/Localisation/AccountCreationStrings.cs
@@ -34,11 +34,6 @@ namespace osu.Game.Localisation
///
public static LocalisableString AccountIsntForMe => new TranslatableString(getKey(@"account_isnt_for_me"), @"I understand. This account isn't for me.");
- ///
- /// "email address"
- ///
- public static LocalisableString EmailAddress => new TranslatableString(getKey(@"email_address"), @"email address");
-
///
/// "This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!"
///
diff --git a/osu.Game/Localisation/LoginPanelStrings.cs b/osu.Game/Localisation/LoginPanelStrings.cs
index 6dfb48fbdc..535d86fbc5 100644
--- a/osu.Game/Localisation/LoginPanelStrings.cs
+++ b/osu.Game/Localisation/LoginPanelStrings.cs
@@ -19,11 +19,6 @@ namespace osu.Game.Localisation
///
public static LocalisableString AppearOffline => new TranslatableString(getKey(@"appear_offline"), @"Appear offline");
- ///
- /// "Sign out"
- ///
- public static LocalisableString SignOut => new TranslatableString(getKey(@"sign_out"), @"Sign out");
-
///
/// "Signed in"
///
diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
index 6718b72805..fc450c7a91 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
@@ -87,7 +87,7 @@ namespace osu.Game.Overlays.AccountCreation
},
emailTextBox = new OsuTextBox
{
- PlaceholderText = AccountCreationStrings.EmailAddress,
+ PlaceholderText = ModelValidationStrings.UserAttributesUserEmail.ToLower(),
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this
},
diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs
index 813968a053..d4d639f2fb 100644
--- a/osu.Game/Overlays/Login/UserAction.cs
+++ b/osu.Game/Overlays/Login/UserAction.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Login
[LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.AppearOffline))]
AppearOffline,
- [LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.SignOut))]
+ [LocalisableDescription(typeof(UserVerificationStrings), nameof(UserVerificationStrings.BoxInfoLogoutLink))]
SignOut,
}
}
From ad32d99daabe400402e59d4046de6de1e95a1d43 Mon Sep 17 00:00:00 2001
From: ansel <79257300125@ya.ru>
Date: Mon, 16 Jan 2023 23:08:29 +0300
Subject: [PATCH 0050/1604] Localise caps lock warning
---
osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs | 3 ++-
osu.Game/Localisation/CommonStrings.cs | 7 ++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
index 63c98d7838..9de9eceb07 100644
--- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
@@ -16,6 +16,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Platform;
+using osu.Game.Localisation;
namespace osu.Game.Graphics.UserInterface
{
@@ -112,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
private partial class CapsWarning : SpriteIcon, IHasTooltip
{
- public LocalisableString TooltipText => "caps lock is active";
+ public LocalisableString TooltipText => CommonStrings.CapsLockIsActive;
public CapsWarning()
{
diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs
index 10178915a2..a68f08efcc 100644
--- a/osu.Game/Localisation/CommonStrings.cs
+++ b/osu.Game/Localisation/CommonStrings.cs
@@ -154,6 +154,11 @@ namespace osu.Game.Localisation
///
public static LocalisableString Exit => new TranslatableString(getKey(@"exit"), @"Exit");
+ ///
+ /// "caps lock is active"
+ ///
+ public static LocalisableString CapsLockIsActive => new TranslatableString(getKey(@"caps_lock_is_active"), @"caps lock is active");
+
private static string getKey(string key) => $@"{prefix}:{key}";
}
-}
+}
\ No newline at end of file
From df74bccaaa7345e2b016516b799e85dc790a184c Mon Sep 17 00:00:00 2001
From: ansel <79257300125@ya.ru>
Date: Tue, 17 Jan 2023 13:31:03 +0300
Subject: [PATCH 0051/1604] Replace 2 strings with one formattable
---
osu.Game/Localisation/AccountCreationStrings.cs | 11 +++--------
osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 10 +++++++---
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/osu.Game/Localisation/AccountCreationStrings.cs b/osu.Game/Localisation/AccountCreationStrings.cs
index 0b27944a61..6acfaaa9ac 100644
--- a/osu.Game/Localisation/AccountCreationStrings.cs
+++ b/osu.Game/Localisation/AccountCreationStrings.cs
@@ -52,21 +52,16 @@ namespace osu.Game.Localisation
public static LocalisableString MakeSureToGetIt => new TranslatableString(getKey(@"make_sure_to_get_it"), @" Make sure to get it right!");
///
- /// "At least "
+ /// "At least {0}. Choose something long but also something you will remember, like a line from your favourite song."
///
- public static LocalisableString BeforeCharactersLong => new TranslatableString(getKey(@"before_characters_long"), @"At least ");
+ public static LocalisableString PasswordRequirements(string arg0) => new TranslatableString(getKey(@"password_requirements"),
+ @"At least {0}. Choose something long but also something you will remember, like a line from your favourite song.", arg0);
///
/// "8 characters long"
///
public static LocalisableString CharactersLong => new TranslatableString(getKey(@"characters_long"), @"8 characters long");
- ///
- /// ". Choose something long but also something you will remember, like a line from your favourite song."
- ///
- public static LocalisableString AfterCharactersLong =>
- new TranslatableString(getKey(@"after_characters_long"), @". Choose something long but also something you will remember, like a line from your favourite song.");
-
private static string getKey(string key) => $@"{prefix}:{key}";
}
}
diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
index fc450c7a91..192b95b963 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
@@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Localisation;
using osu.Framework.Utils;
using osu.Framework.Platform;
using osu.Framework.Screens;
@@ -52,7 +53,7 @@ namespace osu.Game.Overlays.AccountCreation
private OsuGame game { get; set; }
[BackgroundDependencyLoader]
- private void load()
+ private void load(LocalisationManager localisationManager)
{
InternalChildren = new Drawable[]
{
@@ -138,9 +139,12 @@ namespace osu.Game.Overlays.AccountCreation
emailAddressDescription.AddText(AccountCreationStrings.EmailUsage);
emailAddressDescription.AddText(AccountCreationStrings.MakeSureToGetIt, cp => cp.Font = cp.Font.With(Typeface.Torus, weight: FontWeight.Bold));
- passwordDescription.AddText(AccountCreationStrings.BeforeCharactersLong);
+ string[] passwordReq = localisationManager.GetLocalisedBindableString(AccountCreationStrings.PasswordRequirements("{}")).Value.Split("{}");
+ if (passwordReq.Length != 2) passwordReq = AccountCreationStrings.PasswordRequirements("{}").ToString().Split("{}");
+
+ passwordDescription.AddText(passwordReq[0]);
characterCheckText = passwordDescription.AddText(AccountCreationStrings.CharactersLong);
- passwordDescription.AddText(AccountCreationStrings.AfterCharactersLong);
+ passwordDescription.AddText(passwordReq[1]);
passwordTextBox.Current.BindValueChanged(_ => updateCharacterCheckTextColour(), true);
characterCheckText.DrawablePartsRecreated += _ => updateCharacterCheckTextColour();
From 7c81f1e75bb40981a55f16a3544d9521280bf49c Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Fri, 27 Jan 2023 11:21:11 +0100
Subject: [PATCH 0052/1604] Remove unnecessary BDL from bubble drawable
Improve animation duration formula
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 85 +++++++++------------
1 file changed, 38 insertions(+), 47 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index f5e7e035b2..6613d84e0e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -3,7 +3,6 @@
using System;
using System.Linq;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -12,7 +11,6 @@ using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
-using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Pooling;
@@ -22,7 +20,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -63,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
// Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen
bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f);
- bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimeFadeIn * 2;
+ bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2;
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
drawableRuleset.Playfield.DisplayJudgements.Value = false;
@@ -125,8 +122,8 @@ namespace osu.Game.Rulesets.Osu.Mods
#region Pooled Bubble drawable
- //LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it
- //Todo: find solution to bubbles rewinding in "groups"
+ // LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it
+ // Todo: find solution to bubbles rewinding in "groups"
private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer
{
protected override bool RemoveRewoundEntry => true;
@@ -166,8 +163,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private void apply(BubbleLifeTimeEntry? entry)
{
- if (entry == null)
- return;
+ if (entry == null) return;
ApplyTransformsAt(float.MinValue, true);
ClearTransforms(true);
@@ -180,38 +176,36 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
- private partial class BubbleDrawable : CompositeDrawable, IHasAccentColour
+ private partial class BubbleDrawable : CircularContainer
{
- public Color4 AccentColour { get; set; }
+ private readonly Circle innerCircle;
+ private readonly Box colourBox;
- private Circle outerCircle = null!;
- private Circle innerCircle = null!;
-
- [BackgroundDependencyLoader]
- private void load()
+ public BubbleDrawable()
{
- InternalChildren = new Drawable[]
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ Masking = true;
+ MaskingSmoothness = 2;
+ BorderThickness = 0;
+ BorderColour = Colour4.Transparent;
+ EdgeEffect = new EdgeEffectParameters
{
- outerCircle = new Circle
- {
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- MaskingSmoothness = 2,
- BorderThickness = 0,
- BorderColour = Colour4.Transparent,
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Radius = 3,
- Colour = Colour4.Black.Opacity(0.05f)
- }
- },
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Colour = Colour4.Black.Opacity(0.05f)
+ };
+
+ Children = new Drawable[]
+ {
+ colourBox = new Box { RelativeSizeAxes = Axes.Both, },
innerCircle = new Circle
{
+ Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
- Scale = new Vector2(0.5f)
+ Size = new Vector2(0.5f),
}
};
}
@@ -219,27 +213,24 @@ namespace osu.Game.Rulesets.Osu.Mods
public void Animate(BubbleLifeTimeEntry entry)
{
Size = entry.InitialSize;
- this
- .ScaleTo(entry.MaxSize, entry.FadeTime * 6, Easing.OutSine)
+
+ this.ScaleTo(entry.MaxSize, getAnimationTime() * 0.8f, Easing.OutSine)
.Then()
- .ScaleTo(entry.MaxSize * 1.5f, entry.FadeTime, Easing.OutSine)
- .FadeTo(0, entry.FadeTime, Easing.OutExpo);
+ .ScaleTo(entry.MaxSize * 1.5f, getAnimationTime() * 0.2f, Easing.OutSine)
+ .FadeTo(0, getAnimationTime() * 0.2f, Easing.OutExpo);
- animateCircles(entry);
- }
+ colourBox.FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, getAnimationTime() * 0.1f, Easing.OutSine)
+ .Then()
+ .FadeColour(entry.IsHit ? entry.Colour.Darken(0.4f) : Colour4.Black, getAnimationTime() * 0.9f, Easing.OutSine);
- private void animateCircles(BubbleLifeTimeEntry entry)
- {
innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black)
- .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, entry.FadeTime * 7);
+ .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, getAnimationTime());
- innerCircle.FadeTo(0.5f, entry.FadeTime * 7, Easing.InExpo)
- .ScaleTo(1.1f, entry.FadeTime * 7, Easing.InSine);
+ innerCircle.FadeTo(0.5f, getAnimationTime(), Easing.InExpo)
+ .ScaleTo(2.2f, getAnimationTime(), Easing.InSine);
- outerCircle
- .FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, entry.FadeTime, Easing.OutSine)
- .Delay(entry.FadeTime)
- .FadeColour(entry.IsHit ? entry.Colour.Darken(.4f) : Colour4.Black, entry.FadeTime * 5, Easing.OutSine);
+ // The absolute length of the bubble's animation, can be used in fractions for animations of partial length
+ double getAnimationTime() => 2000 + 450 / (450 / entry.FadeTime);
}
}
From c3090dea5f7bea51e2662cd82fc292d65c56d7ca Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sat, 28 Jan 2023 00:30:30 +0100
Subject: [PATCH 0053/1604] Simplify animations
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 28 +++++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 6613d84e0e..479741b5b9 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -214,23 +214,29 @@ namespace osu.Game.Rulesets.Osu.Mods
{
Size = entry.InitialSize;
- this.ScaleTo(entry.MaxSize, getAnimationTime() * 0.8f, Easing.OutSine)
+ //We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
+ var colourDarker = entry.Colour.Darken(0.1f);
+
+ this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f, Easing.OutSine)
.Then()
- .ScaleTo(entry.MaxSize * 1.5f, getAnimationTime() * 0.2f, Easing.OutSine)
- .FadeTo(0, getAnimationTime() * 0.2f, Easing.OutExpo);
+ .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutSine)
+ .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutExpo);
- colourBox.FadeColour(entry.IsHit ? entry.Colour : Colour4.Black, getAnimationTime() * 0.1f, Easing.OutSine)
- .Then()
- .FadeColour(entry.IsHit ? entry.Colour.Darken(0.4f) : Colour4.Black, getAnimationTime() * 0.9f, Easing.OutSine);
+ innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutCubic);
- innerCircle.FadeColour(entry.IsHit ? entry.Colour.Darken(0.2f) : Colour4.Black)
- .FadeColour(entry.IsHit ? entry.Colour.Lighten(0.2f) : Colour4.Black, getAnimationTime());
+ if (!entry.IsHit)
+ {
+ colourBox.Colour = Colour4.Black;
+ innerCircle.Colour = Colour4.Black;
- innerCircle.FadeTo(0.5f, getAnimationTime(), Easing.InExpo)
- .ScaleTo(2.2f, getAnimationTime(), Easing.InSine);
+ return;
+ }
+
+ colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint);
+ innerCircle.FadeColour(colourDarker);
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
- double getAnimationTime() => 2000 + 450 / (450 / entry.FadeTime);
+ double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.15f);
}
}
From 66da4c0288fd63d87fa7b4ea3547da39796e74e7 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sat, 28 Jan 2023 17:38:24 +0100
Subject: [PATCH 0054/1604] Add colouration to the sliders to better match the
vibrancy of the mod
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 27 +++++++++++++++------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 479741b5b9..0101427f7a 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Pooling;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
@@ -77,6 +78,12 @@ namespace osu.Game.Rulesets.Osu.Mods
private void applyBubbleState(DrawableHitObject drawableObject)
{
+ if (drawableObject is DrawableSlider slider)
+ {
+ slider.Body.OnSkinChanged += () => applySliderState(slider);
+ applySliderState(slider);
+ }
+
if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return;
OsuHitObject hitObject = drawableOsuObject.HitObject;
@@ -93,9 +100,9 @@ namespace osu.Game.Rulesets.Osu.Mods
addBubbleContainer(hitCircle.Position, drawableOsuObject);
break;
- case DrawableSpinnerTick:
case DrawableSlider:
- return;
+ case DrawableSpinnerTick:
+ break;
default:
addBubbleContainer(hitObject.Position, drawableOsuObject);
@@ -103,6 +110,9 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
+ private void applySliderState(DrawableSlider slider) =>
+ ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
+
private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject)
{
bubbleContainer.Add
@@ -217,12 +227,12 @@ namespace osu.Game.Rulesets.Osu.Mods
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
var colourDarker = entry.Colour.Darken(0.1f);
- this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f, Easing.OutSine)
+ this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f)
.Then()
- .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutSine)
- .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutExpo);
+ .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint)
+ .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutQuint);
- innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutCubic);
+ innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutQuint);
if (!entry.IsHit)
{
@@ -232,11 +242,12 @@ namespace osu.Game.Rulesets.Osu.Mods
return;
}
- colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint);
+ colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint
+ );
innerCircle.FadeColour(colourDarker);
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
- double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.15f);
+ double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f);
}
}
From 3bdf83bf44e63000d2a4c23c7467a1aa24b87724 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sat, 28 Jan 2023 22:44:57 +0100
Subject: [PATCH 0055/1604] Redo the drawable structure of bubbledrawable to
run and look better
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 50 ++++++++++-----------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 0101427f7a..2c90bfa399 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Performance;
@@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Mods
//Needs to be done explicitly to avoid being handled by DrawableHitCircle below
case DrawableSliderHead:
addBubbleContainer(hitObject.Position, drawableOsuObject);
- break;
+ return;
//Stack leniency causes placement issues if this isn't handled as such.
case DrawableHitCircle hitCircle:
@@ -188,7 +189,6 @@ namespace osu.Game.Rulesets.Osu.Mods
private partial class BubbleDrawable : CircularContainer
{
- private readonly Circle innerCircle;
private readonly Box colourBox;
public BubbleDrawable()
@@ -196,55 +196,55 @@ namespace osu.Game.Rulesets.Osu.Mods
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- Masking = true;
MaskingSmoothness = 2;
BorderThickness = 0;
- BorderColour = Colour4.Transparent;
+ BorderColour = Colour4.White;
+ Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Radius = 3,
Colour = Colour4.Black.Opacity(0.05f)
};
-
- Children = new Drawable[]
- {
- colourBox = new Box { RelativeSizeAxes = Axes.Both, },
- innerCircle = new Circle
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Size = new Vector2(0.5f),
- }
- };
+ Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, };
}
public void Animate(BubbleLifeTimeEntry entry)
{
Size = entry.InitialSize;
+ BorderThickness = Width / 3.5f;
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
- var colourDarker = entry.Colour.Darken(0.1f);
+ ColourInfo colourDarker = entry.Colour.Darken(0.1f);
+ // Main bubble scaling based on combo
this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f)
.Then()
+ // Pop at the end of the bubbles life time
.ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint)
- .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutQuint);
-
- innerCircle.ScaleTo(2f, getAnimationDuration() * 0.8f, Easing.OutQuint);
+ .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutCirc);
if (!entry.IsHit)
{
- colourBox.Colour = Colour4.Black;
- innerCircle.Colour = Colour4.Black;
+ Colour = Colour4.Black;
+ BorderColour = Colour4.Black;
return;
}
- colourBox.FadeColour(colourDarker, getAnimationDuration() * 0.2f, Easing.OutQuint
- );
- innerCircle.FadeColour(colourDarker);
+ colourBox.FadeColour(colourDarker);
+
+ this.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint);
+
+ // Ripple effect utilises the border to reduce drawable count
+ this.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint)
+
+ // Avoids transparency overlap issues during the bubble "pop"
+ .Then().Schedule(() =>
+ {
+ BorderThickness = 0;
+ BorderColour = Colour4.Transparent;
+ });
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f);
From abcb564a74efeb3bb9e43ee1686f402297d416b5 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Mon, 6 Feb 2023 17:32:17 +0900
Subject: [PATCH 0056/1604] Code quality pass of `OsuModBubbles`
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 24 ++++++---------------
1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 2c90bfa399..3606434042 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Bubbles";
- public override string Acronym => "BB";
+ public override string Acronym => "BU";
- public override LocalisableString Description => "Dont let their popping distract you!";
+ public override LocalisableString Description => "Don't let their popping distract you!";
public override double ScoreMultiplier => 1;
public override ModType Type => ModType.Fun;
- // Compatibility with these seems potentially feasible in the future, blocked for now because they dont work as one would expect
+ // Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect
public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
private PlayfieldAdjustmentContainer adjustmentContainer = null!;
@@ -87,26 +87,14 @@ namespace osu.Game.Rulesets.Osu.Mods
if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return;
- OsuHitObject hitObject = drawableOsuObject.HitObject;
-
switch (drawableOsuObject)
{
- //Needs to be done explicitly to avoid being handled by DrawableHitCircle below
- case DrawableSliderHead:
- addBubbleContainer(hitObject.Position, drawableOsuObject);
- return;
-
- //Stack leniency causes placement issues if this isn't handled as such.
- case DrawableHitCircle hitCircle:
- addBubbleContainer(hitCircle.Position, drawableOsuObject);
- break;
-
case DrawableSlider:
case DrawableSpinnerTick:
break;
default:
- addBubbleContainer(hitObject.Position, drawableOsuObject);
+ addBubbleForObject(drawableOsuObject);
break;
}
}
@@ -114,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private void applySliderState(DrawableSlider slider) =>
((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
- private void addBubbleContainer(Vector2 position, DrawableHitObject hitObject)
+ private void addBubbleForObject(DrawableOsuHitObject hitObject)
{
bubbleContainer.Add
(
@@ -122,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
LifetimeStart = bubbleContainer.Time.Current,
Colour = hitObject.AccentColour.Value,
- Position = position,
+ Position = hitObject.HitObject.Position,
InitialSize = new Vector2(bubbleRadius),
MaxSize = maxSize,
FadeTime = bubbleFade,
From f0d4b9f0ca339c79c74fba39f1d9a97de37f5f6e Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Mon, 6 Feb 2023 17:00:47 +0100
Subject: [PATCH 0057/1604] Add inline comment for colour border override
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 3606434042..41430bb323 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -99,6 +99,7 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
+ // Makes the slider border coloured on all skins
private void applySliderState(DrawableSlider slider) =>
((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
From 5e0c4aa904f28435bb2ffad3967f2f4fe2b08802 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 8 Feb 2023 11:12:14 +0100
Subject: [PATCH 0058/1604] Refactor pooling for bubbles, tweak the animations
a tad, add some clarifying comments
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 231 ++++++++++----------
1 file changed, 114 insertions(+), 117 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 41430bb323..8cf9c619d7 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -8,13 +8,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Objects.Pooling;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Skinning.Default;
@@ -41,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
private PlayfieldAdjustmentContainer adjustmentContainer = null!;
- private BubbleContainer bubbleContainer = null!;
+ private Container bubbleContainer = null!;
private readonly Bindable currentCombo = new BindableInt();
@@ -49,6 +47,10 @@ namespace osu.Game.Rulesets.Osu.Mods
private float bubbleRadius;
private double bubbleFade;
+ private readonly DrawablePool bubblePool = new DrawablePool(100);
+
+ private DrawableOsuHitObject lastJudgedHitobject = null!;
+
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
@@ -56,6 +58,63 @@ namespace osu.Game.Rulesets.Osu.Mods
currentCombo.BindTo(scoreProcessor.Combo);
currentCombo.BindValueChanged(combo =>
maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
+
+ scoreProcessor.NewJudgement += result =>
+ {
+ if (result.HitObject is not OsuHitObject osuHitObject) return;
+
+ DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitobject;
+
+ switch (result.HitObject)
+ {
+ case Slider:
+ case SpinnerTick:
+ break;
+
+ default:
+ addBubble();
+ break;
+ }
+
+ void addBubble()
+ {
+ BubbleDrawable bubble = bubblePool.Get();
+ bubble.Info = new BubbleInfo
+ {
+ InitialSize = new Vector2(bubbleRadius),
+ MaxSize = maxSize,
+ Position = getPosition(),
+ FadeTime = bubbleFade,
+ Colour = drawableOsuHitObject.AccentColour.Value,
+ IsHit = drawableOsuHitObject.IsHit,
+ };
+ bubbleContainer.Add(bubble);
+ }
+
+ Vector2 getPosition()
+ {
+ switch (drawableOsuHitObject)
+ {
+ // SliderHeads are derived from HitCircles,
+ // so we must handle them before to avoid them using the wrong positioning logic
+ case DrawableSliderHead:
+ return osuHitObject.Position;
+
+ // Using hitobject position will cause issues with HitCircle placement due to stack leniency.
+ case DrawableHitCircle:
+ return drawableOsuHitObject.Position;
+
+ default:
+ return osuHitObject.Position;
+ }
+ }
+ };
+
+ scoreProcessor.JudgementReverted += _ =>
+ {
+ bubbleContainer.LastOrDefault()?.FinishTransforms();
+ bubbleContainer.LastOrDefault()?.Expire();
+ };
}
public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
@@ -69,178 +128,116 @@ namespace osu.Game.Rulesets.Osu.Mods
adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
- adjustmentContainer.Add(bubbleContainer = new BubbleContainer());
+ adjustmentContainer.Add(bubbleContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ });
drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer);
}
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
-
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
private void applyBubbleState(DrawableHitObject drawableObject)
{
+ DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)drawableObject;
+
if (drawableObject is DrawableSlider slider)
{
slider.Body.OnSkinChanged += () => applySliderState(slider);
applySliderState(slider);
}
- if (drawableObject is not DrawableOsuHitObject drawableOsuObject || !drawableObject.Judged) return;
+ if (osuHitObject == lastJudgedHitobject || !osuHitObject.Judged) return;
- switch (drawableOsuObject)
+ switch (osuHitObject)
{
case DrawableSlider:
case DrawableSpinnerTick:
break;
default:
- addBubbleForObject(drawableOsuObject);
+ lastJudgedHitobject = osuHitObject;
break;
}
}
- // Makes the slider border coloured on all skins
+ // Makes the slider border coloured on all skins (for aesthetics)
private void applySliderState(DrawableSlider slider) =>
((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
- private void addBubbleForObject(DrawableOsuHitObject hitObject)
- {
- bubbleContainer.Add
- (
- new BubbleLifeTimeEntry
- {
- LifetimeStart = bubbleContainer.Time.Current,
- Colour = hitObject.AccentColour.Value,
- Position = hitObject.HitObject.Position,
- InitialSize = new Vector2(bubbleRadius),
- MaxSize = maxSize,
- FadeTime = bubbleFade,
- IsHit = hitObject.IsHit
- }
- );
- }
-
#region Pooled Bubble drawable
- // LifetimeEntry flow is necessary to allow for correct rewind behaviour, can probably be made generic later if more mods are made requiring it
- // Todo: find solution to bubbles rewinding in "groups"
- private sealed partial class BubbleContainer : PooledDrawableWithLifetimeContainer
- {
- protected override bool RemoveRewoundEntry => true;
-
- private readonly DrawablePool pool;
-
- public BubbleContainer()
- {
- RelativeSizeAxes = Axes.Both;
- AddInternal(pool = new DrawablePool(10, 1000));
- }
-
- protected override BubbleObject GetDrawable(BubbleLifeTimeEntry entry) => pool.Get(d => d.Apply(entry));
- }
-
- private sealed partial class BubbleObject : PoolableDrawableWithLifetime
- {
- private readonly BubbleDrawable bubbleDrawable;
-
- public BubbleObject()
- {
- InternalChild = bubbleDrawable = new BubbleDrawable();
- }
-
- protected override void OnApply(BubbleLifeTimeEntry entry)
- {
- base.OnApply(entry);
- if (IsLoaded)
- apply(entry);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- apply(Entry);
- }
-
- private void apply(BubbleLifeTimeEntry? entry)
- {
- if (entry == null) return;
-
- ApplyTransformsAt(float.MinValue, true);
- ClearTransforms(true);
-
- Position = entry.Position;
-
- bubbleDrawable.Animate(entry);
-
- LifetimeEnd = bubbleDrawable.LatestTransformEndTime;
- }
- }
-
- private partial class BubbleDrawable : CircularContainer
+ private partial class BubbleDrawable : PoolableDrawable
{
private readonly Box colourBox;
+ private readonly CircularContainer content;
+
+ public BubbleInfo Info { get; set; }
public BubbleDrawable()
{
- Anchor = Anchor.Centre;
Origin = Anchor.Centre;
-
- MaskingSmoothness = 2;
- BorderThickness = 0;
- BorderColour = Colour4.White;
- Masking = true;
- EdgeEffect = new EdgeEffectParameters
+ InternalChild = content = new CircularContainer
{
- Type = EdgeEffectType.Shadow,
- Radius = 3,
- Colour = Colour4.Black.Opacity(0.05f)
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ MaskingSmoothness = 2,
+ BorderThickness = 0,
+ BorderColour = Colour4.White,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Colour = Colour4.Black.Opacity(0.05f),
+ },
+ Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, }
};
- Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, };
}
- public void Animate(BubbleLifeTimeEntry entry)
+ protected override void PrepareForUse()
{
- Size = entry.InitialSize;
- BorderThickness = Width / 3.5f;
+ Alpha = 1;
+ Colour = Colour4.White;
+ Scale = new Vector2(1);
+ Position = Info.Position;
+ Size = Info.InitialSize;
+ content.BorderThickness = Info.InitialSize.X / 3.5f;
+ content.BorderColour = Colour4.White;
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
- ColourInfo colourDarker = entry.Colour.Darken(0.1f);
+ ColourInfo colourDarker = Info.Colour.Darken(0.1f);
// Main bubble scaling based on combo
- this.ScaleTo(entry.MaxSize, getAnimationDuration() * 0.8f)
+ this.ScaleTo(Info.MaxSize, getAnimationDuration() * 0.8f)
.Then()
// Pop at the end of the bubbles life time
- .ScaleTo(entry.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint)
- .FadeTo(0, getAnimationDuration() * 0.2f, Easing.OutCirc);
+ .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint)
+ .FadeOutFromOne(getAnimationDuration() * 0.2f, Easing.OutCirc).Expire();
- if (!entry.IsHit)
+ if (Info.IsHit)
{
- Colour = Colour4.Black;
- BorderColour = Colour4.Black;
+ colourBox.FadeColour(colourDarker);
+
+ content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint);
+ // Ripple effect utilises the border to reduce drawable count
+ content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint)
+ // Avoids transparency overlap issues during the bubble "pop"
+ .Then().Schedule(() => content.BorderThickness = 0);
return;
}
- colourBox.FadeColour(colourDarker);
-
- this.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint);
-
- // Ripple effect utilises the border to reduce drawable count
- this.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint)
-
- // Avoids transparency overlap issues during the bubble "pop"
- .Then().Schedule(() =>
- {
- BorderThickness = 0;
- BorderColour = Colour4.Transparent;
- });
+ Colour = Colour4.Black;
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
- double getAnimationDuration() => 1700 + Math.Pow(entry.FadeTime, 1.07f);
+ double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f);
}
}
- private class BubbleLifeTimeEntry : LifetimeEntry
+ private struct BubbleInfo
{
public Vector2 InitialSize { get; set; }
From 6ff6e06a69256987d7a409a154c021df9988df30 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Sun, 12 Feb 2023 11:37:07 +0100
Subject: [PATCH 0059/1604] Simplify bubble container structure, modify some
comments
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 8cf9c619d7..3e3fce5c27 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -38,8 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods
// Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect
public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
- private PlayfieldAdjustmentContainer adjustmentContainer = null!;
- private Container bubbleContainer = null!;
+ private PlayfieldAdjustmentContainer bubbleContainer = null!;
private readonly Bindable currentCombo = new BindableInt();
@@ -119,20 +118,17 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
{
- // Multiplying by 2 results in an initial size that is too large, hence 1.85 has been chosen
- bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.85f);
+ // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
+ // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
+ bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f);
bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2;
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
drawableRuleset.Playfield.DisplayJudgements.Value = false;
- adjustmentContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
+ bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
- adjustmentContainer.Add(bubbleContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- });
- drawableRuleset.KeyBindingInputManager.Add(adjustmentContainer);
+ drawableRuleset.KeyBindingInputManager.Add(bubbleContainer);
}
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
From d100a4a4915b3c67c48cf790822e93da90dc02be Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Tue, 14 Feb 2023 10:12:37 +0100
Subject: [PATCH 0060/1604] Make `lastJudgedHitObject` nullable, and fix typo
in name.
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 23 ++++++++-------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 3e3fce5c27..40c235911c 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -2,8 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@@ -48,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private readonly DrawablePool bubblePool = new DrawablePool(100);
- private DrawableOsuHitObject lastJudgedHitobject = null!;
+ private DrawableOsuHitObject? lastJudgedHitObject;
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
@@ -60,9 +62,11 @@ namespace osu.Game.Rulesets.Osu.Mods
scoreProcessor.NewJudgement += result =>
{
- if (result.HitObject is not OsuHitObject osuHitObject) return;
+ if (result.HitObject is not OsuHitObject osuHitObject || lastJudgedHitObject.IsNull()) return;
- DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitobject;
+ Debug.Assert(result.HitObject == lastJudgedHitObject.HitObject);
+
+ DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitObject;
switch (result.HitObject)
{
@@ -144,18 +148,9 @@ namespace osu.Game.Rulesets.Osu.Mods
applySliderState(slider);
}
- if (osuHitObject == lastJudgedHitobject || !osuHitObject.Judged) return;
+ if (osuHitObject == lastJudgedHitObject || !osuHitObject.Judged) return;
- switch (osuHitObject)
- {
- case DrawableSlider:
- case DrawableSpinnerTick:
- break;
-
- default:
- lastJudgedHitobject = osuHitObject;
- break;
- }
+ lastJudgedHitObject = osuHitObject;
}
// Makes the slider border coloured on all skins (for aesthetics)
From 2d49b5f9d66150598260451250c11736bdad87bc Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Tue, 14 Feb 2023 14:03:48 +0100
Subject: [PATCH 0061/1604] Move bubbles to ruleset overlays container
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 40c235911c..732626b177 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Mods
bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
- drawableRuleset.KeyBindingInputManager.Add(bubbleContainer);
+ drawableRuleset.Overlays.Add(bubbleContainer);
}
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
From 92c61c73396939363b3c6bd7ffffb083e3c74116 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Tue, 14 Feb 2023 16:31:34 +0100
Subject: [PATCH 0062/1604] move logic for bubble invoking to
`ApplyToDrawableHitobject()`` method
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 85 +++++++++------------
1 file changed, 34 insertions(+), 51 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 732626b177..4a8c11e7ff 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -2,10 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
-using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@@ -25,7 +23,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
- public partial class OsuModBubbles : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, IApplicableToScoreProcessor
+ public partial class OsuModBubbles : Mod, IApplicableToDrawableRuleset, IApplicableToDrawableHitObject, IApplicableToScoreProcessor
{
public override string Name => "Bubbles";
@@ -50,8 +48,6 @@ namespace osu.Game.Rulesets.Osu.Mods
private readonly DrawablePool bubblePool = new DrawablePool(100);
- private DrawableOsuHitObject? lastJudgedHitObject;
-
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
@@ -60,15 +56,41 @@ namespace osu.Game.Rulesets.Osu.Mods
currentCombo.BindValueChanged(combo =>
maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
- scoreProcessor.NewJudgement += result =>
+ scoreProcessor.JudgementReverted += _ =>
{
- if (result.HitObject is not OsuHitObject osuHitObject || lastJudgedHitObject.IsNull()) return;
+ bubbleContainer.LastOrDefault()?.ClearTransforms();
+ bubbleContainer.LastOrDefault()?.Expire();
+ };
+ }
- Debug.Assert(result.HitObject == lastJudgedHitObject.HitObject);
+ public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
+ {
+ // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
+ // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
+ bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f);
+ bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2;
- DrawableOsuHitObject drawableOsuHitObject = lastJudgedHitObject;
+ // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
+ drawableRuleset.Playfield.DisplayJudgements.Value = false;
- switch (result.HitObject)
+ bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
+
+ drawableRuleset.Overlays.Add(bubbleContainer);
+ }
+
+ public void ApplyToDrawableHitObject(DrawableHitObject drawableObject)
+ {
+ if (drawableObject is DrawableSlider slider)
+ {
+ applySliderState(slider);
+ slider.Body.OnSkinChanged += () => applySliderState(slider);
+ }
+
+ drawableObject.OnNewResult += (drawable, _) =>
+ {
+ if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return;
+
+ switch (drawableOsuHitObject.HitObject)
{
case Slider:
case SpinnerTick:
@@ -101,56 +123,17 @@ namespace osu.Game.Rulesets.Osu.Mods
// SliderHeads are derived from HitCircles,
// so we must handle them before to avoid them using the wrong positioning logic
case DrawableSliderHead:
- return osuHitObject.Position;
+ return drawableOsuHitObject.HitObject.Position;
// Using hitobject position will cause issues with HitCircle placement due to stack leniency.
case DrawableHitCircle:
return drawableOsuHitObject.Position;
default:
- return osuHitObject.Position;
+ return drawableOsuHitObject.HitObject.Position;
}
}
};
-
- scoreProcessor.JudgementReverted += _ =>
- {
- bubbleContainer.LastOrDefault()?.FinishTransforms();
- bubbleContainer.LastOrDefault()?.Expire();
- };
- }
-
- public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
- {
- // Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
- // Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
- bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f);
- bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2;
-
- // We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
- drawableRuleset.Playfield.DisplayJudgements.Value = false;
-
- bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
-
- drawableRuleset.Overlays.Add(bubbleContainer);
- }
-
- protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
- protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyBubbleState(hitObject);
-
- private void applyBubbleState(DrawableHitObject drawableObject)
- {
- DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)drawableObject;
-
- if (drawableObject is DrawableSlider slider)
- {
- slider.Body.OnSkinChanged += () => applySliderState(slider);
- applySliderState(slider);
- }
-
- if (osuHitObject == lastJudgedHitObject || !osuHitObject.Judged) return;
-
- lastJudgedHitObject = osuHitObject;
}
// Makes the slider border coloured on all skins (for aesthetics)
From 5db624159b1230e7ca522e9ab6cdec8194e8a0fa Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Tue, 14 Feb 2023 18:06:43 +0100
Subject: [PATCH 0063/1604] Change bubble rewind removal to be in
`ApplyToDrawableHitObject` method.
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 4a8c11e7ff..f521dfb1f8 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -55,12 +55,6 @@ namespace osu.Game.Rulesets.Osu.Mods
currentCombo.BindTo(scoreProcessor.Combo);
currentCombo.BindValueChanged(combo =>
maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
-
- scoreProcessor.JudgementReverted += _ =>
- {
- bubbleContainer.LastOrDefault()?.ClearTransforms();
- bubbleContainer.LastOrDefault()?.Expire();
- };
}
public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
@@ -134,6 +128,16 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
};
+
+ drawableObject.OnRevertResult += (drawable, _) =>
+ {
+ if (drawable.HitObject is SpinnerTick or Slider) return;
+
+ BubbleDrawable? lastBubble = bubbleContainer.OfType().LastOrDefault();
+
+ lastBubble?.ClearTransforms();
+ lastBubble?.Expire();
+ };
}
// Makes the slider border coloured on all skins (for aesthetics)
From 82292d61621e49a158c576879ae56f47a9e52a84 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 15 Feb 2023 09:30:12 +0100
Subject: [PATCH 0064/1604] Make colouring for bubble more intuitive and remove
unnecessary alpha assignment
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index f521dfb1f8..2a4208065d 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -177,8 +177,7 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override void PrepareForUse()
{
- Alpha = 1;
- Colour = Colour4.White;
+ Colour = Info.IsHit ? Colour4.White : Colour4.Black;
Scale = new Vector2(1);
Position = Info.Position;
Size = Info.InitialSize;
@@ -204,12 +203,8 @@ namespace osu.Game.Rulesets.Osu.Mods
content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint)
// Avoids transparency overlap issues during the bubble "pop"
.Then().Schedule(() => content.BorderThickness = 0);
-
- return;
}
- Colour = Colour4.Black;
-
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f);
}
From e9a7d90273c57dbd7dfb30ff32b9ca21dc9b6d39 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 15 Feb 2023 09:33:18 +0100
Subject: [PATCH 0065/1604] make transform duration for bubble a method instead
of a variable
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 2a4208065d..a88bf6b813 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -187,26 +187,26 @@ namespace osu.Game.Rulesets.Osu.Mods
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
ColourInfo colourDarker = Info.Colour.Darken(0.1f);
+ // The absolute length of the bubble's animation, can be used in fractions for animations of partial length
+ double getAnimationDuration = 1700 + Math.Pow(Info.FadeTime, 1.07f);
+
// Main bubble scaling based on combo
- this.ScaleTo(Info.MaxSize, getAnimationDuration() * 0.8f)
+ this.ScaleTo(Info.MaxSize, getAnimationDuration * 0.8f)
.Then()
// Pop at the end of the bubbles life time
- .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration() * 0.2f, Easing.OutQuint)
- .FadeOutFromOne(getAnimationDuration() * 0.2f, Easing.OutCirc).Expire();
+ .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint)
+ .FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire();
if (Info.IsHit)
{
colourBox.FadeColour(colourDarker);
- content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration() * 0.3f, Easing.OutQuint);
+ content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint);
// Ripple effect utilises the border to reduce drawable count
- content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration() * 0.3f, Easing.OutQuint)
+ content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint)
// Avoids transparency overlap issues during the bubble "pop"
.Then().Schedule(() => content.BorderThickness = 0);
}
-
- // The absolute length of the bubble's animation, can be used in fractions for animations of partial length
- double getAnimationDuration() => 1700 + Math.Pow(Info.FadeTime, 1.07f);
}
}
From 1d1c794ccfde23743574d8579da648cf42f4e4d4 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 15 Feb 2023 09:37:47 +0100
Subject: [PATCH 0066/1604] Invert pointless nested `if` statement
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index a88bf6b813..d75c82dc85 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -197,16 +197,15 @@ namespace osu.Game.Rulesets.Osu.Mods
.ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint)
.FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire();
- if (Info.IsHit)
- {
- colourBox.FadeColour(colourDarker);
+ if (!Info.IsHit) return;
- content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint);
- // Ripple effect utilises the border to reduce drawable count
- content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint)
- // Avoids transparency overlap issues during the bubble "pop"
- .Then().Schedule(() => content.BorderThickness = 0);
- }
+ colourBox.FadeColour(colourDarker);
+
+ content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint);
+ // Ripple effect utilises the border to reduce drawable count
+ content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint)
+ // Avoids transparency overlap issues during the bubble "pop"
+ .Then().Schedule(() => content.BorderThickness = 0);
}
}
From 297963b461cfdfd6083784ab0c4561c43c1d4a93 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 15 Feb 2023 10:00:46 +0100
Subject: [PATCH 0067/1604] Remove BubbleInfo struct and consume
`DrawableOsuHitObject`s directly
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 102 +++++++++-----------
1 file changed, 46 insertions(+), 56 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index d75c82dc85..981932c580 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -2,8 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@@ -98,35 +101,14 @@ namespace osu.Game.Rulesets.Osu.Mods
void addBubble()
{
BubbleDrawable bubble = bubblePool.Get();
- bubble.Info = new BubbleInfo
- {
- InitialSize = new Vector2(bubbleRadius),
- MaxSize = maxSize,
- Position = getPosition(),
- FadeTime = bubbleFade,
- Colour = drawableOsuHitObject.AccentColour.Value,
- IsHit = drawableOsuHitObject.IsHit,
- };
+
+ bubble.DrawableOsuHitObject = drawableOsuHitObject;
+ bubble.InitialSize = new Vector2(bubbleRadius);
+ bubble.FadeTime = bubbleFade;
+ bubble.MaxSize = maxSize;
+
bubbleContainer.Add(bubble);
}
-
- Vector2 getPosition()
- {
- switch (drawableOsuHitObject)
- {
- // SliderHeads are derived from HitCircles,
- // so we must handle them before to avoid them using the wrong positioning logic
- case DrawableSliderHead:
- return drawableOsuHitObject.HitObject.Position;
-
- // Using hitobject position will cause issues with HitCircle placement due to stack leniency.
- case DrawableHitCircle:
- return drawableOsuHitObject.Position;
-
- default:
- return drawableOsuHitObject.HitObject.Position;
- }
- }
};
drawableObject.OnRevertResult += (drawable, _) =>
@@ -148,11 +130,15 @@ namespace osu.Game.Rulesets.Osu.Mods
private partial class BubbleDrawable : PoolableDrawable
{
+ public DrawableOsuHitObject? DrawableOsuHitObject { get; set; }
+
+ public Vector2 InitialSize { get; set; }
+ public double FadeTime { get; set; }
+ public float MaxSize { get; set; }
+
private readonly Box colourBox;
private readonly CircularContainer content;
- public BubbleInfo Info { get; set; }
-
public BubbleDrawable()
{
Origin = Anchor.Centre;
@@ -177,27 +163,30 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override void PrepareForUse()
{
- Colour = Info.IsHit ? Colour4.White : Colour4.Black;
+ Debug.Assert(DrawableOsuHitObject.IsNotNull());
+
+ Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black;
+ Alpha = 1;
Scale = new Vector2(1);
- Position = Info.Position;
- Size = Info.InitialSize;
- content.BorderThickness = Info.InitialSize.X / 3.5f;
+ Position = getPosition();
+ Size = InitialSize;
+ content.BorderThickness = InitialSize.X / 3.5f;
content.BorderColour = Colour4.White;
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
- ColourInfo colourDarker = Info.Colour.Darken(0.1f);
+ ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f);
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
- double getAnimationDuration = 1700 + Math.Pow(Info.FadeTime, 1.07f);
+ double getAnimationDuration = 1700 + Math.Pow(FadeTime, 1.07f);
// Main bubble scaling based on combo
- this.ScaleTo(Info.MaxSize, getAnimationDuration * 0.8f)
+ this.ScaleTo(MaxSize, getAnimationDuration * 0.8f)
.Then()
// Pop at the end of the bubbles life time
- .ScaleTo(Info.MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint)
- .FadeOutFromOne(getAnimationDuration * 0.2f, Easing.OutCirc).Expire();
+ .ScaleTo(MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint)
+ .FadeOut(getAnimationDuration * 0.2f, Easing.OutCirc).Expire();
- if (!Info.IsHit) return;
+ if (!DrawableOsuHitObject.IsHit) return;
colourBox.FadeColour(colourDarker);
@@ -206,26 +195,27 @@ namespace osu.Game.Rulesets.Osu.Mods
content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint)
// Avoids transparency overlap issues during the bubble "pop"
.Then().Schedule(() => content.BorderThickness = 0);
+
+ Vector2 getPosition()
+ {
+ switch (DrawableOsuHitObject)
+ {
+ // SliderHeads are derived from HitCircles,
+ // so we must handle them before to avoid them using the wrong positioning logic
+ case DrawableSliderHead:
+ return DrawableOsuHitObject.HitObject.Position;
+
+ // Using hitobject position will cause issues with HitCircle placement due to stack leniency.
+ case DrawableHitCircle:
+ return DrawableOsuHitObject.Position;
+
+ default:
+ return DrawableOsuHitObject.HitObject.Position;
+ }
+ }
}
}
- private struct BubbleInfo
- {
- public Vector2 InitialSize { get; set; }
-
- public float MaxSize { get; set; }
-
- public Vector2 Position { get; set; }
-
- public Colour4 Colour { get; set; }
-
- // FadeTime is based on the approach rate of the beatmap.
- public double FadeTime { get; set; }
-
- // Whether the corresponding HitObject was hit
- public bool IsHit { get; set; }
- }
-
#endregion
}
}
From 8fc35b159f87a10a087c79c469d981ff9750bc95 Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Wed, 15 Feb 2023 10:04:50 +0100
Subject: [PATCH 0068/1604] Remove dysfunctional slider colouring
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 981932c580..4edf726f26 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -18,7 +18,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
@@ -77,12 +76,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableHitObject(DrawableHitObject drawableObject)
{
- if (drawableObject is DrawableSlider slider)
- {
- applySliderState(slider);
- slider.Body.OnSkinChanged += () => applySliderState(slider);
- }
-
drawableObject.OnNewResult += (drawable, _) =>
{
if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return;
@@ -122,10 +115,6 @@ namespace osu.Game.Rulesets.Osu.Mods
};
}
- // Makes the slider border coloured on all skins (for aesthetics)
- private void applySliderState(DrawableSlider slider) =>
- ((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
-
#region Pooled Bubble drawable
private partial class BubbleDrawable : PoolableDrawable
From 74e7cc205601bc4edb9d88e12afb8c63f8e967d2 Mon Sep 17 00:00:00 2001
From: tsrk
Date: Wed, 15 Feb 2023 22:18:02 +0000
Subject: [PATCH 0069/1604] feat: implement new design of key counter
---
.../Visual/Gameplay/TestSceneKeyCounter.cs | 39 +++++++---
osu.Game/Screens/Play/ArgonKeyCounter.cs | 76 +++++++++++++++++++
.../Screens/Play/ArgonKeyCounterDisplay.cs | 42 ++++++++++
3 files changed, 147 insertions(+), 10 deletions(-)
create mode 100644 osu.Game/Screens/Play/ArgonKeyCounter.cs
create mode 100644 osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
index 975a5c9465..41add82245 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyCounter.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Screens.Play;
+using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
@@ -18,24 +19,44 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestSceneKeyCounter()
{
DefaultKeyCounter testCounter;
+ KeyCounterDisplay kc;
+ KeyCounterDisplay argonKc;
- KeyCounterDisplay kc = new DefaultKeyCounterDisplay
+ Children = new Drawable[]
{
- Origin = Anchor.Centre,
- Anchor = Anchor.Centre,
- Children = new[]
+ kc = new DefaultKeyCounterDisplay
{
- testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
- new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
- new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)),
- new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)),
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Position = new Vector2(0, -50),
+ Children = new[]
+ {
+ testCounter = new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
+ new DefaultKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
+ new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)),
+ new DefaultKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)),
+ },
},
+ argonKc = new ArgonKeyCounterDisplay
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Position = new Vector2(0, 50),
+ Children = new[]
+ {
+ new ArgonKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
+ new ArgonKeyCounter(new KeyCounterKeyboardTrigger(Key.X)),
+ new ArgonKeyCounter(new KeyCounterMouseTrigger(MouseButton.Left)),
+ new ArgonKeyCounter(new KeyCounterMouseTrigger(MouseButton.Right)),
+ },
+ }
};
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
kc.Add(kc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key)));
+ argonKc.Add(argonKc.CreateKeyCounter(new KeyCounterKeyboardTrigger(key)));
});
Key testKey = ((KeyCounterKeyboardTrigger)kc.Children.First().Trigger).Key;
@@ -52,8 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("Disable counting", () => testCounter.IsCounting = false);
addPressKeyStep();
AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2);
-
- Add(kc);
}
}
}
diff --git a/osu.Game/Screens/Play/ArgonKeyCounter.cs b/osu.Game/Screens/Play/ArgonKeyCounter.cs
new file mode 100644
index 0000000000..a275a7e017
--- /dev/null
+++ b/osu.Game/Screens/Play/ArgonKeyCounter.cs
@@ -0,0 +1,76 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK;
+
+namespace osu.Game.Screens.Play
+{
+ public partial class ArgonKeyCounter : KeyCounter
+ {
+ private Circle inputIndicator = null!;
+ private OsuSpriteText countText = null!;
+
+ // These values were taken from Figma
+ private const float line_height = 3;
+ private const float name_font_size = 10;
+ private const float count_font_size = 14;
+
+ // Make things look bigger without using Scale
+ private const float scale_factor = 1.5f;
+
+ public ArgonKeyCounter(InputTrigger trigger)
+ : base(trigger)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Children = new Drawable[]
+ {
+ inputIndicator = new Circle
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ Height = line_height * scale_factor,
+ Alpha = 0.5f
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Position = new Vector2(0, -13) * scale_factor,
+ Font = OsuFont.Torus.With(size: name_font_size * scale_factor, weight: FontWeight.Bold),
+ Colour = colours.Blue0,
+ Text = Name
+ },
+ countText = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.Torus.With(size: count_font_size * scale_factor, weight: FontWeight.Bold),
+ Text = "0"
+ },
+ };
+
+ // Values from Figma didn't match visually
+ // So these were just eyeballed
+ Height = 30 * scale_factor;
+ Width = 35 * scale_factor;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ IsLit.BindValueChanged(e => inputIndicator.Alpha = e.NewValue ? 1 : 0.5f, true);
+ PressesCount.BindValueChanged(e => countText.Text = e.NewValue.ToString(@"#,0"), true);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs
new file mode 100644
index 0000000000..da34a64a4c
--- /dev/null
+++ b/osu.Game/Screens/Play/ArgonKeyCounterDisplay.cs
@@ -0,0 +1,42 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osuTK;
+
+namespace osu.Game.Screens.Play
+{
+ public partial class ArgonKeyCounterDisplay : KeyCounterDisplay
+ {
+ private const int duration = 100;
+
+ public new IReadOnlyList Children
+ {
+ get => (IReadOnlyList)base.Children;
+ set => base.Children = value;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ KeyFlow.Direction = FillDirection.Horizontal;
+ KeyFlow.AutoSizeAxes = Axes.Both;
+ KeyFlow.Spacing = new Vector2(2);
+
+ InternalChildren = new[]
+ {
+ KeyFlow
+ };
+ }
+
+ protected override bool CheckType(KeyCounter key) => key is ArgonKeyCounter;
+
+ protected override void UpdateVisibility()
+ => KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration);
+
+ public override KeyCounter CreateKeyCounter(KeyCounter.InputTrigger trigger) => new ArgonKeyCounter(trigger);
+ }
+}
From d5bc8e2941fc0a6d948fda24546cc976b12165fa Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Thu, 16 Feb 2023 11:12:30 +0100
Subject: [PATCH 0070/1604] Code cleanup pass:
Make bubble transform logic more sane.
Extract bubble `getPosition()` method.
Address poorly named variables.
---
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs | 47 +++++++++++----------
1 file changed, 25 insertions(+), 22 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
index 4edf726f26..0fc27c8f1d 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Mods
private readonly Bindable currentCombo = new BindableInt();
private float maxSize;
- private float bubbleRadius;
+ private float bubbleSize;
private double bubbleFade;
private readonly DrawablePool bubblePool = new DrawablePool(100);
@@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
// Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
// Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
- bubbleRadius = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f);
+ bubbleSize = (float)(drawableRuleset.Beatmap.HitObjects.OfType().First().Radius * 1.90f);
bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType().First().TimePreempt * 2;
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
@@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
BubbleDrawable bubble = bubblePool.Get();
bubble.DrawableOsuHitObject = drawableOsuHitObject;
- bubble.InitialSize = new Vector2(bubbleRadius);
+ bubble.InitialSize = new Vector2(bubbleSize);
bubble.FadeTime = bubbleFade;
bubble.MaxSize = maxSize;
@@ -122,9 +122,11 @@ namespace osu.Game.Rulesets.Osu.Mods
public DrawableOsuHitObject? DrawableOsuHitObject { get; set; }
public Vector2 InitialSize { get; set; }
- public double FadeTime { get; set; }
+
public float MaxSize { get; set; }
+ public double FadeTime { get; set; }
+
private readonly Box colourBox;
private readonly CircularContainer content;
@@ -155,12 +157,9 @@ namespace osu.Game.Rulesets.Osu.Mods
Debug.Assert(DrawableOsuHitObject.IsNotNull());
Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black;
- Alpha = 1;
Scale = new Vector2(1);
- Position = getPosition();
+ Position = getPosition(DrawableOsuHitObject);
Size = InitialSize;
- content.BorderThickness = InitialSize.X / 3.5f;
- content.BorderColour = Colour4.White;
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f);
@@ -169,7 +168,8 @@ namespace osu.Game.Rulesets.Osu.Mods
double getAnimationDuration = 1700 + Math.Pow(FadeTime, 1.07f);
// Main bubble scaling based on combo
- this.ScaleTo(MaxSize, getAnimationDuration * 0.8f)
+ this.FadeTo(1)
+ .ScaleTo(MaxSize, getAnimationDuration * 0.8f)
.Then()
// Pop at the end of the bubbles life time
.ScaleTo(MaxSize * 1.5f, getAnimationDuration * 0.2f, Easing.OutQuint)
@@ -177,6 +177,9 @@ namespace osu.Game.Rulesets.Osu.Mods
if (!DrawableOsuHitObject.IsHit) return;
+ content.BorderThickness = InitialSize.X / 3.5f;
+ content.BorderColour = Colour4.White;
+
colourBox.FadeColour(colourDarker);
content.TransformTo(nameof(BorderColour), colourDarker, getAnimationDuration * 0.3f, Easing.OutQuint);
@@ -184,23 +187,23 @@ namespace osu.Game.Rulesets.Osu.Mods
content.TransformTo(nameof(BorderThickness), 2f, getAnimationDuration * 0.3f, Easing.OutQuint)
// Avoids transparency overlap issues during the bubble "pop"
.Then().Schedule(() => content.BorderThickness = 0);
+ }
- Vector2 getPosition()
+ private Vector2 getPosition(DrawableOsuHitObject drawableOsuHitObject)
+ {
+ switch (drawableOsuHitObject)
{
- switch (DrawableOsuHitObject)
- {
- // SliderHeads are derived from HitCircles,
- // so we must handle them before to avoid them using the wrong positioning logic
- case DrawableSliderHead:
- return DrawableOsuHitObject.HitObject.Position;
+ // SliderHeads are derived from HitCircles,
+ // so we must handle them before to avoid them using the wrong positioning logic
+ case DrawableSliderHead:
+ return drawableOsuHitObject.HitObject.Position;
- // Using hitobject position will cause issues with HitCircle placement due to stack leniency.
- case DrawableHitCircle:
- return DrawableOsuHitObject.Position;
+ // Using hitobject position will cause issues with HitCircle placement due to stack leniency.
+ case DrawableHitCircle:
+ return drawableOsuHitObject.Position;
- default:
- return DrawableOsuHitObject.HitObject.Position;
- }
+ default:
+ return drawableOsuHitObject.HitObject.Position;
}
}
}
From eac0aa79a3bcaf23982b590144ec5471cc4f952c Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Fri, 17 Feb 2023 21:52:10 +0900
Subject: [PATCH 0071/1604] cancellationToken pass, notification adujust
---
osu.Game/Database/LegacyModelExporter.cs | 73 ++++++++++++++----------
osu.Game/Database/LegacyScoreExporter.cs | 3 +-
2 files changed, 45 insertions(+), 31 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index d896e4bce6..1475f29f89 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -32,33 +33,52 @@ namespace osu.Game.Database
protected Storage UserFileStorage;
private readonly Storage exportStorage;
- protected RealmAccess RealmAccess;
+ private readonly RealmAccess realmAccess;
private string filename = string.Empty;
public Action? PostNotification { get; set; }
+ ///
+ /// Construct exporter.
+ /// Create a new exporter for each export, otherwise it will cause confusing notifications.
+ ///
+ /// Storage for storing exported files. Basically it is used to provide export stream
+ /// The RealmAccess used to provide the exported file.
protected LegacyModelExporter(Storage storage, RealmAccess realm)
{
exportStorage = storage.GetStorageForDirectory(@"exports");
UserFileStorage = storage.GetStorageForDirectory(@"files");
- RealmAccess = realm;
+ realmAccess = realm;
}
///
/// Export the model to default folder.
///
/// The model should export.
+ ///
+ /// The Cancellation token that can cancel the exporting.
+ /// If specified CancellationToken, then use it. Otherwise use PostNotification's CancellationToken.
+ ///
///
- public async Task ExportAsync(TModel model)
+ public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
string itemFilename = model.GetDisplayString().GetValidFilename();
IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
bool success;
+ ProgressNotification notification = new ProgressNotification
+ {
+ State = ProgressNotificationState.Active,
+ Text = "Exporting...",
+ CompletionText = "Export completed"
+ };
+ notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
+ PostNotification?.Invoke(notification);
+
using (var stream = exportStorage.CreateFileSafely(filename))
{
- success = await ExportToStreamAsync(model, stream);
+ success = await ExportToStreamAsync(model, stream, notification, cancellationToken ?? notification.CancellationToken);
}
if (!success)
@@ -72,44 +92,34 @@ namespace osu.Game.Database
///
/// The medel which have .
/// The stream to export.
+ /// The notification will displayed to the user
+ /// The Cancellation token that can cancel the exporting.
/// Whether the export was successful
- public async Task ExportToStreamAsync(TModel model, Stream stream)
+ public async Task ExportToStreamAsync(TModel model, Stream stream, ProgressNotification? notification = null, CancellationToken cancellationToken = default)
{
- ProgressNotification notification = new ProgressNotification
- {
- State = ProgressNotificationState.Active,
- Text = "Exporting...",
- CompletionText = "Export completed"
- };
- notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
- PostNotification?.Invoke(notification);
+ ProgressNotification notify = notification ?? new ProgressNotification();
Guid id = model.ID;
return await Task.Run(() =>
{
- RealmAccess.Run(r =>
+ realmAccess.Run(r =>
{
TModel refetchModel = r.Find(id);
- ExportToStream(refetchModel, stream, notification);
+ ExportToStream(refetchModel, stream, notify, cancellationToken);
});
- }, notification.CancellationToken).ContinueWith(t =>
+ }, cancellationToken).ContinueWith(t =>
{
- if (t.IsCanceled)
- {
- return false;
- }
-
if (t.IsFaulted)
{
- notification.State = ProgressNotificationState.Cancelled;
+ notify.State = ProgressNotificationState.Cancelled;
Logger.Error(t.Exception, "An error occurred while exporting");
return false;
}
- notification.CompletionText = "Export Complete, Click to open the folder";
- notification.State = ProgressNotificationState.Completed;
+ notify.CompletionText = "Export Complete, Click to open the folder";
+ notify.State = ProgressNotificationState.Completed;
return true;
- });
+ }, cancellationToken);
}
///
@@ -119,7 +129,8 @@ namespace osu.Game.Database
/// The item to export.
/// The output stream to export to.
/// The notification will displayed to the user
- protected abstract void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification);
+ /// The Cancellation token that can cancel the exporting.
+ protected abstract void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default);
}
public abstract class LegacyArchiveExporter : LegacyModelExporter
@@ -130,15 +141,17 @@ namespace osu.Game.Database
{
}
- protected override void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification) => exportZipArchive(model, outputStream, notification);
+ protected override void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
+ => exportZipArchive(model, outputStream, notification, cancellationToken);
///
/// Exports an item to Stream as a legacy (.zip based) package.
///
- /// The item to export.
+ /// The model will be exported.
/// The output stream to export to.
/// The notification will displayed to the user
- private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification)
+ /// The Cancellation token that can cancel the exporting.
+ private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
{
using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
@@ -147,7 +160,7 @@ namespace osu.Game.Database
foreach (var file in model.Files)
{
- notification.CancellationToken.ThrowIfCancellationRequested();
+ cancellationToken.ThrowIfCancellationRequested();
using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
{
diff --git a/osu.Game/Database/LegacyScoreExporter.cs b/osu.Game/Database/LegacyScoreExporter.cs
index 1a30d823e1..b3a9276d5e 100644
--- a/osu.Game/Database/LegacyScoreExporter.cs
+++ b/osu.Game/Database/LegacyScoreExporter.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
+using System.Threading;
using osu.Framework.Platform;
using osu.Game.Extensions;
using osu.Game.Overlays.Notifications;
@@ -19,7 +20,7 @@ namespace osu.Game.Database
protected override string FileExtension => ".osr";
- protected override void ExportToStream(ScoreInfo model, Stream stream, ProgressNotification notification)
+ protected override void ExportToStream(ScoreInfo model, Stream stream, ProgressNotification notification, CancellationToken cancellationToken = default)
{
var file = model.Files.SingleOrDefault();
if (file == null)
From 29d6483e172597216df72e884ff8f9096985820d Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Fri, 17 Feb 2023 22:19:24 +0900
Subject: [PATCH 0072/1604] ConfigureAwait for awaited task
---
osu.Game/Database/LegacyModelExporter.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 1475f29f89..ff0e3de20a 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -78,7 +78,7 @@ namespace osu.Game.Database
using (var stream = exportStorage.CreateFileSafely(filename))
{
- success = await ExportToStreamAsync(model, stream, notification, cancellationToken ?? notification.CancellationToken);
+ success = await ExportToStreamAsync(model, stream, notification, cancellationToken ?? notification.CancellationToken).ConfigureAwait(false);
}
if (!success)
@@ -119,7 +119,7 @@ namespace osu.Game.Database
notify.CompletionText = "Export Complete, Click to open the folder";
notify.State = ProgressNotificationState.Completed;
return true;
- }, cancellationToken);
+ }, cancellationToken).ConfigureAwait(false);
}
///
From 843d841f5a4687caccde1dc0a5d3570ee653875f Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Fri, 17 Feb 2023 22:23:50 +0900
Subject: [PATCH 0073/1604] GetFilename and something other
https://github.com/ppy/osu/pull/21739
---
osu.Game/Database/LegacyModelExporter.cs | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index ff0e3de20a..644b486c2d 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -32,10 +32,10 @@ namespace osu.Game.Database
protected Storage UserFileStorage;
private readonly Storage exportStorage;
+ protected virtual string GetFilename(TModel item) => item.GetDisplayString();
private readonly RealmAccess realmAccess;
- private string filename = string.Empty;
public Action? PostNotification { get; set; }
///
@@ -62,9 +62,12 @@ namespace osu.Game.Database
///
public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
- string itemFilename = model.GetDisplayString().GetValidFilename();
- IEnumerable existingExports = exportStorage.GetFiles("", $"{itemFilename}*{FileExtension}");
- filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
+ string itemFilename = GetFilename(model).GetValidFilename();
+ IEnumerable existingExports =
+ exportStorage
+ .GetFiles(string.Empty, $"{itemFilename}*{FileExtension}")
+ .Concat(exportStorage.GetDirectories(string.Empty));
+ string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
bool success;
ProgressNotification notification = new ProgressNotification
From 309e9b24e28135dc9ef9e341292ddb7137efaa33 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 01:18:27 +0900
Subject: [PATCH 0074/1604] split LegacyArchiveExporter
---
osu.Game/Database/LegacyArchiveExporter.cs | 73 ++++++++++++++++++++++
osu.Game/Database/LegacyModelExporter.cs | 60 ------------------
2 files changed, 73 insertions(+), 60 deletions(-)
create mode 100644 osu.Game/Database/LegacyArchiveExporter.cs
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
new file mode 100644
index 0000000000..f87f373624
--- /dev/null
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -0,0 +1,73 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using System.Linq;
+using System.Threading;
+using osu.Framework.Platform;
+using osu.Game.Extensions;
+using osu.Game.Overlays.Notifications;
+using Realms;
+using SharpCompress.Common;
+using SharpCompress.Writers;
+using SharpCompress.Writers.Zip;
+
+namespace osu.Game.Database
+{
+ public abstract class LegacyArchiveExporter : LegacyModelExporter
+ where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
+ {
+ protected LegacyArchiveExporter(Storage storage, RealmAccess realm)
+ : base(storage, realm)
+ {
+ }
+
+ protected override void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
+ => exportZipArchive(model, outputStream, notification, cancellationToken);
+
+ ///
+ /// Exports an item to Stream as a legacy (.zip based) package.
+ ///
+ /// The model will be exported.
+ /// The output stream to export to.
+ /// The notification will displayed to the user
+ /// The Cancellation token that can cancel the exporting.
+ private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
+ {
+ using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
+
+ float i = 0;
+ bool fileMissing = false;
+
+ foreach (var file in model.Files)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ {
+ // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
+ if (stream == null)
+ {
+ // Only pop up once to prevent spam.
+ if (!fileMissing)
+ {
+ PostNotification?.Invoke(new SimpleErrorNotification
+ {
+ Text = "Some of your files are missing, they will not be included in the archive"
+ });
+ fileMissing = true;
+ }
+ }
+ else
+ {
+ writer.Write(file.Filename, stream);
+ }
+ }
+
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ }
+ }
+ }
+}
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 644b486c2d..69277879dc 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -13,9 +13,6 @@ using osu.Game.Extensions;
using osu.Game.Overlays.Notifications;
using osu.Game.Utils;
using Realms;
-using SharpCompress.Common;
-using SharpCompress.Writers;
-using SharpCompress.Writers.Zip;
namespace osu.Game.Database
{
@@ -135,61 +132,4 @@ namespace osu.Game.Database
/// The Cancellation token that can cancel the exporting.
protected abstract void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default);
}
-
- public abstract class LegacyArchiveExporter : LegacyModelExporter
- where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey
- {
- protected LegacyArchiveExporter(Storage storage, RealmAccess realm)
- : base(storage, realm)
- {
- }
-
- protected override void ExportToStream(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
- => exportZipArchive(model, outputStream, notification, cancellationToken);
-
- ///
- /// Exports an item to Stream as a legacy (.zip based) package.
- ///
- /// The model will be exported.
- /// The output stream to export to.
- /// The notification will displayed to the user
- /// The Cancellation token that can cancel the exporting.
- private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
- {
- using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
-
- float i = 0;
- bool fileMissing = false;
-
- foreach (var file in model.Files)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
- {
- // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
- if (stream == null)
- {
- // Only pop up once to prevent spam.
- if (!fileMissing)
- {
- PostNotification?.Invoke(new SimpleErrorNotification
- {
- Text = "Some of your files are missing, they will not be included in the archive"
- });
- fileMissing = true;
- }
- }
- else
- {
- writer.Write(file.Filename, stream);
- }
- }
-
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
- }
- }
- }
}
From d61160374255aff4b4564207afd2c6aeca93405f Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 01:45:09 +0900
Subject: [PATCH 0075/1604] catch OperationCanceledException
---
osu.Game/Database/LegacyModelExporter.cs | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 69277879dc..e17805f417 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -76,9 +76,16 @@ namespace osu.Game.Database
notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
PostNotification?.Invoke(notification);
- using (var stream = exportStorage.CreateFileSafely(filename))
+ try
{
- success = await ExportToStreamAsync(model, stream, notification, cancellationToken ?? notification.CancellationToken).ConfigureAwait(false);
+ using (var stream = exportStorage.CreateFileSafely(filename))
+ {
+ success = await ExportToStreamAsync(model, stream, notification, cancellationToken ?? notification.CancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ success = false;
}
if (!success)
From 30985f192ee462a7e08a5a12792906b16f2229b0 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:06:07 +0900
Subject: [PATCH 0076/1604] catch ObjectDisposedException
---
osu.Game/Database/LegacyArchiveExporter.cs | 52 +++++++++++++---------
1 file changed, 32 insertions(+), 20 deletions(-)
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
index f87f373624..2f75e36fee 100644
--- a/osu.Game/Database/LegacyArchiveExporter.cs
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.IO;
using System.Linq;
using System.Threading;
@@ -39,34 +40,45 @@ namespace osu.Game.Database
float i = 0;
bool fileMissing = false;
- foreach (var file in model.Files)
+ try
{
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
+ foreach (var file in model.Files)
{
- // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
- if (stream == null)
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
{
- // Only pop up once to prevent spam.
- if (!fileMissing)
+ // Sometimes we cannot find the file(probably deleted by the user), so we handle this and post a error.
+ if (stream == null)
{
- PostNotification?.Invoke(new SimpleErrorNotification
+ // Only pop up once to prevent spam.
+ if (!fileMissing)
{
- Text = "Some of your files are missing, they will not be included in the archive"
- });
- fileMissing = true;
+ PostNotification?.Invoke(new SimpleErrorNotification
+ {
+ Text = "Some of your files are missing, they will not be included in the archive"
+ });
+ fileMissing = true;
+ }
+ }
+ else
+ {
+ writer.Write(file.Filename, stream);
}
}
- else
- {
- writer.Write(file.Filename, stream);
- }
- }
- i++;
- notification.Progress = i / model.Files.Count();
- notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ i++;
+ notification.Progress = i / model.Files.Count();
+ notification.Text = $"Exporting... ({i}/{model.Files.Count()})";
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // outputStream may close before writing when request cancel
+ if (cancellationToken.IsCancellationRequested)
+ return;
+
+ throw;
}
}
}
From 2a6ea99e6a0da51b7e610125e4bbb2d6175675b3 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:09:59 +0900
Subject: [PATCH 0077/1604] store exportingModels for all exporter
No one wants to export multiple copies of the same model at the same time, right?
---
osu.Game/Database/LegacyModelExporter.cs | 28 ++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index e17805f417..f9605140e3 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -35,6 +35,9 @@ namespace osu.Game.Database
public Action? PostNotification { get; set; }
+ // Store the model being exported.
+ private readonly List exportingModels = new List();
+
///
/// Construct exporter.
/// Create a new exporter for each export, otherwise it will cause confusing notifications.
@@ -59,13 +62,26 @@ namespace osu.Game.Database
///
public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
+ if (!exportingModels.Contains(model))
+ {
+ exportingModels.Add(model);
+ }
+ else
+ {
+ PostNotification?.Invoke(new SimpleErrorNotification()
+ {
+ Text = "File is being exported"
+ });
+ return;
+ }
+
string itemFilename = GetFilename(model).GetValidFilename();
IEnumerable existingExports =
exportStorage
.GetFiles(string.Empty, $"{itemFilename}*{FileExtension}")
.Concat(exportStorage.GetDirectories(string.Empty));
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
- bool success;
+ bool success = false;
ProgressNotification notification = new ProgressNotification
{
@@ -87,10 +103,14 @@ namespace osu.Game.Database
{
success = false;
}
-
- if (!success)
+ finally
{
- exportStorage.Delete(filename);
+ if (!success)
+ {
+ exportStorage.Delete(filename);
+ }
+
+ exportingModels.Remove(model);
}
}
From 8446e7d841993bcb8644d542daa6a3ae9b482905 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:17:24 +0900
Subject: [PATCH 0078/1604] comment
---
osu.Game/Database/LegacyArchiveExporter.cs | 2 +-
osu.Game/Database/LegacyModelExporter.cs | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
index 2f75e36fee..24a5d52f73 100644
--- a/osu.Game/Database/LegacyArchiveExporter.cs
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -74,7 +74,7 @@ namespace osu.Game.Database
}
catch (ObjectDisposedException)
{
- // outputStream may close before writing when request cancel
+ // outputStream may close before writing when request cancel.
if (cancellationToken.IsCancellationRequested)
return;
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index f9605140e3..fd63b5f28a 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Database
public Action? PostNotification { get; set; }
- // Store the model being exported.
+ // Store the model being exporting.
private readonly List exportingModels = new List();
///
@@ -62,6 +62,7 @@ namespace osu.Game.Database
///
public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
+ // check if the model is being exporting already
if (!exportingModels.Contains(model))
{
exportingModels.Add(model);
@@ -105,6 +106,7 @@ namespace osu.Game.Database
}
finally
{
+ // cleanup if export is failed or canceled.
if (!success)
{
exportStorage.Delete(filename);
From 79715fe37b3ac24ce30e1dd50e441c67adc27e5e Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:24:07 +0900
Subject: [PATCH 0079/1604] catch when zipWriter dispose
ObjectDisposedException also appear when zipwriter dispose after user request cancel
---
osu.Game/Database/LegacyArchiveExporter.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
index 24a5d52f73..fd2befd2e0 100644
--- a/osu.Game/Database/LegacyArchiveExporter.cs
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -35,13 +35,13 @@ namespace osu.Game.Database
/// The Cancellation token that can cancel the exporting.
private void exportZipArchive(TModel model, Stream outputStream, ProgressNotification notification, CancellationToken cancellationToken = default)
{
- using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
-
- float i = 0;
- bool fileMissing = false;
-
try
{
+ var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
+
+ float i = 0;
+ bool fileMissing = false;
+
foreach (var file in model.Files)
{
cancellationToken.ThrowIfCancellationRequested();
From fba99b344ce666f04057096f42a0edf70fb41dcd Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:41:08 +0900
Subject: [PATCH 0080/1604] Accidentally deleted using
wtf
---
osu.Game/Database/LegacyArchiveExporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
index fd2befd2e0..e5215e13a6 100644
--- a/osu.Game/Database/LegacyArchiveExporter.cs
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Database
{
try
{
- var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
+ using var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate));
float i = 0;
bool fileMissing = false;
From 229b31520f039a9dbacb99cabab04e132dfdf9fb Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:42:33 +0900
Subject: [PATCH 0081/1604] remove ()
---
osu.Game/Database/LegacyModelExporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index fd63b5f28a..acb83ce1d5 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Database
}
else
{
- PostNotification?.Invoke(new SimpleErrorNotification()
+ PostNotification?.Invoke(new SimpleErrorNotification
{
Text = "File is being exported"
});
From 0667b8396083dd52fc1f4d321d49f56068c45ff4 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 19 Feb 2023 02:56:53 +0900
Subject: [PATCH 0082/1604] Path.GetExtension() will not get null
---
osu.Game/Database/LegacyScoreImporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyScoreImporter.cs b/osu.Game/Database/LegacyScoreImporter.cs
index 131b4ffb0e..b80a35f90a 100644
--- a/osu.Game/Database/LegacyScoreImporter.cs
+++ b/osu.Game/Database/LegacyScoreImporter.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Database
return Enumerable.Empty();
return storage.GetFiles(ImportFromStablePath)
- .Where(p => Importer.HandledExtensions.Any(ext => Path.GetExtension(p)?.Equals(ext, StringComparison.OrdinalIgnoreCase) ?? false))
+ .Where(p => Importer.HandledExtensions.Any(ext => Path.GetExtension(p).Equals(ext, StringComparison.OrdinalIgnoreCase)))
.Select(path => storage.GetFullPath(path));
}
From 04dcd661e06988365775fec6044ddad12c39aa51 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Tue, 21 Feb 2023 20:53:02 +0900
Subject: [PATCH 0083/1604] async logic fix
---
osu.Game/Database/LegacyModelExporter.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index acb83ce1d5..cd405f90be 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -124,12 +124,12 @@ namespace osu.Game.Database
/// The notification will displayed to the user
/// The Cancellation token that can cancel the exporting.
/// Whether the export was successful
- public async Task ExportToStreamAsync(TModel model, Stream stream, ProgressNotification? notification = null, CancellationToken cancellationToken = default)
+ public Task ExportToStreamAsync(TModel model, Stream stream, ProgressNotification? notification = null, CancellationToken cancellationToken = default)
{
ProgressNotification notify = notification ?? new ProgressNotification();
Guid id = model.ID;
- return await Task.Run(() =>
+ return Task.Run(() =>
{
realmAccess.Run(r =>
{
@@ -148,7 +148,7 @@ namespace osu.Game.Database
notify.CompletionText = "Export Complete, Click to open the folder";
notify.State = ProgressNotificationState.Completed;
return true;
- }, cancellationToken).ConfigureAwait(false);
+ }, cancellationToken);
}
///
From d20e1df6030cba04cc2d75385608b59b560c5b93 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Tue, 21 Feb 2023 20:54:06 +0900
Subject: [PATCH 0084/1604] wrong xmldoc
because of https://github.com/ppy/osu/pull/21308/commits/6900d0120a70287ef531a4c0facd398508a76b06
---
osu.Game/Database/LegacyModelExporter.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index cd405f90be..96104a68a0 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -152,10 +152,9 @@ namespace osu.Game.Database
}
///
- /// Exports an item to Stream.
- /// Override if custom export method is required.
+ /// Exports model to Stream.
///
- /// The item to export.
+ /// The model to export.
/// The output stream to export to.
/// The notification will displayed to the user
/// The Cancellation token that can cancel the exporting.
From 191604340f8cbaf4d42c31cbc2ef2665fdfbb318 Mon Sep 17 00:00:00 2001
From: Terochi
Date: Tue, 21 Feb 2023 19:05:10 +0100
Subject: [PATCH 0085/1604] Added a way for mod settings to be kept when
changing ruleset + test
---
.../TestSceneModSelectOverlay.cs | 48 +++++++++++++++++++
osu.Game/OsuGameBase.cs | 17 ++++++-
osu.Game/Rulesets/Mods/Mod.cs | 31 ++++++++++--
3 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 5ccaebd721..25edcd4e0a 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -21,6 +21,7 @@ using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Tests.Mods;
using osuTK;
using osuTK.Input;
@@ -371,6 +372,53 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("no mod selected", () => SelectedMods.Value.Count == 0);
}
+ [Test]
+ public void TestKeepSharedSettingsFromSimilarMods()
+ {
+ const float setting_change = 1.2f;
+
+ createScreen();
+ changeRuleset(0);
+
+ AddStep("select difficulty adjust mod", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateMod()! });
+
+ changeRuleset(0);
+ AddAssert("ensure mod still selected", () => SelectedMods.Value.SingleOrDefault() is OsuModDifficultyAdjust);
+
+ AddStep("change mod settings", () =>
+ {
+ var osuMod = (getMod() as OsuModDifficultyAdjust)!;
+ osuMod.ExtendedLimits.Value = true;
+ osuMod.CircleSize.Value = setting_change;
+ osuMod.DrainRate.Value = setting_change;
+ osuMod.OverallDifficulty.Value = setting_change;
+ osuMod.ApproachRate.Value = setting_change;
+ });
+
+ changeRuleset(1);
+ AddAssert("taiko variant selected", () => SelectedMods.Value.SingleOrDefault() is TaikoModDifficultyAdjust);
+
+ AddAssert("shared settings didn't change", () =>
+ {
+ var taikoMod = getMod() as TaikoModDifficultyAdjust;
+ return
+ taikoMod?.ExtendedLimits.Value == true &&
+ taikoMod?.DrainRate.Value == setting_change &&
+ taikoMod?.OverallDifficulty.Value == setting_change;
+ });
+
+ AddAssert("non-shared settings at default", () =>
+ {
+ var taikoMod = getMod() as TaikoModDifficultyAdjust;
+ if (taikoMod == null)
+ return false;
+
+ return taikoMod.ScrollSpeed.Value == taikoMod.ScrollSpeed.Default;
+ });
+
+ ModDifficultyAdjust getMod() => (SelectedMods.Value.Single() as ModDifficultyAdjust)!;
+ }
+
[Test]
public void TestExternallySetCustomizedMod()
{
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index cf58d07b9e..9644e3fc75 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -393,8 +393,11 @@ namespace osu.Game
Ruleset.BindValueChanged(onRulesetChanged);
Beatmap.BindValueChanged(onBeatmapChanged);
+ SelectedMods.BindValueChanged(change => Logger.Log($"{modSetting(change.OldValue)} => {modSetting(change.NewValue)}"));
}
+ private object modSetting(IReadOnlyList mods) => mods.OfType().FirstOrDefault()?.GetSettingsSourceProperties().First().Item2.GetValue(mods.OfType().First())!;
+
private void addFilesWarning()
{
var realmStore = new RealmFileStore(realm, Storage);
@@ -626,7 +629,8 @@ namespace osu.Game
return;
}
- var previouslySelectedMods = SelectedMods.Value.ToArray();
+ //for some reason emptying SelectedMods resets all SettingSources Bindables to default value
+ var previouslySelectedMods = SelectedMods.Value.Select(mod => mod.DeepClone()).ToArray();
if (!SelectedMods.Disabled)
SelectedMods.Value = Array.Empty();
@@ -634,7 +638,16 @@ namespace osu.Game
AvailableMods.Value = dict;
if (!SelectedMods.Disabled)
- SelectedMods.Value = previouslySelectedMods.Select(m => instance.CreateModFromAcronym(m.Acronym)).Where(m => m != null).ToArray();
+ {
+ SelectedMods.Value = previouslySelectedMods.Select(oldMod =>
+ {
+ Mod newMod = instance.CreateModFromAcronym(oldMod.Acronym);
+
+ newMod?.CopyFromSimilar(oldMod);
+
+ return newMod;
+ }).Where(m => m != null).ToArray();
+ }
void revertRulesetChange() => Ruleset.Value = r.OldValue?.Available == true ? r.OldValue : RulesetStore.AvailableRulesets.First();
}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index 04d55bc5fe..7cb647d223 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Configuration;
+using osu.Game.Extensions;
using osu.Game.Rulesets.UI;
using osu.Game.Utils;
@@ -139,6 +140,30 @@ namespace osu.Game.Rulesets.Mods
return result;
}
+ ///
+ /// Copies all shared mod setting values from into this instance.
+ ///
+ /// The mod to copy properties from.
+ public void CopyFromSimilar(Mod source)
+ {
+ Dictionary oldSettings = new Dictionary();
+
+ foreach (var (_, property) in source.GetSettingsSourceProperties())
+ {
+ oldSettings.Add(property.Name.ToSnakeCase(), property.GetValue(source)!);
+ }
+
+ foreach (var (_, property) in this.GetSettingsSourceProperties())
+ {
+ var targetBindable = (IBindable)property.GetValue(this)!;
+
+ if (!oldSettings.TryGetValue(property.Name.ToSnakeCase(), out object? sourceSetting))
+ continue;
+
+ CopyAdjustedSetting(targetBindable, sourceSetting);
+ }
+ }
+
///
/// Copies mod setting values from into this instance, overwriting all existing settings.
///
@@ -148,10 +173,10 @@ namespace osu.Game.Rulesets.Mods
if (source.GetType() != GetType())
throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source));
- foreach (var (_, prop) in this.GetSettingsSourceProperties())
+ foreach (var (_, property) in this.GetSettingsSourceProperties())
{
- var targetBindable = (IBindable)prop.GetValue(this)!;
- var sourceBindable = (IBindable)prop.GetValue(source)!;
+ var targetBindable = (IBindable)property.GetValue(this)!;
+ var sourceBindable = (IBindable)property.GetValue(source)!;
CopyAdjustedSetting(targetBindable, sourceBindable);
}
From dd53a700711dd79fac8466fca94ea881db5fa537 Mon Sep 17 00:00:00 2001
From: Terochi
Date: Tue, 21 Feb 2023 20:59:36 +0100
Subject: [PATCH 0086/1604] Addressed change requests
---
.../TestSceneModSelectOverlay.cs | 4 +-
osu.Game/OsuGameBase.cs | 8 +---
osu.Game/Rulesets/Mods/Mod.cs | 40 +++++++++----------
3 files changed, 24 insertions(+), 28 deletions(-)
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 25edcd4e0a..7a46876737 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -403,8 +403,8 @@ namespace osu.Game.Tests.Visual.UserInterface
var taikoMod = getMod() as TaikoModDifficultyAdjust;
return
taikoMod?.ExtendedLimits.Value == true &&
- taikoMod?.DrainRate.Value == setting_change &&
- taikoMod?.OverallDifficulty.Value == setting_change;
+ taikoMod.DrainRate.Value == setting_change &&
+ taikoMod.OverallDifficulty.Value == setting_change;
});
AddAssert("non-shared settings at default", () =>
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 9644e3fc75..af2fb123d3 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -58,7 +58,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Skinning;
using osu.Game.Utils;
-using File = System.IO.File;
using RuntimeInfo = osu.Framework.RuntimeInfo;
namespace osu.Game
@@ -393,11 +392,8 @@ namespace osu.Game
Ruleset.BindValueChanged(onRulesetChanged);
Beatmap.BindValueChanged(onBeatmapChanged);
- SelectedMods.BindValueChanged(change => Logger.Log($"{modSetting(change.OldValue)} => {modSetting(change.NewValue)}"));
}
- private object modSetting(IReadOnlyList mods) => mods.OfType().FirstOrDefault()?.GetSettingsSourceProperties().First().Item2.GetValue(mods.OfType().First())!;
-
private void addFilesWarning()
{
var realmStore = new RealmFileStore(realm, Storage);
@@ -629,7 +625,7 @@ namespace osu.Game
return;
}
- //for some reason emptying SelectedMods resets all SettingSources Bindables to default value
+ //DeepCloning collection, because emptying SelectedMods resets all SettingSources Bindables to their default value
var previouslySelectedMods = SelectedMods.Value.Select(mod => mod.DeepClone()).ToArray();
if (!SelectedMods.Disabled)
@@ -643,7 +639,7 @@ namespace osu.Game
{
Mod newMod = instance.CreateModFromAcronym(oldMod.Acronym);
- newMod?.CopyFromSimilar(oldMod);
+ newMod?.CopySharedSettings(oldMod);
return newMod;
}).Where(m => m != null).ToArray();
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index 7cb647d223..bc588a5cd8 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -141,10 +141,28 @@ namespace osu.Game.Rulesets.Mods
}
///
- /// Copies all shared mod setting values from into this instance.
+ /// Copies mod setting values from into this instance, overwriting all existing settings.
///
/// The mod to copy properties from.
- public void CopyFromSimilar(Mod source)
+ public void CopyFrom(Mod source)
+ {
+ if (source.GetType() != GetType())
+ throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source));
+
+ foreach (var (_, property) in this.GetSettingsSourceProperties())
+ {
+ var targetBindable = (IBindable)property.GetValue(this)!;
+ var sourceBindable = (IBindable)property.GetValue(source)!;
+
+ CopyAdjustedSetting(targetBindable, sourceBindable);
+ }
+ }
+
+ ///
+ /// Copies all mod setting values sharing same from into this instance.
+ ///
+ /// The mod to copy properties from.
+ internal void CopySharedSettings(Mod source)
{
Dictionary oldSettings = new Dictionary();
@@ -164,24 +182,6 @@ namespace osu.Game.Rulesets.Mods
}
}
- ///
- /// Copies mod setting values from into this instance, overwriting all existing settings.
- ///
- /// The mod to copy properties from.
- public void CopyFrom(Mod source)
- {
- if (source.GetType() != GetType())
- throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source));
-
- foreach (var (_, property) in this.GetSettingsSourceProperties())
- {
- var targetBindable = (IBindable)property.GetValue(this)!;
- var sourceBindable = (IBindable)property.GetValue(source)!;
-
- CopyAdjustedSetting(targetBindable, sourceBindable);
- }
- }
-
///
/// When creating copies or clones of a Mod, this method will be called
/// to copy explicitly adjusted user settings from .
From 82b07d19f86374a24128ee4e8ff7918bf1aa7acf Mon Sep 17 00:00:00 2001
From: Terochi
Date: Tue, 21 Feb 2023 21:48:11 +0100
Subject: [PATCH 0087/1604] Fix of incorrect `using` optimization
---
osu.Game/OsuGameBase.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index af2fb123d3..e16aaf39ee 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -58,6 +58,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Skinning;
using osu.Game.Utils;
+using File = System.IO.File;
using RuntimeInfo = osu.Framework.RuntimeInfo;
namespace osu.Game
From e321536acc2299c19b131b457838c84f15c1aed3 Mon Sep 17 00:00:00 2001
From: Terochi
Date: Wed, 22 Feb 2023 07:48:43 +0100
Subject: [PATCH 0088/1604] Small clean up
---
osu.Game/OsuGameBase.cs | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index e16aaf39ee..de1f2e810c 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -626,17 +626,12 @@ namespace osu.Game
return;
}
- //DeepCloning collection, because emptying SelectedMods resets all SettingSources Bindables to their default value
- var previouslySelectedMods = SelectedMods.Value.Select(mod => mod.DeepClone()).ToArray();
-
- if (!SelectedMods.Disabled)
- SelectedMods.Value = Array.Empty();
-
AvailableMods.Value = dict;
if (!SelectedMods.Disabled)
{
- SelectedMods.Value = previouslySelectedMods.Select(oldMod =>
+ //converting mods from one ruleset to the other, while also keeping their shared settings unchanged
+ SelectedMods.Value = SelectedMods.Value.Select(oldMod =>
{
Mod newMod = instance.CreateModFromAcronym(oldMod.Acronym);
From 6e48860c79646d0f60e86e7e010954773d300730 Mon Sep 17 00:00:00 2001
From: Dean Herbert
Date: Wed, 22 Feb 2023 17:13:55 +0900
Subject: [PATCH 0089/1604] Update in line with framework menu handling changes
---
osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
index ad02e3b2ab..0adff11342 100644
--- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
+++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs
@@ -77,10 +77,12 @@ namespace osu.Game.Graphics.UserInterface
private void updateState()
{
- hoverClickSounds.Enabled.Value = !Item.Action.Disabled;
- Alpha = Item.Action.Disabled ? 0.2f : 1;
+ bool enabledState = IsActionable || HasSubmenu;
- if (IsHovered && !Item.Action.Disabled)
+ hoverClickSounds.Enabled.Value = enabledState;
+ Alpha = enabledState ? 1 : 0.2f;
+
+ if (IsHovered && enabledState)
{
text.BoldText.FadeIn(transition_length, Easing.OutQuint);
text.NormalText.FadeOut(transition_length, Easing.OutQuint);
From ba345e559131e2c7c9dadc1237346c45883119e8 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 23 Feb 2023 20:10:50 +0900
Subject: [PATCH 0090/1604] delete notify post when duplicate export
---
osu.Game/Database/LegacyModelExporter.cs | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 96104a68a0..c826b37689 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Database
/// If specified CancellationToken, then use it. Otherwise use PostNotification's CancellationToken.
///
///
- public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
+ public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
// check if the model is being exporting already
if (!exportingModels.Contains(model))
@@ -69,10 +69,7 @@ namespace osu.Game.Database
}
else
{
- PostNotification?.Invoke(new SimpleErrorNotification
- {
- Text = "File is being exported"
- });
+ // model is being exported
return;
}
@@ -114,6 +111,8 @@ namespace osu.Game.Database
exportingModels.Remove(model);
}
+
+ return success;
}
///
From 9e1eb50d9bf65390dcceec2835dc0e3d41e7083d Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 23 Feb 2023 20:20:54 +0900
Subject: [PATCH 0091/1604] use log
---
osu.Game/Database/LegacyArchiveExporter.cs | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs
index e5215e13a6..1af8c454c1 100644
--- a/osu.Game/Database/LegacyArchiveExporter.cs
+++ b/osu.Game/Database/LegacyArchiveExporter.cs
@@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Linq;
using System.Threading;
+using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Extensions;
using osu.Game.Overlays.Notifications;
@@ -12,6 +13,7 @@ using Realms;
using SharpCompress.Common;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
+using Logger = osu.Framework.Logging.Logger;
namespace osu.Game.Database
{
@@ -54,10 +56,7 @@ namespace osu.Game.Database
// Only pop up once to prevent spam.
if (!fileMissing)
{
- PostNotification?.Invoke(new SimpleErrorNotification
- {
- Text = "Some of your files are missing, they will not be included in the archive"
- });
+ Logger.Log("Some of model files are missing, they will not be included in the archive", LoggingTarget.Database, LogLevel.Error);
fileMissing = true;
}
}
From 60bdae41b62a36ed48eb4b2a0978518d5c6a987e Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 23 Feb 2023 22:17:13 +0900
Subject: [PATCH 0092/1604] make static
---
osu.Game/Database/LegacyModelExporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index c826b37689..635af14e76 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Database
public Action? PostNotification { get; set; }
// Store the model being exporting.
- private readonly List exportingModels = new List();
+ private static readonly List exportingModels = new List();
///
/// Construct exporter.
From 78201c464905b418379ce66013c19a04ef036946 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 23 Feb 2023 22:17:35 +0900
Subject: [PATCH 0093/1604] log to database
---
osu.Game/Database/LegacyModelExporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 635af14e76..f14cae62e1 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -140,7 +140,7 @@ namespace osu.Game.Database
if (t.IsFaulted)
{
notify.State = ProgressNotificationState.Cancelled;
- Logger.Error(t.Exception, "An error occurred while exporting");
+ Logger.Error(t.Exception, "An error occurred while exporting", LoggingTarget.Database);
return false;
}
From 09e7c21b23a71c8df297c4037af11f1d45046052 Mon Sep 17 00:00:00 2001
From: Terochi
Date: Fri, 24 Feb 2023 15:11:22 +0100
Subject: [PATCH 0094/1604] Implemented a more complex setting conversion logic
+ tests
---
osu.Game.Tests/Mods/ModSettingsTest.cs | 42 ++++++++++++
.../TestSceneModSelectOverlay.cs | 67 +++++++++++++++----
osu.Game/Rulesets/Mods/Mod.cs | 38 ++++++++++-
3 files changed, 133 insertions(+), 14 deletions(-)
diff --git a/osu.Game.Tests/Mods/ModSettingsTest.cs b/osu.Game.Tests/Mods/ModSettingsTest.cs
index b9ea1f2567..99198f6bae 100644
--- a/osu.Game.Tests/Mods/ModSettingsTest.cs
+++ b/osu.Game.Tests/Mods/ModSettingsTest.cs
@@ -2,6 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
+using osu.Framework.Bindables;
+using osu.Framework.Localisation;
+using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
@@ -32,5 +35,44 @@ namespace osu.Game.Tests.Mods
Assert.That(((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value, Is.EqualTo(2.0));
Assert.That(((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value, Is.EqualTo(1.5));
}
+
+ [Test]
+ public void TestCopySharedSettingsOfDifferentType()
+ {
+ const double setting_change = 2.5;
+
+ var osuMod = new TestNonMatchinSettingTypeOsuMod();
+ var maniaMod = new TestNonMatchinSettingTypeManiaMod();
+
+ osuMod.TestSetting.Value = setting_change;
+ maniaMod.CopySharedSettings(osuMod);
+ osuMod.CopySharedSettings(maniaMod);
+
+ Assert.That(maniaMod.TestSetting.IsDefault, "Value has been changed");
+ Assert.That(osuMod.TestSetting.Value == setting_change);
+ }
+
+ private class TestNonMatchinSettingTypeOsuMod : TestNonMatchinSettingTypeMod
+ {
+ public override string Acronym => "NMO";
+ public override BindableNumber TestSetting { get; } = new BindableDouble(3.5);
+ }
+
+ private class TestNonMatchinSettingTypeManiaMod : TestNonMatchinSettingTypeMod
+ {
+ public override string Acronym => "NMM";
+ public override Bindable TestSetting { get; } = new BindableBool(true);
+ }
+
+ private abstract class TestNonMatchinSettingTypeMod : Mod
+ {
+ public override string Name => "Non-matching setting type mod";
+ public override LocalisableString Description => "Description";
+ public override double ScoreMultiplier => 1;
+ public override ModType Type => ModType.Conversion;
+
+ [SettingSource("Test setting")]
+ public abstract IBindable TestSetting { get; }
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index 7a46876737..a354a22fb7 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -18,6 +18,7 @@ using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch.Mods;
+using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
@@ -387,7 +388,8 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("change mod settings", () =>
{
- var osuMod = (getMod() as OsuModDifficultyAdjust)!;
+ var osuMod = getMod();
+
osuMod.ExtendedLimits.Value = true;
osuMod.CircleSize.Value = setting_change;
osuMod.DrainRate.Value = setting_change;
@@ -400,23 +402,60 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("shared settings didn't change", () =>
{
- var taikoMod = getMod() as TaikoModDifficultyAdjust;
- return
- taikoMod?.ExtendedLimits.Value == true &&
- taikoMod.DrainRate.Value == setting_change &&
- taikoMod.OverallDifficulty.Value == setting_change;
+ var taikoMod = getMod();
+
+ return taikoMod.ExtendedLimits.Value &&
+ taikoMod.DrainRate.Value == setting_change &&
+ taikoMod.OverallDifficulty.Value == setting_change;
});
- AddAssert("non-shared settings at default", () =>
+ AddAssert("non-shared settings unchanged", () =>
{
- var taikoMod = getMod() as TaikoModDifficultyAdjust;
- if (taikoMod == null)
- return false;
+ var taikoMod = getMod();
- return taikoMod.ScrollSpeed.Value == taikoMod.ScrollSpeed.Default;
+ return taikoMod.ScrollSpeed.IsDefault;
+ });
+ }
+
+ [Test]
+ public void TestKeepSharedSettingsRatio()
+ {
+ const float setting_change = 1.8f;
+
+ createScreen();
+ changeRuleset(0);
+
+ AddStep("select flashlight mod", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateMod()! });
+
+ changeRuleset(0);
+ AddAssert("ensure mod still selected", () => SelectedMods.Value.SingleOrDefault() is OsuModFlashlight);
+
+ AddStep("change mod settings", () =>
+ {
+ var osuMod = getMod();
+
+ // range <0.5 - 2>
+ osuMod.SizeMultiplier.Value = setting_change;
});
- ModDifficultyAdjust getMod() => (SelectedMods.Value.Single() as ModDifficultyAdjust)!;
+ changeRuleset(3);
+ AddAssert("mania variant selected", () => SelectedMods.Value.SingleOrDefault() is ManiaModFlashlight);
+
+ AddAssert("shared settings changed to closest ratio", () =>
+ {
+ var maniaMod = getMod();
+
+ // range <0.5 - 3>
+ // converted value based on ratio = (setting_change - 0.5) / (2 - 0.5) * (3 - 0.5) + 0.5 = 2.66
+ return maniaMod.SizeMultiplier.Value == 2.7f; // taking precision into account
+ });
+
+ AddAssert("other settings unchanged", () =>
+ {
+ var maniaMod = getMod();
+
+ return maniaMod.ComboBasedSize.IsDefault;
+ });
}
[Test]
@@ -514,6 +553,8 @@ namespace osu.Game.Tests.Visual.UserInterface
waitForColumnLoad();
AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value);
+
+ AddStep("disable panel filtering", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value = false);
}
[Test]
@@ -629,6 +670,8 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => modSelectOverlay.CustomisationButton.AsNonNull().Active.Value == active);
}
+ private T getMod() where T : Mod => (T)SelectedMods.Value.Single();
+
private ModPanel getPanelForMod(Type modType)
=> modSelectOverlay.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType);
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index bc588a5cd8..f6c0e851fd 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using AutoMapper.Internal;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions;
@@ -173,13 +174,46 @@ namespace osu.Game.Rulesets.Mods
foreach (var (_, property) in this.GetSettingsSourceProperties())
{
- var targetBindable = (IBindable)property.GetValue(this)!;
+ object targetSetting = property.GetValue(this)!;
if (!oldSettings.TryGetValue(property.Name.ToSnakeCase(), out object? sourceSetting))
continue;
- CopyAdjustedSetting(targetBindable, sourceSetting);
+ if (((IBindable)sourceSetting).IsDefault)
+ // keep at default value if the source is default
+ continue;
+
+ Type? targetType = getGenericBaseType(targetSetting, typeof(BindableNumber<>));
+ Type? sourceType = getGenericBaseType(sourceSetting, typeof(BindableNumber<>));
+
+ if (targetType == null || sourceType == null)
+ {
+ if (getGenericBaseType(targetSetting, typeof(Bindable<>))!.GenericTypeArguments.Single() ==
+ getGenericBaseType(sourceSetting, typeof(Bindable<>))!.GenericTypeArguments.Single())
+ // change settings only if the type is the same
+ CopyAdjustedSetting((IBindable)targetSetting, sourceSetting);
+
+ continue;
+ }
+
+ double targetMin = getValue(targetSetting, nameof(IBindableNumber.MinValue));
+ double targetMax = getValue(targetSetting, nameof(IBindableNumber.MaxValue));
+ double sourceMin = getValue(sourceSetting, nameof(IBindableNumber.MinValue));
+ double sourceMax = getValue(sourceSetting, nameof(IBindableNumber.MaxValue));
+ double sourceValue = getValue(sourceSetting, nameof(IBindableNumber.Value));
+
+ // convert value to same ratio
+ double targetValue = (sourceValue - sourceMin) / (sourceMax - sourceMin) * (targetMax - targetMin) + targetMin;
+
+ targetType.GetProperty(nameof(IBindableNumber.Value))!.SetValue(targetSetting,
+ Convert.ChangeType(targetValue, targetType.GenericTypeArguments.Single()));
}
+
+ double getValue(object target, string name) =>
+ Convert.ToDouble(target.GetType().GetProperty(name)!.GetValue(target)!);
+
+ Type? getGenericBaseType(object target, Type genericType) =>
+ target.GetType().GetTypeInheritance().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType);
}
///
From a20e2685bee85872cadd858036f6938fb5375f71 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sat, 25 Feb 2023 20:11:08 +0900
Subject: [PATCH 0095/1604] make static name fix
---
osu.Game/Database/LegacyModelExporter.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index f14cae62e1..4ac48aaa09 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Database
public Action? PostNotification { get; set; }
// Store the model being exporting.
- private static readonly List exportingModels = new List();
+ private static readonly List exporting_models = new List();
///
/// Construct exporter.
@@ -63,14 +63,14 @@ namespace osu.Game.Database
public async Task ExportAsync(TModel model, CancellationToken? cancellationToken = null)
{
// check if the model is being exporting already
- if (!exportingModels.Contains(model))
+ if (!exporting_models.Contains(model))
{
- exportingModels.Add(model);
+ exporting_models.Add(model);
}
else
{
// model is being exported
- return;
+ return false;
}
string itemFilename = GetFilename(model).GetValidFilename();
@@ -109,7 +109,7 @@ namespace osu.Game.Database
exportStorage.Delete(filename);
}
- exportingModels.Remove(model);
+ exporting_models.Remove(model);
}
return success;
From fdf95446882f67ab27621a70ce05095b8ee25216 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 26 Feb 2023 15:28:10 +0900
Subject: [PATCH 0096/1604] cancel handle
---
osu.Game/Database/LegacyModelExporter.cs | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 4ac48aaa09..b47e0a9913 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -137,6 +137,12 @@ namespace osu.Game.Database
});
}, cancellationToken).ContinueWith(t =>
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ notify.State = ProgressNotificationState.Cancelled;
+ return false;
+ }
+
if (t.IsFaulted)
{
notify.State = ProgressNotificationState.Cancelled;
@@ -144,10 +150,8 @@ namespace osu.Game.Database
return false;
}
- notify.CompletionText = "Export Complete, Click to open the folder";
- notify.State = ProgressNotificationState.Completed;
return true;
- }, cancellationToken);
+ }, CancellationToken.None);
}
///
From e8092bff4686eb4491c4751cb5d927f4f74a1515 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 26 Feb 2023 15:28:24 +0900
Subject: [PATCH 0097/1604] logic fix?
---
osu.Game/Database/LegacyModelExporter.cs | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index b47e0a9913..06e4497e81 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -87,7 +87,6 @@ namespace osu.Game.Database
Text = "Exporting...",
CompletionText = "Export completed"
};
- notification.CompletionClickAction += () => exportStorage.PresentFileExternally(filename);
PostNotification?.Invoke(notification);
try
@@ -101,16 +100,20 @@ namespace osu.Game.Database
{
success = false;
}
- finally
- {
- // cleanup if export is failed or canceled.
- if (!success)
- {
- exportStorage.Delete(filename);
- }
- exporting_models.Remove(model);
+ // cleanup if export is failed or canceled.
+ if (!success)
+ {
+ exportStorage.Delete(filename);
}
+ else
+ {
+ notification.CompletionText = "Export Complete, Click to open the folder";
+ notification.CompletionClickAction = () => exportStorage.PresentFileExternally(filename);
+ notification.State = ProgressNotificationState.Completed;
+ }
+
+ exporting_models.Remove(model);
return success;
}
From 1d5c87039eb42bf4fb342e8ced0e5fbcba61d535 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Sun, 26 Feb 2023 15:28:43 +0900
Subject: [PATCH 0098/1604] typo
---
osu.Game/Database/LegacyModelExporter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Database/LegacyModelExporter.cs b/osu.Game/Database/LegacyModelExporter.cs
index 06e4497e81..6f2fe90957 100644
--- a/osu.Game/Database/LegacyModelExporter.cs
+++ b/osu.Game/Database/LegacyModelExporter.cs
@@ -121,7 +121,7 @@ namespace osu.Game.Database
///
/// Export model to stream.
///
- /// The medel which have .
+ /// The model which have .
/// The stream to export.
/// The notification will displayed to the user
/// The Cancellation token that can cancel the exporting.
From 4858d3fd4282e0f2ea48f3a5f1138f341d00edc6 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Tue, 7 Mar 2023 02:00:40 +0900
Subject: [PATCH 0099/1604] Added ability to edit mod presets
---
.../UserInterface/TestSceneModPresetColumn.cs | 171 +++++++++++++++++-
osu.Game/Overlays/Mods/EditPresetPopover.cs | 140 ++++++++++++++
osu.Game/Overlays/Mods/ModPresetPanel.cs | 8 +-
3 files changed, 316 insertions(+), 3 deletions(-)
create mode 100644 osu.Game/Overlays/Mods/EditPresetPopover.cs
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
index 1090764788..f48c52ad40 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs
@@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddUntilStep("wait for context menu", () => this.ChildrenOfType().Any());
AddStep("click delete", () =>
{
- var deleteItem = this.ChildrenOfType().Single();
+ var deleteItem = this.ChildrenOfType().ElementAt(1);
InputManager.MoveMouseTo(deleteItem);
InputManager.Click(MouseButton.Left);
});
@@ -261,6 +261,175 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("preset soft-deleted", () => Realm.Run(r => r.All().Count(preset => preset.DeletePending) == 1));
}
+ [Test]
+ public void TestEditPresentName()
+ {
+ ModPresetColumn modPresetColumn = null!;
+ string presetName = null!;
+
+ AddStep("clear mods", () => SelectedMods.Value = Array.Empty());
+ AddStep("create content", () => Child = modPresetColumn = new ModPresetColumn
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+
+ AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded);
+ AddStep("right click first panel", () =>
+ {
+ var panel = this.ChildrenOfType().First();
+ presetName = panel.Preset.Value.Name;
+ InputManager.MoveMouseTo(panel);
+ InputManager.Click(MouseButton.Right);
+ });
+
+ AddUntilStep("wait for context menu", () => this.ChildrenOfType().Any());
+ AddStep("click edit", () =>
+ {
+ var editItem = this.ChildrenOfType().ElementAt(0);
+ InputManager.MoveMouseTo(editItem);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ OsuPopover? popover = null;
+ AddUntilStep("wait for popover", () => (popover = this.ChildrenOfType().FirstOrDefault()) != null);
+ AddStep("clear preset name", () => popover.ChildrenOfType().First().Current.Value = "");
+ AddStep("attempt preset edit", () =>
+ {
+ InputManager.MoveMouseTo(popover.ChildrenOfType().Single());
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddWaitStep("wait some", 3);
+ AddAssert("present is not changed", () => this.ChildrenOfType().First().Preset.Value.Name == presetName);
+ AddUntilStep("popover is unchanged", () => this.ChildrenOfType