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 001/862] 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 002/862] 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 003/862] 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 004/862] 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 005/862] 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 006/862] 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 007/862] 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 008/862] 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 009/862] 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 010/862] 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 011/862] 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 012/862] 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 013/862] 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 014/862] 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 015/862] 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 016/862] 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 017/862] 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 018/862] 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 019/862] 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 020/862] 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 021/862] 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 022/862] 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 023/862] 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 024/862] 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 025/862] 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 026/862] 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 027/862] 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 028/862] `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 029/862] 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 030/862] 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 031/862] 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 032/862] 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 033/862] 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 034/862] 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 035/862] 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 036/862] 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 037/862] 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 038/862] 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 039/862] 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 040/862] 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 041/862] 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 042/862] 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 043/862] 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 044/862] 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 045/862] 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 046/862] 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 7c81f1e75bb40981a55f16a3544d9521280bf49c Mon Sep 17 00:00:00 2001
From: mk56-spn
Date: Fri, 27 Jan 2023 11:21:11 +0100
Subject: [PATCH 047/862] 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 048/862] 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 049/862] 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 050/862] 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 051/862] 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 052/862] 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 053/862] 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